From df986a402a2706fe12a7dae0caec181da8c0e5f0 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 7 Jul 2020 19:45:06 +0300 Subject: SL-5894 Changed joystick init to select device with apropriate manufacturer and type --- indra/llwindow/llwindow.h | 4 + indra/llwindow/llwindowwin32.cpp | 41 ++++++ indra/llwindow/llwindowwin32.h | 3 + indra/newview/llviewerjoystick.cpp | 251 ++++++++++++++++++++++++++++++++----- indra/newview/llviewerjoystick.h | 1 + 5 files changed, 270 insertions(+), 30 deletions(-) (limited to 'indra') diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index a05ba8cbba..90107a7c29 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -168,6 +168,10 @@ public: // Get system UI size based on DPI (for 96 DPI UI size should be 1.0) virtual F32 getSystemUISize() { return 1.0; } + + // windows only DirectInput8 for joysticks + virtual void* getDirectInput8() { return NULL; }; + virtual bool getInputDevices(U32 device_type_filter, void * devices_callback) { return false; }; protected: LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags); virtual ~LLWindow(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 0b3936f8a5..2bc9c8d63c 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -59,6 +59,9 @@ #include #include +#include +#pragma comment(lib, "dinput8") +#pragma comment(lib, "dxguid.lib") const S32 MAX_MESSAGE_PER_UPDATE = 20; const S32 BITS_PER_PIXEL = 32; @@ -76,6 +79,7 @@ const F32 ICON_FLASH_TIME = 0.5f; extern BOOL gDebugWindowProc; LPWSTR gIconResource = IDI_APPLICATION; +LPDIRECTINPUT8 gDirectInput8; LLW32MsgCallback gAsyncMsgCallback = NULL; @@ -482,6 +486,21 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, mhInstance = GetModuleHandle(NULL); mWndProc = NULL; + // Init Direct Input - needed for joystick / Spacemouse + + LPDIRECTINPUT8 di8_interface; + HRESULT status = DirectInput8Create( + mhInstance, // HINSTANCE hinst, + DIRECTINPUT_VERSION, // DWORD dwVersion, + IID_IDirectInput8, // REFIID riidltf, + (LPVOID*)&di8_interface, // LPVOID * ppvOut, + NULL // LPUNKNOWN punkOuter + ); + if (status == DI_OK) + { + gDirectInput8 = di8_interface; + } + mSwapMethod = SWAP_METHOD_UNDEFINED; // No WPARAM yet. @@ -4157,6 +4176,28 @@ void LLWindowWin32::setDPIAwareness() } } +void* LLWindowWin32::getDirectInput8() +{ + return &gDirectInput8; +} + +bool LLWindowWin32::getInputDevices(U32 device_type_filter, void * di8_devices_callback) +{ + if (gDirectInput8 != NULL) + { + // Enumerate devices + HRESULT status = gDirectInput8->EnumDevices( + (DWORD) device_type_filter, // DWORD dwDevType, + (LPDIENUMDEVICESCALLBACK)di8_devices_callback, // LPDIENUMDEVICESCALLBACK lpCallback, // BOOL DIEnumDevicesCallback( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef ) // BOOL CALLBACK DinputDevice::DevicesCallback + (LPVOID*)gDirectInput8, // LPVOID pvRef + DIEDFL_ATTACHEDONLY // DWORD dwFlags + ); + + return status == DI_OK; + } + return false; +} + F32 LLWindowWin32::getSystemUISize() { F32 scale_value = 1.f; diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 9cd16eb993..dc02528bf2 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -116,6 +116,9 @@ public: static std::vector getDynamicFallbackFontList(); static void setDPIAwareness(); + + /*virtual*/ void* getDirectInput8(); + /*virtual*/ bool getInputDevices(U32 device_type_filter, void * di8_devices_callback); protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index e44d80b7ce..9775bf646a 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -36,10 +36,22 @@ #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 +//#pragma comment(lib, "dinput8") +//#pragma comment(lib, "dxguid.lib") +#include +#endif + // ---------------------------------------------------------------------------- // Constants @@ -62,6 +74,152 @@ F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0}; #define MAX_SPACENAVIGATOR_INPUT 3000.0f #define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT +#if LL_WINDOWS && !LL_MESA_HEADLESS + +// took this from ndofdev_win.cpp +BOOL CALLBACK EnumNDOFObjectsCallback(const DIDEVICEOBJECTINSTANCE* inst, + VOID* user_data) +{ + if (inst->dwType & DIDFT_AXIS) + { + HRESULT hr = DI_OK; + 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; + hr = device->SetProperty(DIPROP_RANGE, &diprg.diph); + assert(hr == DI_OK); + + + if (FAILED(hr)) + return DIENUM_STOP; + } + + return DIENUM_CONTINUE; +} + +// vvvv Product ID +// 0xC62B046D +// ^^^^ Vendor ID +// 0x046D Logitech's Vendor ID +// 0x256F 3Dconnexion's Vendor ID +// +// See: +// https://github.com/janoc/libndofdev/blob/master/ndofdev.c +// http://spacemice.org/index.php?title=Dev +// https://www.3dconnexion.com/nc/service/faq/faq/how-can-i-check-if-my-usb-3d-mouse-is-recognized-by-windows.html +bool is_space_mouse(const U16 product_id, const U16 vendor_id) +{ + if (vendor_id == 0x046d) // Logitech's Vendor ID + { + if (false + || (product_id == 0xc603) // SpaceMouse Plus USB, SpaceMouse Plus XT USB + || (product_id == 0xc605) // CadMan: USB + || (product_id == 0xc606) // SpaceMouse Classic USB + || (product_id == 0xc621) // SpaceBall 5000 USB + || (product_id == 0xc623) // SpaceTraveler: USB + || (product_id == 0xc625) // SpacePilot: USB + || (product_id == 0xc626) // SpaceNavigator: USB + || (product_id == 0xc627) // SpaceExplorer: USB + || (product_id == 0xc628) // SpaceNavigator for Notebooks: USB + || (product_id == 0xc629) // SpacePilot Pro: USB + || (product_id == 0xc62b) // SpaceMouse Pro: USB + ) + { + return true; + } + } + else + if (vendor_id == 0x256F) // 3Dconnexion's Vendor ID + { + if (false + || (product_id == 0xc62E) // SpaceMouse Wireless (cabled) + || (product_id == 0xc62F) // SpaceMouse Wireless Receiver + || (product_id == 0xc631) // Spacemouse Wireless (cabled) + || (product_id == 0xc632) // SpacemousePro Wireless Receiver + || (product_id == 0xc633) // SpaceMouse Enterprise + || (product_id == 0xc635) // Spacemouse Compact + || (product_id == 0xc650) // CadMouse + || (product_id == 0xc651) // CadMouse Wireless + || (product_id == 0xc652) // Universal Receiver + || (product_id == 0xc654) // CadMouse Pro Wireless + || (product_id == 0xc657) // CadMouse Pro Wireless Left + ) + { + return true; + } + } + + return false; +} + +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 && pvRef) + { + std::string product_name = utf16str_to_utf8str(llutf16string(device_instance_ptr->tszProductName)); + + LL_DEBUGS("Joystick") << "DirectInput8 Devices: " << product_name << LL_ENDL; + + U16 product_id = (device_instance_ptr->guidProduct.Data1 >> 16) & 0xFFFF; + U16 vendor_id = (device_instance_ptr->guidProduct.Data1 >> 0) & 0xFFFF; + + if (!LLViewerJoystick::getInstance()->isJoystickInitialized()) + { + bool found_space_mouse = is_space_mouse(product_id, vendor_id); + + if (found_space_mouse) + { + LL_DEBUGS("Joystick") << "Found device that matches criteria: " << product_name << LL_ENDL; + LPDIRECTINPUT8 di8_interface = (LPDIRECTINPUT8)pvRef; + 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(EnumNDOFObjectsCallback, &device, DIDFT_ALL); + } + + // todo: record device name, ndof won't fill it from passed device + // strncpy(mNdofDev->product, inst->tszProductName, sizeof(mNdofDev->product)); + // strncpy(mNdofDev->product, product_name.c_str(), sizeof(mNdofDev->product)); + + if (status == DI_OK) + { + LL_DEBUGS("Joystick") << "Properties updated" << LL_ENDL; + LLViewerJoystick::getInstance()->initDevice(&device); + return DIENUM_STOP; + } + } + } + } + return DIENUM_CONTINUE; +} +#endif + // ----------------------------------------------------------------------------- void LLViewerJoystick::updateEnabled(bool autoenable) { @@ -175,7 +333,7 @@ void LLViewerJoystick::init(bool autoenable) // Note: The HotPlug callbacks are not actually getting called on Windows if (ndof_libinit(HotPlugAddCallback, HotPlugRemovalCallback, - NULL)) + gViewerWindow->getWindow()->getDirectInput8())) { mDriverState = JDS_UNINITIALIZED; } @@ -192,35 +350,34 @@ void LLViewerJoystick::init(bool autoenable) if (libinit) { if (mNdofDev) - { - // 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 - if (ndof_init_first(mNdofDev, NULL)) - { - mDriverState = JDS_UNINITIALIZED; - LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; - } - else - { - mDriverState = JDS_INITIALIZED; - } + { + // 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 + U32 device_type = DI8DEVCLASS_GAMECTRL; + void* callback = &di8_devices_callback; +#elif + // MAC doesn't support device search yet + // On MAC there is an ndof_idsearch and it is possible to specify product + // and manufacturer in NDOF_Device for ndof_init_first to pick specific one + U32 device_type = 0; + void* callback = NULL; +#endif + if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback)) + { + LL_INFOS() << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL; + // Failed to gather devices from windows, init first suitable one + void *preffered_device = NULL; + initDevice(preffered_device); + } + + // make sure a device was found: + if (mDriverState == JDS_INITIALIZING) + { + LL_INFOS() << "Found no suitable devices. Trying ndof's default init" << LL_ENDL; + void *preffered_device = NULL; + initDevice(preffered_device); + } } else { @@ -264,6 +421,40 @@ void LLViewerJoystick::init(bool autoenable) #endif } +void 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; + } +#endif +} + // ----------------------------------------------------------------------------- void LLViewerJoystick::terminate() { diff --git a/indra/newview/llviewerjoystick.h b/indra/newview/llviewerjoystick.h index 016b435ee8..9c59f811f1 100644 --- a/indra/newview/llviewerjoystick.h +++ b/indra/newview/llviewerjoystick.h @@ -50,6 +50,7 @@ class LLViewerJoystick : public LLSingleton public: void init(bool autoenable); + void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/); void terminate(); void updateStatus(); -- cgit v1.2.3