From 9b27b6e5098aee7a050d4c3f3f14050c509f74ec Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 14 Dec 2022 19:41:07 +0200 Subject: SL-13610 [MAC] WIP List HID available devices in joystick selection Doesn't filter the list yet, just shows full list of usb devices Selecting visible devices doesn't work yet --- indra/newview/llviewerjoystick.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index e35cb26ce1..26a626f60f 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -227,8 +227,17 @@ std::string string_from_guid(const GUID &guid) return res; } +#elif LL_DARWIN + +bool macos_devices_callback(std::string &product, LLSD::Binary &data, void* userdata) +{ + //LLViewerJoystick::getInstance()->initDevice(&device, product_name, data); + return false; +} + #endif + // ----------------------------------------------------------------------------- void LLViewerJoystick::updateEnabled(bool autoenable) { @@ -365,19 +374,21 @@ void LLViewerJoystick::init(bool autoenable) { if (mNdofDev) { + 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 U32 device_type = DI8DEVCLASS_GAMECTRL; - void* callback = &di8_devices_callback; + win_callback = &di8_devices_callback; #else // 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; + osx_callback = macos_devices_callback; #endif - if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL)) + if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL)) { LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL; // Failed to gather devices from windows, init first suitable one @@ -438,21 +449,23 @@ void LLViewerJoystick::initDevice(LLSD &guid) { #if LIB_NDOF mLastDeviceUUID = guid; + void* win_callback = nullptr; + std::function osx_callback; #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; + win_callback = &di8_devices_callback; #else // 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; + osx_callback = macos_devices_callback; #endif mDriverState = JDS_INITIALIZING; - if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL)) + if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL)) { LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL; // Failed to gather devices from windows, init first suitable one -- cgit v1.2.3 From 453a4a13f87d5cbfa6150a4f76f9e976692b54e6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 20 Dec 2022 17:44:08 +0200 Subject: SL-13610 [MAC] WIP enable initing devices by local id --- indra/newview/llviewerjoystick.cpp | 166 +++++++++++++++++++++++++++++-------- 1 file changed, 133 insertions(+), 33 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 26a626f60f..e182b31ed2 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -229,9 +229,17 @@ std::string string_from_guid(const GUID &guid) } #elif LL_DARWIN -bool macos_devices_callback(std::string &product, LLSD::Binary &data, void* userdata) +bool macos_devices_callback(std::string &product_name, LLSD::Binary &data, void* userdata) { - //LLViewerJoystick::getInstance()->initDevice(&device, product_name, data); + S32 size = sizeof(long); + long id; + memcpy(&id, &data[0], size); + + NDOF_Device *device = ndof_idsearch(id); + if (device) + { + return LLViewerJoystick::getInstance()->initDevice(device, data); + } return false; } @@ -374,27 +382,50 @@ void LLViewerJoystick::init(bool autoenable) { if (mNdofDev) { + U32 device_type = 0; void* win_callback = nullptr; - std::function osx_callback; + 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 - U32 device_type = DI8DEVCLASS_GAMECTRL; + device_type = DI8DEVCLASS_GAMECTRL; win_callback = &di8_devices_callback; -#else - // 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; +#elif LL_DARWIN osx_callback = macos_devices_callback; + + if (mLastDeviceUUID.isBinary()) + { + S32 size = sizeof(long); + long id; + memcpy(&id, &mLastDeviceUUID[0], size); + + NDOF_Device *device = ndof_idsearch(id); + if (device) + { + if (ndof_init_first(device, nullptr)) + { + mDriverState = JDS_INITIALIZING; + // Saved device no longer exist + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mNdofDev = device; + mDriverState = JDS_INITIALIZED; + } + } + } #endif - if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL)) + if (mDriverState != JDS_INITIALIZED) { - LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL; - // Failed to gather devices from windows, init first suitable one - mLastDeviceUUID = LLSD(); - void *preffered_device = NULL; - initDevice(preffered_device); + 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) @@ -449,29 +480,51 @@ void LLViewerJoystick::initDevice(LLSD &guid) { #if LIB_NDOF mLastDeviceUUID = guid; + U32 device_type = 0; void* win_callback = nullptr; - std::function osx_callback; - + std::function osx_callback; + mDriverState = JDS_INITIALIZING; + #if LL_WINDOWS && !LL_MESA_HEADLESS // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib - U32 device_type = DI8DEVCLASS_GAMECTRL; + device_type = DI8DEVCLASS_GAMECTRL; win_callback = &di8_devices_callback; -#else - // 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; +#elif LL_DARWIN osx_callback = macos_devices_callback; + if (mLastDeviceUUID.isBinary()) + { + S32 size = sizeof(long); + long id; + memcpy(&id, &mLastDeviceUUID[0], size); + + NDOF_Device *device = ndof_idsearch(id); + if (device) + { + if (ndof_init_first(device, nullptr)) + { + mDriverState = JDS_INITIALIZING; + // Saved device no longer exist + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mNdofDev = device; + mDriverState = JDS_INITIALIZED; + } + } + } #endif - mDriverState = JDS_INITIALIZING; - if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL)) + if (mDriverState != JDS_INITIALIZED) { - LL_INFOS("Joystick") << "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; - mLastDeviceUUID = LLSD(); - initDevice(preffered_device); + 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 windows, init first suitable one + void *preffered_device = NULL; + mLastDeviceUUID = LLSD(); + initDevice(preffered_device); + } } if (mDriverState == JDS_INITIALIZING) @@ -528,6 +581,25 @@ void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE #endif } +bool LLViewerJoystick::initDevice(NDOF_Device * ndof_device, LLSD::Binary &data) +{ + mLastDeviceUUID = data; +#if LIB_NDOF + if (ndof_init_first(ndof_device, nullptr)) + { + mDriverState = JDS_UNINITIALIZED; + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mNdofDev = ndof_device; + mDriverState = JDS_INITIALIZED; + return true; + } +#endif + return false; +} + // ----------------------------------------------------------------------------- void LLViewerJoystick::terminate() { @@ -1359,9 +1431,21 @@ std::string LLViewerJoystick::getDeviceUUIDString() { return std::string(); } +#elif LL_DARWIN + if (mLastDeviceUUID.isBinary()) + { + S32 size = sizeof(long); + LLSD::Binary data = mLastDeviceUUID.asBinary(); + long id; + memcpy(&id, &data[0], size); + return std::to_string(id); + } + else + { + return std::string(); + } #else return std::string(); - // return mLastDeviceUUID; #endif } @@ -1385,8 +1469,24 @@ void LLViewerJoystick::loadDeviceIdFromSettings() LLSD::Binary data; //just an std::vector data.resize(size); memcpy(&data[0], &guid /*POD _GUID*/, size); - // We store this data in LLSD since LLSD is versatile and will be able to handle both GUID2 - // and any data MAC will need for device selection + // We store this data in LLSD since it can handle both GUID2 and long + mLastDeviceUUID = LLSD(data); + } +#elif LL_DARWIN + std::string device_string = gSavedSettings.getString("JoystickDeviceUUID"); + if (device_string.empty()) + { + mLastDeviceUUID = LLSD(); + } + else + { + LL_DEBUGS("Joystick") << "Looking for device by id: " << device_string << LL_ENDL; + long id = std::stol(device_string); + S32 size = sizeof(long); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &id, size); + // We store this data in LLSD since it can handle both GUID2 and long mLastDeviceUUID = LLSD(data); } #else -- cgit v1.2.3 From 6481d36c69dbcafdb23cb27dc07aa047b3d7d64e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 27 Dec 2022 15:27:29 +0200 Subject: SL-13610 [MAC] WIP filter out incompatible devices --- indra/newview/llviewerjoystick.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index e182b31ed2..95cfa5cd78 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -399,6 +399,7 @@ void LLViewerJoystick::init(bool autoenable) long id; memcpy(&id, &mLastDeviceUUID[0], size); + // todo: search by manufcturer instead, locid might have changed NDOF_Device *device = ndof_idsearch(id); if (device) { @@ -500,6 +501,7 @@ void LLViewerJoystick::initDevice(LLSD &guid) NDOF_Device *device = ndof_idsearch(id); if (device) { + // todo: search by manufcturer instead, locid might have changed if (ndof_init_first(device, nullptr)) { mDriverState = JDS_INITIALIZING; -- cgit v1.2.3 From 3084f864176dffbc5da19e6f958a55513f63e795 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 29 Dec 2022 19:16:15 +0200 Subject: SL-13610 [MAC] Manufacturer based search --- indra/newview/llviewerjoystick.cpp | 175 +++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 86 deletions(-) (limited to 'indra/newview/llviewerjoystick.cpp') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 95cfa5cd78..ef96ea4493 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -229,18 +229,11 @@ std::string string_from_guid(const GUID &guid) } #elif LL_DARWIN -bool macos_devices_callback(std::string &product_name, LLSD::Binary &data, void* userdata) +bool macos_devices_callback(std::string &product_name, LLSD &data, void* userdata) { - S32 size = sizeof(long); - long id; - memcpy(&id, &data[0], size); + std::string product = data["product"].asString(); - NDOF_Device *device = ndof_idsearch(id); - if (device) - { - return LLViewerJoystick::getInstance()->initDevice(device, data); - } - return false; + return LLViewerJoystick::getInstance()->initDevice(nullptr, product, data); } #endif @@ -384,7 +377,7 @@ void LLViewerJoystick::init(bool autoenable) { U32 device_type = 0; void* win_callback = nullptr; - std::function osx_callback; + 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 @@ -393,27 +386,24 @@ void LLViewerJoystick::init(bool autoenable) #elif LL_DARWIN osx_callback = macos_devices_callback; - if (mLastDeviceUUID.isBinary()) + if (mLastDeviceUUID.isMap()) { - S32 size = sizeof(long); - long id; - memcpy(&id, &mLastDeviceUUID[0], size); + 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)); - // todo: search by manufcturer instead, locid might have changed - NDOF_Device *device = ndof_idsearch(id); - if (device) + if (ndof_init_first(mNdofDev, nullptr)) { - if (ndof_init_first(device, nullptr)) - { - mDriverState = JDS_INITIALIZING; - // Saved device no longer exist - LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; - } - else - { - mNdofDev = device; - mDriverState = JDS_INITIALIZED; - } + mDriverState = JDS_INITIALIZING; + // Saved device no longer exist + // No device found + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mDriverState = JDS_INITIALIZED; } } #endif @@ -483,7 +473,7 @@ void LLViewerJoystick::initDevice(LLSD &guid) mLastDeviceUUID = guid; U32 device_type = 0; void* win_callback = nullptr; - std::function osx_callback; + std::function osx_callback; mDriverState = JDS_INITIALIZING; #if LL_WINDOWS && !LL_MESA_HEADLESS @@ -492,27 +482,24 @@ void LLViewerJoystick::initDevice(LLSD &guid) win_callback = &di8_devices_callback; #elif LL_DARWIN osx_callback = macos_devices_callback; - if (mLastDeviceUUID.isBinary()) + if (mLastDeviceUUID.isMap()) { - S32 size = sizeof(long); - long id; - memcpy(&id, &mLastDeviceUUID[0], size); + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); - NDOF_Device *device = ndof_idsearch(id); - if (device) + strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer)); + strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product)); + + if (ndof_init_first(mNdofDev, nullptr)) { - // todo: search by manufcturer instead, locid might have changed - if (ndof_init_first(device, nullptr)) - { - mDriverState = JDS_INITIALIZING; - // Saved device no longer exist - LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; - } - else - { - mNdofDev = device; - mDriverState = JDS_INITIALIZED; - } + 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 @@ -522,7 +509,7 @@ void LLViewerJoystick::initDevice(LLSD &guid) 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 windows, init first suitable one + // Failed to gather devices from window, init first suitable one void *preffered_device = NULL; mLastDeviceUUID = LLSD(); initDevice(preffered_device); @@ -537,19 +524,37 @@ void LLViewerJoystick::initDevice(LLSD &guid) #endif } -void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid) +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 - initDevice(preffered_device); + return initDevice(preffered_device); +#else + return false; #endif } -void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */) +bool LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */) { #if LIB_NDOF // Different joysticks will return different ranges of raw values. @@ -579,23 +584,6 @@ void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE else { mDriverState = JDS_INITIALIZED; - } -#endif -} - -bool LLViewerJoystick::initDevice(NDOF_Device * ndof_device, LLSD::Binary &data) -{ - mLastDeviceUUID = data; -#if LIB_NDOF - if (ndof_init_first(ndof_device, nullptr)) - { - mDriverState = JDS_UNINITIALIZED; - LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; - } - else - { - mNdofDev = ndof_device; - mDriverState = JDS_INITIALIZED; return true; } #endif @@ -1407,6 +1395,8 @@ 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 @@ -1434,13 +1424,11 @@ std::string LLViewerJoystick::getDeviceUUIDString() return std::string(); } #elif LL_DARWIN - if (mLastDeviceUUID.isBinary()) + if (mLastDeviceUUID.isMap()) { - S32 size = sizeof(long); - LLSD::Binary data = mLastDeviceUUID.asBinary(); - long id; - memcpy(&id, &data[0], size); - return std::to_string(id); + std::string manufacturer = mLastDeviceUUID["manufacturer"].asString(); + std::string product = mLastDeviceUUID["product"].asString(); + return manufacturer + ":" + product; } else { @@ -1451,13 +1439,32 @@ std::string LLViewerJoystick::getDeviceUUIDString() #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 = gSavedSettings.getString("JoystickDeviceUUID"); + std::string device_string; + if (dev_id.isString()) + { + device_string = dev_id.asString(); + } if (device_string.empty()) { mLastDeviceUUID = LLSD(); @@ -1475,21 +1482,17 @@ void LLViewerJoystick::loadDeviceIdFromSettings() mLastDeviceUUID = LLSD(data); } #elif LL_DARWIN - std::string device_string = gSavedSettings.getString("JoystickDeviceUUID"); - if (device_string.empty()) + if (!dev_id.isMap()) { mLastDeviceUUID = LLSD(); } else { - LL_DEBUGS("Joystick") << "Looking for device by id: " << device_string << LL_ENDL; - long id = std::stol(device_string); - S32 size = sizeof(long); - LLSD::Binary data; //just an std::vector - data.resize(size); - memcpy(&data[0], &id, size); + 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 = LLSD(data); + mLastDeviceUUID = dev_id; } #else mLastDeviceUUID = LLSD(); -- cgit v1.2.3 From 92f2cf60896e0beb65ab5faaf6894b683121cdbe Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 23 Oct 2023 16:08:46 +0300 Subject: SL-13610 build fix --- 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 ef96ea4493..4577f71061 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -1446,7 +1446,7 @@ void LLViewerJoystick::saveDeviceIdToSettings() // someone editing the xml will corrupt it // so convert to string first std::string device_string = getDeviceUUIDString(); - gSavedSettings.setLLSD("JoystickDeviceUUID", LLSD(device_string); + gSavedSettings.setLLSD("JoystickDeviceUUID", LLSD(device_string)); #else LLSD device_id = getDeviceUUID(); gSavedSettings.setLLSD("JoystickDeviceUUID", device_id); -- cgit v1.2.3