diff options
Diffstat (limited to 'indra/newview/llviewerjoystick.cpp')
-rw-r--r-- | indra/newview/llviewerjoystick.cpp | 3202 |
1 files changed, 1601 insertions, 1601 deletions
diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index a1d5f4c53e..ce6dfa4ad1 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -1,1601 +1,1601 @@ -/**
- * @file llviewerjoystick.cpp
- * @brief Joystick / NDOF device functionality.
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llviewerjoystick.h"
-
-#include "llviewercontrol.h"
-#include "llviewerwindow.h"
-#include "llviewercamera.h"
-#include "llappviewer.h"
-#include "llkeyboard.h"
-#include "lltoolmgr.h"
-#include "llselectmgr.h"
-#include "llviewermenu.h"
-#include "llviewerwindow.h"
-#include "llwindow.h"
-#include "llagent.h"
-#include "llagentcamera.h"
-#include "llfocusmgr.h"
-
-#if LL_WINDOWS && !LL_MESA_HEADLESS
-// Require DirectInput version 8
-#define DIRECTINPUT_VERSION 0x0800
-
-#include <dinput.h>
-#endif
-
-
-// ----------------------------------------------------------------------------
-// Constants
-
-#define X_I 1
-#define Y_I 2
-#define Z_I 0
-#define RX_I 4
-#define RY_I 5
-#define RZ_I 3
-
-F32 LLViewerJoystick::sLastDelta[] = {0,0,0,0,0,0,0};
-F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0};
-
-// These constants specify the maximum absolute value coming in from the device.
-// HACK ALERT! the value of MAX_JOYSTICK_INPUT_VALUE is not arbitrary as it
-// should be. It has to be equal to 3000 because the SpaceNavigator on Windows
-// refuses to respond to the DirectInput SetProperty call; it always returns
-// values in the [-3000, 3000] range.
-#define MAX_SPACENAVIGATOR_INPUT 3000.0f
-#define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT
-
-
-#if LIB_NDOF
-std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr)
-{
- if (! ptr)
- {
- return out << "nullptr";
- }
- out << "NDOF_Device{ ";
- out << "axes [";
- const char* delim = "";
- for (short axis = 0; axis < ptr->axes_count; ++axis)
- {
- out << delim << ptr->axes[axis];
- delim = ", ";
- }
- out << "]";
- out << ", buttons [";
- delim = "";
- for (short button = 0; button < ptr->btn_count; ++button)
- {
- out << delim << ptr->buttons[button];
- delim = ", ";
- }
- out << "]";
- out << ", range " << ptr->axes_min << ':' << ptr->axes_max;
- // If we don't coerce these to unsigned, they're streamed as characters,
- // e.g. ctrl-A or nul.
- out << ", absolute " << unsigned(ptr->absolute);
- out << ", valid " << unsigned(ptr->valid);
- out << ", manufacturer '" << ptr->manufacturer << "'";
- out << ", product '" << ptr->product << "'";
- out << ", private " << ptr->private_data;
- out << " }";
- return out;
-}
-#endif // LIB_NDOF
-
-
-#if LL_WINDOWS && !LL_MESA_HEADLESS
-// this should reflect ndof and set axises, see ndofdev_win.cpp from ndof package
-BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* inst, VOID* user_data)
-{
- if (inst->dwType & DIDFT_AXIS)
- {
- LPDIRECTINPUTDEVICE8 device = *((LPDIRECTINPUTDEVICE8 *)user_data);
- DIPROPRANGE diprg;
- diprg.diph.dwSize = sizeof(DIPROPRANGE);
- diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
- diprg.diph.dwHow = DIPH_BYID;
- diprg.diph.dwObj = inst->dwType; // specify the enumerated axis
-
- // Set the range for the axis
- diprg.lMin = (long)-MAX_JOYSTICK_INPUT_VALUE;
- diprg.lMax = (long)+MAX_JOYSTICK_INPUT_VALUE;
- HRESULT hr = device->SetProperty(DIPROP_RANGE, &diprg.diph);
-
- if (FAILED(hr))
- {
- return DIENUM_STOP;
- }
- }
-
- return DIENUM_CONTINUE;
-}
-
-BOOL CALLBACK di8_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef)
-{
- // Note: If a single device can function as more than one DirectInput
- // device type, it is enumerated as each device type that it supports.
- // Capable of detecting devices like Oculus Rift
- if (device_instance_ptr)
- {
- std::string product_name = utf16str_to_utf8str(llutf16string(device_instance_ptr->tszProductName));
-
- LLSD guid = LLViewerJoystick::getInstance()->getDeviceUUID();
-
- bool init_device = false;
- if (guid.isBinary())
- {
- std::vector<U8> bin_bucket = guid.asBinary();
- init_device = memcmp(&bin_bucket[0], &device_instance_ptr->guidInstance, sizeof(GUID)) == 0;
- }
- else
- {
- // It might be better to init space navigator here, but if system doesn't has one,
- // ndof will pick a random device, it is simpler to pick first device now to have an id
- init_device = true;
- }
-
- if (init_device)
- {
- LL_DEBUGS("Joystick") << "Found and attempting to use device: " << product_name << LL_ENDL;
- LPDIRECTINPUT8 di8_interface = *((LPDIRECTINPUT8 *)gViewerWindow->getWindow()->getDirectInput8());
- LPDIRECTINPUTDEVICE8 device = NULL;
-
- HRESULT status = di8_interface->CreateDevice(
- device_instance_ptr->guidInstance, // REFGUID rguid,
- &device, // LPDIRECTINPUTDEVICE * lplpDirectInputDevice,
- NULL // LPUNKNOWN pUnkOuter
- );
-
- if (status == DI_OK)
- {
- // prerequisite for aquire()
- LL_DEBUGS("Joystick") << "Device created" << LL_ENDL;
- status = device->SetDataFormat(&c_dfDIJoystick); // c_dfDIJoystick2
- }
-
- if (status == DI_OK)
- {
- // set properties
- LL_DEBUGS("Joystick") << "Format set" << LL_ENDL;
- status = device->EnumObjects(EnumObjectsCallback, &device, DIDFT_ALL);
- }
-
- if (status == DI_OK)
- {
- LL_DEBUGS("Joystick") << "Properties updated" << LL_ENDL;
-
- S32 size = sizeof(GUID);
- LLSD::Binary data; //just an std::vector
- data.resize(size);
- memcpy(&data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size);
- LLViewerJoystick::getInstance()->initDevice(&device, product_name, LLSD(data));
- return DIENUM_STOP;
- }
- }
- else
- {
- LL_DEBUGS("Joystick") << "Found device: " << product_name << LL_ENDL;
- }
- }
- return DIENUM_CONTINUE;
-}
-
-// Windows guids
-// This is GUID2 so teoretically it can be memcpy copied into LLUUID
-void guid_from_string(GUID &guid, const std::string &input)
-{
- CLSIDFromString(utf8str_to_utf16str(input).c_str(), &guid);
-}
-
-std::string string_from_guid(const GUID &guid)
-{
- OLECHAR* guidString; //wchat
- StringFromCLSID(guid, &guidString);
-
- // use guidString...
-
- std::string res = utf16str_to_utf8str(llutf16string(guidString));
- // ensure memory is freed
- ::CoTaskMemFree(guidString);
-
- return res;
-}
-#elif LL_DARWIN
-
-bool macos_devices_callback(std::string &product_name, LLSD &data, void* userdata)
-{
- std::string product = data["product"].asString();
-
- return LLViewerJoystick::getInstance()->initDevice(nullptr, product, data);
-}
-
-#endif
-
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::updateEnabled(bool autoenable)
-{
- if (mDriverState == JDS_UNINITIALIZED)
- {
- gSavedSettings.setBOOL("JoystickEnabled", false);
- }
- else
- {
- // autoenable if user specifically chose this device
- if (autoenable && (isLikeSpaceNavigator() || isDeviceUUIDSet()))
- {
- gSavedSettings.setBOOL("JoystickEnabled", true );
- }
- }
- if (!gSavedSettings.getBOOL("JoystickEnabled"))
- {
- mOverrideCamera = false;
- }
-}
-
-void LLViewerJoystick::setOverrideCamera(bool val)
-{
- if (!gSavedSettings.getBOOL("JoystickEnabled"))
- {
- mOverrideCamera = false;
- }
- else
- {
- mOverrideCamera = val;
- }
-
- if (mOverrideCamera)
- {
- gAgentCamera.changeCameraToDefault();
- }
-}
-
-// -----------------------------------------------------------------------------
-#if LIB_NDOF
-NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev)
-{
- NDOF_HotPlugResult res = NDOF_DISCARD_HOTPLUGGED;
- LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
- if (joystick->mDriverState == JDS_UNINITIALIZED)
- {
- LL_INFOS("Joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL;
- ndof_dump(stderr, dev);
- joystick->mNdofDev = dev;
- joystick->mDriverState = JDS_INITIALIZED;
- res = NDOF_KEEP_HOTPLUGGED;
- }
- joystick->updateEnabled(true);
- return res;
-}
-#endif
-
-// -----------------------------------------------------------------------------
-#if LIB_NDOF
-void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev)
-{
- LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
- if (joystick->mNdofDev == dev)
- {
- LL_INFOS("Joystick") << "HotPlugRemovalCallback: joystick->mNdofDev="
- << joystick->mNdofDev << "; removed device:" << LL_ENDL;
- ndof_dump(stderr, dev);
- joystick->mDriverState = JDS_UNINITIALIZED;
- }
- joystick->updateEnabled(true);
-}
-#endif
-
-// -----------------------------------------------------------------------------
-LLViewerJoystick::LLViewerJoystick()
-: mDriverState(JDS_UNINITIALIZED),
- mNdofDev(NULL),
- mResetFlag(false),
- mCameraUpdated(true),
- mOverrideCamera(false),
- mJoystickRun(0)
-{
- for (int i = 0; i < 6; i++)
- {
- mAxes[i] = sDelta[i] = sLastDelta[i] = 0.0f;
- }
-
- memset(mBtn, 0, sizeof(mBtn));
-
- // factor in bandwidth? bandwidth = gViewerStats->mKBitStat
- mPerfScale = 4000.f / gSysCPU.getMHz(); // hmm. why?
-
- mLastDeviceUUID = LLSD::Integer(1);
-}
-
-// -----------------------------------------------------------------------------
-LLViewerJoystick::~LLViewerJoystick()
-{
- if (mDriverState == JDS_INITIALIZED)
- {
- terminate();
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::init(bool autoenable)
-{
-#if LIB_NDOF
- static bool libinit = false;
- mDriverState = JDS_INITIALIZING;
-
- loadDeviceIdFromSettings();
-
- if (!libinit)
- {
- // Note: The HotPlug callbacks are not actually getting called on Windows
- if (ndof_libinit(HotPlugAddCallback,
- HotPlugRemovalCallback,
- gViewerWindow->getWindow()->getDirectInput8()))
- {
- mDriverState = JDS_UNINITIALIZED;
- }
- else
- {
- // NB: ndof_libinit succeeds when there's no device
- libinit = true;
-
- // allocate memory once for an eventual device
- mNdofDev = ndof_create();
- }
- }
-
- if (libinit)
- {
- if (mNdofDev)
- {
- U32 device_type = 0;
- void* win_callback = nullptr;
- std::function<bool(std::string&, LLSD&, void*)> osx_callback;
- // di8_devices_callback callback is immediate and happens in scope of getInputDevices()
-#if LL_WINDOWS && !LL_MESA_HEADLESS
- // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
- device_type = DI8DEVCLASS_GAMECTRL;
- win_callback = &di8_devices_callback;
-#elif LL_DARWIN
- osx_callback = macos_devices_callback;
-
- if (mLastDeviceUUID.isMap())
- {
- std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
- std::string product = mLastDeviceUUID["product"].asString();
-
- strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
- strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
-
- if (ndof_init_first(mNdofDev, nullptr))
- {
- mDriverState = JDS_INITIALIZING;
- // Saved device no longer exist
- // No device found
- LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
- }
- else
- {
- mDriverState = JDS_INITIALIZED;
- }
- }
-#endif
- if (mDriverState != JDS_INITIALIZED)
- {
- if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
- {
- LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL;
- // Failed to gather devices, init first suitable one
- mLastDeviceUUID = LLSD();
- void *preffered_device = NULL;
- initDevice(preffered_device);
- }
- }
-
- if (mDriverState == JDS_INITIALIZING)
- {
- LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL;
- mDriverState = JDS_UNINITIALIZED;
- }
- }
- else
- {
- mDriverState = JDS_UNINITIALIZED;
- }
- }
-
- // Autoenable the joystick for recognized devices if nothing was connected previously
- if (!autoenable)
- {
- autoenable = gSavedSettings.getString("JoystickInitialized").empty();
- }
- updateEnabled(autoenable);
-
- if (mDriverState == JDS_INITIALIZED)
- {
- // A Joystick device is plugged in
- if (isLikeSpaceNavigator())
- {
- // It's a space navigator, we have defaults for it.
- if (gSavedSettings.getString("JoystickInitialized") != "SpaceNavigator")
- {
- // Only set the defaults if we haven't already (in case they were overridden)
- setSNDefaults();
- gSavedSettings.setString("JoystickInitialized", "SpaceNavigator");
- }
- }
- else
- {
- // It's not a Space Navigator
- gSavedSettings.setString("JoystickInitialized", "UnknownDevice");
- }
- }
- else
- {
- // No device connected, don't change any settings
- }
-
- LL_INFOS("Joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev="
- << mNdofDev << "; libinit=" << libinit << LL_ENDL;
-#endif
-}
-
-void LLViewerJoystick::initDevice(LLSD &guid)
-{
-#if LIB_NDOF
- mLastDeviceUUID = guid;
- U32 device_type = 0;
- void* win_callback = nullptr;
- std::function<bool(std::string&, LLSD&, void*)> osx_callback;
- mDriverState = JDS_INITIALIZING;
-
-#if LL_WINDOWS && !LL_MESA_HEADLESS
- // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
- device_type = DI8DEVCLASS_GAMECTRL;
- win_callback = &di8_devices_callback;
-#elif LL_DARWIN
- osx_callback = macos_devices_callback;
- if (mLastDeviceUUID.isMap())
- {
- std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
- std::string product = mLastDeviceUUID["product"].asString();
-
- strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
- strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
-
- if (ndof_init_first(mNdofDev, nullptr))
- {
- mDriverState = JDS_INITIALIZING;
- // Saved device no longer exist
- // Np other device present
- LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
- }
- else
- {
- mDriverState = JDS_INITIALIZED;
- }
- }
-#endif
-
- if (mDriverState != JDS_INITIALIZED)
- {
- if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
- {
- LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL;
- // Failed to gather devices from window, init first suitable one
- void *preffered_device = NULL;
- mLastDeviceUUID = LLSD();
- initDevice(preffered_device);
- }
- }
-
- if (mDriverState == JDS_INITIALIZING)
- {
- LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL;
- mDriverState = JDS_UNINITIALIZED;
- }
-#endif
-}
-
-bool LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid)
-{
-#if LIB_NDOF
- mLastDeviceUUID = guid;
-
-#if LL_DARWIN
- if (guid.isMap())
- {
- std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
- std::string product = mLastDeviceUUID["product"].asString();
-
- strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
- strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
- }
- else
- {
- mNdofDev->product[0] = '\0';
- mNdofDev->manufacturer[0] = '\0';
- }
-#else
- strncpy(mNdofDev->product, name.c_str(), sizeof(mNdofDev->product));
- mNdofDev->manufacturer[0] = '\0';
-#endif
-
- return initDevice(preffered_device);
-#else
- return false;
-#endif
-}
-
-bool LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */)
-{
-#if LIB_NDOF
- // Different joysticks will return different ranges of raw values.
- // Since we want to handle every device in the same uniform way,
- // we initialize the mNdofDev struct and we set the range
- // of values we would like to receive.
- //
- // HACK: On Windows, libndofdev passes our range to DI with a
- // SetProperty call. This works but with one notable exception, the
- // SpaceNavigator, who doesn't seem to care about the SetProperty
- // call. In theory, we should handle this case inside libndofdev.
- // However, the range we're setting here is arbitrary anyway,
- // so let's just use the SpaceNavigator range for our purposes.
- mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE;
- mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE;
-
- // libndofdev could be used to return deltas. Here we choose to
- // just have the absolute values instead.
- mNdofDev->absolute = 1;
- // init & use the first suitable NDOF device found on the USB chain
- // On windows preffered_device needs to be a pointer to LPDIRECTINPUTDEVICE8
- if (ndof_init_first(mNdofDev, preffered_device))
- {
- mDriverState = JDS_UNINITIALIZED;
- LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
- }
- else
- {
- mDriverState = JDS_INITIALIZED;
- return true;
- }
-#endif
- return false;
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::terminate()
-{
-#if LIB_NDOF
- if (mNdofDev != NULL)
- {
- ndof_libcleanup(); // frees alocated memory in mNdofDev
- mDriverState = JDS_UNINITIALIZED;
- mNdofDev = NULL;
- LL_INFOS("Joystick") << "Terminated connection with NDOF device." << LL_ENDL;
- }
-#endif
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::updateStatus()
-{
-#if LIB_NDOF
-
- ndof_update(mNdofDev);
-
- for (int i=0; i<6; i++)
- {
- mAxes[i] = (F32) mNdofDev->axes[i] / mNdofDev->axes_max;
- }
-
- for (int i=0; i<16; i++)
- {
- mBtn[i] = mNdofDev->buttons[i];
- }
-
-#endif
-}
-
-// -----------------------------------------------------------------------------
-F32 LLViewerJoystick::getJoystickAxis(U32 axis) const
-{
- if (axis < 6)
- {
- return mAxes[axis];
- }
- return 0.f;
-}
-
-// -----------------------------------------------------------------------------
-U32 LLViewerJoystick::getJoystickButton(U32 button) const
-{
- if (button < 16)
- {
- return mBtn[button];
- }
- return 0;
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::handleRun(F32 inc)
-{
- // Decide whether to walk or run by applying a threshold, with slight
- // hysteresis to avoid oscillating between the two with input spikes.
- // Analog speed control would be better, but not likely any time soon.
- if (inc > gSavedSettings.getF32("JoystickRunThreshold"))
- {
- if (1 == mJoystickRun)
- {
- ++mJoystickRun;
- gAgent.setRunning();
- gAgent.sendWalkRun(gAgent.getRunning());
- }
- else if (0 == mJoystickRun)
- {
- // hysteresis - respond NEXT frame
- ++mJoystickRun;
- }
- }
- else
- {
- if (mJoystickRun > 0)
- {
- --mJoystickRun;
- if (0 == mJoystickRun)
- {
- gAgent.clearRunning();
- gAgent.sendWalkRun(gAgent.getRunning());
- }
- }
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::agentJump()
-{
- gAgent.moveUp(1);
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::agentSlide(F32 inc)
-{
- if (inc < 0.f)
- {
- gAgent.moveLeft(1);
- }
- else if (inc > 0.f)
- {
- gAgent.moveLeft(-1);
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::agentPush(F32 inc)
-{
- if (inc < 0.f) // forward
- {
- gAgent.moveAt(1, false);
- }
- else if (inc > 0.f) // backward
- {
- gAgent.moveAt(-1, false);
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::agentFly(F32 inc)
-{
- if (inc < 0.f)
- {
- if (! (gAgent.getFlying() ||
- !gAgent.canFly() ||
- gAgent.upGrabbed() ||
- !gSavedSettings.getBOOL("AutomaticFly")) )
- {
- gAgent.setFlying(true);
- }
- gAgent.moveUp(1);
- }
- else if (inc > 0.f)
- {
- // crouch
- gAgent.moveUp(-1);
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::agentPitch(F32 pitch_inc)
-{
- if (pitch_inc < 0)
- {
- gAgent.setControlFlags(AGENT_CONTROL_PITCH_POS);
- }
- else if (pitch_inc > 0)
- {
- gAgent.setControlFlags(AGENT_CONTROL_PITCH_NEG);
- }
-
- gAgent.pitch(-pitch_inc);
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::agentYaw(F32 yaw_inc)
-{
- // Cannot steer some vehicles in mouselook if the script grabs the controls
- if (gAgentCamera.cameraMouselook() && !gSavedSettings.getBOOL("JoystickMouselookYaw"))
- {
- gAgent.rotate(-yaw_inc, gAgent.getReferenceUpVector());
- }
- else
- {
- if (yaw_inc < 0)
- {
- gAgent.setControlFlags(AGENT_CONTROL_YAW_POS);
- }
- else if (yaw_inc > 0)
- {
- gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG);
- }
-
- gAgent.yaw(-yaw_inc);
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::resetDeltas(S32 axis[])
-{
- for (U32 i = 0; i < 6; i++)
- {
- sLastDelta[i] = -mAxes[axis[i]];
- sDelta[i] = 0.f;
- }
-
- sLastDelta[6] = sDelta[6] = 0.f;
- mResetFlag = false;
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::moveObjects(bool reset)
-{
- static bool toggle_send_to_sim = false;
-
- if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
- || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickBuildEnabled"))
- {
- return;
- }
-
- S32 axis[] =
- {
- gSavedSettings.getS32("JoystickAxis0"),
- gSavedSettings.getS32("JoystickAxis1"),
- gSavedSettings.getS32("JoystickAxis2"),
- gSavedSettings.getS32("JoystickAxis3"),
- gSavedSettings.getS32("JoystickAxis4"),
- gSavedSettings.getS32("JoystickAxis5"),
- };
-
- if (reset || mResetFlag)
- {
- resetDeltas(axis);
- return;
- }
-
- F32 axis_scale[] =
- {
- gSavedSettings.getF32("BuildAxisScale0"),
- gSavedSettings.getF32("BuildAxisScale1"),
- gSavedSettings.getF32("BuildAxisScale2"),
- gSavedSettings.getF32("BuildAxisScale3"),
- gSavedSettings.getF32("BuildAxisScale4"),
- gSavedSettings.getF32("BuildAxisScale5"),
- };
-
- F32 dead_zone[] =
- {
- gSavedSettings.getF32("BuildAxisDeadZone0"),
- gSavedSettings.getF32("BuildAxisDeadZone1"),
- gSavedSettings.getF32("BuildAxisDeadZone2"),
- gSavedSettings.getF32("BuildAxisDeadZone3"),
- gSavedSettings.getF32("BuildAxisDeadZone4"),
- gSavedSettings.getF32("BuildAxisDeadZone5"),
- };
-
- F32 cur_delta[6];
- F32 time = gFrameIntervalSeconds.value();
-
- // avoid making ridicously big movements if there's a big drop in fps
- if (time > .2f)
- {
- time = .2f;
- }
-
- // max feather is 32
- F32 feather = gSavedSettings.getF32("BuildFeathering");
- bool is_zero = true, absolute = gSavedSettings.getBOOL("Cursor3D");
-
- for (U32 i = 0; i < 6; i++)
- {
- cur_delta[i] = -mAxes[axis[i]];
- F32 tmp = cur_delta[i];
- if (absolute)
- {
- cur_delta[i] = cur_delta[i] - sLastDelta[i];
- }
- sLastDelta[i] = tmp;
- is_zero = is_zero && (cur_delta[i] == 0.f);
-
- if (cur_delta[i] > 0)
- {
- cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
- }
- else
- {
- cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
- }
- cur_delta[i] *= axis_scale[i];
-
- if (!absolute)
- {
- cur_delta[i] *= time;
- }
-
- sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
- }
-
- U32 upd_type = UPD_NONE;
- LLVector3 v;
-
- if (!is_zero)
- {
- // Clear AFK state if moved beyond the deadzone
- if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
- {
- gAgent.clearAFK();
- }
-
- if (sDelta[0] || sDelta[1] || sDelta[2])
- {
- upd_type |= UPD_POSITION;
- v.setVec(sDelta[0], sDelta[1], sDelta[2]);
- }
-
- if (sDelta[3] || sDelta[4] || sDelta[5])
- {
- upd_type |= UPD_ROTATION;
- }
-
- // the selection update could fail, so we won't send
- if (LLSelectMgr::getInstance()->selectionMove(v, sDelta[3],sDelta[4],sDelta[5], upd_type))
- {
- toggle_send_to_sim = true;
- }
- }
- else if (toggle_send_to_sim)
- {
- LLSelectMgr::getInstance()->sendSelectionMove();
- toggle_send_to_sim = false;
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::moveAvatar(bool reset)
-{
- if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
- || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickAvatarEnabled"))
- {
- return;
- }
-
- S32 axis[] =
- {
- // [1 0 2 4 3 5]
- // [Z X Y RZ RX RY]
- gSavedSettings.getS32("JoystickAxis0"),
- gSavedSettings.getS32("JoystickAxis1"),
- gSavedSettings.getS32("JoystickAxis2"),
- gSavedSettings.getS32("JoystickAxis3"),
- gSavedSettings.getS32("JoystickAxis4"),
- gSavedSettings.getS32("JoystickAxis5")
- };
-
- if (reset || mResetFlag)
- {
- resetDeltas(axis);
- if (reset)
- {
- // Note: moving the agent triggers agent camera mode;
- // don't do this every time we set mResetFlag (e.g. because we gained focus)
- gAgent.moveAt(0, true);
- }
- return;
- }
-
- bool is_zero = true;
- static bool button_held = false;
-
- if (mBtn[1] == 1)
- {
- // If AutomaticFly is enabled, then button1 merely causes a
- // jump (as the up/down axis already controls flying) if on the
- // ground, or cease flight if already flying.
- // If AutomaticFly is disabled, then button1 toggles flying.
- if (gSavedSettings.getBOOL("AutomaticFly"))
- {
- if (!gAgent.getFlying())
- {
- gAgent.moveUp(1);
- }
- else if (!button_held)
- {
- button_held = true;
- gAgent.setFlying(false);
- }
- }
- else if (!button_held)
- {
- button_held = true;
- gAgent.setFlying(!gAgent.getFlying());
- }
-
- is_zero = false;
- }
- else
- {
- button_held = false;
- }
-
- F32 axis_scale[] =
- {
- gSavedSettings.getF32("AvatarAxisScale0"),
- gSavedSettings.getF32("AvatarAxisScale1"),
- gSavedSettings.getF32("AvatarAxisScale2"),
- gSavedSettings.getF32("AvatarAxisScale3"),
- gSavedSettings.getF32("AvatarAxisScale4"),
- gSavedSettings.getF32("AvatarAxisScale5")
- };
-
- F32 dead_zone[] =
- {
- gSavedSettings.getF32("AvatarAxisDeadZone0"),
- gSavedSettings.getF32("AvatarAxisDeadZone1"),
- gSavedSettings.getF32("AvatarAxisDeadZone2"),
- gSavedSettings.getF32("AvatarAxisDeadZone3"),
- gSavedSettings.getF32("AvatarAxisDeadZone4"),
- gSavedSettings.getF32("AvatarAxisDeadZone5")
- };
-
- // time interval in seconds between this frame and the previous
- F32 time = gFrameIntervalSeconds.value();
-
- // avoid making ridicously big movements if there's a big drop in fps
- if (time > .2f)
- {
- time = .2f;
- }
-
- // note: max feather is 32.0
- F32 feather = gSavedSettings.getF32("AvatarFeathering");
-
- F32 cur_delta[6];
- F32 val, dom_mov = 0.f;
- U32 dom_axis = Z_I;
-#if LIB_NDOF
- bool absolute = (gSavedSettings.getBOOL("Cursor3D") && mNdofDev->absolute);
-#else
- bool absolute = false;
-#endif
- // remove dead zones and determine biggest movement on the joystick
- for (U32 i = 0; i < 6; i++)
- {
- cur_delta[i] = -mAxes[axis[i]];
- if (absolute)
- {
- F32 tmp = cur_delta[i];
- cur_delta[i] = cur_delta[i] - sLastDelta[i];
- sLastDelta[i] = tmp;
- }
-
- if (cur_delta[i] > 0)
- {
- cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
- }
- else
- {
- cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
- }
-
- // we don't care about Roll (RZ) and Z is calculated after the loop
- if (i != Z_I && i != RZ_I)
- {
- // find out the axis with the biggest joystick motion
- val = fabs(cur_delta[i]);
- if (val > dom_mov)
- {
- dom_axis = i;
- dom_mov = val;
- }
- }
-
- is_zero = is_zero && (cur_delta[i] == 0.f);
- }
-
- if (!is_zero)
- {
- // Clear AFK state if moved beyond the deadzone
- if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
- {
- gAgent.clearAFK();
- }
-
- setCameraNeedsUpdate(true);
- }
-
- // forward|backward movements overrule the real dominant movement if
- // they're bigger than its 20%. This is what you want 'cos moving forward
- // is what you do most. We also added a special (even more lenient) case
- // for RX|RY to allow walking while pitching and turning
- if (fabs(cur_delta[Z_I]) > .2f * dom_mov
- || ((dom_axis == RX_I || dom_axis == RY_I)
- && fabs(cur_delta[Z_I]) > .05f * dom_mov))
- {
- dom_axis = Z_I;
- }
-
- sDelta[X_I] = -cur_delta[X_I] * axis_scale[X_I];
- sDelta[Y_I] = -cur_delta[Y_I] * axis_scale[Y_I];
- sDelta[Z_I] = -cur_delta[Z_I] * axis_scale[Z_I];
- cur_delta[RX_I] *= -axis_scale[RX_I] * mPerfScale;
- cur_delta[RY_I] *= -axis_scale[RY_I] * mPerfScale;
-
- if (!absolute)
- {
- cur_delta[RX_I] *= time;
- cur_delta[RY_I] *= time;
- }
- sDelta[RX_I] += (cur_delta[RX_I] - sDelta[RX_I]) * time * feather;
- sDelta[RY_I] += (cur_delta[RY_I] - sDelta[RY_I]) * time * feather;
-
- handleRun((F32) sqrt(sDelta[Z_I]*sDelta[Z_I] + sDelta[X_I]*sDelta[X_I]));
-
- // Allow forward/backward movement some priority
- if (dom_axis == Z_I)
- {
- agentPush(sDelta[Z_I]); // forward/back
-
- if (fabs(sDelta[X_I]) > .1f)
- {
- agentSlide(sDelta[X_I]); // move sideways
- }
-
- if (fabs(sDelta[Y_I]) > .1f)
- {
- agentFly(sDelta[Y_I]); // up/down & crouch
- }
-
- // too many rotations during walking can be confusing, so apply
- // the deadzones one more time (quick & dirty), at 50%|30% power
- F32 eff_rx = .3f * dead_zone[RX_I];
- F32 eff_ry = .3f * dead_zone[RY_I];
-
- if (sDelta[RX_I] > 0)
- {
- eff_rx = llmax(sDelta[RX_I] - eff_rx, 0.f);
- }
- else
- {
- eff_rx = llmin(sDelta[RX_I] + eff_rx, 0.f);
- }
-
- if (sDelta[RY_I] > 0)
- {
- eff_ry = llmax(sDelta[RY_I] - eff_ry, 0.f);
- }
- else
- {
- eff_ry = llmin(sDelta[RY_I] + eff_ry, 0.f);
- }
-
-
- if (fabs(eff_rx) > 0.f || fabs(eff_ry) > 0.f)
- {
- if (gAgent.getFlying())
- {
- agentPitch(eff_rx);
- agentYaw(eff_ry);
- }
- else
- {
- agentPitch(eff_rx);
- agentYaw(2.f * eff_ry);
- }
- }
- }
- else
- {
- agentSlide(sDelta[X_I]); // move sideways
- agentFly(sDelta[Y_I]); // up/down & crouch
- agentPush(sDelta[Z_I]); // forward/back
- agentPitch(sDelta[RX_I]); // pitch
- agentYaw(sDelta[RY_I]); // turn
- }
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::moveFlycam(bool reset)
-{
- static LLQuaternion sFlycamRotation;
- static LLVector3 sFlycamPosition;
- static F32 sFlycamZoom;
-
- if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
- || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled"))
- {
- return;
- }
-
- S32 axis[] =
- {
- gSavedSettings.getS32("JoystickAxis0"),
- gSavedSettings.getS32("JoystickAxis1"),
- gSavedSettings.getS32("JoystickAxis2"),
- gSavedSettings.getS32("JoystickAxis3"),
- gSavedSettings.getS32("JoystickAxis4"),
- gSavedSettings.getS32("JoystickAxis5"),
- gSavedSettings.getS32("JoystickAxis6")
- };
-
- bool in_build_mode = LLToolMgr::getInstance()->inBuildMode();
- if (reset || mResetFlag)
- {
- sFlycamPosition = LLViewerCamera::getInstance()->getOrigin();
- sFlycamRotation = LLViewerCamera::getInstance()->getQuaternion();
- sFlycamZoom = LLViewerCamera::getInstance()->getView();
-
- resetDeltas(axis);
-
- return;
- }
-
- F32 axis_scale[] =
- {
- gSavedSettings.getF32("FlycamAxisScale0"),
- gSavedSettings.getF32("FlycamAxisScale1"),
- gSavedSettings.getF32("FlycamAxisScale2"),
- gSavedSettings.getF32("FlycamAxisScale3"),
- gSavedSettings.getF32("FlycamAxisScale4"),
- gSavedSettings.getF32("FlycamAxisScale5"),
- gSavedSettings.getF32("FlycamAxisScale6")
- };
-
- F32 dead_zone[] =
- {
- gSavedSettings.getF32("FlycamAxisDeadZone0"),
- gSavedSettings.getF32("FlycamAxisDeadZone1"),
- gSavedSettings.getF32("FlycamAxisDeadZone2"),
- gSavedSettings.getF32("FlycamAxisDeadZone3"),
- gSavedSettings.getF32("FlycamAxisDeadZone4"),
- gSavedSettings.getF32("FlycamAxisDeadZone5"),
- gSavedSettings.getF32("FlycamAxisDeadZone6")
- };
-
- F32 time = gFrameIntervalSeconds.value();
-
- // avoid making ridiculously big movements if there's a big drop in fps
- if (time > .2f)
- {
- time = .2f;
- }
-
- F32 cur_delta[7];
- F32 feather = gSavedSettings.getF32("FlycamFeathering");
- bool absolute = gSavedSettings.getBOOL("Cursor3D");
- bool is_zero = true;
-
- for (U32 i = 0; i < 7; i++)
- {
- cur_delta[i] = -getJoystickAxis(axis[i]);
-
-
- F32 tmp = cur_delta[i];
- if (absolute)
- {
- cur_delta[i] = cur_delta[i] - sLastDelta[i];
- }
- sLastDelta[i] = tmp;
-
- if (cur_delta[i] > 0)
- {
- cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
- }
- else
- {
- cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
- }
-
- // We may want to scale camera movements up or down in build mode.
- // NOTE: this needs to remain after the deadzone calculation, otherwise
- // we have issues with flycam "jumping" when the build dialog is opened/closed -Nyx
- if (in_build_mode)
- {
- if (i == X_I || i == Y_I || i == Z_I)
- {
- static LLCachedControl<F32> build_mode_scale(gSavedSettings,"FlycamBuildModeScale", 1.0);
- cur_delta[i] *= build_mode_scale;
- }
- }
-
- cur_delta[i] *= axis_scale[i];
-
- if (!absolute)
- {
- cur_delta[i] *= time;
- }
-
- sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
-
- is_zero = is_zero && (cur_delta[i] == 0.f);
-
- }
-
- // Clear AFK state if moved beyond the deadzone
- if (!is_zero && gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
- {
- gAgent.clearAFK();
- }
-
- sFlycamPosition += LLVector3(sDelta) * sFlycamRotation;
-
- LLMatrix3 rot_mat(sDelta[3], sDelta[4], sDelta[5]);
- sFlycamRotation = LLQuaternion(rot_mat)*sFlycamRotation;
-
- if (gSavedSettings.getBOOL("AutoLeveling"))
- {
- LLMatrix3 level(sFlycamRotation);
-
- LLVector3 x = LLVector3(level.mMatrix[0]);
- LLVector3 y = LLVector3(level.mMatrix[1]);
- LLVector3 z = LLVector3(level.mMatrix[2]);
-
- y.mV[2] = 0.f;
- y.normVec();
-
- level.setRows(x,y,z);
- level.orthogonalize();
-
- LLQuaternion quat(level);
- sFlycamRotation = nlerp(llmin(feather*time,1.f), sFlycamRotation, quat);
- }
-
- if (gSavedSettings.getBOOL("ZoomDirect"))
- {
- sFlycamZoom = sLastDelta[6]*axis_scale[6]+dead_zone[6];
- }
- else
- {
- sFlycamZoom += sDelta[6];
- }
-
- LLMatrix3 mat(sFlycamRotation);
-
- LLViewerCamera::getInstance()->setView(sFlycamZoom);
- LLViewerCamera::getInstance()->setOrigin(sFlycamPosition);
- LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]);
- LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]);
- LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]);
-}
-
-// -----------------------------------------------------------------------------
-bool LLViewerJoystick::toggleFlycam()
-{
- if (!gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled"))
- {
- mOverrideCamera = false;
- return false;
- }
-
- if (!mOverrideCamera)
- {
- gAgentCamera.changeCameraToDefault();
- }
-
- if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
- {
- gAgent.clearAFK();
- }
-
- mOverrideCamera = !mOverrideCamera;
- if (mOverrideCamera)
- {
- moveFlycam(true);
-
- }
- else
- {
- // Exiting from the flycam mode: since we are going to keep the flycam POV for
- // the main camera until the avatar moves, we need to track this situation.
- setCameraNeedsUpdate(false);
- setNeedsReset(true);
- }
- return true;
-}
-
-void LLViewerJoystick::scanJoystick()
-{
- if (mDriverState != JDS_INITIALIZED || !gSavedSettings.getBOOL("JoystickEnabled"))
- {
- return;
- }
-
-#if LL_WINDOWS
- // On windows, the flycam is updated syncronously with a timer, so there is
- // no need to update the status of the joystick here.
- if (!mOverrideCamera)
-#endif
- updateStatus();
-
- // App focus check Needs to happen AFTER updateStatus in case the joystick
- // is not centred when the app loses focus.
- if (!gFocusMgr.getAppHasFocus())
- {
- return;
- }
-
- static long toggle_flycam = 0;
-
- if (mBtn[0] == 1)
- {
- if (mBtn[0] != toggle_flycam)
- {
- toggle_flycam = toggleFlycam() ? 1 : 0;
- }
- }
- else
- {
- toggle_flycam = 0;
- }
-
- if (!mOverrideCamera && !(LLToolMgr::getInstance()->inBuildMode() && gSavedSettings.getBOOL("JoystickBuildEnabled")))
- {
- moveAvatar();
- }
-}
-
-// -----------------------------------------------------------------------------
-bool LLViewerJoystick::isDeviceUUIDSet()
-{
-#if LL_WINDOWS && !LL_MESA_HEADLESS
- // for ease of comparison and to dial less with platform specific variables, we store id as LLSD binary
- return mLastDeviceUUID.isBinary();
-#elif LL_DARWIN
- return mLastDeviceUUID.isMap();
-#else
- return false;
-#endif
-}
-
-LLSD LLViewerJoystick::getDeviceUUID()
-{
- return mLastDeviceUUID;
-}
-
-std::string LLViewerJoystick::getDeviceUUIDString()
-{
-#if LL_WINDOWS && !LL_MESA_HEADLESS
- // Might be simpler to just convert _GUID into string everywhere, store and compare as string
- if (mLastDeviceUUID.isBinary())
- {
- S32 size = sizeof(GUID);
- LLSD::Binary data = mLastDeviceUUID.asBinary();
- GUID guid;
- memcpy(&guid, &data[0], size);
- return string_from_guid(guid);
- }
- else
- {
- return std::string();
- }
-#elif LL_DARWIN
- if (mLastDeviceUUID.isMap())
- {
- std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
- std::string product = mLastDeviceUUID["product"].asString();
- return manufacturer + ":" + product;
- }
- else
- {
- return std::string();
- }
-#else
- return std::string();
-#endif
-}
-
-void LLViewerJoystick::saveDeviceIdToSettings()
-{
-#if LL_WINDOWS && !LL_MESA_HEADLESS
- // can't save as binary directly,
- // someone editing the xml will corrupt it
- // so convert to string first
- std::string device_string = getDeviceUUIDString();
- gSavedSettings.setLLSD("JoystickDeviceUUID", LLSD(device_string));
-#else
- LLSD device_id = getDeviceUUID();
- gSavedSettings.setLLSD("JoystickDeviceUUID", device_id);
-#endif
-}
-
-void LLViewerJoystick::loadDeviceIdFromSettings()
-{
- LLSD dev_id = gSavedSettings.getLLSD("JoystickDeviceUUID");
-#if LL_WINDOWS && !LL_MESA_HEADLESS
- // We can't save binary data to gSavedSettings, somebody editing the file will corrupt it,
- // so _GUID data gets converted to string (we probably can convert it to LLUUID with memcpy)
- // and here we need to convert it back to binary from string
- std::string device_string;
- if (dev_id.isString())
- {
- device_string = dev_id.asString();
- }
- if (device_string.empty())
- {
- mLastDeviceUUID = LLSD();
- }
- else
- {
- LL_DEBUGS("Joystick") << "Looking for device by id: " << device_string << LL_ENDL;
- GUID guid;
- guid_from_string(guid, device_string);
- S32 size = sizeof(GUID);
- LLSD::Binary data; //just an std::vector
- data.resize(size);
- memcpy(&data[0], &guid /*POD _GUID*/, size);
- // We store this data in LLSD since it can handle both GUID2 and long
- mLastDeviceUUID = LLSD(data);
- }
-#elif LL_DARWIN
- if (!dev_id.isMap())
- {
- mLastDeviceUUID = LLSD();
- }
- else
- {
- std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
- std::string product = mLastDeviceUUID["product"].asString();
- LL_DEBUGS("Joystick") << "Looking for device by manufacturer: " << manufacturer << " and product: " << product << LL_ENDL;
- // We store this data in LLSD since it can handle both GUID2 and long
- mLastDeviceUUID = dev_id;
- }
-#else
- mLastDeviceUUID = LLSD();
- //mLastDeviceUUID = gSavedSettings.getLLSD("JoystickDeviceUUID");
-#endif
-}
-
-// -----------------------------------------------------------------------------
-std::string LLViewerJoystick::getDescription()
-{
- std::string res;
-#if LIB_NDOF
- if (mDriverState == JDS_INITIALIZED && mNdofDev)
- {
- res = ll_safe_string(mNdofDev->product);
- }
-#endif
- return res;
-}
-
-bool LLViewerJoystick::isLikeSpaceNavigator() const
-{
-#if LIB_NDOF
- return (isJoystickInitialized()
- && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0
- || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0
- || strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0
- || strncmp(mNdofDev->product, "SpacePilot", 10) == 0));
-#else
- return false;
-#endif
-}
-
-// -----------------------------------------------------------------------------
-void LLViewerJoystick::setSNDefaults()
-{
-#if LL_DARWIN || LL_LINUX
- const float platformScale = 20.f;
- const float platformScaleAvXZ = 1.f;
- // The SpaceNavigator doesn't act as a 3D cursor on macOS / Linux.
- const bool is_3d_cursor = false;
-#else
- const float platformScale = 1.f;
- const float platformScaleAvXZ = 2.f;
- const bool is_3d_cursor = true;
-#endif
-
- //gViewerWindow->alertXml("CacheWillClear");
- LL_INFOS("Joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL;
-
- gSavedSettings.setS32("JoystickAxis0", 1); // z (at)
- gSavedSettings.setS32("JoystickAxis1", 0); // x (slide)
- gSavedSettings.setS32("JoystickAxis2", 2); // y (up)
- gSavedSettings.setS32("JoystickAxis3", 4); // pitch
- gSavedSettings.setS32("JoystickAxis4", 3); // roll
- gSavedSettings.setS32("JoystickAxis5", 5); // yaw
- gSavedSettings.setS32("JoystickAxis6", -1);
-
- gSavedSettings.setBOOL("Cursor3D", is_3d_cursor);
- gSavedSettings.setBOOL("AutoLeveling", true);
- gSavedSettings.setBOOL("ZoomDirect", false);
-
- gSavedSettings.setF32("AvatarAxisScale0", 1.f * platformScaleAvXZ);
- gSavedSettings.setF32("AvatarAxisScale1", 1.f * platformScaleAvXZ);
- gSavedSettings.setF32("AvatarAxisScale2", 1.f);
- gSavedSettings.setF32("AvatarAxisScale4", .1f * platformScale);
- gSavedSettings.setF32("AvatarAxisScale5", .1f * platformScale);
- gSavedSettings.setF32("AvatarAxisScale3", 0.f * platformScale);
- gSavedSettings.setF32("BuildAxisScale1", .3f * platformScale);
- gSavedSettings.setF32("BuildAxisScale2", .3f * platformScale);
- gSavedSettings.setF32("BuildAxisScale0", .3f * platformScale);
- gSavedSettings.setF32("BuildAxisScale4", .3f * platformScale);
- gSavedSettings.setF32("BuildAxisScale5", .3f * platformScale);
- gSavedSettings.setF32("BuildAxisScale3", .3f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale1", 2.f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale2", 2.f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale0", 2.1f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale4", .1f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale5", .15f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale3", 0.f * platformScale);
- gSavedSettings.setF32("FlycamAxisScale6", 0.f * platformScale);
-
- gSavedSettings.setF32("AvatarAxisDeadZone0", .1f);
- gSavedSettings.setF32("AvatarAxisDeadZone1", .1f);
- gSavedSettings.setF32("AvatarAxisDeadZone2", .1f);
- gSavedSettings.setF32("AvatarAxisDeadZone3", 1.f);
- gSavedSettings.setF32("AvatarAxisDeadZone4", .02f);
- gSavedSettings.setF32("AvatarAxisDeadZone5", .01f);
- gSavedSettings.setF32("BuildAxisDeadZone0", .01f);
- gSavedSettings.setF32("BuildAxisDeadZone1", .01f);
- gSavedSettings.setF32("BuildAxisDeadZone2", .01f);
- gSavedSettings.setF32("BuildAxisDeadZone3", .01f);
- gSavedSettings.setF32("BuildAxisDeadZone4", .01f);
- gSavedSettings.setF32("BuildAxisDeadZone5", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone0", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone1", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone2", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone3", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone4", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone5", .01f);
- gSavedSettings.setF32("FlycamAxisDeadZone6", 1.f);
-
- gSavedSettings.setF32("AvatarFeathering", 6.f);
- gSavedSettings.setF32("BuildFeathering", 12.f);
- gSavedSettings.setF32("FlycamFeathering", 5.f);
-}
+/** + * @file llviewerjoystick.cpp + * @brief Joystick / NDOF device functionality. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerjoystick.h" + +#include "llviewercontrol.h" +#include "llviewerwindow.h" +#include "llviewercamera.h" +#include "llappviewer.h" +#include "llkeyboard.h" +#include "lltoolmgr.h" +#include "llselectmgr.h" +#include "llviewermenu.h" +#include "llviewerwindow.h" +#include "llwindow.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llfocusmgr.h" + +#if LL_WINDOWS && !LL_MESA_HEADLESS +// Require DirectInput version 8 +#define DIRECTINPUT_VERSION 0x0800 + +#include <dinput.h> +#endif + + +// ---------------------------------------------------------------------------- +// Constants + +#define X_I 1 +#define Y_I 2 +#define Z_I 0 +#define RX_I 4 +#define RY_I 5 +#define RZ_I 3 + +F32 LLViewerJoystick::sLastDelta[] = {0,0,0,0,0,0,0}; +F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0}; + +// These constants specify the maximum absolute value coming in from the device. +// HACK ALERT! the value of MAX_JOYSTICK_INPUT_VALUE is not arbitrary as it +// should be. It has to be equal to 3000 because the SpaceNavigator on Windows +// refuses to respond to the DirectInput SetProperty call; it always returns +// values in the [-3000, 3000] range. +#define MAX_SPACENAVIGATOR_INPUT 3000.0f +#define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT + + +#if LIB_NDOF +std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr) +{ + if (! ptr) + { + return out << "nullptr"; + } + out << "NDOF_Device{ "; + out << "axes ["; + const char* delim = ""; + for (short axis = 0; axis < ptr->axes_count; ++axis) + { + out << delim << ptr->axes[axis]; + delim = ", "; + } + out << "]"; + out << ", buttons ["; + delim = ""; + for (short button = 0; button < ptr->btn_count; ++button) + { + out << delim << ptr->buttons[button]; + delim = ", "; + } + out << "]"; + out << ", range " << ptr->axes_min << ':' << ptr->axes_max; + // If we don't coerce these to unsigned, they're streamed as characters, + // e.g. ctrl-A or nul. + out << ", absolute " << unsigned(ptr->absolute); + out << ", valid " << unsigned(ptr->valid); + out << ", manufacturer '" << ptr->manufacturer << "'"; + out << ", product '" << ptr->product << "'"; + out << ", private " << ptr->private_data; + out << " }"; + return out; +} +#endif // LIB_NDOF + + +#if LL_WINDOWS && !LL_MESA_HEADLESS +// this should reflect ndof and set axises, see ndofdev_win.cpp from ndof package +BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* inst, VOID* user_data) +{ + if (inst->dwType & DIDFT_AXIS) + { + LPDIRECTINPUTDEVICE8 device = *((LPDIRECTINPUTDEVICE8 *)user_data); + DIPROPRANGE diprg; + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); + diprg.diph.dwHow = DIPH_BYID; + diprg.diph.dwObj = inst->dwType; // specify the enumerated axis + + // Set the range for the axis + diprg.lMin = (long)-MAX_JOYSTICK_INPUT_VALUE; + diprg.lMax = (long)+MAX_JOYSTICK_INPUT_VALUE; + HRESULT hr = device->SetProperty(DIPROP_RANGE, &diprg.diph); + + if (FAILED(hr)) + { + return DIENUM_STOP; + } + } + + return DIENUM_CONTINUE; +} + +BOOL CALLBACK di8_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef) +{ + // Note: If a single device can function as more than one DirectInput + // device type, it is enumerated as each device type that it supports. + // Capable of detecting devices like Oculus Rift + if (device_instance_ptr) + { + std::string product_name = utf16str_to_utf8str(llutf16string(device_instance_ptr->tszProductName)); + + LLSD guid = LLViewerJoystick::getInstance()->getDeviceUUID(); + + bool init_device = false; + if (guid.isBinary()) + { + std::vector<U8> bin_bucket = guid.asBinary(); + init_device = memcmp(&bin_bucket[0], &device_instance_ptr->guidInstance, sizeof(GUID)) == 0; + } + else + { + // It might be better to init space navigator here, but if system doesn't has one, + // ndof will pick a random device, it is simpler to pick first device now to have an id + init_device = true; + } + + if (init_device) + { + LL_DEBUGS("Joystick") << "Found and attempting to use device: " << product_name << LL_ENDL; + LPDIRECTINPUT8 di8_interface = *((LPDIRECTINPUT8 *)gViewerWindow->getWindow()->getDirectInput8()); + LPDIRECTINPUTDEVICE8 device = NULL; + + HRESULT status = di8_interface->CreateDevice( + device_instance_ptr->guidInstance, // REFGUID rguid, + &device, // LPDIRECTINPUTDEVICE * lplpDirectInputDevice, + NULL // LPUNKNOWN pUnkOuter + ); + + if (status == DI_OK) + { + // prerequisite for aquire() + LL_DEBUGS("Joystick") << "Device created" << LL_ENDL; + status = device->SetDataFormat(&c_dfDIJoystick); // c_dfDIJoystick2 + } + + if (status == DI_OK) + { + // set properties + LL_DEBUGS("Joystick") << "Format set" << LL_ENDL; + status = device->EnumObjects(EnumObjectsCallback, &device, DIDFT_ALL); + } + + if (status == DI_OK) + { + LL_DEBUGS("Joystick") << "Properties updated" << LL_ENDL; + + S32 size = sizeof(GUID); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size); + LLViewerJoystick::getInstance()->initDevice(&device, product_name, LLSD(data)); + return DIENUM_STOP; + } + } + else + { + LL_DEBUGS("Joystick") << "Found device: " << product_name << LL_ENDL; + } + } + return DIENUM_CONTINUE; +} + +// Windows guids +// This is GUID2 so teoretically it can be memcpy copied into LLUUID +void guid_from_string(GUID &guid, const std::string &input) +{ + CLSIDFromString(utf8str_to_utf16str(input).c_str(), &guid); +} + +std::string string_from_guid(const GUID &guid) +{ + OLECHAR* guidString; //wchat + StringFromCLSID(guid, &guidString); + + // use guidString... + + std::string res = utf16str_to_utf8str(llutf16string(guidString)); + // ensure memory is freed + ::CoTaskMemFree(guidString); + + return res; +} +#elif LL_DARWIN + +bool macos_devices_callback(std::string &product_name, LLSD &data, void* userdata) +{ + std::string product = data["product"].asString(); + + return LLViewerJoystick::getInstance()->initDevice(nullptr, product, data); +} + +#endif + + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::updateEnabled(bool autoenable) +{ + if (mDriverState == JDS_UNINITIALIZED) + { + gSavedSettings.setBOOL("JoystickEnabled", false); + } + else + { + // autoenable if user specifically chose this device + if (autoenable && (isLikeSpaceNavigator() || isDeviceUUIDSet())) + { + gSavedSettings.setBOOL("JoystickEnabled", true ); + } + } + if (!gSavedSettings.getBOOL("JoystickEnabled")) + { + mOverrideCamera = false; + } +} + +void LLViewerJoystick::setOverrideCamera(bool val) +{ + if (!gSavedSettings.getBOOL("JoystickEnabled")) + { + mOverrideCamera = false; + } + else + { + mOverrideCamera = val; + } + + if (mOverrideCamera) + { + gAgentCamera.changeCameraToDefault(); + } +} + +// ----------------------------------------------------------------------------- +#if LIB_NDOF +NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev) +{ + NDOF_HotPlugResult res = NDOF_DISCARD_HOTPLUGGED; + LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); + if (joystick->mDriverState == JDS_UNINITIALIZED) + { + LL_INFOS("Joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL; + ndof_dump(stderr, dev); + joystick->mNdofDev = dev; + joystick->mDriverState = JDS_INITIALIZED; + res = NDOF_KEEP_HOTPLUGGED; + } + joystick->updateEnabled(true); + return res; +} +#endif + +// ----------------------------------------------------------------------------- +#if LIB_NDOF +void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev) +{ + LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); + if (joystick->mNdofDev == dev) + { + LL_INFOS("Joystick") << "HotPlugRemovalCallback: joystick->mNdofDev=" + << joystick->mNdofDev << "; removed device:" << LL_ENDL; + ndof_dump(stderr, dev); + joystick->mDriverState = JDS_UNINITIALIZED; + } + joystick->updateEnabled(true); +} +#endif + +// ----------------------------------------------------------------------------- +LLViewerJoystick::LLViewerJoystick() +: mDriverState(JDS_UNINITIALIZED), + mNdofDev(NULL), + mResetFlag(false), + mCameraUpdated(true), + mOverrideCamera(false), + mJoystickRun(0) +{ + for (int i = 0; i < 6; i++) + { + mAxes[i] = sDelta[i] = sLastDelta[i] = 0.0f; + } + + memset(mBtn, 0, sizeof(mBtn)); + + // factor in bandwidth? bandwidth = gViewerStats->mKBitStat + mPerfScale = 4000.f / gSysCPU.getMHz(); // hmm. why? + + mLastDeviceUUID = LLSD::Integer(1); +} + +// ----------------------------------------------------------------------------- +LLViewerJoystick::~LLViewerJoystick() +{ + if (mDriverState == JDS_INITIALIZED) + { + terminate(); + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::init(bool autoenable) +{ +#if LIB_NDOF + static bool libinit = false; + mDriverState = JDS_INITIALIZING; + + loadDeviceIdFromSettings(); + + if (!libinit) + { + // Note: The HotPlug callbacks are not actually getting called on Windows + if (ndof_libinit(HotPlugAddCallback, + HotPlugRemovalCallback, + gViewerWindow->getWindow()->getDirectInput8())) + { + mDriverState = JDS_UNINITIALIZED; + } + else + { + // NB: ndof_libinit succeeds when there's no device + libinit = true; + + // allocate memory once for an eventual device + mNdofDev = ndof_create(); + } + } + + if (libinit) + { + if (mNdofDev) + { + U32 device_type = 0; + void* win_callback = nullptr; + std::function<bool(std::string&, LLSD&, void*)> osx_callback; + // di8_devices_callback callback is immediate and happens in scope of getInputDevices() +#if LL_WINDOWS && !LL_MESA_HEADLESS + // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib + device_type = DI8DEVCLASS_GAMECTRL; + win_callback = &di8_devices_callback; +#elif LL_DARWIN + osx_callback = macos_devices_callback; + + if (mLastDeviceUUID.isMap()) + { + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); + + strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer)); + strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product)); + + if (ndof_init_first(mNdofDev, nullptr)) + { + mDriverState = JDS_INITIALIZING; + // Saved device no longer exist + // No device found + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mDriverState = JDS_INITIALIZED; + } + } +#endif + if (mDriverState != JDS_INITIALIZED) + { + if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL)) + { + LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL; + // Failed to gather devices, init first suitable one + mLastDeviceUUID = LLSD(); + void *preffered_device = NULL; + initDevice(preffered_device); + } + } + + if (mDriverState == JDS_INITIALIZING) + { + LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL; + mDriverState = JDS_UNINITIALIZED; + } + } + else + { + mDriverState = JDS_UNINITIALIZED; + } + } + + // Autoenable the joystick for recognized devices if nothing was connected previously + if (!autoenable) + { + autoenable = gSavedSettings.getString("JoystickInitialized").empty(); + } + updateEnabled(autoenable); + + if (mDriverState == JDS_INITIALIZED) + { + // A Joystick device is plugged in + if (isLikeSpaceNavigator()) + { + // It's a space navigator, we have defaults for it. + if (gSavedSettings.getString("JoystickInitialized") != "SpaceNavigator") + { + // Only set the defaults if we haven't already (in case they were overridden) + setSNDefaults(); + gSavedSettings.setString("JoystickInitialized", "SpaceNavigator"); + } + } + else + { + // It's not a Space Navigator + gSavedSettings.setString("JoystickInitialized", "UnknownDevice"); + } + } + else + { + // No device connected, don't change any settings + } + + LL_INFOS("Joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" + << mNdofDev << "; libinit=" << libinit << LL_ENDL; +#endif +} + +void LLViewerJoystick::initDevice(LLSD &guid) +{ +#if LIB_NDOF + mLastDeviceUUID = guid; + U32 device_type = 0; + void* win_callback = nullptr; + std::function<bool(std::string&, LLSD&, void*)> osx_callback; + mDriverState = JDS_INITIALIZING; + +#if LL_WINDOWS && !LL_MESA_HEADLESS + // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib + device_type = DI8DEVCLASS_GAMECTRL; + win_callback = &di8_devices_callback; +#elif LL_DARWIN + osx_callback = macos_devices_callback; + if (mLastDeviceUUID.isMap()) + { + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); + + strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer)); + strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product)); + + if (ndof_init_first(mNdofDev, nullptr)) + { + mDriverState = JDS_INITIALIZING; + // Saved device no longer exist + // Np other device present + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mDriverState = JDS_INITIALIZED; + } + } +#endif + + if (mDriverState != JDS_INITIALIZED) + { + if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL)) + { + LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL; + // Failed to gather devices from window, init first suitable one + void *preffered_device = NULL; + mLastDeviceUUID = LLSD(); + initDevice(preffered_device); + } + } + + if (mDriverState == JDS_INITIALIZING) + { + LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL; + mDriverState = JDS_UNINITIALIZED; + } +#endif +} + +bool LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid) +{ +#if LIB_NDOF + mLastDeviceUUID = guid; + +#if LL_DARWIN + if (guid.isMap()) + { + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); + + strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer)); + strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product)); + } + else + { + mNdofDev->product[0] = '\0'; + mNdofDev->manufacturer[0] = '\0'; + } +#else + strncpy(mNdofDev->product, name.c_str(), sizeof(mNdofDev->product)); + mNdofDev->manufacturer[0] = '\0'; +#endif + + return initDevice(preffered_device); +#else + return false; +#endif +} + +bool LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */) +{ +#if LIB_NDOF + // Different joysticks will return different ranges of raw values. + // Since we want to handle every device in the same uniform way, + // we initialize the mNdofDev struct and we set the range + // of values we would like to receive. + // + // HACK: On Windows, libndofdev passes our range to DI with a + // SetProperty call. This works but with one notable exception, the + // SpaceNavigator, who doesn't seem to care about the SetProperty + // call. In theory, we should handle this case inside libndofdev. + // However, the range we're setting here is arbitrary anyway, + // so let's just use the SpaceNavigator range for our purposes. + mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE; + mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE; + + // libndofdev could be used to return deltas. Here we choose to + // just have the absolute values instead. + mNdofDev->absolute = 1; + // init & use the first suitable NDOF device found on the USB chain + // On windows preffered_device needs to be a pointer to LPDIRECTINPUTDEVICE8 + if (ndof_init_first(mNdofDev, preffered_device)) + { + mDriverState = JDS_UNINITIALIZED; + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mDriverState = JDS_INITIALIZED; + return true; + } +#endif + return false; +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::terminate() +{ +#if LIB_NDOF + if (mNdofDev != NULL) + { + ndof_libcleanup(); // frees alocated memory in mNdofDev + mDriverState = JDS_UNINITIALIZED; + mNdofDev = NULL; + LL_INFOS("Joystick") << "Terminated connection with NDOF device." << LL_ENDL; + } +#endif +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::updateStatus() +{ +#if LIB_NDOF + + ndof_update(mNdofDev); + + for (int i=0; i<6; i++) + { + mAxes[i] = (F32) mNdofDev->axes[i] / mNdofDev->axes_max; + } + + for (int i=0; i<16; i++) + { + mBtn[i] = mNdofDev->buttons[i]; + } + +#endif +} + +// ----------------------------------------------------------------------------- +F32 LLViewerJoystick::getJoystickAxis(U32 axis) const +{ + if (axis < 6) + { + return mAxes[axis]; + } + return 0.f; +} + +// ----------------------------------------------------------------------------- +U32 LLViewerJoystick::getJoystickButton(U32 button) const +{ + if (button < 16) + { + return mBtn[button]; + } + return 0; +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::handleRun(F32 inc) +{ + // Decide whether to walk or run by applying a threshold, with slight + // hysteresis to avoid oscillating between the two with input spikes. + // Analog speed control would be better, but not likely any time soon. + if (inc > gSavedSettings.getF32("JoystickRunThreshold")) + { + if (1 == mJoystickRun) + { + ++mJoystickRun; + gAgent.setRunning(); + gAgent.sendWalkRun(gAgent.getRunning()); + } + else if (0 == mJoystickRun) + { + // hysteresis - respond NEXT frame + ++mJoystickRun; + } + } + else + { + if (mJoystickRun > 0) + { + --mJoystickRun; + if (0 == mJoystickRun) + { + gAgent.clearRunning(); + gAgent.sendWalkRun(gAgent.getRunning()); + } + } + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::agentJump() +{ + gAgent.moveUp(1); +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::agentSlide(F32 inc) +{ + if (inc < 0.f) + { + gAgent.moveLeft(1); + } + else if (inc > 0.f) + { + gAgent.moveLeft(-1); + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::agentPush(F32 inc) +{ + if (inc < 0.f) // forward + { + gAgent.moveAt(1, false); + } + else if (inc > 0.f) // backward + { + gAgent.moveAt(-1, false); + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::agentFly(F32 inc) +{ + if (inc < 0.f) + { + if (! (gAgent.getFlying() || + !gAgent.canFly() || + gAgent.upGrabbed() || + !gSavedSettings.getBOOL("AutomaticFly")) ) + { + gAgent.setFlying(true); + } + gAgent.moveUp(1); + } + else if (inc > 0.f) + { + // crouch + gAgent.moveUp(-1); + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::agentPitch(F32 pitch_inc) +{ + if (pitch_inc < 0) + { + gAgent.setControlFlags(AGENT_CONTROL_PITCH_POS); + } + else if (pitch_inc > 0) + { + gAgent.setControlFlags(AGENT_CONTROL_PITCH_NEG); + } + + gAgent.pitch(-pitch_inc); +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::agentYaw(F32 yaw_inc) +{ + // Cannot steer some vehicles in mouselook if the script grabs the controls + if (gAgentCamera.cameraMouselook() && !gSavedSettings.getBOOL("JoystickMouselookYaw")) + { + gAgent.rotate(-yaw_inc, gAgent.getReferenceUpVector()); + } + else + { + if (yaw_inc < 0) + { + gAgent.setControlFlags(AGENT_CONTROL_YAW_POS); + } + else if (yaw_inc > 0) + { + gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG); + } + + gAgent.yaw(-yaw_inc); + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::resetDeltas(S32 axis[]) +{ + for (U32 i = 0; i < 6; i++) + { + sLastDelta[i] = -mAxes[axis[i]]; + sDelta[i] = 0.f; + } + + sLastDelta[6] = sDelta[6] = 0.f; + mResetFlag = false; +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::moveObjects(bool reset) +{ + static bool toggle_send_to_sim = false; + + if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED + || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickBuildEnabled")) + { + return; + } + + S32 axis[] = + { + gSavedSettings.getS32("JoystickAxis0"), + gSavedSettings.getS32("JoystickAxis1"), + gSavedSettings.getS32("JoystickAxis2"), + gSavedSettings.getS32("JoystickAxis3"), + gSavedSettings.getS32("JoystickAxis4"), + gSavedSettings.getS32("JoystickAxis5"), + }; + + if (reset || mResetFlag) + { + resetDeltas(axis); + return; + } + + F32 axis_scale[] = + { + gSavedSettings.getF32("BuildAxisScale0"), + gSavedSettings.getF32("BuildAxisScale1"), + gSavedSettings.getF32("BuildAxisScale2"), + gSavedSettings.getF32("BuildAxisScale3"), + gSavedSettings.getF32("BuildAxisScale4"), + gSavedSettings.getF32("BuildAxisScale5"), + }; + + F32 dead_zone[] = + { + gSavedSettings.getF32("BuildAxisDeadZone0"), + gSavedSettings.getF32("BuildAxisDeadZone1"), + gSavedSettings.getF32("BuildAxisDeadZone2"), + gSavedSettings.getF32("BuildAxisDeadZone3"), + gSavedSettings.getF32("BuildAxisDeadZone4"), + gSavedSettings.getF32("BuildAxisDeadZone5"), + }; + + F32 cur_delta[6]; + F32 time = gFrameIntervalSeconds.value(); + + // avoid making ridicously big movements if there's a big drop in fps + if (time > .2f) + { + time = .2f; + } + + // max feather is 32 + F32 feather = gSavedSettings.getF32("BuildFeathering"); + bool is_zero = true, absolute = gSavedSettings.getBOOL("Cursor3D"); + + for (U32 i = 0; i < 6; i++) + { + cur_delta[i] = -mAxes[axis[i]]; + F32 tmp = cur_delta[i]; + if (absolute) + { + cur_delta[i] = cur_delta[i] - sLastDelta[i]; + } + sLastDelta[i] = tmp; + is_zero = is_zero && (cur_delta[i] == 0.f); + + if (cur_delta[i] > 0) + { + cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f); + } + else + { + cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f); + } + cur_delta[i] *= axis_scale[i]; + + if (!absolute) + { + cur_delta[i] *= time; + } + + sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather; + } + + U32 upd_type = UPD_NONE; + LLVector3 v; + + if (!is_zero) + { + // Clear AFK state if moved beyond the deadzone + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + if (sDelta[0] || sDelta[1] || sDelta[2]) + { + upd_type |= UPD_POSITION; + v.setVec(sDelta[0], sDelta[1], sDelta[2]); + } + + if (sDelta[3] || sDelta[4] || sDelta[5]) + { + upd_type |= UPD_ROTATION; + } + + // the selection update could fail, so we won't send + if (LLSelectMgr::getInstance()->selectionMove(v, sDelta[3],sDelta[4],sDelta[5], upd_type)) + { + toggle_send_to_sim = true; + } + } + else if (toggle_send_to_sim) + { + LLSelectMgr::getInstance()->sendSelectionMove(); + toggle_send_to_sim = false; + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::moveAvatar(bool reset) +{ + if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED + || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickAvatarEnabled")) + { + return; + } + + S32 axis[] = + { + // [1 0 2 4 3 5] + // [Z X Y RZ RX RY] + gSavedSettings.getS32("JoystickAxis0"), + gSavedSettings.getS32("JoystickAxis1"), + gSavedSettings.getS32("JoystickAxis2"), + gSavedSettings.getS32("JoystickAxis3"), + gSavedSettings.getS32("JoystickAxis4"), + gSavedSettings.getS32("JoystickAxis5") + }; + + if (reset || mResetFlag) + { + resetDeltas(axis); + if (reset) + { + // Note: moving the agent triggers agent camera mode; + // don't do this every time we set mResetFlag (e.g. because we gained focus) + gAgent.moveAt(0, true); + } + return; + } + + bool is_zero = true; + static bool button_held = false; + + if (mBtn[1] == 1) + { + // If AutomaticFly is enabled, then button1 merely causes a + // jump (as the up/down axis already controls flying) if on the + // ground, or cease flight if already flying. + // If AutomaticFly is disabled, then button1 toggles flying. + if (gSavedSettings.getBOOL("AutomaticFly")) + { + if (!gAgent.getFlying()) + { + gAgent.moveUp(1); + } + else if (!button_held) + { + button_held = true; + gAgent.setFlying(false); + } + } + else if (!button_held) + { + button_held = true; + gAgent.setFlying(!gAgent.getFlying()); + } + + is_zero = false; + } + else + { + button_held = false; + } + + F32 axis_scale[] = + { + gSavedSettings.getF32("AvatarAxisScale0"), + gSavedSettings.getF32("AvatarAxisScale1"), + gSavedSettings.getF32("AvatarAxisScale2"), + gSavedSettings.getF32("AvatarAxisScale3"), + gSavedSettings.getF32("AvatarAxisScale4"), + gSavedSettings.getF32("AvatarAxisScale5") + }; + + F32 dead_zone[] = + { + gSavedSettings.getF32("AvatarAxisDeadZone0"), + gSavedSettings.getF32("AvatarAxisDeadZone1"), + gSavedSettings.getF32("AvatarAxisDeadZone2"), + gSavedSettings.getF32("AvatarAxisDeadZone3"), + gSavedSettings.getF32("AvatarAxisDeadZone4"), + gSavedSettings.getF32("AvatarAxisDeadZone5") + }; + + // time interval in seconds between this frame and the previous + F32 time = gFrameIntervalSeconds.value(); + + // avoid making ridicously big movements if there's a big drop in fps + if (time > .2f) + { + time = .2f; + } + + // note: max feather is 32.0 + F32 feather = gSavedSettings.getF32("AvatarFeathering"); + + F32 cur_delta[6]; + F32 val, dom_mov = 0.f; + U32 dom_axis = Z_I; +#if LIB_NDOF + bool absolute = (gSavedSettings.getBOOL("Cursor3D") && mNdofDev->absolute); +#else + bool absolute = false; +#endif + // remove dead zones and determine biggest movement on the joystick + for (U32 i = 0; i < 6; i++) + { + cur_delta[i] = -mAxes[axis[i]]; + if (absolute) + { + F32 tmp = cur_delta[i]; + cur_delta[i] = cur_delta[i] - sLastDelta[i]; + sLastDelta[i] = tmp; + } + + if (cur_delta[i] > 0) + { + cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f); + } + else + { + cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f); + } + + // we don't care about Roll (RZ) and Z is calculated after the loop + if (i != Z_I && i != RZ_I) + { + // find out the axis with the biggest joystick motion + val = fabs(cur_delta[i]); + if (val > dom_mov) + { + dom_axis = i; + dom_mov = val; + } + } + + is_zero = is_zero && (cur_delta[i] == 0.f); + } + + if (!is_zero) + { + // Clear AFK state if moved beyond the deadzone + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + setCameraNeedsUpdate(true); + } + + // forward|backward movements overrule the real dominant movement if + // they're bigger than its 20%. This is what you want 'cos moving forward + // is what you do most. We also added a special (even more lenient) case + // for RX|RY to allow walking while pitching and turning + if (fabs(cur_delta[Z_I]) > .2f * dom_mov + || ((dom_axis == RX_I || dom_axis == RY_I) + && fabs(cur_delta[Z_I]) > .05f * dom_mov)) + { + dom_axis = Z_I; + } + + sDelta[X_I] = -cur_delta[X_I] * axis_scale[X_I]; + sDelta[Y_I] = -cur_delta[Y_I] * axis_scale[Y_I]; + sDelta[Z_I] = -cur_delta[Z_I] * axis_scale[Z_I]; + cur_delta[RX_I] *= -axis_scale[RX_I] * mPerfScale; + cur_delta[RY_I] *= -axis_scale[RY_I] * mPerfScale; + + if (!absolute) + { + cur_delta[RX_I] *= time; + cur_delta[RY_I] *= time; + } + sDelta[RX_I] += (cur_delta[RX_I] - sDelta[RX_I]) * time * feather; + sDelta[RY_I] += (cur_delta[RY_I] - sDelta[RY_I]) * time * feather; + + handleRun((F32) sqrt(sDelta[Z_I]*sDelta[Z_I] + sDelta[X_I]*sDelta[X_I])); + + // Allow forward/backward movement some priority + if (dom_axis == Z_I) + { + agentPush(sDelta[Z_I]); // forward/back + + if (fabs(sDelta[X_I]) > .1f) + { + agentSlide(sDelta[X_I]); // move sideways + } + + if (fabs(sDelta[Y_I]) > .1f) + { + agentFly(sDelta[Y_I]); // up/down & crouch + } + + // too many rotations during walking can be confusing, so apply + // the deadzones one more time (quick & dirty), at 50%|30% power + F32 eff_rx = .3f * dead_zone[RX_I]; + F32 eff_ry = .3f * dead_zone[RY_I]; + + if (sDelta[RX_I] > 0) + { + eff_rx = llmax(sDelta[RX_I] - eff_rx, 0.f); + } + else + { + eff_rx = llmin(sDelta[RX_I] + eff_rx, 0.f); + } + + if (sDelta[RY_I] > 0) + { + eff_ry = llmax(sDelta[RY_I] - eff_ry, 0.f); + } + else + { + eff_ry = llmin(sDelta[RY_I] + eff_ry, 0.f); + } + + + if (fabs(eff_rx) > 0.f || fabs(eff_ry) > 0.f) + { + if (gAgent.getFlying()) + { + agentPitch(eff_rx); + agentYaw(eff_ry); + } + else + { + agentPitch(eff_rx); + agentYaw(2.f * eff_ry); + } + } + } + else + { + agentSlide(sDelta[X_I]); // move sideways + agentFly(sDelta[Y_I]); // up/down & crouch + agentPush(sDelta[Z_I]); // forward/back + agentPitch(sDelta[RX_I]); // pitch + agentYaw(sDelta[RY_I]); // turn + } +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::moveFlycam(bool reset) +{ + static LLQuaternion sFlycamRotation; + static LLVector3 sFlycamPosition; + static F32 sFlycamZoom; + + if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED + || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled")) + { + return; + } + + S32 axis[] = + { + gSavedSettings.getS32("JoystickAxis0"), + gSavedSettings.getS32("JoystickAxis1"), + gSavedSettings.getS32("JoystickAxis2"), + gSavedSettings.getS32("JoystickAxis3"), + gSavedSettings.getS32("JoystickAxis4"), + gSavedSettings.getS32("JoystickAxis5"), + gSavedSettings.getS32("JoystickAxis6") + }; + + bool in_build_mode = LLToolMgr::getInstance()->inBuildMode(); + if (reset || mResetFlag) + { + sFlycamPosition = LLViewerCamera::getInstance()->getOrigin(); + sFlycamRotation = LLViewerCamera::getInstance()->getQuaternion(); + sFlycamZoom = LLViewerCamera::getInstance()->getView(); + + resetDeltas(axis); + + return; + } + + F32 axis_scale[] = + { + gSavedSettings.getF32("FlycamAxisScale0"), + gSavedSettings.getF32("FlycamAxisScale1"), + gSavedSettings.getF32("FlycamAxisScale2"), + gSavedSettings.getF32("FlycamAxisScale3"), + gSavedSettings.getF32("FlycamAxisScale4"), + gSavedSettings.getF32("FlycamAxisScale5"), + gSavedSettings.getF32("FlycamAxisScale6") + }; + + F32 dead_zone[] = + { + gSavedSettings.getF32("FlycamAxisDeadZone0"), + gSavedSettings.getF32("FlycamAxisDeadZone1"), + gSavedSettings.getF32("FlycamAxisDeadZone2"), + gSavedSettings.getF32("FlycamAxisDeadZone3"), + gSavedSettings.getF32("FlycamAxisDeadZone4"), + gSavedSettings.getF32("FlycamAxisDeadZone5"), + gSavedSettings.getF32("FlycamAxisDeadZone6") + }; + + F32 time = gFrameIntervalSeconds.value(); + + // avoid making ridiculously big movements if there's a big drop in fps + if (time > .2f) + { + time = .2f; + } + + F32 cur_delta[7]; + F32 feather = gSavedSettings.getF32("FlycamFeathering"); + bool absolute = gSavedSettings.getBOOL("Cursor3D"); + bool is_zero = true; + + for (U32 i = 0; i < 7; i++) + { + cur_delta[i] = -getJoystickAxis(axis[i]); + + + F32 tmp = cur_delta[i]; + if (absolute) + { + cur_delta[i] = cur_delta[i] - sLastDelta[i]; + } + sLastDelta[i] = tmp; + + if (cur_delta[i] > 0) + { + cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f); + } + else + { + cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f); + } + + // We may want to scale camera movements up or down in build mode. + // NOTE: this needs to remain after the deadzone calculation, otherwise + // we have issues with flycam "jumping" when the build dialog is opened/closed -Nyx + if (in_build_mode) + { + if (i == X_I || i == Y_I || i == Z_I) + { + static LLCachedControl<F32> build_mode_scale(gSavedSettings,"FlycamBuildModeScale", 1.0); + cur_delta[i] *= build_mode_scale; + } + } + + cur_delta[i] *= axis_scale[i]; + + if (!absolute) + { + cur_delta[i] *= time; + } + + sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather; + + is_zero = is_zero && (cur_delta[i] == 0.f); + + } + + // Clear AFK state if moved beyond the deadzone + if (!is_zero && gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + sFlycamPosition += LLVector3(sDelta) * sFlycamRotation; + + LLMatrix3 rot_mat(sDelta[3], sDelta[4], sDelta[5]); + sFlycamRotation = LLQuaternion(rot_mat)*sFlycamRotation; + + if (gSavedSettings.getBOOL("AutoLeveling")) + { + LLMatrix3 level(sFlycamRotation); + + LLVector3 x = LLVector3(level.mMatrix[0]); + LLVector3 y = LLVector3(level.mMatrix[1]); + LLVector3 z = LLVector3(level.mMatrix[2]); + + y.mV[2] = 0.f; + y.normVec(); + + level.setRows(x,y,z); + level.orthogonalize(); + + LLQuaternion quat(level); + sFlycamRotation = nlerp(llmin(feather*time,1.f), sFlycamRotation, quat); + } + + if (gSavedSettings.getBOOL("ZoomDirect")) + { + sFlycamZoom = sLastDelta[6]*axis_scale[6]+dead_zone[6]; + } + else + { + sFlycamZoom += sDelta[6]; + } + + LLMatrix3 mat(sFlycamRotation); + + LLViewerCamera::getInstance()->setView(sFlycamZoom); + LLViewerCamera::getInstance()->setOrigin(sFlycamPosition); + LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]); + LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]); + LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]); +} + +// ----------------------------------------------------------------------------- +bool LLViewerJoystick::toggleFlycam() +{ + if (!gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled")) + { + mOverrideCamera = false; + return false; + } + + if (!mOverrideCamera) + { + gAgentCamera.changeCameraToDefault(); + } + + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + mOverrideCamera = !mOverrideCamera; + if (mOverrideCamera) + { + moveFlycam(true); + + } + else + { + // Exiting from the flycam mode: since we are going to keep the flycam POV for + // the main camera until the avatar moves, we need to track this situation. + setCameraNeedsUpdate(false); + setNeedsReset(true); + } + return true; +} + +void LLViewerJoystick::scanJoystick() +{ + if (mDriverState != JDS_INITIALIZED || !gSavedSettings.getBOOL("JoystickEnabled")) + { + return; + } + +#if LL_WINDOWS + // On windows, the flycam is updated syncronously with a timer, so there is + // no need to update the status of the joystick here. + if (!mOverrideCamera) +#endif + updateStatus(); + + // App focus check Needs to happen AFTER updateStatus in case the joystick + // is not centred when the app loses focus. + if (!gFocusMgr.getAppHasFocus()) + { + return; + } + + static long toggle_flycam = 0; + + if (mBtn[0] == 1) + { + if (mBtn[0] != toggle_flycam) + { + toggle_flycam = toggleFlycam() ? 1 : 0; + } + } + else + { + toggle_flycam = 0; + } + + if (!mOverrideCamera && !(LLToolMgr::getInstance()->inBuildMode() && gSavedSettings.getBOOL("JoystickBuildEnabled"))) + { + moveAvatar(); + } +} + +// ----------------------------------------------------------------------------- +bool LLViewerJoystick::isDeviceUUIDSet() +{ +#if LL_WINDOWS && !LL_MESA_HEADLESS + // for ease of comparison and to dial less with platform specific variables, we store id as LLSD binary + return mLastDeviceUUID.isBinary(); +#elif LL_DARWIN + return mLastDeviceUUID.isMap(); +#else + return false; +#endif +} + +LLSD LLViewerJoystick::getDeviceUUID() +{ + return mLastDeviceUUID; +} + +std::string LLViewerJoystick::getDeviceUUIDString() +{ +#if LL_WINDOWS && !LL_MESA_HEADLESS + // Might be simpler to just convert _GUID into string everywhere, store and compare as string + if (mLastDeviceUUID.isBinary()) + { + S32 size = sizeof(GUID); + LLSD::Binary data = mLastDeviceUUID.asBinary(); + GUID guid; + memcpy(&guid, &data[0], size); + return string_from_guid(guid); + } + else + { + return std::string(); + } +#elif LL_DARWIN + if (mLastDeviceUUID.isMap()) + { + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); + return manufacturer + ":" + product; + } + else + { + return std::string(); + } +#else + return std::string(); +#endif +} + +void LLViewerJoystick::saveDeviceIdToSettings() +{ +#if LL_WINDOWS && !LL_MESA_HEADLESS + // can't save as binary directly, + // someone editing the xml will corrupt it + // so convert to string first + std::string device_string = getDeviceUUIDString(); + gSavedSettings.setLLSD("JoystickDeviceUUID", LLSD(device_string)); +#else + LLSD device_id = getDeviceUUID(); + gSavedSettings.setLLSD("JoystickDeviceUUID", device_id); +#endif +} + +void LLViewerJoystick::loadDeviceIdFromSettings() +{ + LLSD dev_id = gSavedSettings.getLLSD("JoystickDeviceUUID"); +#if LL_WINDOWS && !LL_MESA_HEADLESS + // We can't save binary data to gSavedSettings, somebody editing the file will corrupt it, + // so _GUID data gets converted to string (we probably can convert it to LLUUID with memcpy) + // and here we need to convert it back to binary from string + std::string device_string; + if (dev_id.isString()) + { + device_string = dev_id.asString(); + } + if (device_string.empty()) + { + mLastDeviceUUID = LLSD(); + } + else + { + LL_DEBUGS("Joystick") << "Looking for device by id: " << device_string << LL_ENDL; + GUID guid; + guid_from_string(guid, device_string); + S32 size = sizeof(GUID); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &guid /*POD _GUID*/, size); + // We store this data in LLSD since it can handle both GUID2 and long + mLastDeviceUUID = LLSD(data); + } +#elif LL_DARWIN + if (!dev_id.isMap()) + { + mLastDeviceUUID = LLSD(); + } + else + { + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); + LL_DEBUGS("Joystick") << "Looking for device by manufacturer: " << manufacturer << " and product: " << product << LL_ENDL; + // We store this data in LLSD since it can handle both GUID2 and long + mLastDeviceUUID = dev_id; + } +#else + mLastDeviceUUID = LLSD(); + //mLastDeviceUUID = gSavedSettings.getLLSD("JoystickDeviceUUID"); +#endif +} + +// ----------------------------------------------------------------------------- +std::string LLViewerJoystick::getDescription() +{ + std::string res; +#if LIB_NDOF + if (mDriverState == JDS_INITIALIZED && mNdofDev) + { + res = ll_safe_string(mNdofDev->product); + } +#endif + return res; +} + +bool LLViewerJoystick::isLikeSpaceNavigator() const +{ +#if LIB_NDOF + return (isJoystickInitialized() + && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0 + || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0 + || strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0 + || strncmp(mNdofDev->product, "SpacePilot", 10) == 0)); +#else + return false; +#endif +} + +// ----------------------------------------------------------------------------- +void LLViewerJoystick::setSNDefaults() +{ +#if LL_DARWIN || LL_LINUX + const float platformScale = 20.f; + const float platformScaleAvXZ = 1.f; + // The SpaceNavigator doesn't act as a 3D cursor on macOS / Linux. + const bool is_3d_cursor = false; +#else + const float platformScale = 1.f; + const float platformScaleAvXZ = 2.f; + const bool is_3d_cursor = true; +#endif + + //gViewerWindow->alertXml("CacheWillClear"); + LL_INFOS("Joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL; + + gSavedSettings.setS32("JoystickAxis0", 1); // z (at) + gSavedSettings.setS32("JoystickAxis1", 0); // x (slide) + gSavedSettings.setS32("JoystickAxis2", 2); // y (up) + gSavedSettings.setS32("JoystickAxis3", 4); // pitch + gSavedSettings.setS32("JoystickAxis4", 3); // roll + gSavedSettings.setS32("JoystickAxis5", 5); // yaw + gSavedSettings.setS32("JoystickAxis6", -1); + + gSavedSettings.setBOOL("Cursor3D", is_3d_cursor); + gSavedSettings.setBOOL("AutoLeveling", true); + gSavedSettings.setBOOL("ZoomDirect", false); + + gSavedSettings.setF32("AvatarAxisScale0", 1.f * platformScaleAvXZ); + gSavedSettings.setF32("AvatarAxisScale1", 1.f * platformScaleAvXZ); + gSavedSettings.setF32("AvatarAxisScale2", 1.f); + gSavedSettings.setF32("AvatarAxisScale4", .1f * platformScale); + gSavedSettings.setF32("AvatarAxisScale5", .1f * platformScale); + gSavedSettings.setF32("AvatarAxisScale3", 0.f * platformScale); + gSavedSettings.setF32("BuildAxisScale1", .3f * platformScale); + gSavedSettings.setF32("BuildAxisScale2", .3f * platformScale); + gSavedSettings.setF32("BuildAxisScale0", .3f * platformScale); + gSavedSettings.setF32("BuildAxisScale4", .3f * platformScale); + gSavedSettings.setF32("BuildAxisScale5", .3f * platformScale); + gSavedSettings.setF32("BuildAxisScale3", .3f * platformScale); + gSavedSettings.setF32("FlycamAxisScale1", 2.f * platformScale); + gSavedSettings.setF32("FlycamAxisScale2", 2.f * platformScale); + gSavedSettings.setF32("FlycamAxisScale0", 2.1f * platformScale); + gSavedSettings.setF32("FlycamAxisScale4", .1f * platformScale); + gSavedSettings.setF32("FlycamAxisScale5", .15f * platformScale); + gSavedSettings.setF32("FlycamAxisScale3", 0.f * platformScale); + gSavedSettings.setF32("FlycamAxisScale6", 0.f * platformScale); + + gSavedSettings.setF32("AvatarAxisDeadZone0", .1f); + gSavedSettings.setF32("AvatarAxisDeadZone1", .1f); + gSavedSettings.setF32("AvatarAxisDeadZone2", .1f); + gSavedSettings.setF32("AvatarAxisDeadZone3", 1.f); + gSavedSettings.setF32("AvatarAxisDeadZone4", .02f); + gSavedSettings.setF32("AvatarAxisDeadZone5", .01f); + gSavedSettings.setF32("BuildAxisDeadZone0", .01f); + gSavedSettings.setF32("BuildAxisDeadZone1", .01f); + gSavedSettings.setF32("BuildAxisDeadZone2", .01f); + gSavedSettings.setF32("BuildAxisDeadZone3", .01f); + gSavedSettings.setF32("BuildAxisDeadZone4", .01f); + gSavedSettings.setF32("BuildAxisDeadZone5", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone0", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone1", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone2", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone3", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone4", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone5", .01f); + gSavedSettings.setF32("FlycamAxisDeadZone6", 1.f); + + gSavedSettings.setF32("AvatarFeathering", 6.f); + gSavedSettings.setF32("BuildFeathering", 12.f); + gSavedSettings.setF32("FlycamFeathering", 5.f); +} |