mumble-voip_mumble/src/mumble/GKey.cpp

155 lines
6.1 KiB
C++

// Copyright 2015-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
/* SPECIFICATION
* The code interfacing with the Logitech G-Keys DLL was implemented using the
* following spec:
*
* The G-keys DLL lives in
* "C:\Program Files\Logitech Gaming Software\SDK\G-key\x64\LogitechGkey.dll" for x64 and
* "C:\Program Files\Logitech Gaming Software\SDK\G-key\x86\LogitechGkey.dll" for x86.
*
* Its location can also be read from the registry, using the following keys:
*
* x86:
* "HKEY_CLASSES_ROOT\CLSID\{7bded654-f278-4977-a20f-6e72a0d07859}\ServerBinary"
*
* x64:
* "HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{7bded654-f278-4977-a20f-6e72a0d07859}\ServerBinary"
*
* The registry keys are needed if a user installed the Logitech Gaming
* Software in a non-standard location.
*
* The DLL has an init function, it's called "LogiGkeyInit". It takes a
* pointer, but the parameter must always be nullptr. The function returns a BOOL
* as a status code.
*
* The DLL also has a shutdown function, called "LogiGkeyShutdown". It takes
* no parameters and does not return anything.
*
* You can poll for button states with the DLL using the functions
* "LogiGkeyIsMouseButtonPressed" and "LogiGkeyIsKeyboardGkeyPressed".
*
* The function "LogiGkeyIsMouseButtonPressed" takes a single int parameter, a
* button number. Mouse button numbers run from 6 up to and including 20. The
* function returns a BOOL that is true if the button is pressed, and false if
* not.
*
* The function "LogiGkeyIsKeyboardGkeyPressed" takes two int parameters, a
* button number and a mode number. Keyboard button numbers run from 1 up to
* and including 29. The mode number can 1, 2 or 3. The mode checks the button
* state in a specific mode. Typically, one queries all buttons for all modes,
* so one ends up with 29*3 calls to the function. The function returns a BOOL
* that is true if the button in the given mode is pressed, and false if not.
*
* There are also two functions, "LogiGkeyGetMouseButtonString" and
* "LogiGkeyGetKeyboardGkeyString". They take the same parameters as the
* polling functions above, but do not check whether the button is pressed or
* not. Instead, they return the name of the button being queried as a pointer
* to a NUL-terminated array of wchar_t's. Presumably, the pointer will be
* nullptr if the name cannot be retrieved or translated.
*/
/* USAGE
* In order to use the gkeys on a logitech keyboard, any user must have the
* Logitech Gaming Software version 8.55+ installed on their computer. Then
* (re)start mumble. When mumble initializes the library, the LGS (Logitech
* Gaming Software) will create a profile called "mumble", featuring the mumble
* icon. In LGS, right click this icon, and select either "Set as Default" or
* "Set as Persistent". (See "What are persistent and default profiles?" in the
* LGS help by clicking on the "?" icon on the LGS window, or at
* http://www.logitech.com/assets/51813/3/lgs-guide.pdf). If mumble is not set
* as the default or persistent profile, then your keys will not be active
* unless mumble is the active window.
*/
#include "GKey.h"
#ifdef Q_CC_GNU
# define RESOLVE(var) \
{ \
var = reinterpret_cast< __typeof__(var) >(qlLogiGkey.resolve(#var)); \
bValid = bValid && var; \
}
#else
# define RESOLVE(var) \
{ \
*reinterpret_cast< void ** >(&var) = static_cast< void * >(qlLogiGkey.resolve(#var)); \
bValid = bValid && var; \
}
#endif
const QUuid GKeyLibrary::quMouse = QUuid(QString::fromLatin1(GKEY_MOUSE_GUID));
const QUuid GKeyLibrary::quKeyboard = QUuid(QString::fromLatin1(GKEY_KEYBOARD_GUID));
GKeyLibrary::GKeyLibrary() {
QStringList alternatives;
HKEY key = nullptr;
DWORD type = 0;
WCHAR wcLocation[510];
DWORD len = 510;
if (RegOpenKeyEx(GKEY_LOGITECH_DLL_REG_HKEY, GKEY_LOGITECH_DLL_REG_PATH, 0, KEY_READ, &key) == ERROR_SUCCESS) {
LONG err = RegQueryValueEx(key, L"", nullptr, &type, reinterpret_cast< LPBYTE >(wcLocation), &len);
if (err == ERROR_SUCCESS && type == REG_SZ) {
QString qsLocation = QString::fromUtf16(reinterpret_cast< ushort * >(wcLocation), len / 2);
qWarning("GKeyLibrary: Found ServerBinary with libLocation = \"%s\", len = %lu", qPrintable(qsLocation),
static_cast< unsigned long >(len));
alternatives << qsLocation;
} else {
qWarning("GKeyLibrary: Error looking up ServerBinary (Error: 0x%lx, Type: 0x%lx, len: %lu)",
static_cast< unsigned long >(err), static_cast< unsigned long >(type),
static_cast< unsigned long >(len));
}
}
alternatives << QString::fromLatin1(GKEY_LOGITECH_DLL_DEFAULT_LOCATION);
foreach (const QString &lib, alternatives) {
qlLogiGkey.setFileName(lib);
if (qlLogiGkey.load()) {
bValid = true;
break;
}
}
RESOLVE(LogiGkeyInit);
RESOLVE(LogiGkeyShutdown);
RESOLVE(LogiGkeyIsMouseButtonPressed);
RESOLVE(LogiGkeyIsKeyboardGkeyPressed);
RESOLVE(LogiGkeyGetMouseButtonString);
RESOLVE(LogiGkeyGetKeyboardGkeyString);
if (bValid)
bValid = LogiGkeyInit(nullptr);
}
GKeyLibrary::~GKeyLibrary() {
if (LogiGkeyShutdown)
LogiGkeyShutdown();
}
bool GKeyLibrary::isValid() const {
return bValid;
}
bool GKeyLibrary::isMouseButtonPressed(int button) {
return LogiGkeyIsMouseButtonPressed(button);
}
bool GKeyLibrary::isKeyboardGkeyPressed(int key, int mode) {
return LogiGkeyIsKeyboardGkeyPressed(key, mode);
}
QString GKeyLibrary::getMouseButtonString(int button) {
return QString::fromWCharArray(LogiGkeyGetMouseButtonString(button));
}
QString GKeyLibrary::getKeyboardGkeyString(int key, int mode) {
return QString::fromWCharArray(LogiGkeyGetKeyboardGkeyString(key, mode));
}
#undef RESOLVE