mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
[KBM] Migrate Engine and Editor into separate processes (#10774)
* Move KBM engine into separate process (#10672) * [KBM] Migrate KBM UI out of the runner (#10709) * Clean up keyboard hook handles (#10817) * [C++ common] Unhandled exception handler (#10821) * [KBM] Use icon in the KeyboardManagerEditor (#10845) * [KBM] Move resources from the Common project to the Editor. (#10844) * KBM Editor tests (#10858) * Rename engine executable (#10868) * clean up (#10870) * [KBM] Changed Editor and libraries output folders (#10871) * [KBM] New logs structure (#10872) * Add unhandled exception handling to the editor (#10874) * [KBM] Trace for edit keyboard window * Logging for XamlBridge message loop * [KBM] Added Editor and Engine to the installer (#10876) * Fix spelling * Interprocess communication logs, remove unnecessary windows message logs * [KBM] Separated telemetry for the engine and editor. (#10889) * [KBM] Editor test project (#10891) * Versions for the engine and the editor (#10897) * Add the editor's and the engine's executables to signing process (#10900) * [KBM editor] Run only one instance, exit when parent process exits (#10890) * [KBM] Force kill editor process to avoid XAML crash (#10907) * [KBM] Force kill editor process to avoid XAML crash * Fix event releasing Co-authored-by: mykhailopylyp <17161067+mykhailopylyp@users.noreply.github.com> * Make the editor dpi aware (#10908) * [KBM] KeyboardManagerCommon refactoring (#10909) * Do not start the process if it is already started (#10910) * logs * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * [KBM] Rename InitUnhandledExceptionHandler to make it explicit that is for x64 only. We will fix it properly when adding support for ARM64 and add a header with the proper conditional building. * [KBM] rename file/class/variables using camel case * [KBM] Rename "event_locker" -> "EventLocker" * [KBM] rename process_waiter Add a TODO comment * [KBM] rename methods Add TODO comment * [KBM] use uppercase for function names * [KBM] use uppercase for methos, lowercase for properties * [KBM] rename method, make methods private, formatting * [KBM] rename private variables * [KBM] use uppercase for function names * [KBM] Added support to run the editor stand-alone when built in debug mode * Update src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp * Check success of event creation, comment (#10947) * [KBM] code formatting (#10951) * [KBM] code formatting * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp * [KBM] tracing * [KBM] Remappings not showing fix. (#10954) * removed mutex * retry loop for reading * retry on reading config once * log error Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Seraphima Zykova <zykovas91@gmail.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "Input.h"
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
|
||||
// Function to simulate input
|
||||
UINT Input::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
|
||||
{
|
||||
return SendInput(cInputs, pInputs, cbSize);
|
||||
}
|
||||
|
||||
// Function to get the state of a particular key
|
||||
bool Input::GetVirtualKeyState(int key)
|
||||
{
|
||||
return (GetAsyncKeyState(key) & 0x8000);
|
||||
}
|
||||
|
||||
// Function to get the foreground process name
|
||||
void Input::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
|
||||
{
|
||||
foregroundProcess = KeyboardManagerHelper::GetCurrentApplication(false);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include <keyboardmanager/common/InputInterface.h>
|
||||
|
||||
// Class used to wrap keyboard input library methods
|
||||
class Input :
|
||||
public InputInterface
|
||||
{
|
||||
public:
|
||||
// Function to simulate input
|
||||
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize);
|
||||
|
||||
// Function to get the state of a particular key
|
||||
bool GetVirtualKeyState(int key);
|
||||
|
||||
// Function to get the foreground process name
|
||||
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
|
||||
};
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 113 KiB |
@@ -1,856 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "KeyboardEventHandlers.h"
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
#include "keyboardmanager/common/RemapShortcut.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/InputInterface.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
|
||||
namespace KeyboardEventHandlers
|
||||
{
|
||||
// Function to a handle a single key remap
|
||||
__declspec(dllexport) intptr_t HandleSingleKeyRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||
{
|
||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
|
||||
{
|
||||
const auto remapping = keyboardManagerState.GetSingleKeyRemap(data->lParam->vkCode);
|
||||
if (remapping)
|
||||
{
|
||||
auto it = remapping.value();
|
||||
|
||||
// Check if the remap is to a key or a shortcut
|
||||
bool remapToKey = (it->second.index() == 0);
|
||||
|
||||
// If mapped to VK_DISABLED then the key is disabled
|
||||
if (remapToKey)
|
||||
{
|
||||
if (std::get<DWORD>(it->second) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int key_count;
|
||||
if (remapToKey)
|
||||
{
|
||||
key_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_count = std::get<Shortcut>(it->second).Size();
|
||||
}
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Handle remaps to VK_WIN_BOTH
|
||||
DWORD target;
|
||||
if (remapToKey)
|
||||
{
|
||||
target = KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second));
|
||||
}
|
||||
else
|
||||
{
|
||||
target = KeyboardManagerHelper::FilterArtificialKeys(std::get<Shortcut>(it->second).GetActionKey());
|
||||
}
|
||||
|
||||
// If Ctrl/Alt/Shift is being remapped to Caps Lock, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, it->first, target);
|
||||
}
|
||||
|
||||
if (remapToKey)
|
||||
{
|
||||
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
Shortcut targetShortcut = std::get<Shortcut>(it->second);
|
||||
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
// Dummy key is not required here since SetModifierKeyEvents will only add key-up events for the modifiers here, and the action key key-up is already sent before it
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dummy key is not required here since SetModifierKeyEvents will only add key-down events for the modifiers here, and the action key key-down is already sent after it
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
// Log telemetry event when the key remap is invoked
|
||||
Trace::KeyRemapInvoked(remapToKey);
|
||||
|
||||
// If Caps Lock is being remapped to Ctrl/Alt/Shift, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397
|
||||
if (remapToKey)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, target, it->first);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<DWORD> shortcutKeys = std::get<Shortcut>(it->second).GetKeyCodes();
|
||||
for (auto& itSk : shortcutKeys)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, itSk, it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This feature has not been enabled (code from proof of concept stage)
|
||||
*
|
||||
// Function to a change a key's behavior from toggle to modifier
|
||||
__declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||
{
|
||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
|
||||
{
|
||||
// The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837
|
||||
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyToggleToMod_mutex);
|
||||
auto it = keyboardManagerState.singleKeyToggleToMod.find(data->lParam->vkCode);
|
||||
if (it != keyboardManagerState.singleKeyToggleToMod.end())
|
||||
{
|
||||
// To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
if (it->second == false)
|
||||
{
|
||||
keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int key_count = 2;
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
|
||||
lock.unlock();
|
||||
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
|
||||
// Reset the long press flag when the key has been lifted.
|
||||
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
|
||||
{
|
||||
lock.lock();
|
||||
keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = false;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
// Function to a handle a shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp) noexcept
|
||||
{
|
||||
// Check if any shortcut is currently in the invoked state
|
||||
bool isShortcutInvoked = keyboardManagerState.CheckShortcutRemapInvoked(activatedApp);
|
||||
|
||||
// Get shortcut table for given activatedApp
|
||||
ShortcutRemapTable& reMap = keyboardManagerState.GetShortcutRemapTable(activatedApp);
|
||||
|
||||
// Iterate through the shortcut remaps and apply whichever has been pressed
|
||||
for (auto& itShortcut : keyboardManagerState.GetSortedShortcutRemapVector(activatedApp))
|
||||
{
|
||||
const auto it = reMap.find(itShortcut);
|
||||
|
||||
// If a shortcut is currently in the invoked state then skip till the shortcut that is currently invoked
|
||||
if (isShortcutInvoked && !it->second.isShortcutInvoked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Check if the remap is to a key or a shortcut
|
||||
bool remapToShortcut = (it->second.targetShortcut.index() == 1);
|
||||
|
||||
const size_t src_size = it->first.Size();
|
||||
const size_t dest_size = remapToShortcut ? std::get<Shortcut>(it->second.targetShortcut).Size() : 1;
|
||||
|
||||
// If the shortcut has been pressed down
|
||||
if (!it->second.isShortcutInvoked && it->first.CheckModifiersKeyboardState(ii))
|
||||
{
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
{
|
||||
// Check if any other keys have been pressed apart from the shortcut. If true, then check for the next shortcut. This is to be done only for shortcut to shortcut remaps
|
||||
if (!it->first.IsKeyboardStateClearExceptShortcut(ii) && (remapToShortcut || std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t key_count;
|
||||
LPINPUT 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 (remapToShortcut)
|
||||
{
|
||||
// Get the common keys between the two shortcuts
|
||||
int commonKeys = it->first.GetCommonModifiersCount(std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// If the original shortcut modifiers are a subset of the new shortcut
|
||||
if (commonKeys == src_size - 1)
|
||||
{
|
||||
// key down for all new shortcut keys except the common modifiers
|
||||
key_count = dest_size - commonKeys;
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated
|
||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * (size_t)commonKeys);
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release original shortcut state (release in reverse order of shortcut to be accurate)
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// Set new shortcut key down state
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifiers - ex: Win+Caps -> Ctrl+A
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
{
|
||||
Shortcut temp = std::get<Shortcut>(it->second.targetShortcut);
|
||||
for (auto keys : temp.GetKeyCodes())
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, keys, data->lParam->vkCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
|
||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + dest_size;
|
||||
// Do not send Disable key
|
||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
key_count--;
|
||||
// Since the original shortcut's action key is pressed, set it to true
|
||||
it->second.isOriginalActionKeyPressed = true;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release original shortcut state (release in reverse order of shortcut to be accurate)
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Set target key down state
|
||||
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), data->lParam->vkCode);
|
||||
}
|
||||
}
|
||||
|
||||
it->second.isShortcutInvoked = true;
|
||||
// If app specific shortcut is invoked, store the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(*activatedApp);
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
|
||||
// Log telemetry event when shortcut remap is invoked
|
||||
Trace::ShortcutRemapInvoked(remapToShortcut, activatedApp.has_value());
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// The shortcut has already been pressed down at least once, i.e. the shortcut has been invoked
|
||||
// There are 6 cases to be handled if the shortcut has been pressed down
|
||||
// 1. The user lets go of one of the modifier keys - reset the keyboard back to the state of the keys actually being pressed down
|
||||
// 2. The user keeps the shortcut pressed - the shortcut is repeated (for example you could hold down Ctrl+V and it will keep pasting)
|
||||
// 3. The user lets go of the action key - keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||
// 4. The user presses a modifier key in the original shortcut - suppress that key event since the original shortcut is already held down physically (This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both")
|
||||
// 5. The user presses any key apart from the action key or a modifier key in the original shortcut - revert the keyboard state to just the original modifiers being held down along with the current key press
|
||||
// 6. The user releases any key apart from original modifier or original action key - This can't happen since the key down would have to happen first, which is handled above
|
||||
else if (it->second.isShortcutInvoked)
|
||||
{
|
||||
// Get the common keys between the two shortcuts
|
||||
int commonKeys = remapToShortcut ? it->first.GetCommonModifiersCount(std::get<Shortcut>(it->second.targetShortcut)) : 0;
|
||||
|
||||
// Case 1: If any of the modifier keys of the original shortcut are released before the action key
|
||||
if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
{
|
||||
// Release new shortcut, and set original shortcut keys except the one released
|
||||
size_t key_count;
|
||||
LPINPUT keyEventList;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// if the released key is present in both shortcuts' modifiers (i.e part of the common modifiers)
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).CheckWinKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckCtrlKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckAltKey(data->lParam->vkCode) || std::get<Shortcut>(it->second.targetShortcut).CheckShiftKey(data->lParam->vkCode))
|
||||
{
|
||||
// release all new shortcut keys and the common released modifier except the other common modifiers, and add all original shortcut modifiers except the common ones, and dummy key
|
||||
key_count = (dest_size - commonKeys) + (src_size - 1 - commonKeys) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones, and dummy key
|
||||
key_count = (dest_size - 1) + (src_size - 2) - (2 * (size_t)commonKeys) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
}
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released
|
||||
bool isActionKeyPressed = false;
|
||||
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 1;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new shortcut state (release in reverse order of shortcut to be accurate)
|
||||
int i = 0;
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode);
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode);
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers except the one released and dummy key
|
||||
key_count = dest_size + src_size - 2 + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
bool isTargetKeyPressed = false;
|
||||
|
||||
// Do not send Disable key up
|
||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
key_count--;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))))
|
||||
{
|
||||
isTargetKeyPressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isTargetKeyPressed = false;
|
||||
key_count--;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate it's own key message
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
|
||||
// key count can be 0 if both shortcuts have same modifiers and the action key is not held down. delete will throw an error if keyEventList is empty
|
||||
if (key_count > 0)
|
||||
{
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The system will see the modifiers of the new shortcut as being held down because of the shortcut remap
|
||||
if (!remapToShortcut || std::get<Shortcut>(it->second.targetShortcut).CheckModifiersKeyboardState(ii))
|
||||
{
|
||||
// Case 2: If the original shortcut is still held down the keyboard will get a key down message of the action key in the original shortcut and the new shortcut's modifiers will be held down (keys held down send repeated keydown messages)
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
{
|
||||
// In case of mapping to disable do not send anything
|
||||
if (!remapToShortcut && std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// Since the original shortcut's action key is pressed, set it to true
|
||||
it->second.isOriginalActionKeyPressed = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t key_count = 1;
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
if (remapToShortcut)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Case 3: If the action key is released from the original shortcut, keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||
if (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
|
||||
{
|
||||
size_t key_count = 1;
|
||||
LPINPUT keyEventList;
|
||||
if (remapToShortcut)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
// If remapped to disable, do nothing and suppress the key event
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// Since the original shortcut's action key is released, set it to false
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
|
||||
bool isKeyboardStateClear = Shortcut(std::vector<int32_t>({ KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii);
|
||||
// If the keyboard state is clear, we release the target key but do not reset the remap state
|
||||
if (isKeyboardStateClear)
|
||||
{
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
// If any other key is pressed, then the keyboard state must be reverted back to the physical keys. This is to take cases like Ctrl+A->D remap and user presses B+Ctrl+A and releases A, or Ctrl+A+B and releases A
|
||||
else
|
||||
{
|
||||
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
||||
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new key state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
|
||||
// Set original shortcut key down state except the action key
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// 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
|
||||
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Case 4: If a modifier key in the original shortcut is pressed then suppress that key event since the original shortcut is already held down physically - This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both"
|
||||
if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
|
||||
{
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
}
|
||||
}
|
||||
// If it is not remapped to Disable
|
||||
else if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||
}
|
||||
|
||||
// Suppress the modifier as it is already physically pressed
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Case 5: If any key apart from the action key or a modifier key in the original shortcut is pressed then revert the keyboard state to just the original modifiers being held down along with the current key press
|
||||
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
|
||||
{
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
}
|
||||
|
||||
size_t key_count;
|
||||
LPINPUT keyEventList;
|
||||
|
||||
// If the original shortcut is a subset of the new shortcut
|
||||
if (commonKeys == src_size - 1)
|
||||
{
|
||||
key_count = dest_size - commonKeys;
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
bool isActionKeyPressed = false;
|
||||
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 2;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
int i = 0;
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
|
||||
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// 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
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated
|
||||
key_count = (dest_size) + (src_size - 1) - (2 * (size_t)commonKeys);
|
||||
|
||||
// If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set
|
||||
bool isActionKeyPressed = false;
|
||||
if (ii.GetVirtualKeyState((std::get<Shortcut>(it->second.targetShortcut).GetActionKey())))
|
||||
{
|
||||
isActionKeyPressed = true;
|
||||
key_count += 2;
|
||||
}
|
||||
|
||||
keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Release new shortcut state (release in reverse order of shortcut to be accurate)
|
||||
int i = 0;
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
|
||||
// Set old shortcut key down state
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
|
||||
if (isActionKeyPressed)
|
||||
{
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
// For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A
|
||||
else
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||
|
||||
// If the shortcut is remapped to Disable then we have to revert the keyboard state to the physical keys
|
||||
bool isRemapToDisable = (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED);
|
||||
bool isOriginalActionKeyPressed = false;
|
||||
|
||||
if (!isRemapToDisable)
|
||||
{
|
||||
// If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys
|
||||
if (ii.GetVirtualKeyState((KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)))))
|
||||
{
|
||||
isOriginalActionKeyPressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isOriginalActionKeyPressed = it->second.isOriginalActionKeyPressed;
|
||||
}
|
||||
|
||||
if (isRemapToDisable || !isOriginalActionKeyPressed)
|
||||
{
|
||||
// Key down for original shortcut modifiers and action key, and current key press
|
||||
size_t key_count = src_size + 1;
|
||||
|
||||
LPINPUT keyEventList = new INPUT[key_count]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Set original shortcut key down state
|
||||
int i = 0;
|
||||
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Send the original action key only if it is physically pressed. For remappings to keys other than disabled we already check earlier that it is not pressed in this scenario. For remap to disable
|
||||
if (isRemapToDisable && isOriginalActionKeyPressed)
|
||||
{
|
||||
// Set original action key
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_count--;
|
||||
}
|
||||
|
||||
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
|
||||
i++;
|
||||
|
||||
// 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
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
|
||||
}
|
||||
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Case 6: If any key apart from original modifier or original action key is released - This can't happen since the key down would have to happen first, which is handled above. If a key up message is generated for some other key (maybe by code) do not suppress it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function to a handle an os-level shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleOSLevelShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||
{
|
||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||
{
|
||||
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState);
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function to a handle an app-specific shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleAppSpecificShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||
{
|
||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||
{
|
||||
std::wstring process_name;
|
||||
|
||||
// Allocate MAX_PATH amount of memory
|
||||
process_name.resize(MAX_PATH);
|
||||
ii.GetForegroundProcess(process_name);
|
||||
|
||||
// Remove elements after null character
|
||||
process_name.erase(std::find(process_name.begin(), process_name.end(), L'\0'), process_name.end());
|
||||
|
||||
if (process_name.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert process name to lower case
|
||||
std::transform(process_name.begin(), process_name.end(), process_name.begin(), towlower);
|
||||
|
||||
std::wstring query_string;
|
||||
|
||||
AppSpecificShortcutRemapTable::iterator it;
|
||||
// Check if an app-specific shortcut is already activated
|
||||
if (keyboardManagerState.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
query_string = process_name;
|
||||
it = keyboardManagerState.appSpecificShortcutReMap.find(query_string);
|
||||
|
||||
// If no entry is found, search for the process name without it's file extension
|
||||
if (it == keyboardManagerState.appSpecificShortcutReMap.end())
|
||||
{
|
||||
// Find index of the file extension
|
||||
size_t extensionIndex = process_name.find_last_of(L".");
|
||||
query_string = process_name.substr(0, extensionIndex);
|
||||
it = keyboardManagerState.appSpecificShortcutReMap.find(query_string);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query_string = keyboardManagerState.GetActivatedApp();
|
||||
it = keyboardManagerState.appSpecificShortcutReMap.find(query_string);
|
||||
}
|
||||
|
||||
if (it != keyboardManagerState.appSpecificShortcutReMap.end())
|
||||
{
|
||||
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState, query_string);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function to ensure Num Lock state does not change when it is suppressed by the low level hook
|
||||
void SetNumLockToPreviousState(InputInterface& ii)
|
||||
{
|
||||
// Num Lock's key state is applied before it is intercepted by low level keyboard hooks, so we have to manually set back the state when we suppress the key. This is done by sending an additional key up, key down set of messages.
|
||||
// We need 2 key events because after Num Lock is suppressed, key up to release num lock key and key down to revert the num lock state
|
||||
int key_count = 2;
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
}
|
||||
|
||||
// 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(InputInterface& ii, DWORD key, DWORD target)
|
||||
{
|
||||
// If the target is Caps Lock and the other key is either Ctrl/Alt/Shift then reset the modifier state to lower level handlers
|
||||
if (target == VK_CAPITAL)
|
||||
{
|
||||
// If the argument is either of the Ctrl/Shift/Alt modifier key codes
|
||||
if (KeyboardManagerHelper::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH))
|
||||
{
|
||||
int key_count = 1;
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
// Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts
|
||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)key, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
|
||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
||||
delete[] keyEventList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include "keyboardmanager/common/KeyboardManagerConstants.h"
|
||||
|
||||
#include <common/hooks/LowlevelKeyboardEvent.h>
|
||||
|
||||
class InputInterface;
|
||||
class KeyboardManagerState;
|
||||
class Shortcut;
|
||||
class RemapShortcut;
|
||||
|
||||
namespace KeyboardEventHandlers
|
||||
{
|
||||
// Function to a handle a single key remap
|
||||
__declspec(dllexport) intptr_t HandleSingleKeyRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||
|
||||
/* This feature has not been enabled (code from proof of concept stage)
|
||||
*
|
||||
// Function to a change a key's behavior from toggle to modifier
|
||||
__declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||
*/
|
||||
|
||||
// Function to a handle a shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp = std::nullopt) noexcept;
|
||||
|
||||
// Function to a handle an os-level shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleOSLevelShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||
|
||||
// Function to a handle an app-specific shortcut remap
|
||||
__declspec(dllexport) intptr_t HandleAppSpecificShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||
|
||||
// Function to ensure Num Lock state does not change when it is suppressed by the low level hook
|
||||
void SetNumLockToPreviousState(InputInterface& ii);
|
||||
|
||||
// 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(InputInterface& ii, DWORD key, DWORD target);
|
||||
};
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
IDS_KEYBOARDMANAGER_ICON ICON L"../Keyboard.ico"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
|
||||
@@ -42,16 +42,14 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input.h" />
|
||||
<ClInclude Include="KeyboardEventHandlers.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="Generated Files\resource.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<None Include="resource.base.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="Input.cpp" />
|
||||
<ClCompile Include="KeyboardEventHandlers.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<None Include="KeyboardManager.base.rc" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
@@ -67,16 +65,10 @@
|
||||
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ui\KeyboardManagerUI.vcxproj">
|
||||
<Project>{eaf23649-ef6e-478b-980e-81fad96cca2a}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\KeyboardManager.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="Keyboard.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -4,29 +4,23 @@
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardEventHandlers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input.cpp">
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="KeyboardEventHandlers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Input.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Generated Files\resource.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
@@ -45,11 +39,6 @@
|
||||
<UniqueIdentifier>{6581121b-1555-4598-8034-ab196538f593}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="Keyboard.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="KeyboardManager.base.rc">
|
||||
|
||||
@@ -123,182 +123,4 @@
|
||||
<data name="KeyboardManager" xml:space="preserve">
|
||||
<value>Keyboard Manager</value>
|
||||
</data>
|
||||
<data name="CreateWindowFailed_ErrorMessage" xml:space="preserve">
|
||||
<value>Call to CreateWindow failed!</value>
|
||||
</data>
|
||||
<data name="CreateWindowFailed_ErrorTitle" xml:space="preserve">
|
||||
<value>Error</value>
|
||||
</data>
|
||||
<data name="RegisterClassFailed_ErrorMessage" xml:space="preserve">
|
||||
<value>Windows registration failed!</value>
|
||||
<comment>This refers to an application window</comment>
|
||||
</data>
|
||||
<data name="RegisterClassFailed_ErrorTitle" xml:space="preserve">
|
||||
<value>Error</value>
|
||||
</data>
|
||||
<data name="EditKeyboard_WindowName" xml:space="preserve">
|
||||
<value>Remap keys</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_WindowName" xml:space="preserve">
|
||||
<value>Remap shortcuts</value>
|
||||
</data>
|
||||
<data name="Ok_Button" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
</data>
|
||||
<data name="Cancel_Button" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="Continue_Button" xml:space="preserve">
|
||||
<value>Continue Anyway</value>
|
||||
</data>
|
||||
<data name="EditKeyboard_SourceHeader" xml:space="preserve">
|
||||
<value>Key:</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="EditKeyboard_TargetHeader" xml:space="preserve">
|
||||
<value>Mapped To:</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_SourceHeader" xml:space="preserve">
|
||||
<value>Shortcut:</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_TargetHeader" xml:space="preserve">
|
||||
<value>Mapped To:</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_TargetAppHeader" xml:space="preserve">
|
||||
<value>Target App:</value>
|
||||
</data>
|
||||
<data name="EditKeyboard_OrphanedDialogTitle" xml:space="preserve">
|
||||
<value>The following keys have been reassigned and you won't be able to use them for their original assignment:</value>
|
||||
</data>
|
||||
<data name="EditKeyboard_PartialConfirmationDialogTitle" xml:space="preserve">
|
||||
<value>Some of the keys could not be remapped. Do you want to continue anyway?</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_PartialConfirmationDialogTitle" xml:space="preserve">
|
||||
<value>Some of the shortcuts could not be remapped. Do you want to continue anyway?</value>
|
||||
</data>
|
||||
<data name="EditKeyboard_Info" xml:space="preserve">
|
||||
<value>Select the key you want to change (Key) and then the key or shortcut you want it to become (Mapped To).</value>
|
||||
</data>
|
||||
<data name="EditKeyboard_InfoExample" xml:space="preserve">
|
||||
<value>For example, if you want to press A and get "Ctrl+C", key "A" would be your "Key" column and the shortcut "Ctrl+C" would be your "Mapped To" column.</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_Info" xml:space="preserve">
|
||||
<value>Select the shortcut you want to change (Shortcut) and then the key or shortcut you want it to invoke (Mapped To).</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_InfoExample" xml:space="preserve">
|
||||
<value>For example, if you want to press "Ctrl+C" and get "Alt" only on Microsoft Edge, "Ctrl+C" would be your "Shortcut" column, the key "Alt" would be your "Mapped To" column, and "MSEdge" would be your "Target App" column. If no target app is entered, it will apply globally. The name must be the process name and not the app name.</value>
|
||||
</data>
|
||||
<data name="Type_Button" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
<comment>As in type a key</comment>
|
||||
</data>
|
||||
<data name="TypeKey_Title" xml:space="preserve">
|
||||
<value>Press a key on selected keyboard:</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="TypeShortcut_Title" xml:space="preserve">
|
||||
<value>Press the keys in shortcut:</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="TypeKey_Header" xml:space="preserve">
|
||||
<value>Key Pressed:</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="TypeShortcut_Header" xml:space="preserve">
|
||||
<value>Keys Pressed:</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="Type_HoldEnter" xml:space="preserve">
|
||||
<value>Hold Enter to continue</value>
|
||||
</data>
|
||||
<data name="Type_HoldEsc" xml:space="preserve">
|
||||
<value>Hold Esc to discard</value>
|
||||
</data>
|
||||
<data name="EditShortcuts_AllApps" xml:space="preserve">
|
||||
<value>All Apps</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_RemapSuccessful" xml:space="preserve">
|
||||
<value>Remapping successful</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_RemapUnsuccessful" xml:space="preserve">
|
||||
<value>Some remappings were not applied</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_SameKeyPreviouslyMapped" xml:space="preserve">
|
||||
<value>Cannot remap a key more than once</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_MappedToSameKey" xml:space="preserve">
|
||||
<value>Cannot remap a key to itself</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_ConflictingModifierKey" xml:space="preserve">
|
||||
<value>Cannot remap this key as it conflicts with another remapped key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_SameShortcutPreviouslyMapped" xml:space="preserve">
|
||||
<value>Cannot remap a shortcut more than once for the same target app</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_MapToSameShortcut" xml:space="preserve">
|
||||
<value>Cannot remap a shortcut to itself</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_ConflictingModifierShortcut" xml:space="preserve">
|
||||
<value>Cannot remap this shortcut as it conflicts with another remapped shortcut</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_WinL" xml:space="preserve">
|
||||
<value>Cannot remap from/to Win L</value>
|
||||
<comment>Win refers to Windows as in the OS</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_CtrlAltDel" xml:space="preserve">
|
||||
<value>Cannot remap from/to Ctrl Alt Del</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_SaveFailed" xml:space="preserve">
|
||||
<value>Failed to save the remappings</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_ShortcutStartWithModifier" xml:space="preserve">
|
||||
<value>Shortcut must start with a modifier key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_ShortcutNoRepeatedModifier" xml:space="preserve">
|
||||
<value>Shortcut cannot contain a repeated modifier</value>
|
||||
</data>
|
||||
<data name="ErrorMessage_ShortcutAtleast2Keys" xml:space="preserve">
|
||||
<value>Shortcut must have atleast 2 keys</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_ShortcutOneActionKey" xml:space="preserve">
|
||||
<value>Shortcut must contain an action key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_ShortcutMaxOneActionKey" xml:space="preserve">
|
||||
<value>Shortcut cannot have more than one action key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_MaxShortcutSize" xml:space="preserve">
|
||||
<value>Shortcuts can only have up to 2 modifier keys</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="ErrorMessage_Default" xml:space="preserve">
|
||||
<value>Unexpected error</value>
|
||||
</data>
|
||||
<data name="Key_DropDown_Combobox" xml:space="preserve">
|
||||
<value>Key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="Add_Key_Remap_Button" xml:space="preserve">
|
||||
<value>Add Key Remap</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
<data name="Add_Shortcut_Button" xml:space="preserve">
|
||||
<value>Add Shortcut Remapping</value>
|
||||
</data>
|
||||
<data name="Delete_Remapping_Button" xml:space="preserve">
|
||||
<value>Delete Remapping</value>
|
||||
</data>
|
||||
<data name="AutomationProperties_Row" xml:space="preserve">
|
||||
<value>Row </value>
|
||||
</data>
|
||||
<data name="ERRORMESSAGE_DISABLEASACTIONKEY" xml:space="preserve">
|
||||
<value>Disable can not be an action or a modifier key</value>
|
||||
<comment>Key on a keyboard</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,21 +1,13 @@
|
||||
#include "pch.h"
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include "Generated Files/resource.h"
|
||||
#include <keyboardmanager/ui/EditKeyboardWindow.h>
|
||||
#include <keyboardmanager/ui/EditShortcutsWindow.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <keyboardmanager/common/RemapShortcut.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||
#include <common/debug_control.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/logger/logger_settings.h>
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "KeyboardEventHandlers.h"
|
||||
#include "Input.h"
|
||||
#include <keyboardmanager/dll/trace.h>
|
||||
#include <shellapi.h>
|
||||
#include <common/utils/logger_helper.h>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
@@ -47,189 +39,21 @@ private:
|
||||
//contains the non localized key of the powertoy
|
||||
std::wstring app_key = KeyboardManagerConstants::ModuleName;
|
||||
|
||||
// Low level hook handles
|
||||
static HHOOK hook_handle;
|
||||
|
||||
// Required for Unhook in old versions of Windows
|
||||
static HHOOK hook_handle_copy;
|
||||
|
||||
// Static pointer to the current keyboardmanager object required for accessing the HandleKeyboardHookEvent function in the hook procedure (Only global or static variables can be accessed in a hook procedure CALLBACK)
|
||||
static KeyboardManager* keyboardmanager_object_ptr;
|
||||
|
||||
// Variable which stores all the state information to be shared between the UI and back-end
|
||||
KeyboardManagerState keyboardManagerState;
|
||||
|
||||
// Object of class which implements InputInterface. Required for calling library functions while enabling testing
|
||||
Input inputHandler;
|
||||
|
||||
HANDLE m_hProcess = nullptr;
|
||||
public:
|
||||
// Constructor
|
||||
KeyboardManager()
|
||||
{
|
||||
std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(app_key));
|
||||
logFilePath.append(LogSettings::keyboardManagerLogPath);
|
||||
Logger::init(LogSettings::keyboardManagerLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
||||
|
||||
// Load the initial configuration.
|
||||
load_config();
|
||||
|
||||
// Set the static pointer to the newest object of the class
|
||||
keyboardmanager_object_ptr = this;
|
||||
LoggerHelpers::init_logger(KeyboardManagerConstants::ModuleName, L"ModuleInterface", LogSettings::keyboardManagerLoggerName);
|
||||
|
||||
std::filesystem::path oldLogPath(PTSettingsHelper::get_module_save_folder_location(app_key));
|
||||
oldLogPath.append("Logs");
|
||||
LoggerHelpers::delete_old_log_folder(oldLogPath);
|
||||
};
|
||||
|
||||
// Load config from the saved settings.
|
||||
void load_config()
|
||||
{
|
||||
try
|
||||
{
|
||||
PowerToysSettings::PowerToyValues settings =
|
||||
PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
|
||||
auto current_config = settings.get_string_value(KeyboardManagerConstants::ActiveConfigurationSettingName);
|
||||
|
||||
if (current_config)
|
||||
{
|
||||
keyboardManagerState.SetCurrentConfigName(*current_config);
|
||||
// Read the config file and load the remaps.
|
||||
auto configFile = json::from_file(PTSettingsHelper::get_module_save_folder_location(KeyboardManagerConstants::ModuleName) + L"\\" + *current_config + L".json");
|
||||
if (configFile)
|
||||
{
|
||||
auto jsonData = *configFile;
|
||||
|
||||
// Load single key remaps
|
||||
try
|
||||
{
|
||||
auto remapKeysData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapKeysSettingName);
|
||||
keyboardManagerState.ClearSingleKeyRemaps();
|
||||
|
||||
if (remapKeysData)
|
||||
{
|
||||
auto inProcessRemapKeys = remapKeysData.GetNamedArray(KeyboardManagerConstants::InProcessRemapKeysSettingName);
|
||||
for (const auto& it : inProcessRemapKeys)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto originalKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKey).find(L";") != std::string::npos)
|
||||
{
|
||||
keyboardManagerState.AddSingleKeyRemap(std::stoul(originalKey.c_str()), Shortcut(newRemapKey.c_str()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
keyboardManagerState.AddSingleKeyRemap(std::stoul(originalKey.c_str()), std::stoul(newRemapKey.c_str()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper Key Data JSON. Try the next remap.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper JSON format for single key remaps. Skip to next remap type
|
||||
}
|
||||
|
||||
// Load shortcut remaps
|
||||
try
|
||||
{
|
||||
auto remapShortcutsData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapShortcutsSettingName);
|
||||
keyboardManagerState.ClearOSLevelShortcuts();
|
||||
keyboardManagerState.ClearAppSpecificShortcuts();
|
||||
if (remapShortcutsData)
|
||||
{
|
||||
// Load os level shortcut remaps
|
||||
try
|
||||
{
|
||||
auto globalRemapShortcuts = remapShortcutsData.GetNamedArray(KeyboardManagerConstants::GlobalRemapShortcutsSettingName);
|
||||
for (const auto& it : globalRemapShortcuts)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
{
|
||||
keyboardManagerState.AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
keyboardManagerState.AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper Key Data JSON. Try the next shortcut.
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper JSON format for os level shortcut remaps. Skip to next remap type
|
||||
}
|
||||
|
||||
// Load app specific shortcut remaps
|
||||
try
|
||||
{
|
||||
auto appSpecificRemapShortcuts = remapShortcutsData.GetNamedArray(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName);
|
||||
for (const auto& it : appSpecificRemapShortcuts)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
|
||||
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
|
||||
auto targetApp = it.GetObjectW().GetNamedString(KeyboardManagerConstants::TargetAppSettingName);
|
||||
|
||||
// If remapped to a shortcut
|
||||
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
|
||||
{
|
||||
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
|
||||
}
|
||||
|
||||
// If remapped to a key
|
||||
else
|
||||
{
|
||||
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper Key Data JSON. Try the next shortcut.
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper JSON format for os level shortcut remaps. Skip to next remap type
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Improper JSON format for shortcut remaps. Skip to next remap type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Unable to load inital config.
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the powertoy and free memory
|
||||
virtual void destroy() override
|
||||
{
|
||||
stop_lowlevel_keyboard_hook();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -259,36 +83,8 @@ public:
|
||||
}
|
||||
|
||||
// Signal from the Settings editor to call a custom action.
|
||||
// This can be used to spawn more complex editors.
|
||||
virtual void call_custom_action(const wchar_t* action) override
|
||||
{
|
||||
static UINT custom_action_num_calls = 0;
|
||||
try
|
||||
{
|
||||
// Parse the action values, including name.
|
||||
PowerToysSettings::CustomActionObject action_object =
|
||||
PowerToysSettings::CustomActionObject::from_json_string(action);
|
||||
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
if (action_object.get_name() == L"RemapKeyboard")
|
||||
{
|
||||
if (!CheckEditKeyboardWindowActive() && !CheckEditShortcutsWindowActive())
|
||||
{
|
||||
std::thread(createEditKeyboardWindow, hInstance, std::ref(keyboardManagerState)).detach();
|
||||
}
|
||||
}
|
||||
else if (action_object.get_name() == L"EditShortcut")
|
||||
{
|
||||
if (!CheckEditKeyboardWindowActive() && !CheckEditShortcutsWindowActive())
|
||||
{
|
||||
std::thread(createEditShortcutsWindow, hInstance, std::ref(keyboardManagerState)).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
// Improper JSON.
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the runner to pass the updated settings values as a serialized JSON.
|
||||
@@ -316,8 +112,30 @@ public:
|
||||
m_enabled = true;
|
||||
// Log telemetry
|
||||
Trace::EnableKeyboardManager(true);
|
||||
// Start keyboard hook
|
||||
start_lowlevel_keyboard_hook();
|
||||
|
||||
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||
std::wstring executable_args = L"";
|
||||
executable_args.append(std::to_wstring(powertoys_pid));
|
||||
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||
sei.lpFile = L"modules\\KeyboardManager\\KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe";
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = executable_args.data();
|
||||
if (ShellExecuteExW(&sei) == false)
|
||||
{
|
||||
Logger::error(L"Failed to start keyboard manager engine");
|
||||
auto message = get_last_error_message(GetLastError());
|
||||
if (message.has_value())
|
||||
{
|
||||
Logger::error(message.value());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hProcess = sei.hProcess;
|
||||
SetPriorityClass(m_hProcess, REALTIME_PRIORITY_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -326,11 +144,12 @@ public:
|
||||
m_enabled = false;
|
||||
// Log telemetry
|
||||
Trace::EnableKeyboardManager(false);
|
||||
// Close active windows
|
||||
CloseActiveEditKeyboardWindow();
|
||||
CloseActiveEditShortcutsWindow();
|
||||
// Stop keyboard hook
|
||||
stop_lowlevel_keyboard_hook();
|
||||
|
||||
if (m_hProcess)
|
||||
{
|
||||
TerminateProcess(m_hProcess, 0);
|
||||
m_hProcess = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
@@ -338,142 +157,8 @@ public:
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
// Hook procedure definition
|
||||
static LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
LowlevelKeyboardEvent event;
|
||||
if (nCode == HC_ACTION)
|
||||
{
|
||||
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
event.wParam = wParam;
|
||||
if (keyboardmanager_object_ptr->HandleKeyboardHookEvent(&event) == 1)
|
||||
{
|
||||
// Reset Num Lock whenever a NumLock key down event is suppressed since Num Lock key state change occurs before it is intercepted by low level hooks
|
||||
if (event.lParam->vkCode == VK_NUMLOCK && (event.wParam == WM_KEYDOWN || event.wParam == WM_SYSKEYDOWN) && event.lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
{
|
||||
KeyboardEventHandlers::SetNumLockToPreviousState(keyboardmanager_object_ptr->inputHandler);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(hook_handle_copy, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
void start_lowlevel_keyboard_hook()
|
||||
{
|
||||
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!hook_handle)
|
||||
{
|
||||
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL);
|
||||
hook_handle_copy = hook_handle;
|
||||
if (!hook_handle)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Keyboard Manager");
|
||||
auto errorMessage = get_last_error_message(errorCode);
|
||||
Trace::Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"start_lowlevel_keyboard_hook.SetWindowsHookEx");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to terminate the low level hook
|
||||
void stop_lowlevel_keyboard_hook()
|
||||
{
|
||||
if (hook_handle)
|
||||
{
|
||||
UnhookWindowsHookEx(hook_handle);
|
||||
hook_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Function called by the hook procedure to handle the events. This is the starting point function for remapping
|
||||
intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
|
||||
{
|
||||
// If remappings are disabled (due to the remap tables getting updated) skip the rest of the hook
|
||||
if (!keyboardManagerState.AreRemappingsEnabled())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If key has suppress flag, then suppress it
|
||||
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If the Detect Key Window is currently activated, then suppress the keyboard event
|
||||
KeyboardManagerHelper::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data);
|
||||
if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event
|
||||
KeyboardManagerHelper::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true);
|
||||
if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remap a key
|
||||
intptr_t SingleKeyRemapResult = KeyboardEventHandlers::HandleSingleKeyRemapEvent(inputHandler, data, keyboardManagerState);
|
||||
|
||||
// Single key remaps have priority. If a key is remapped, only the remapped version should be visible to the shortcuts and hence the event should be suppressed here.
|
||||
if (SingleKeyRemapResult == 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If the Detect Shortcut Window is currently activated, then suppress the keyboard event
|
||||
KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false);
|
||||
if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This feature has not been enabled (code from proof of concept stage)
|
||||
*
|
||||
//// Remap a key to behave like a modifier instead of a toggle
|
||||
//intptr_t SingleKeyToggleToModResult = KeyboardEventHandlers::HandleSingleKeyToggleToModEvent(inputHandler, data, keyboardManagerState);
|
||||
*/
|
||||
|
||||
// Handle an app-specific shortcut remapping
|
||||
intptr_t AppSpecificShortcutRemapResult = KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
||||
|
||||
// If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed.
|
||||
if (AppSpecificShortcutRemapResult == 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Handle an os-level shortcut remapping
|
||||
return KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
||||
}
|
||||
};
|
||||
|
||||
HHOOK KeyboardManager::hook_handle = nullptr;
|
||||
HHOOK KeyboardManager::hook_handle_copy = nullptr;
|
||||
KeyboardManager* KeyboardManager::keyboardmanager_object_ptr = nullptr;
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new KeyboardManager();
|
||||
|
||||
30
src/modules/keyboardmanager/dll/trace.cpp
Normal file
30
src/modules/keyboardmanager/dll/trace.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToys",
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
void Trace::RegisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::UnregisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
// Log if the user has KBM enabled or disabled - Can also be used to see how often users have to restart the keyboard hook
|
||||
void Trace::EnableKeyboardManager(const bool enabled) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"KeyboardManager_EnableKeyboardManager",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(enabled, "Enabled"));
|
||||
}
|
||||
11
src/modules/keyboardmanager/dll/trace.h
Normal file
11
src/modules/keyboardmanager/dll/trace.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider() noexcept;
|
||||
static void UnregisterProvider() noexcept;
|
||||
|
||||
// Log if the user has KBM enabled or disabled - Can also be used to see how often users have to restart the keyboard hook
|
||||
static void EnableKeyboardManager(const bool enabled) noexcept;
|
||||
};
|
||||
Reference in New Issue
Block a user