From da967da9c7aa0d88f478d476e8bb059ba79ca818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Thu, 11 Jan 2024 11:32:15 +0100 Subject: Rename OS X to macOS, mostly in comments We only support 10.13+ now, and it's been called macOS since 10.12. References in code to older versions are unchanged. --- indra/newview/llviewerjoystick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index e35cb26ce1..dfa47e82b3 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -1414,7 +1414,7 @@ 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 OS X / Linux. + // The SpaceNavigator doesn't act as a 3D cursor on macOS / Linux. const bool is_3d_cursor = false; #else const float platformScale = 1.f; -- cgit v1.2.3 From 8c16ec2b53153a10f40181e0e8108d24331451d4 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 13:57:07 +0100 Subject: Convert BOOL to bool in LLControlGroup and related classes --- indra/newview/llviewerjoystick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index dfa47e82b3..5be3ddad6f 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -234,14 +234,14 @@ void LLViewerJoystick::updateEnabled(bool autoenable) { if (mDriverState == JDS_UNINITIALIZED) { - gSavedSettings.setBOOL("JoystickEnabled", FALSE ); + gSavedSettings.setBOOL("JoystickEnabled", false); } else { // autoenable if user specifically chose this device if (autoenable && (isLikeSpaceNavigator() || isDeviceUUIDSet())) { - gSavedSettings.setBOOL("JoystickEnabled", TRUE ); + gSavedSettings.setBOOL("JoystickEnabled", true ); } } if (!gSavedSettings.getBOOL("JoystickEnabled")) -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/llviewerjoystick.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 5be3ddad6f..a53b8ebd88 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -246,7 +246,7 @@ void LLViewerJoystick::updateEnabled(bool autoenable) } if (!gSavedSettings.getBOOL("JoystickEnabled")) { - mOverrideCamera = FALSE; + mOverrideCamera = false; } } @@ -254,7 +254,7 @@ void LLViewerJoystick::setOverrideCamera(bool val) { if (!gSavedSettings.getBOOL("JoystickEnabled")) { - mOverrideCamera = FALSE; + mOverrideCamera = false; } else { @@ -882,7 +882,7 @@ void LLViewerJoystick::moveAvatar(bool reset) else if (!button_held) { button_held = true; - gAgent.setFlying(FALSE); + gAgent.setFlying(false); } } else if (!button_held) -- cgit v1.2.3 From f9473e8afcb624cc1b101195bf15943ec372b56f Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/newview/llviewerjoystick.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index a53b8ebd88..0aba982ba9 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -342,7 +342,7 @@ void LLViewerJoystick::init(bool autoenable) loadDeviceIdFromSettings(); - if (libinit == false) + if (!libinit) { // Note: The HotPlug callbacks are not actually getting called on Windows if (ndof_libinit(HotPlugAddCallback, @@ -401,10 +401,10 @@ void LLViewerJoystick::init(bool autoenable) // Autoenable the joystick for recognized devices if nothing was connected previously if (!autoenable) { - autoenable = gSavedSettings.getString("JoystickInitialized").empty() ? true : false; + autoenable = gSavedSettings.getString("JoystickInitialized").empty(); } updateEnabled(autoenable); - + if (mDriverState == JDS_INITIALIZED) { // A Joystick device is plugged in -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llviewerjoystick.cpp | 3202 ++++++++++++++++++------------------ 1 file changed, 1601 insertions(+), 1601 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') 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 -#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 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 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 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 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 +#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 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 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 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 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); +} -- cgit v1.2.3 From 9fdca96f8bd2211a99fe88e57b70cbecefa20b6d Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 8 Jul 2024 20:27:14 +0200 Subject: Re-enable compiler warnings C4244 and C4396 except for lltracerecording.h and llunittype.h for now --- indra/newview/llviewerjoystick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index ce6dfa4ad1..787ea02e4c 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -329,7 +329,7 @@ LLViewerJoystick::LLViewerJoystick() memset(mBtn, 0, sizeof(mBtn)); // factor in bandwidth? bandwidth = gViewerStats->mKBitStat - mPerfScale = 4000.f / gSysCPU.getMHz(); // hmm. why? + mPerfScale = 4000.f / (F32)gSysCPU.getMHz(); // hmm. why? mLastDeviceUUID = LLSD::Integer(1); } -- cgit v1.2.3