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,