[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:
Arjun Balgovind
2020-07-06 16:45:53 -07:00
committed by GitHub
parent 70405045d7
commit 1533c9315f
15 changed files with 241 additions and 52 deletions

View File

@@ -189,8 +189,8 @@ namespace KeyboardManagerHelper
keyEventArray[index].ki.dwExtraInfo = extraInfo;
}
// Function to return the window in focus
HWND GetFocusWindowHandle()
// Function to return window handle for a full screen UWP app
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
GUITHREADINFO guiThreadInfo;
@@ -208,30 +208,44 @@ namespace KeyboardManagerHelper
// Function to return the executable name of the application in focus
std::wstring GetCurrentApplication(bool keepPath)
{
HWND current_window_handle = GetFocusWindowHandle();
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);
HWND current_window_handle = GetForegroundWindow();
std::wstring process_name;
CloseHandle(hProc);
process_name = buffer;
if (res)
if (current_window_handle != nullptr)
{
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)
{
process_name = buffer;
process_name = process_path;
}
}
return process_name;
}
}

View File

@@ -73,8 +73,8 @@ namespace KeyboardManagerHelper
// 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);
// Function to return the window in focus
HWND GetFocusWindowHandle();
// Function to return window handle for a full screen UWP app
HWND GetFullscreenUWPWindowHandle();
// Function to return the executable name of the application in focus
std::wstring GetCurrentApplication(bool keepPath);

View File

@@ -1,5 +1,6 @@
#pragma once
#include "windows.h"
#include <string>
// Interface used to wrap keyboard input library methods
class InputInterface
@@ -10,4 +11,7 @@ public:
// Function to get the state of a particular key
virtual bool GetVirtualKeyState(int key) = 0;
// Function to get the foreground process name
virtual void GetForegroundProcess(_Out_ std::wstring& foregroundProcess) = 0;
};

View File

@@ -110,6 +110,13 @@ void KeyboardManagerState::ClearSingleKeyRemaps()
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
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const Shortcut& newSC)
{
@@ -142,6 +149,32 @@ bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const DWO
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
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2)
{

View File

@@ -59,7 +59,7 @@ private:
StackPanel currentShortcutUI1;
StackPanel currentShortcutUI2;
std::mutex currentShortcutUI_mutex;
// Stores the current configuration name.
std::wstring currentConfig = KeyboardManagerConstants::DefaultConfiguration;
std::mutex currentConfig_mutex;
@@ -120,12 +120,18 @@ public:
// Function to clear the Keys remapping table
void ClearSingleKeyRemaps();
// Function to clear the App specific shortcut remapping table
void ClearAppSpecificShortcuts();
// Function to add a new single key remapping
bool AddSingleKeyRemap(const DWORD& originalKey, const DWORD& newRemapKey);
// Function to add a new OS level shortcut remapping
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
void ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2);