Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(windows): right modifer keys include in hotkey if configured #12217

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions common/windows/cpp/include/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 */

Expand Down
3 changes: 1 addition & 2 deletions common/windows/delphi/general/RegistryKeys.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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';
Expand Down
5 changes: 5 additions & 0 deletions windows/src/desktop/kmshell/xml/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,11 @@
<!-- Introduced: 7.0.230.0 -->
<string name="koAltGrCtrlAlt" comment="General options - Ctrl+Alt simulates AltGr on computers without AltGr">Simulate AltGr with Ctrl+Alt</string>

<!-- Context: Configuration Dialog - Options tab -->
<!-- String Type: FormatString -->
<!-- Introduced: 18.0.91 -->
<string name="koRightModifierHK" comment="General options - Right Ctrl Alt and Shift Work for HotKey">Right Modifier keys work with Hotkeys</string>

<!-- Context: Configuration Dialog - Options tab -->
<!-- String Type: FormatString -->
<!-- Introduced: 9.0.480.0 -->
Expand Down
38 changes: 36 additions & 2 deletions windows/src/engine/keyman/UfrmKeyman7Main.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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', []);

Expand Down Expand Up @@ -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', []);

Expand Down
10 changes: 7 additions & 3 deletions windows/src/engine/keyman32/hotkeys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
107 changes: 43 additions & 64 deletions windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion windows/src/engine/kmcomapi/util/utilkeymanoption.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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'),

Expand Down
22 changes: 1 addition & 21 deletions windows/src/global/delphi/general/Keyman.System.Settings.pas
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ TKeymanSettings = class(TObjectList<TKeymanSetting>)
ValueType: kstInteger
);

BaseKeymanSettings: array[0..33] of TKeymanSettingBase = (
BaseKeymanSettings: array[0..31] of TKeymanSettingBase = (

// TIKE:UTikeDebugMode.TikeDebugMode
(
Expand Down Expand Up @@ -370,14 +370,6 @@ TKeymanSettings = class(TObjectList<TKeymanSetting>)
// 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';
Expand All @@ -391,18 +383,6 @@ TKeymanSettings = class(TObjectList<TKeymanSetting>)
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';
Expand Down
5 changes: 4 additions & 1 deletion windows/src/global/delphi/general/KeymanOptionNames.pas
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ interface
type
TUtilKeymanOption = (
// General options
koKeyboardHotkeysAreToggle, koAltGrCtrlAlt, koReleaseShiftKeysAfterKeyPress,
koKeyboardHotkeysAreToggle,
koAltGrCtrlAlt,
koRightModifierHK,
koReleaseShiftKeysAfterKeyPress,
koShowHints, // I1256
// Startup options
koTestKeymanFunctioning,
Expand Down
Loading