mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-09 20:57:22 +02:00
[Keyboard Manager] Enable App-specific shortcuts in the KBM backend (#4628)
* Enable app specific shortcut remapping * Fixed lowercase function call * Add test file * Moved GetForegroundProcess to II and added tests * Fixed runtime error while testing due to heap allocation across dll boundary * Renamed function * Remove unused code * Changed process checking step to include substrings * Changed back to exact match included match without file extension
This commit is contained in:
@@ -189,8 +189,8 @@ namespace KeyboardManagerHelper
|
|||||||
keyEventArray[index].ki.dwExtraInfo = extraInfo;
|
keyEventArray[index].ki.dwExtraInfo = extraInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to return the window in focus
|
// Function to return window handle for a full screen UWP app
|
||||||
HWND GetFocusWindowHandle()
|
HWND GetFullscreenUWPWindowHandle()
|
||||||
{
|
{
|
||||||
// Using GetGUIThreadInfo for getting the process of the window in focus. GetForegroundWindow has issues with UWP apps as it returns the Application Frame Host as its linked process
|
// Using GetGUIThreadInfo for getting the process of the window in focus. GetForegroundWindow has issues with UWP apps as it returns the Application Frame Host as its linked process
|
||||||
GUITHREADINFO guiThreadInfo;
|
GUITHREADINFO guiThreadInfo;
|
||||||
@@ -208,30 +208,44 @@ namespace KeyboardManagerHelper
|
|||||||
// Function to return the executable name of the application in focus
|
// Function to return the executable name of the application in focus
|
||||||
std::wstring GetCurrentApplication(bool keepPath)
|
std::wstring GetCurrentApplication(bool keepPath)
|
||||||
{
|
{
|
||||||
HWND current_window_handle = GetFocusWindowHandle();
|
HWND current_window_handle = GetForegroundWindow();
|
||||||
DWORD process_id;
|
|
||||||
DWORD nSize = MAX_PATH;
|
|
||||||
WCHAR buffer[MAX_PATH] = { 0 };
|
|
||||||
|
|
||||||
// Get process ID of the focus window
|
|
||||||
DWORD thread_id = GetWindowThreadProcessId(current_window_handle, &process_id);
|
|
||||||
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id);
|
|
||||||
|
|
||||||
// Get full path of the executable
|
|
||||||
bool res = QueryFullProcessImageName(hProc, 0, buffer, &nSize);
|
|
||||||
std::wstring process_name;
|
std::wstring process_name;
|
||||||
CloseHandle(hProc);
|
|
||||||
|
|
||||||
process_name = buffer;
|
if (current_window_handle != nullptr)
|
||||||
if (res)
|
|
||||||
{
|
{
|
||||||
PathStripPath(buffer);
|
std::wstring process_path = get_process_path(current_window_handle);
|
||||||
|
process_name = process_path;
|
||||||
|
|
||||||
|
// Get process name from path
|
||||||
|
PathStripPath(&process_path[0]);
|
||||||
|
|
||||||
|
// Remove elements after null character
|
||||||
|
process_path.erase(std::find(process_path.begin(), process_path.end(), L'\0'), process_path.end());
|
||||||
|
|
||||||
|
// If the UWP app is in full-screen, then using GetForegroundWindow approach might fail
|
||||||
|
if (process_path == L"ApplicationFrameHost.exe")
|
||||||
|
{
|
||||||
|
HWND fullscreen_window_handle = GetFullscreenUWPWindowHandle();
|
||||||
|
if (fullscreen_window_handle != nullptr)
|
||||||
|
{
|
||||||
|
process_path = get_process_path(fullscreen_window_handle);
|
||||||
|
process_name = process_path;
|
||||||
|
|
||||||
|
// Get process name from path
|
||||||
|
PathStripPath(&process_path[0]);
|
||||||
|
|
||||||
|
// Remove elements after null character
|
||||||
|
process_path.erase(std::find(process_path.begin(), process_path.end(), L'\0'), process_path.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If keepPath is false, then return only the name of the process
|
||||||
if (!keepPath)
|
if (!keepPath)
|
||||||
{
|
{
|
||||||
process_name = buffer;
|
process_name = process_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_name;
|
return process_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ namespace KeyboardManagerHelper
|
|||||||
// Function to set the value of a key event based on the arguments
|
// Function to set the value of a key event based on the arguments
|
||||||
void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo);
|
void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo);
|
||||||
|
|
||||||
// Function to return the window in focus
|
// Function to return window handle for a full screen UWP app
|
||||||
HWND GetFocusWindowHandle();
|
HWND GetFullscreenUWPWindowHandle();
|
||||||
|
|
||||||
// Function to return the executable name of the application in focus
|
// Function to return the executable name of the application in focus
|
||||||
std::wstring GetCurrentApplication(bool keepPath);
|
std::wstring GetCurrentApplication(bool keepPath);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// Interface used to wrap keyboard input library methods
|
// Interface used to wrap keyboard input library methods
|
||||||
class InputInterface
|
class InputInterface
|
||||||
@@ -10,4 +11,7 @@ public:
|
|||||||
|
|
||||||
// Function to get the state of a particular key
|
// Function to get the state of a particular key
|
||||||
virtual bool GetVirtualKeyState(int key) = 0;
|
virtual bool GetVirtualKeyState(int key) = 0;
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
virtual void GetForegroundProcess(_Out_ std::wstring& foregroundProcess) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ void KeyboardManagerState::ClearSingleKeyRemaps()
|
|||||||
singleKeyReMap.clear();
|
singleKeyReMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to clear the App specific shortcut remapping table
|
||||||
|
void KeyboardManagerState::ClearAppSpecificShortcuts()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
|
||||||
|
appSpecificShortcutReMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Function to add a new OS level shortcut remapping
|
// Function to add a new OS level shortcut remapping
|
||||||
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC)
|
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC)
|
||||||
{
|
{
|
||||||
@@ -142,6 +149,32 @@ bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWO
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to add a new App specific shortcut remapping
|
||||||
|
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const Shortcut& newSC)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
|
||||||
|
|
||||||
|
// Check if there are any app specific shortcuts for this app
|
||||||
|
auto appIt = appSpecificShortcutReMap.find(app);
|
||||||
|
if (appIt != appSpecificShortcutReMap.end())
|
||||||
|
{
|
||||||
|
// Check if the shortcut is already remapped
|
||||||
|
auto shortcutIt = appSpecificShortcutReMap[app].find(originalSC);
|
||||||
|
if (shortcutIt != appSpecificShortcutReMap[app].end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert app name to lower case
|
||||||
|
std::wstring process_name;
|
||||||
|
process_name.resize(app.length());
|
||||||
|
std::transform(app.begin(), app.end(), process_name.begin(), towlower);
|
||||||
|
|
||||||
|
appSpecificShortcutReMap[process_name][originalSC] = RemapShortcut(newSC);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
||||||
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2)
|
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ private:
|
|||||||
StackPanel currentShortcutUI1;
|
StackPanel currentShortcutUI1;
|
||||||
StackPanel currentShortcutUI2;
|
StackPanel currentShortcutUI2;
|
||||||
std::mutex currentShortcutUI_mutex;
|
std::mutex currentShortcutUI_mutex;
|
||||||
|
|
||||||
// Stores the current configuration name.
|
// Stores the current configuration name.
|
||||||
std::wstring currentConfig = KeyboardManagerConstants::DefaultConfiguration;
|
std::wstring currentConfig = KeyboardManagerConstants::DefaultConfiguration;
|
||||||
std::mutex currentConfig_mutex;
|
std::mutex currentConfig_mutex;
|
||||||
@@ -120,12 +120,18 @@ public:
|
|||||||
// Function to clear the Keys remapping table
|
// Function to clear the Keys remapping table
|
||||||
void ClearSingleKeyRemaps();
|
void ClearSingleKeyRemaps();
|
||||||
|
|
||||||
|
// Function to clear the App specific shortcut remapping table
|
||||||
|
void ClearAppSpecificShortcuts();
|
||||||
|
|
||||||
// Function to add a new single key remapping
|
// Function to add a new single key remapping
|
||||||
bool AddSingleKeyRemap(const DWORD& originalKey, const DWORD& newRemapKey);
|
bool AddSingleKeyRemap(const DWORD& originalKey, const DWORD& newRemapKey);
|
||||||
|
|
||||||
// Function to add a new OS level shortcut remapping
|
// Function to add a new OS level shortcut remapping
|
||||||
bool AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC);
|
bool AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC);
|
||||||
|
|
||||||
|
// Function to add a new App specific level shortcut remapping
|
||||||
|
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const Shortcut& newSC);
|
||||||
|
|
||||||
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
|
||||||
void ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2);
|
void ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "Input.h"
|
#include "Input.h"
|
||||||
|
#include <keyboardmanager/common/Helpers.h>
|
||||||
|
|
||||||
// Function to simulate input
|
// Function to simulate input
|
||||||
UINT Input::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
|
UINT Input::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
|
||||||
@@ -12,3 +13,9 @@ bool Input::GetVirtualKeyState(int key)
|
|||||||
{
|
{
|
||||||
return (GetAsyncKeyState(key) & 0x8000);
|
return (GetAsyncKeyState(key) & 0x8000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void Input::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
|
||||||
|
{
|
||||||
|
foregroundProcess = KeyboardManagerHelper::GetCurrentApplication(false);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,4 +11,7 @@ public:
|
|||||||
|
|
||||||
// Function to get the state of a particular key
|
// Function to get the state of a particular key
|
||||||
bool GetVirtualKeyState(int key);
|
bool GetVirtualKeyState(int key);
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -596,18 +596,40 @@ namespace KeyboardEventHandlers
|
|||||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||||
{
|
{
|
||||||
std::wstring process_name = KeyboardManagerHelper::GetCurrentApplication(false);
|
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())
|
if (process_name.empty())
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert process name to lower case
|
||||||
|
std::transform(process_name.begin(), process_name.end(), process_name.begin(), towlower);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(keyboardManagerState.appSpecificShortcutReMap_mutex);
|
std::unique_lock<std::mutex> lock(keyboardManagerState.appSpecificShortcutReMap_mutex);
|
||||||
auto it = keyboardManagerState.appSpecificShortcutReMap.find(process_name);
|
std::wstring query_string = process_name;
|
||||||
|
auto 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);
|
||||||
|
}
|
||||||
|
|
||||||
if (it != keyboardManagerState.appSpecificShortcutReMap.end())
|
if (it != keyboardManagerState.appSpecificShortcutReMap.end())
|
||||||
{
|
{
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState.appSpecificShortcutReMap[process_name], keyboardManagerState.appSpecificShortcutReMap_mutex);
|
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState.appSpecificShortcutReMap[query_string], keyboardManagerState.appSpecificShortcutReMap_mutex);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,17 +140,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is used to add the hardcoded mappings
|
|
||||||
void init_map()
|
|
||||||
{
|
|
||||||
////App-specific shortcut remappings
|
|
||||||
//keyboardManagerState.appSpecificShortcutReMap[L"msedge.exe"][std::vector<DWORD>({ VK_LCONTROL, 0x43 })] = std::make_pair(std::vector<WORD>({ VK_LCONTROL, 0x56 }), false); // Ctrl+C to Ctrl+V
|
|
||||||
//keyboardManagerState.appSpecificShortcutReMap[L"msedge.exe"][std::vector<DWORD>({ VK_LMENU, 0x44 })] = std::make_pair(std::vector<WORD>({ VK_LCONTROL, 0x46 }), false); // Alt+D to Ctrl+F
|
|
||||||
//keyboardManagerState.appSpecificShortcutReMap[L"OUTLOOK.EXE"][std::vector<DWORD>({ VK_LCONTROL, 0x46 })] = std::make_pair(std::vector<WORD>({ VK_LCONTROL, 0x45 }), false); // Ctrl+F to Ctrl+E
|
|
||||||
//keyboardManagerState.appSpecificShortcutReMap[L"MicrosoftEdge.exe"][std::vector<DWORD>({ VK_LCONTROL, 0x58 })] = std::make_pair(std::vector<WORD>({ VK_LCONTROL, 0x56 }), false); // Ctrl+X to Ctrl+V
|
|
||||||
//keyboardManagerState.appSpecificShortcutReMap[L"Calculator.exe"][std::vector<DWORD>({ VK_LCONTROL, 0x47 })] = std::make_pair(std::vector<WORD>({ VK_LSHIFT, 0x32 }), false); // Ctrl+G to Shift+2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy the powertoy and free memory
|
// Destroy the powertoy and free memory
|
||||||
virtual void destroy() override
|
virtual void destroy() override
|
||||||
{
|
{
|
||||||
@@ -372,27 +361,17 @@ public:
|
|||||||
//// Remap a key to behave like a modifier instead of a toggle
|
//// Remap a key to behave like a modifier instead of a toggle
|
||||||
//intptr_t SingleKeyToggleToModResult = KeyboardEventHandlers::HandleSingleKeyToggleToModEvent(inputHandler, data, keyboardManagerState);
|
//intptr_t SingleKeyToggleToModResult = KeyboardEventHandlers::HandleSingleKeyToggleToModEvent(inputHandler, data, keyboardManagerState);
|
||||||
|
|
||||||
//// Handle an app-specific shortcut remapping
|
// Handle an app-specific shortcut remapping
|
||||||
//intptr_t AppSpecificShortcutRemapResult = KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
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 an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed.
|
||||||
//if (AppSpecificShortcutRemapResult == 1)
|
if (AppSpecificShortcutRemapResult == 1)
|
||||||
//{
|
|
||||||
// return 1;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Handle an os-level shortcut remapping
|
|
||||||
intptr_t OSLevelShortcutRemapResult = KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
|
||||||
|
|
||||||
// If any of the supported types of remappings took place, then suppress the key event
|
|
||||||
if ((SingleKeyRemapResult + OSLevelShortcutRemapResult) > 0)
|
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Handle an os-level shortcut remapping
|
||||||
return 0;
|
return KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "CppUnitTest.h"
|
||||||
|
#include "MockedInput.h"
|
||||||
|
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||||
|
#include <keyboardmanager/dll/KeyboardEventHandlers.h>
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
||||||
|
|
||||||
|
namespace RemappingLogicTests
|
||||||
|
{
|
||||||
|
TEST_CLASS (AppSpecificShortcutRemappingTests)
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
MockedInput mockedInputHandler;
|
||||||
|
KeyboardManagerState testState;
|
||||||
|
std::wstring testApp1 = L"TestProcess1.exe";
|
||||||
|
std::wstring testApp2 = L"TestProcess2.exe";
|
||||||
|
|
||||||
|
public:
|
||||||
|
TEST_METHOD_INITIALIZE(InitializeTestEnv)
|
||||||
|
{
|
||||||
|
// Reset test environment
|
||||||
|
TestHelpers::ResetTestEnv(mockedInputHandler, testState);
|
||||||
|
|
||||||
|
// Set HandleOSLevelShortcutRemapEvent as the hook procedure
|
||||||
|
std::function<intptr_t(LowlevelKeyboardEvent*)> currentHookProc = std::bind(&KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent, std::ref(mockedInputHandler), std::placeholders::_1, std::ref(testState));
|
||||||
|
mockedInputHandler.SetHookProc(currentHookProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the app specific remap takes place when the target app is in foreground
|
||||||
|
TEST_METHOD (AppSpecificShortcut_ShouldGetRemapped_WhenAppIsInForeground)
|
||||||
|
{
|
||||||
|
// Remap Ctrl+A to Alt+V
|
||||||
|
Shortcut src;
|
||||||
|
src.SetKey(VK_CONTROL);
|
||||||
|
src.SetKey(0x41);
|
||||||
|
Shortcut dest;
|
||||||
|
dest.SetKey(VK_MENU);
|
||||||
|
dest.SetKey(0x56);
|
||||||
|
testState.AddAppSpecificShortcut(testApp1, src, dest);
|
||||||
|
|
||||||
|
// Set the testApp as the foreground process
|
||||||
|
mockedInputHandler.SetForegroundProcess(testApp1);
|
||||||
|
|
||||||
|
const int nInputs = 2;
|
||||||
|
INPUT input[nInputs] = {};
|
||||||
|
input[0].type = INPUT_KEYBOARD;
|
||||||
|
input[0].ki.wVk = VK_CONTROL;
|
||||||
|
input[1].type = INPUT_KEYBOARD;
|
||||||
|
input[1].ki.wVk = 0x41;
|
||||||
|
|
||||||
|
// Send Ctrl+A keydown
|
||||||
|
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||||
|
|
||||||
|
// Ctrl and A key states should be unchanged, Alt and V key states should be true
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), false);
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), false);
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), true);
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the app specific remap takes place when the target app is not in foreground
|
||||||
|
TEST_METHOD (AppSpecificShortcut_ShouldNotGetRemapped_WhenAppIsNotInForeground)
|
||||||
|
{
|
||||||
|
// Remap Ctrl+A to Alt+V
|
||||||
|
Shortcut src;
|
||||||
|
src.SetKey(VK_CONTROL);
|
||||||
|
src.SetKey(0x41);
|
||||||
|
Shortcut dest;
|
||||||
|
dest.SetKey(VK_MENU);
|
||||||
|
dest.SetKey(0x56);
|
||||||
|
testState.AddAppSpecificShortcut(testApp1, src, dest);
|
||||||
|
|
||||||
|
// Set the testApp as the foreground process
|
||||||
|
mockedInputHandler.SetForegroundProcess(testApp2);
|
||||||
|
|
||||||
|
const int nInputs = 2;
|
||||||
|
INPUT input[nInputs] = {};
|
||||||
|
input[0].type = INPUT_KEYBOARD;
|
||||||
|
input[0].ki.wVk = VK_CONTROL;
|
||||||
|
input[1].type = INPUT_KEYBOARD;
|
||||||
|
input[1].ki.wVk = 0x41;
|
||||||
|
|
||||||
|
// Send Ctrl+A keydown
|
||||||
|
mockedInputHandler.SendVirtualInput(nInputs, input, sizeof(INPUT));
|
||||||
|
|
||||||
|
// Ctrl and A key states should be unchanged, Alt and V key states should be true
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_CONTROL), true);
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x41), true);
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(VK_MENU), false);
|
||||||
|
Assert::AreEqual(mockedInputHandler.GetVirtualKeyState(0x56), false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -102,6 +102,7 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp" />
|
||||||
<ClCompile Include="MockedInputSanityTests.cpp" />
|
<ClCompile Include="MockedInputSanityTests.cpp" />
|
||||||
<ClCompile Include="SetKeyEventTests.cpp" />
|
<ClCompile Include="SetKeyEventTests.cpp" />
|
||||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp">
|
<ClCompile Include="OSLevelShortcutRemappingTests.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="pch.h">
|
<ClInclude Include="pch.h">
|
||||||
|
|||||||
@@ -147,3 +147,15 @@ int MockedInput::GetSendVirtualInputCallCount()
|
|||||||
{
|
{
|
||||||
return sendVirtualInputCallCount;
|
return sendVirtualInputCallCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void MockedInput::SetForegroundProcess(std::wstring process)
|
||||||
|
{
|
||||||
|
currentProcess = process;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void MockedInput::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
|
||||||
|
{
|
||||||
|
foregroundProcess = currentProcess;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ private:
|
|||||||
int sendVirtualInputCallCount = 0;
|
int sendVirtualInputCallCount = 0;
|
||||||
std::function<bool(LowlevelKeyboardEvent*)> sendVirtualInputCallCondition;
|
std::function<bool(LowlevelKeyboardEvent*)> sendVirtualInputCallCondition;
|
||||||
|
|
||||||
|
std::wstring currentProcess;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MockedInput()
|
MockedInput()
|
||||||
{
|
{
|
||||||
@@ -45,4 +47,10 @@ public:
|
|||||||
|
|
||||||
// Function to get SendVirtualInput call count
|
// Function to get SendVirtualInput call count
|
||||||
int GetSendVirtualInputCallCount();
|
int GetSendVirtualInputCallCount();
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void SetForegroundProcess(std::wstring process);
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace TestHelpers
|
|||||||
input.ResetKeyboardState();
|
input.ResetKeyboardState();
|
||||||
input.SetHookProc(nullptr);
|
input.SetHookProc(nullptr);
|
||||||
input.SetSendVirtualInputTestHandler(nullptr);
|
input.SetSendVirtualInputTestHandler(nullptr);
|
||||||
|
input.SetForegroundProcess(L"");
|
||||||
state.ClearSingleKeyRemaps();
|
state.ClearSingleKeyRemaps();
|
||||||
state.ClearOSLevelShortcuts();
|
state.ClearOSLevelShortcuts();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user