diff --git a/common/windows/cpp/include/registry.h b/common/windows/cpp/include/registry.h index 67cdfc2f69e..c6d1bf85ec1 100644 --- a/common/windows/cpp/include/registry.h +++ b/common/windows/cpp/include/registry.h @@ -110,6 +110,10 @@ #define REGSZ_KeyboardHotkeysAreToggle "hotkeys are toggles" #define REGSZ_DeadkeyConversionMode "deadkey conversion mode" // CU // I4552 #define REGSZ_ZapVirtualKeyCode "zap virtual key code" // LM, defaults to 0x0E (_VK_PREFIX_DEFAULT) +/* Non-chiral use of hotkeys instead of left-only hotkeys */ +#define REGSZ_UseRightModifierHotKey "use right modifier for hotkey" + + /* Debug flags These are all stored in HKCU\Software\Keyman\Debug @@ -121,13 +125,7 @@ #define REGSZ_Flag_ShouldSerializeInput "Flag_ShouldSerializeInput" -/* REGSZ_Keyman_Debug DWORD: Use old non-chiral Win32 API RegisterHotkey instead of left-only hotkeys */ - -#define REGSZ_Flag_UseRegisterHotkey "Flag_UseRegisterHotkey" - -/* REGSZ_Flag_UseCachedHotkeyModifierState DWORD: Use old cached modifier state when checking hotkeys; ignores UseRegisterHotkey if FALSE */ -#define REGSZ_Flag_UseCachedHotkeyModifierState "Flag_UseCachedHotkeyModifierState" /* DWORD: Enable/disable deep TSF integration, default enabled; 0 = disabled, 1 = enabled, 2 = default */ diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas index 47e7df754a0..0553b823065 100644 --- a/common/windows/delphi/general/RegistryKeys.pas +++ b/common/windows/delphi/general/RegistryKeys.pas @@ -115,6 +115,7 @@ interface SRegValue_AltGrCtrlAlt = 'simulate altgr'; // CU SRegValue_KeyboardHotKeysAreToggle = 'hotkeys are toggles'; // CU + SRegValue_UseRightModifierHotKey = 'use right modifier for hotkey'; // CU SRegValue_ReleaseShiftKeysAfterKeyPress = 'release shift keys after key press'; // CU SRegValue_TestKeymanFunctioning = 'test keyman functioning'; // CU, default true @@ -379,8 +380,6 @@ interface SRegKey_KeymanEngineDebug_CU = SRegKey_KeymanEngineRoot_CU + '\Debug'; - SRegValue_Flag_UseRegisterHotkey = 'Flag_UseRegisterHotkey'; - SRegValue_Flag_UseCachedHotkeyModifierState = 'Flag_UseCachedHotkeyModifierState'; SRegValue_Flag_ShouldSerializeInput = 'Flag_ShouldSerializeInput'; SRegValue_Flag_UseAutoStartTask = 'Flag_UseAutoStartTask'; SRegValue_Flag_SyncLanguagesToCloud = 'Flag_SyncLanguagesToCloud'; diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml index 1e1df09ce00..e15ba789453 100644 --- a/windows/src/desktop/kmshell/xml/strings.xml +++ b/windows/src/desktop/kmshell/xml/strings.xml @@ -377,6 +377,11 @@ Simulate AltGr with Ctrl+Alt + + + + Right Modifier keys work with Hotkeys + diff --git a/windows/src/engine/keyman/UfrmKeyman7Main.pas b/windows/src/engine/keyman/UfrmKeyman7Main.pas index 5034c0e664f..fbed70b24d0 100644 --- a/windows/src/engine/keyman/UfrmKeyman7Main.pas +++ b/windows/src/engine/keyman/UfrmKeyman7Main.pas @@ -2049,8 +2049,25 @@ procedure TfrmKeyman7Main.RegisterHotkeys; i: Integer; language: IKeymanLanguage; id: Integer; + RegistryErrorControlled: TRegistryErrorControlled;// I2890 + UseRegisterHotKey: Boolean; // Use Win32 API RegisterHotkey begin - if not Reg_GetDebugFlag(SRegValue_Flag_UseRegisterHotkey) then Exit; + RegistryErrorControlled := TRegistryErrorControlled.Create; + try + if RegistryErrorControlled.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and + RegistryErrorControlled.ValueExists(SRegValue_UseRightModifierHotKey) then + begin + UseRegisterHotKey := RegistryErrorControlled.ReadBool(SRegValue_UseRightModifierHotKey); + end + else + begin + UseRegisterHotKey := False; + end; + finally + RegistryErrorControlled.Free; + end; + + if not UseRegisterHotKey then Exit; TDebugLogClient.Instance.WriteMessage('Enter RegisterHotkeys', []); @@ -2103,8 +2120,25 @@ procedure TfrmKeyman7Main.RegisterHotkeys; procedure TfrmKeyman7Main.UnregisterHotkeys; var i, hk: Integer; + RegistryErrorControlled: TRegistryErrorControlled;// I2890 + UseRegisterHotKey: Boolean; // Use Win32 API RegisterHotkey begin - if not Reg_GetDebugFlag(SRegValue_Flag_UseRegisterHotkey) then Exit; + RegistryErrorControlled := TRegistryErrorControlled.Create; + try + if RegistryErrorControlled.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and + RegistryErrorControlled.ValueExists(SRegValue_UseRightModifierHotKey) then + begin + UseRegisterHotKey := RegistryErrorControlled.ReadBool(SRegValue_UseRightModifierHotKey); + end + else + begin + UseRegisterHotKey := False; + end; + finally + RegistryErrorControlled.Free; + end; + + if not UseRegisterHotKey then Exit; TDebugLogClient.Instance.WriteMessage('Enter UnregisterHotkeys', []); diff --git a/windows/src/engine/keyman32/hotkeys.cpp b/windows/src/engine/keyman32/hotkeys.cpp index 8327a786498..e7fdbe3d79d 100644 --- a/windows/src/engine/keyman32/hotkeys.cpp +++ b/windows/src/engine/keyman32/hotkeys.cpp @@ -137,9 +137,13 @@ void Hotkeys::Load() { // I4390 Hotkey *Hotkeys::GetHotkey(DWORD hotkey) { - for(int i = 0; i < m_nHotkeys; i++) - if(m_hotkeys[i].HotkeyValue == hotkey) - return &m_hotkeys[i]; + for (int i = 0; i < m_nHotkeys; i++) { + if (m_hotkeys[i].HotkeyValue == hotkey) { + SendDebugMessageFormat( + "LanguageHotkey[%d] = {HotkeyValue: %x, hkl: %x} passed in: %x", i, m_hotkeys[i].HotkeyValue, m_hotkeys[i].hkl, hotkey); + return &m_hotkeys[i]; + } + } return NULL; } diff --git a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp index 019f88b0737..2bdfae62e39 100644 --- a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp +++ b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp @@ -130,26 +130,24 @@ BOOL IsTouchPanelVisible() { } /* - Cache UseRegisterHotkey debug flag for this session + Cache UseRightModifierHotKey debug flag for this session + when using right modifier for Hotkeys use the Win32 API + ResisterHotkey functionality */ BOOL UseRegisterHotkey() { - static BOOL flag_UseRegisterHotkey = FALSE; + static BOOL flag_UseRightModifierHotKey = FALSE; static BOOL loaded = FALSE; - if (!loaded) { - loaded = TRUE; - flag_UseRegisterHotkey = Reg_GetDebugFlag(REGSZ_Flag_UseRegisterHotkey, FALSE); - } - return flag_UseRegisterHotkey; -} -BOOL UseCachedHotkeyModifierState() { - static BOOL flag_UseCachedHotkeyModifierState = FALSE; - static BOOL loaded = FALSE; if (!loaded) { - loaded = TRUE; - flag_UseCachedHotkeyModifierState = Reg_GetDebugFlag(REGSZ_Flag_UseCachedHotkeyModifierState, FALSE); + RegistryReadOnly reg(HKEY_CURRENT_USER); + if (reg.OpenKeyReadOnly(REGSZ_KeymanCU)) { + if (reg.ValueExists(REGSZ_UseRightModifierHotKey)) { + flag_UseRightModifierHotKey = !!reg.ReadInteger(REGSZ_UseRightModifierHotKey); + } + } + loaded = TRUE; // Set loaded to TRUE whether or not the key exists } - return flag_UseCachedHotkeyModifierState; + return flag_UseRightModifierHotKey; } LRESULT _kmnLowLevelKeyboardProc( @@ -171,60 +169,37 @@ LRESULT _kmnLowLevelKeyboardProc( SendDebugMessageFormat("wparam: %x lparam: %x [vk:%s scan:%x flags:%x extra:%x]", wParam, lParam, Debug_VirtualKey((WORD) hs->vkCode), hs->scanCode, hs->flags, hs->dwExtraInfo); // I4674 - DWORD Flag = 0; - if (!UseCachedHotkeyModifierState()) { - // #5190: Don't cache modifier state because sometimes we won't receive - // modifier change events (e.g. on lock screen) - FHotkeyShiftState = 0; - if (GetKeyState(VK_LCONTROL) < 0) FHotkeyShiftState |= HK_CTRL; - if (GetKeyState(VK_RCONTROL) < 0) FHotkeyShiftState |= HK_RCTRL_INVALID; - if (GetKeyState(VK_LMENU) < 0) FHotkeyShiftState |= HK_ALT; - if (GetKeyState(VK_RMENU) < 0) FHotkeyShiftState |= HK_RALT_INVALID; - if (GetKeyState(VK_LSHIFT) < 0) FHotkeyShiftState |= HK_SHIFT; - if (GetKeyState(VK_RSHIFT) < 0) FHotkeyShiftState |= HK_RSHIFT_INVALID; - //TODO: #8064. Can remove debug message once issue #8064 is resolved - SendDebugMessageFormat("!UseCachedHotkeyModifierState [FHotkeyShiftState:%x Flag:%x]", FHotkeyShiftState, Flag); + if (GetKeyState(VK_LCONTROL) < 0) { + FHotkeyShiftState |= HK_CTRL; + // TODO remove + SendDebugMessageFormat("ProcessHotkey VK_LCONTROL [vkCode:%x isUp:%d FHotkeyShiftState:%x useRight:%d", hs->vkCode, isUp, FHotkeyShiftState, UseRegisterHotkey()); } - else if (UseRegisterHotkey()) { - // The old RegisterHotkey pattern does not support chiral modifier keys - switch (hs->vkCode) { - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: Flag = HK_CTRL; break; - case VK_LMENU: - case VK_RMENU: - case VK_MENU: Flag = HK_ALT; break; - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: Flag = HK_SHIFT; break; - } - //TODO: #8064. Can remove debug message once issue #8064 is resolved - SendDebugMessageFormat("UseRegisterHotkey [FHotkeyShiftState:%x Flag:%x]", FHotkeyShiftState, Flag); + if (GetKeyState(VK_RCONTROL) < 0) { + FHotkeyShiftState |= UseRegisterHotkey() ? HK_CTRL : HK_RCTRL_INVALID; + // TODO remove + SendDebugMessageFormat("ProcessHotkey VK_RCONTROL [vkCode:%x isUp:%d FHotkeyShiftState:%x useRight:%d", hs->vkCode, isUp, FHotkeyShiftState, UseRegisterHotkey()); } - else { - // #4619: We differentiate between Left and Right Ctrl/Shift/Alt. The right modifiers are - // not used for hotkeys, leaving them available for use as keystroke modifiers within a - // Keyman keyboard. But we need to track them anyway, so that a user doesn't press them - // and receive a hotkey event when they shouldn't (e.g. RALT+F4 if the hotkey is F4). - switch (hs->vkCode) { - case VK_LCONTROL: Flag = HK_CTRL; break; - case VK_RCONTROL: Flag = HK_RCTRL_INVALID; break; - case VK_CONTROL: Flag = extended ? HK_RCTRL_INVALID : HK_CTRL; break; - case VK_LMENU: Flag = HK_ALT; break; - case VK_RMENU: Flag = HK_RALT_INVALID; break; - case VK_MENU: Flag = extended ? HK_RALT_INVALID : HK_ALT; break; - case VK_LSHIFT: Flag = HK_SHIFT; break; - case VK_RSHIFT: Flag = HK_RSHIFT_INVALID; break; - case VK_SHIFT: Flag = hs->scanCode == SCANCODE_RSHIFT ? HK_RSHIFT_INVALID : HK_SHIFT; break; - } + + if (GetKeyState(VK_LMENU) < 0) { + FHotkeyShiftState |= HK_ALT; } - if(Flag != 0) { - if(isUp) FHotkeyShiftState &= ~Flag; - else FHotkeyShiftState |= Flag; + if (GetKeyState(VK_RMENU) < 0) { + FHotkeyShiftState |= UseRegisterHotkey() ? HK_ALT : HK_RALT_INVALID; } + if (GetKeyState(VK_LSHIFT) < 0) { + FHotkeyShiftState |= HK_SHIFT; + } + if (GetKeyState(VK_RSHIFT) < 0) { + FHotkeyShiftState |= UseRegisterHotkey() ? HK_SHIFT : HK_RSHIFT_INVALID; + } + + //if (GetKeyState(VK_SHIFT) < 0) { + // FHotkeyShiftState |= (!UseRegisterHotkey() && extended) ? HK_RSHIFT_INVALID : HK_SHIFT; + //} + // #7337 Post the modifier state ensuring the serialized queue is in sync // Note that the modifier key may be posted again with WM_KEYMAN_KEY_EVENT, // later in this function. This is intentional, as the WM_KEYMAN_MODIFIER_EVENT @@ -304,31 +279,35 @@ LRESULT _kmnLowLevelKeyboardProc( } BOOL ProcessHotkey(UINT vkCode, BOOL isUp, DWORD ShiftState) { - if (UseRegisterHotkey()) { + if (UseRegisterHotkey()){ return FALSE; } - Hotkeys *hotkeys = Hotkeys::Instance(); // I4641 if (!hotkeys) { + SendDebugMessageFormat("Failed to get Instance"); return FALSE; } Hotkey *hotkey = hotkeys->GetHotkey(ShiftState | vkCode); // I4641 if (!hotkey) { + SendDebugMessageFormat("GetHotkey Null"); return FALSE; } if (isUp) { + SendDebugMessageFormat("Is Up"); return TRUE; } if (hotkey->HotkeyType == hktInterface) { + SendDebugMessageFormat("PostMasterController"); Globals::PostMasterController(wm_keyman_control, MAKELONG(KMC_INTERFACEHOTKEY, hotkey->Target), 0); } else { + SendDebugMessageFormat("ReportKeyboardChanged"); ReportKeyboardChanged(PC_HOTKEYCHANGE, hotkey->hkl == 0 ? TF_PROFILETYPE_INPUTPROCESSOR : TF_PROFILETYPE_KEYBOARDLAYOUT, 0, hotkey->hkl, GUID_NULL, hotkey->profileGUID); } - + SendDebugMessageFormat("PostDummyKeyEvent"); /* Generate a dummy keystroke to block menu activations, etc but let the shift key through */ PostDummyKeyEvent(); // I3301 - this is imperfect because we don't deal with HC_NOREMOVE. But good enough? // I3534 // I4844 diff --git a/windows/src/engine/kmcomapi/util/utilkeymanoption.pas b/windows/src/engine/kmcomapi/util/utilkeymanoption.pas index ce1b8af33a8..2df38b5c825 100644 --- a/windows/src/engine/kmcomapi/util/utilkeymanoption.pas +++ b/windows/src/engine/kmcomapi/util/utilkeymanoption.pas @@ -121,12 +121,13 @@ TKeymanOptionInfo = record GroupName: string; end; -const KeymanOptionInfo: array[0..15] of TKeymanOptionInfo = ( // I3331 // I3620 // I4552 +const KeymanOptionInfo: array[0..16] of TKeymanOptionInfo = ( // I3331 // I3620 // I4552 // Global options (opt: koKeyboardHotkeysAreToggle; RegistryName: SRegValue_KeyboardHotkeysAreToggle; OptionType: kotBool; BoolValue: False; GroupName: 'kogGeneral'), (opt: koSwitchLanguageForAllApplications; RegistryName: SRegValue_SwitchLanguageForAllApplications; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'), // I2277 // I4393 (opt: koAltGrCtrlAlt; RegistryName: SRegValue_AltGrCtrlAlt; OptionType: kotBool; BoolValue: False; GroupName: 'kogGeneral'), + (opt: koRightModifierHK; RegistryName: SRegValue_UseRightModifierHotKey; OptionType: kotBool; BoolValue: False; GroupName: 'kogGeneral'), (opt: koShowHints; RegistryName: SRegValue_EnableHints; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'), (opt: koBaseLayout; RegistryName: SRegValue_UnderlyingLayout; OptionType: kotLong; IntValue: 0; GroupName: 'kogGeneral'), diff --git a/windows/src/global/delphi/general/Keyman.System.Settings.pas b/windows/src/global/delphi/general/Keyman.System.Settings.pas index b021e56cddf..d8acd9d25a6 100644 --- a/windows/src/global/delphi/general/Keyman.System.Settings.pas +++ b/windows/src/global/delphi/general/Keyman.System.Settings.pas @@ -95,7 +95,7 @@ TKeymanSettings = class(TObjectList) ValueType: kstInteger ); - BaseKeymanSettings: array[0..33] of TKeymanSettingBase = ( + BaseKeymanSettings: array[0..31] of TKeymanSettingBase = ( // TIKE:UTikeDebugMode.TikeDebugMode ( @@ -370,14 +370,6 @@ TKeymanSettings = class(TObjectList) // keyman:TfrmKeyman7Main.RegisterHotkeys, // keyman:TfrmKeyman7Main.UnregisterHotkeys // keyman32:k32_lowlevelkeyboardhook - ( - ID: 'engine.compatibility.old_hotkey_registration'; - Name: SRegValue_Flag_UseRegisterHotkey; - RootKey: HKCU; - Key: SRegKey_KeymanEngineDebug_CU; - Description: 'Set to 1 for old RegisterHotkey pathway'; - ValueType: kstInteger - ), ( ID: 'engine.compatibility.use_keyman_core'; @@ -391,18 +383,6 @@ TKeymanSettings = class(TObjectList) ValueType: kstInteger ), - // keyman32:k32_lowlevelkeyboardhook - ( - ID: 'engine.compatibility.old_cached_hotkey_modifier_state'; - Name: SRegValue_Flag_UseCachedHotkeyModifierState; - RootKey: HKCU; - Key: SRegKey_KeymanEngineDebug_CU; - Description: 'Set to 1 to use a cached modifer state when checking hotkeys; if '+ - 'set to 0 then engine.compatibility.old_hotkey_registration may not '+ - 'work'; - ValueType: kstInteger - ), - // keyman32:keyman32.Initialise_Flag_ShouldSerializeInput ( ID: 'engine.compatibility.serialize_input'; diff --git a/windows/src/global/delphi/general/KeymanOptionNames.pas b/windows/src/global/delphi/general/KeymanOptionNames.pas index 27aef82ab55..73356d19cb3 100644 --- a/windows/src/global/delphi/general/KeymanOptionNames.pas +++ b/windows/src/global/delphi/general/KeymanOptionNames.pas @@ -5,7 +5,10 @@ interface type TUtilKeymanOption = ( // General options - koKeyboardHotkeysAreToggle, koAltGrCtrlAlt, koReleaseShiftKeysAfterKeyPress, + koKeyboardHotkeysAreToggle, + koAltGrCtrlAlt, + koRightModifierHK, + koReleaseShiftKeysAfterKeyPress, koShowHints, // I1256 // Startup options koTestKeymanFunctioning,