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

KBM - Keyboard Manager Shortcut Bugs #35201

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
65a313f
Test version
gokcekantarci Jun 3, 2024
96c0953
Github URL
gokcekantarci Jun 3, 2024
f2ed58f
repro
gokcekantarci Jun 4, 2024
9c0ef91
os version
gokcekantarci Jun 5, 2024
e7cc3a0
isadmin
gokcekantarci Jun 5, 2024
f97cc86
report
gokcekantarci Jun 6, 2024
c87a344
report
gokcekantarci Jun 6, 2024
3c6d882
report
gokcekantarci Jun 6, 2024
c716aa6
Revert "report"
gokcekantarci Jun 7, 2024
27b234e
Revert "report"
gokcekantarci Jun 7, 2024
ce010f5
Revert "report"
gokcekantarci Jun 7, 2024
51a33af
Revert "isadmin"
gokcekantarci Jun 7, 2024
5596a32
Revert "os version"
gokcekantarci Jun 7, 2024
f9b1588
Revert "repro"
gokcekantarci Jun 7, 2024
5c0e146
Revert "Github URL"
gokcekantarci Jun 7, 2024
d45ae2c
Revert "Test version"
gokcekantarci Jun 7, 2024
8cb7064
Main test bug_report.yml
gokcekantarci Jun 7, 2024
e1fa78c
iselevated
gokcekantarci Jun 10, 2024
e217834
iselevated
gokcekantarci Jun 10, 2024
9324162
[BugReport] bug_report.yml is changed.
gokcekantarci Jul 9, 2024
3de225c
Merge remote-tracking branch 'upstream/main'
gokcekantarci Jul 23, 2024
2234db6
Merge remote-tracking branch 'upstream/main'
gokcekantarci Sep 19, 2024
17dc57e
[KBM] Pressed modifier keys are saved to check before invoking shortcuts
gokcekantarci Sep 24, 2024
1157e8c
[KBM] Fixed invoking a shortcut and sending text with same modifier k…
gokcekantarci Oct 2, 2024
d172fe9
[KBM] Remove unnecessary code.
gokcekantarci Oct 2, 2024
179ef72
Merge remote-tracking branch 'upstream/main' into KBM---Shortcuts-not…
gokcekantarci Oct 2, 2024
cd142d2
[KBM] Unit fix. Set winKeyInvoked before SetPreviousModifierKey and r…
gokcekantarci Oct 2, 2024
d8cbe0f
Merge branch 'main' into pr35201
jaimecbernardo Oct 25, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,65 @@ namespace KeyboardEventHandlers
for (auto& itShortcut : state.GetSortedShortcutRemapVector(activatedApp))
{
const auto it = reMap.find(itShortcut);
static bool isAltRightKeyInvoked = false;

// Release key and delete from previous modifier key vector
if ((Helpers::IsModifierKey(data->lParam->vkCode) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)))
{
std::vector<INPUT> keyEventList;
if (!(isAltRightKeyInvoked && data->lParam->vkCode == VK_LCONTROL))
{
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(data->lParam->vkCode), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
state.ResetPreviousModifierKey(data->lParam->vkCode);
}

if (isAltRightKeyInvoked && data->lParam->vkCode == VK_RMENU && state.FindPreviousModifierKey(VK_LCONTROL))
{
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, VK_LCONTROL, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
state.ResetPreviousModifierKey(it->first.GetCtrlKey());
}

if (data->lParam->vkCode == VK_RWIN || data->lParam->vkCode == VK_LWIN)
{
it->second.winKeyInvoked = ModifierKey::Disabled;
}

ii.SendVirtualInput(keyEventList);

}
else if ((Helpers::IsModifierKey(data->lParam->vkCode) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)))
{
// Remember which win key was pressed initially
if (data->lParam->vkCode == VK_RWIN)
{
it->second.winKeyInvoked = ModifierKey::Right;
}
else if (data->lParam->vkCode == VK_LWIN)
{
it->second.winKeyInvoked = ModifierKey::Left;
}

// Set the previous modifier key of the invoked shortcut
SetPreviousModifierKey(it, data->lParam->vkCode, state);
// Check if the right Alt key (AltGr) is pressed.
if (data->lParam->vkCode == VK_RMENU && state.FindPreviousModifierKey(VK_LCONTROL))
{
isAltRightKeyInvoked = true;
}
}

// If a shortcut is currently in the invoked state then skip till the shortcut that is currently invoked
if (isShortcutInvoked && !it->second.isShortcutInvoked)
{
continue;
}

// If action key is pressed check modifier key from shortcut with previous modifier key saved at state
if ((data->lParam->vkCode == it->first.GetActionKey()) && (state.GetPreviousModifierKey().size() == 0 || !CheckPreviousModifierKey(it, state)))
{
continue;
}

// Check if the remap is to a key or a shortcut
const bool remapToKey = it->second.targetShortcut.index() == 0;
const bool remapToShortcut = it->second.targetShortcut.index() == 1;
Expand All @@ -247,14 +299,6 @@ namespace KeyboardEventHandlers
bool isMatchOnChordEnd = false;
bool isMatchOnChordStart = false;

static bool isAltRightKeyInvoked = false;

// Check if the right Alt key (AltGr) is pressed.
if (data->lParam->vkCode == VK_RMENU && ii.GetVirtualKeyState(VK_LCONTROL))
{
isAltRightKeyInvoked = true;
}

// If the shortcut has been pressed down
if (!it->second.isShortcutInvoked && it->first.CheckModifiersKeyboardState(ii))
{
Expand Down Expand Up @@ -310,16 +354,6 @@ namespace KeyboardEventHandlers

std::vector<INPUT> keyEventList;

// Remember which win key was pressed initially
if (ii.GetVirtualKeyState(VK_RWIN))
{
it->second.winKeyInvoked = ModifierKey::Right;
}
else if (ii.GetVirtualKeyState(VK_LWIN))
{
it->second.winKeyInvoked = ModifierKey::Left;
}

if (isRunProgram)
{
auto threadFunction = [it]() {
Expand Down Expand Up @@ -648,22 +682,13 @@ namespace KeyboardEventHandlers
if (!remapToText && ((!it->first.HasChord() && data->lParam->vkCode == it->first.GetActionKey()) || (it->first.HasChord() && data->lParam->vkCode == it->first.GetSecondKey())) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
{
std::vector<INPUT> keyEventList;
if (remapToShortcut && !it->first.HasChord())
{
// Just lift the action key for no chords.
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
}
else if (remapToShortcut && it->first.HasChord())
if (remapToShortcut)
{
// If it has a chord, we'll want a full clean contemplated in the else, since you can't really repeat chords by pressing the end key again.

// Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated
// Just lift the action key.
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);

// Release new shortcut state (release in reverse order of shortcut to be accurate)
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);

// Set old shortcut key down state
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));

// Reset the remap state
Expand Down Expand Up @@ -711,7 +736,7 @@ namespace KeyboardEventHandlers
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);

if (!isAltRightKeyInvoked)
if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Reset the remap state
it->second.isShortcutInvoked = false;
Expand Down Expand Up @@ -848,7 +873,7 @@ namespace KeyboardEventHandlers
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu
}

if (!isAltRightKeyInvoked)
if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Reset the remap state
it->second.isShortcutInvoked = false;
Expand Down Expand Up @@ -899,7 +924,7 @@ namespace KeyboardEventHandlers
{
std::vector<INPUT> keyEventList;

if (!isAltRightKeyInvoked)
if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Set original shortcut key down state
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
Expand All @@ -917,7 +942,7 @@ namespace KeyboardEventHandlers

// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu

if (!isAltRightKeyInvoked)
if (!isAltRightKeyInvoked || (isAltRightKeyInvoked && data->lParam->vkCode != VK_LCONTROL))
{
// Reset the remap state
it->second.isShortcutInvoked = false;
Expand Down Expand Up @@ -1727,4 +1752,104 @@ namespace KeyboardEventHandlers

return 1;
}

bool CheckPreviousModifierKey(const ShortcutRemapTable::iterator it, State& state)
{
auto previousKeys = state.GetPreviousModifierKey();
if (!previousKeys.empty())
{
if (it->first.GetShiftKey() != 0)
{
if (!state.FindPreviousModifierKey(it->first.GetShiftKey()))
{
return false;
}
}
else
{
for (auto key : previousKeys)
{
if ((VK_SHIFT == key) || (VK_LSHIFT == key) || (VK_RSHIFT == key))
{
return false;
}
}
}

if (it->first.GetAltKey() != 0)
{
if (!state.FindPreviousModifierKey(it->first.GetAltKey()))
{
return false;
}
}
else
{
for (auto key : previousKeys)
{
if ((VK_MENU == key) || (VK_LMENU == key) || (VK_RMENU == key))
{
return false;
}
}
}

if (it->first.GetCtrlKey() != 0)
{
if (!state.FindPreviousModifierKey(it->first.GetCtrlKey()))
{
return false;
}
}
else
{
for (auto key : previousKeys)
{
if ((VK_CONTROL == key) || (VK_LCONTROL == key) || (VK_RCONTROL == key))
{
return false;
}
}
}

if (it->first.GetWinKey(it->second.winKeyInvoked) != 0)
{
if (!state.FindPreviousModifierKey(it->first.GetWinKey(it->second.winKeyInvoked)))
{
return false;
}
}
else
{
for (auto key : previousKeys)
{
if ((VK_LWIN == key) || (VK_RWIN == key))
{
return false;
}
}
}
}
return true;
}

void SetPreviousModifierKey(const ShortcutRemapTable::iterator it, const DWORD key, State& state)
{
if (it->first.GetWinKey(it->second.winKeyInvoked) == key)
{
state.SetPreviousModifierKey(it->first.GetWinKey(it->second.winKeyInvoked));
}
else if (it->first.GetCtrlKey() == key)
{
state.SetPreviousModifierKey(it->first.GetCtrlKey());
}
else if (it->first.GetAltKey() == key)
{
state.SetPreviousModifierKey(it->first.GetAltKey());
}
else if (it->first.GetShiftKey() == key)
{
state.SetPreviousModifierKey(it->first.GetShiftKey());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,10 @@ namespace KeyboardEventHandlers

// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target);

// Function to check previous modifier key with state
bool CheckPreviousModifierKey(const ShortcutRemapTable::iterator it, State& state);

// Function to set previous modifier key to state
void SetPreviousModifierKey(const ShortcutRemapTable::iterator it, const DWORD key, State& state);
};
27 changes: 27 additions & 0 deletions src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,30 @@ std::wstring State::GetActivatedApp()
{
return activatedAppSpecificShortcutTarget;
}

// Sets the previous modifier key to check in another shortcut
void State::SetPreviousModifierKey(const DWORD prevKey)
{
if (!FindPreviousModifierKey(prevKey))
{
previousModifierKey.emplace_back(prevKey);
}
}

// Gets the previous modifier key
std::vector<DWORD> State::GetPreviousModifierKey()
{
return previousModifierKey;
}

// Check if a key exists in the previousModifierKey vector
bool State::FindPreviousModifierKey(const DWORD prevKey)
{
return std::find(previousModifierKey.begin(), previousModifierKey.end(), prevKey) != previousModifierKey.end();
}

// Resets the previous modifier key
void State::ResetPreviousModifierKey(const DWORD prevKey)
{
previousModifierKey.erase(std::remove(previousModifierKey.begin(), previousModifierKey.end(), prevKey), previousModifierKey.end());
}
14 changes: 14 additions & 0 deletions src/modules/keyboardmanager/KeyboardManagerEngineLibrary/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class State : public MappingConfiguration
private:
// Stores the activated target application in app-specific shortcut
std::wstring activatedAppSpecificShortcutTarget;
// Stores the previous modifier key
std::vector<DWORD> previousModifierKey;

public:
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
Expand All @@ -26,4 +28,16 @@ class State : public MappingConfiguration

// Gets the activated target application in app-specific shortcut
std::wstring GetActivatedApp();

// Sets the previous modifier key to check in another shortcut
void SetPreviousModifierKey(const DWORD prevKey);

// Gets the previous modifier key
std::vector<DWORD> GetPreviousModifierKey();

// Check a key if exist in previous modifier key vector
bool FindPreviousModifierKey(const DWORD prevKey);

// Resets the previous modifier key
void ResetPreviousModifierKey(const DWORD prevKey);
};
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ namespace RemappingLogicTests
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
}

// Test if target modifier is still held down even if the action key of the original shortcut is released - required for Alt+Tab/Win+Space cases
// Test if original modifier is still held down even if the action key of the original shortcut is released - required for Alt+Tab/Win+Space cases
TEST_METHOD (RemappedShortcutModifiers_ShouldBeDetectedAsPressed_OnReleasingActionKeyButHoldingModifiers)
{
// Remap Ctrl+A to Alt+Tab
Expand All @@ -882,10 +882,10 @@ namespace RemappingLogicTests
// Send Ctrl+A, release A
mockedInputHandler.SendVirtualInput(inputs);

// Ctrl, A, Tab key states should be unchanged, Alt should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
// Alt, A, Tab key states should be unchanged, Ctrl should be true
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_TAB), false);
}

Expand Down
Loading