diff options
author | Davide Beatrici <git@davidebeatrici.dev> | 2021-07-25 01:34:27 +0300 |
---|---|---|
committer | Robert Adam <dev@robert-adam.de> | 2021-07-26 09:01:34 +0300 |
commit | 44c4f41d3e5f3c8f83378781f3b5fc16a7883a65 (patch) | |
tree | 07b197502e19ce2296e1a283202b5a12de60ab92 | |
parent | c54796060773a5b1fb17ba73ffb5c11b2697ef86 (diff) |
FIX(client): Migrate Windows shortcuts from versions older than 1.4.0 properly
After 3dc7b00d0919cf2400071deba54ff7f64ee3d9ea, shortcuts saved in the old format would just show up as "Unknown" and not work.
This commit implements a proper migration mechanism, making the shortcuts work without any manual steps.
Mice, keyboards, Xbox (XInput) controllers and Logitech G-keys devices are supported.
Unfortunately shortcuts will have to be registered again for DirectInput devices such as controllers.
The migration is performed silently, unless not 100% successful.
In that case a message box appears informing the user about how many shortcuts could not be migrated.
Please note that running a version of Mumble that uses the old format after the migration results in corruption.
As a result, shortcuts would have to be registered again.
Fixes #5025.
-rw-r--r-- | src/mumble/GlobalShortcut_win.cpp | 101 | ||||
-rw-r--r-- | src/mumble/GlobalShortcut_win.h | 4 | ||||
-rw-r--r-- | src/mumble/main.cpp | 43 |
3 files changed, 134 insertions, 14 deletions
diff --git a/src/mumble/GlobalShortcut_win.cpp b/src/mumble/GlobalShortcut_win.cpp index 0d3c989e3..b60155848 100644 --- a/src/mumble/GlobalShortcut_win.cpp +++ b/src/mumble/GlobalShortcut_win.cpp @@ -16,10 +16,10 @@ #include <codecvt> #include <iomanip> +#include <sstream> #include <QTimer> - -#include <sstream> +#include <QUuid> extern "C" { // clang-format off @@ -208,6 +208,103 @@ void GlobalShortcutWin::registerMetaTypes() { } } +QList< Shortcut > GlobalShortcutWin::migrateSettings(const QList< Shortcut > &oldShortcuts) { + constexpr QUuid keyboardUuid(0x6F1D2B61, 0xD5A0, 0x11CF, 0xBF, 0xC7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00); + constexpr QUuid mouseUuid(0x6F1D2B60, 0xD5A0, 0x11CF, 0xBF, 0xC7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00); + constexpr QUuid xinputUuid(0xCA3937E3, 0x640C, 0x4D9E, 0x9E, 0xF3, 0x90, 0x3F, 0x8B, 0x4F, 0xBC, 0xAB); + constexpr QUuid gkeyKeyboardUuid(0x153E64E6, 0x98C8, 0x4E, 0x03, 0x80EF, 0x5F, 0xFD, 0x33, 0xD2, 0x5B, 0x8A); + constexpr QUuid gkeyMouseUuid(0xC41E60AF, 0x9022, 0x46CF, 0xBC, 0x39, 0x37, 0x98, 0x10, 0x82, 0xD7, 0x16); + + QList< Shortcut > newShortcuts; + + for (Shortcut shortcut : oldShortcuts) { + bool ok = true; + + for (QVariant &button : shortcut.qlButtons) { + if (!button.isValid()) { + // This happens when the user runs a version that uses + // the old format after the migration is performed. + ok = false; + break; + } + + if (button.userType() == qMetaTypeId< InputHid >() || button.userType() == qMetaTypeId< InputKeyboard >() + || button.userType() == qMetaTypeId< InputMouse >() || button.userType() == qMetaTypeId< InputXinput >() + || button.userType() == qMetaTypeId< InputGkey >()) { + // Already in the new format. + continue; + } + + const QVariantList entries = button.toList(); + if (entries.size() < 2) { + ok = false; + break; + } + + auto value = entries.at(0).toUInt(&ok); + if (!ok) { + break; + } + + const auto uuid = entries.at(1).toUuid(); + if (uuid == keyboardUuid) { + InputKeyboard input; + input.code = (value & ~0x8000U) >> 8; + input.e0 = value & 0x8000U; + + // With DirectInput the extended bit is: + // - Set for the Pause key. + // - Unset for the Numlock key. + // With raw input it's the opposite. + if (input.code == 0x45) { + input.e0 = !input.e0; + } + + button = QVariant::fromValue(input); + } else if (uuid == mouseUuid) { + value >>= 8; + if (value < 3 || value > 7) { + ok = false; + break; + } + + button = QVariant::fromValue(static_cast< InputMouse >(value - 2)); +#ifdef USE_XBOXINPUT + } else if (uuid == xinputUuid) { + InputXinput input; + input.device = (value >> 24) & 0xFF; + input.code = value & 0x00FFFFFF; + + button = QVariant::fromValue(input); +#endif +#ifdef USE_GKEY + } else if (uuid == gkeyKeyboardUuid) { + InputGkey input = {}; + input.keyboard = true; + input.mode = value >> 16; + input.button = value & 0xFFFF; + + button = QVariant::fromValue(input); + } else if (uuid == gkeyMouseUuid) { + InputGkey input = {}; + input.button = value; + + button = QVariant::fromValue(input); +#endif + } else { + ok = false; + break; + } + } + + if (ok) { + newShortcuts << shortcut; + } + } + + return newShortcuts; +} + GlobalShortcutWin::GlobalShortcutWin() #ifdef USE_XBOXINPUT : m_xinputDevices(0), m_xinputLastPacket() diff --git a/src/mumble/GlobalShortcut_win.h b/src/mumble/GlobalShortcut_win.h index a50d18e45..cd39d7116 100644 --- a/src/mumble/GlobalShortcut_win.h +++ b/src/mumble/GlobalShortcut_win.h @@ -26,6 +26,10 @@ class GlobalShortcutWin : public GlobalShortcutEngine { public: static void registerMetaTypes(); + /// @param oldShortcuts List of shortcuts to migrate. + /// @returns List of shortcuts in the new format. + static QList< Shortcut > migrateSettings(const QList< Shortcut > &oldShortcuts); + /// Inject a native Windows raw input message into GlobalShortcutWin's /// thread. This method is meant to be called from the main thread /// to pass native Windows keyboard messages to GlobalShortcutWin. diff --git a/src/mumble/main.cpp b/src/mumble/main.cpp index 88e8e2455..6c485c4bc 100644 --- a/src/mumble/main.cpp +++ b/src/mumble/main.cpp @@ -12,6 +12,9 @@ #include "Cert.h" #include "Database.h" #include "DeveloperConsole.h" +#ifdef Q_OS_WIN +# include "GlobalShortcut_win.h" +#endif #include "LCD.h" #include "Log.h" #include "LogEmitter.h" @@ -706,19 +709,35 @@ int main(int argc, char **argv) { // Configuration updates bool runaudiowizard = false; - if (Global::get().s.uiUpdateCounter == 0) { - // Previous version was an pre 1.2.3 release or this is the first run - runaudiowizard = true; - - } else if (Global::get().s.uiUpdateCounter == 1) { - // Previous versions used old idle action style, convert it + switch (Global::get().s.uiUpdateCounter) { + case 0: + // Previous version was an pre 1.2.3 release or this is the first run + runaudiowizard = true; + // Fallthrough + case 1: + // Previous versions used old idle action style, convert it + if (Global::get().s.iIdleTime == 5 * 60) { // New default + Global::get().s.iaeIdleAction = Settings::Nothing; + } else { + Global::get().s.iIdleTime = 60 * qRound(Global::get().s.iIdleTime / 60.); // Round to minutes + Global::get().s.iaeIdleAction = Settings::Deafen; // Old behavior + } + // Fallthrough +#ifdef Q_OS_WIN + case 2: { + QList< Shortcut > &shortcuts = Global::get().s.qlShortcuts; + const QList< Shortcut > migratedShortcuts = GlobalShortcutWin::migrateSettings(shortcuts); + if (shortcuts.size() > migratedShortcuts.size()) { + const uint32_t num = shortcuts.size() - migratedShortcuts.size(); + QMessageBox::warning( + nullptr, QObject::tr("Shortcuts migration incomplete"), + QObject::tr("Unfortunately %1 shortcut(s) could not be migrated.\nYou can register them again.") + .arg(num)); + } - if (Global::get().s.iIdleTime == 5 * 60) { // New default - Global::get().s.iaeIdleAction = Settings::Nothing; - } else { - Global::get().s.iIdleTime = 60 * qRound(Global::get().s.iIdleTime / 60.); // Round to minutes - Global::get().s.iaeIdleAction = Settings::Deafen; // Old behavior + shortcuts = migratedShortcuts; } +#endif } if (runaudiowizard) { @@ -727,7 +746,7 @@ int main(int argc, char **argv) { delete aw; } - Global::get().s.uiUpdateCounter = 2; + Global::get().s.uiUpdateCounter = 3; if (!CertWizard::validateCert(Global::get().s.kpCertificate)) { QFile qf(qdCert.absoluteFilePath(QLatin1String("MumbleAutomaticCertificateBackup.p12"))); |