2020-03-23 10:44:02 -07:00
# include "pch.h"
# include "Helpers.h"
# include <sstream>
2020-04-23 08:37:52 -07:00
# include "../common/shared_constants.h"
2020-05-28 14:47:32 -07:00
# include <shlwapi.h>
2020-08-17 13:46:50 -07:00
# include "../../common/common.h"
2020-08-24 15:10:50 -07:00
# include "keyboardmanager/dll/Generated Files/resource.h"
2020-08-13 16:32:15 -07:00
# include "../common/keyboard_layout.h"
2020-08-17 13:46:50 -07:00
extern " C " IMAGE_DOS_HEADER __ImageBase ;
2020-03-23 10:44:02 -07:00
2020-04-20 21:01:21 -07:00
using namespace winrt : : Windows : : Foundation ;
2020-04-20 08:22:36 -07:00
namespace KeyboardManagerHelper
2020-03-23 10:44:02 -07:00
{
2020-04-20 08:22:36 -07:00
// Function to split a wstring based on a delimiter and return a vector of split strings
std : : vector < std : : wstring > splitwstring ( const std : : wstring & input , wchar_t delimiter )
2020-03-23 10:44:02 -07:00
{
2020-04-20 08:22:36 -07:00
std : : wstringstream ss ( input ) ;
std : : wstring item ;
std : : vector < std : : wstring > splittedStrings ;
while ( std : : getline ( ss , item , delimiter ) )
{
splittedStrings . push_back ( item ) ;
}
2020-04-20 21:01:21 -07:00
2020-04-20 08:22:36 -07:00
return splittedStrings ;
2020-03-23 10:44:02 -07:00
}
2020-04-20 21:01:21 -07:00
2020-04-20 08:22:36 -07:00
// Function to return the next sibling element for an element under a stack panel
2020-04-20 21:01:21 -07:00
IInspectable getSiblingElement ( IInspectable const & element )
2020-04-20 08:22:36 -07:00
{
FrameworkElement frameworkElement = element . as < FrameworkElement > ( ) ;
StackPanel parentElement = frameworkElement . Parent ( ) . as < StackPanel > ( ) ;
uint32_t index ;
2020-03-23 10:44:02 -07:00
2020-04-20 08:22:36 -07:00
parentElement . Children ( ) . IndexOf ( frameworkElement , index ) ;
return parentElement . Children ( ) . GetAt ( index + 1 ) ;
}
2020-04-20 21:01:21 -07:00
2020-04-20 08:22:36 -07:00
// Function to check if the key is a modifier key
bool IsModifierKey ( DWORD key )
{
return ( GetKeyType ( key ) ! = KeyType : : Action ) ;
}
2020-04-18 16:12:26 -07:00
2020-04-20 08:22:36 -07:00
// Function to get the type of the key
KeyType GetKeyType ( DWORD key )
2020-04-18 16:12:26 -07:00
{
2020-04-20 08:22:36 -07:00
switch ( key )
{
2020-04-23 08:37:52 -07:00
case CommonSharedConstants : : VK_WIN_BOTH :
2020-04-20 08:22:36 -07:00
case VK_LWIN :
case VK_RWIN :
return KeyType : : Win ;
case VK_CONTROL :
case VK_LCONTROL :
case VK_RCONTROL :
return KeyType : : Ctrl ;
case VK_MENU :
case VK_LMENU :
case VK_RMENU :
return KeyType : : Alt ;
case VK_SHIFT :
case VK_LSHIFT :
case VK_RSHIFT :
return KeyType : : Shift ;
default :
return KeyType : : Action ;
}
2020-04-18 16:12:26 -07:00
}
2020-04-20 08:22:36 -07:00
// Function to return if the key is an extended key which requires the use of the extended key flag
2020-04-26 15:09:40 -07:00
bool IsExtendedKey ( DWORD key )
2020-04-16 15:17:57 -07:00
{
2020-04-20 08:22:36 -07:00
switch ( key )
{
case VK_RCONTROL :
case VK_RMENU :
case VK_NUMLOCK :
case VK_SNAPSHOT :
case VK_CANCEL :
2020-06-24 20:28:54 -07:00
// If the extended flag is not set for the following keys, their NumPad versions are sent. This causes weird behavior when NumLock is on (more information at https://github.com/microsoft/PowerToys/issues/3478)
case VK_INSERT :
case VK_HOME :
case VK_PRIOR :
case VK_DELETE :
case VK_END :
case VK_NEXT :
case VK_LEFT :
case VK_DOWN :
case VK_RIGHT :
case VK_UP :
2020-04-20 08:22:36 -07:00
return true ;
default :
return false ;
}
2020-04-16 15:17:57 -07:00
}
2020-04-26 15:09:40 -07:00
2020-04-20 21:01:21 -07:00
Collections : : IVector < IInspectable > ToBoxValue ( const std : : vector < std : : wstring > & list )
{
Collections : : IVector < IInspectable > boxList = single_threaded_vector < IInspectable > ( ) ;
for ( auto & val : list )
{
boxList . Append ( winrt : : box_value ( val ) ) ;
}
return boxList ;
}
2020-04-26 15:09:40 -07:00
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ErrorType DoKeysOverlap ( DWORD first , DWORD second )
{
// If the keys are same
if ( first = = second )
{
return ErrorType : : SameKeyPreviouslyMapped ;
}
else if ( ( GetKeyType ( first ) = = GetKeyType ( second ) ) & & GetKeyType ( first ) ! = KeyType : : Action )
{
// If the keys are of the same modifier type and overlapping, i.e. one is L/R and other is common
2020-05-08 17:34:24 -07:00
if ( ( ( first = = VK_LWIN & & second = = VK_RWIN ) | | ( first = = VK_RWIN & & second = = VK_LWIN ) ) | | ( ( first = = VK_LCONTROL & & second = = VK_RCONTROL ) | | ( first = = VK_RCONTROL & & second = = VK_LCONTROL ) ) | | ( ( first = = VK_LMENU & & second = = VK_RMENU ) | | ( first = = VK_RMENU & & second = = VK_LMENU ) ) | | ( ( first = = VK_LSHIFT & & second = = VK_RSHIFT ) | | ( first = = VK_RSHIFT & & second = = VK_LSHIFT ) ) )
2020-04-26 15:09:40 -07:00
{
return ErrorType : : NoError ;
}
else
{
return ErrorType : : ConflictingModifierKey ;
}
}
// If no overlap
else
{
return ErrorType : : NoError ;
}
}
// Function to return the error message
winrt : : hstring GetErrorMessage ( ErrorType errorType )
{
switch ( errorType )
{
case ErrorType : : NoError :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_REMAPSUCCESSFUL ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : SameKeyPreviouslyMapped :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SAMEKEYPREVIOUSLYMAPPED ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : MapToSameKey :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_MAPPEDTOSAMEKEY ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ConflictingModifierKey :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_CONFLICTINGMODIFIERKEY ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : SameShortcutPreviouslyMapped :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SAMESHORTCUTPREVIOUSLYMAPPED ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : MapToSameShortcut :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_MAPTOSAMESHORTCUT ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ConflictingModifierShortcut :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_CONFLICTINGMODIFIERSHORTCUT ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : WinL :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_WINL ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : CtrlAltDel :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_CTRLALTDEL ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : RemapUnsuccessful :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_REMAPUNSUCCESSFUL ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : SaveFailed :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SAVEFAILED ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ShortcutStartWithModifier :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SHORTCUTSTARTWITHMODIFIER ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ShortcutCannotHaveRepeatedModifier :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SHORTCUTNOREPEATEDMODIFIER ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ShortcutAtleast2Keys :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SHORTCUTATLEAST2KEYS ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ShortcutOneActionKey :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SHORTCUTONEACTIONKEY ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
case ErrorType : : ShortcutNotMoreThanOneActionKey :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_SHORTCUTMAXONEACTIONKEY ) . c_str ( ) ;
2020-05-11 10:10:36 -07:00
case ErrorType : : ShortcutMaxShortcutSizeOneActionKey :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_MAXSHORTCUTSIZE ) . c_str ( ) ;
2020-10-02 15:36:36 +03:00
case ErrorType : : ShortcutDisableAsActionKey :
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_DISABLEASACTIONKEY ) . c_str ( ) ;
2020-05-12 11:32:17 -07:00
default :
2020-08-17 13:46:50 -07:00
return GET_RESOURCE_STRING ( IDS_ERRORMESSAGE_DEFAULT ) . c_str ( ) ;
2020-04-26 15:09:40 -07:00
}
}
2020-05-28 14:47:32 -07:00
// 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 )
{
keyEventArray [ index ] . type = inputType ;
keyEventArray [ index ] . ki . wVk = keyCode ;
keyEventArray [ index ] . ki . dwFlags = flags ;
if ( IsExtendedKey ( keyCode ) )
{
keyEventArray [ index ] . ki . dwFlags | = KEYEVENTF_EXTENDEDKEY ;
}
keyEventArray [ index ] . ki . dwExtraInfo = extraInfo ;
}
2020-07-06 16:45:53 -07:00
// Function to return window handle for a full screen UWP app
HWND GetFullscreenUWPWindowHandle ( )
2020-05-28 14:47:32 -07:00
{
// 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 . cbSize = sizeof ( GUITHREADINFO ) ;
GetGUIThreadInfo ( 0 , & guiThreadInfo ) ;
// If no window in focus, use the active window
if ( guiThreadInfo . hwndFocus = = nullptr )
{
return guiThreadInfo . hwndActive ;
}
return guiThreadInfo . hwndFocus ;
}
// Function to return the executable name of the application in focus
std : : wstring GetCurrentApplication ( bool keepPath )
{
2020-07-06 16:45:53 -07:00
HWND current_window_handle = GetForegroundWindow ( ) ;
2020-05-28 14:47:32 -07:00
std : : wstring process_name ;
2020-07-06 16:45:53 -07:00
if ( current_window_handle ! = nullptr )
2020-05-28 14:47:32 -07:00
{
2020-07-06 16:45:53 -07:00
std : : wstring process_path = get_process_path ( current_window_handle ) ;
process_name = process_path ;
2020-07-23 16:43:49 -07:00
2020-07-06 16:45:53 -07:00
// Get process name from path
PathStripPath ( & process_path [ 0 ] ) ;
2020-05-28 14:47:32 -07:00
2020-07-06 16:45:53 -07:00
// 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
2020-05-28 14:47:32 -07:00
if ( ! keepPath )
{
2020-07-06 16:45:53 -07:00
process_name = process_path ;
2020-05-28 14:47:32 -07:00
}
}
2020-07-06 16:45:53 -07:00
2020-05-28 14:47:32 -07:00
return process_name ;
}
2020-07-23 16:43:49 -07:00
// Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modfifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
void SetModifierKeyEvents ( const Shortcut & shortcutToBeSent , const ModifierKey & winKeyInvoked , LPINPUT keyEventArray , int & index , bool isKeyDown , ULONG_PTR extraInfoFlag , const Shortcut & shortcutToCompare , const DWORD & keyToBeReleased )
{
// If key down is to be sent, send in the order Win, Ctrl, Alt, Shift
if ( isKeyDown )
{
// If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modfifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
if ( shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = shortcutToCompare . GetWinKey ( winKeyInvoked ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckWinKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetWinKey ( winKeyInvoked ) , 0 , extraInfoFlag ) ;
index + + ;
}
if ( shortcutToBeSent . GetCtrlKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetCtrlKey ( ) ! = shortcutToCompare . GetCtrlKey ( ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckCtrlKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetCtrlKey ( ) , 0 , extraInfoFlag ) ;
index + + ;
}
if ( shortcutToBeSent . GetAltKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetAltKey ( ) ! = shortcutToCompare . GetAltKey ( ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckAltKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetAltKey ( ) , 0 , extraInfoFlag ) ;
index + + ;
}
if ( shortcutToBeSent . GetShiftKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetShiftKey ( ) ! = shortcutToCompare . GetShiftKey ( ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckShiftKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetShiftKey ( ) , 0 , extraInfoFlag ) ;
index + + ;
}
}
// If key up is to be sent, send in the order Shift, Alt, Ctrl, Win
else
{
// If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modfifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
if ( shortcutToBeSent . GetShiftKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetShiftKey ( ) ! = shortcutToCompare . GetShiftKey ( ) | | shortcutToBeSent . CheckShiftKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetShiftKey ( ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
index + + ;
}
if ( shortcutToBeSent . GetAltKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetAltKey ( ) ! = shortcutToCompare . GetAltKey ( ) | | shortcutToBeSent . CheckAltKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetAltKey ( ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
index + + ;
}
if ( shortcutToBeSent . GetCtrlKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetCtrlKey ( ) ! = shortcutToCompare . GetCtrlKey ( ) | | shortcutToBeSent . CheckCtrlKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetCtrlKey ( ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
index + + ;
}
if ( shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = shortcutToCompare . GetWinKey ( winKeyInvoked ) | | shortcutToBeSent . CheckWinKey ( keyToBeReleased ) ) )
{
KeyboardManagerHelper : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , ( WORD ) shortcutToBeSent . GetWinKey ( winKeyInvoked ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
index + + ;
}
}
}
// Function to filter the key codes for artificial key codes
DWORD FilterArtificialKeys ( const DWORD & key )
{
switch ( key )
{
// If a key is remapped to VK_WIN_BOTH, we send VK_LWIN instead
case CommonSharedConstants : : VK_WIN_BOTH :
return VK_LWIN ;
}
return key ;
}
// Function to sort a vector of shortcuts based on it's size
void SortShortcutVectorBasedOnSize ( std : : vector < Shortcut > & shortcutVector )
{
std : : sort ( shortcutVector . begin ( ) , shortcutVector . end ( ) , [ ] ( Shortcut first , Shortcut second ) {
return first . Size ( ) > second . Size ( ) ;
} ) ;
}
2020-08-13 16:32:15 -07:00
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier ( std : : vector < DWORD > & currentKeys , int selectedKeyIndex , const std : : vector < DWORD > & keyCodeList )
{
// check if modifier has already been added before in a previous drop down
int currentDropDownIndex = - 1 ;
// Find the key index of the current drop down selection so that we skip that index while searching for repeated modifiers
for ( int i = 0 ; i < currentKeys . size ( ) ; i + + )
{
if ( currentKeys [ i ] = = keyCodeList [ selectedKeyIndex ] )
{
currentDropDownIndex = i ;
break ;
}
}
bool matchPreviousModifier = false ;
for ( int i = 0 ; i < currentKeys . size ( ) ; i + + )
{
// Skip the current drop down
if ( i ! = currentDropDownIndex )
{
// If the key type for the newly added key matches any of the existing keys in the shortcut
if ( KeyboardManagerHelper : : GetKeyType ( keyCodeList [ selectedKeyIndex ] ) = = KeyboardManagerHelper : : GetKeyType ( currentKeys [ i ] ) )
{
matchPreviousModifier = true ;
break ;
}
}
}
return matchPreviousModifier ;
}
// Function to get the selected key codes from the list of selected indices
std : : vector < DWORD > GetKeyCodesFromSelectedIndices ( const std : : vector < int32_t > & selectedIndices , const std : : vector < DWORD > & keyCodeList )
{
std : : vector < DWORD > keys ;
for ( int i = 0 ; i < selectedIndices . size ( ) ; i + + )
{
int selectedKeyIndex = selectedIndices [ i ] ;
if ( selectedKeyIndex ! = - 1 & & keyCodeList . size ( ) > selectedKeyIndex )
{
// If None is not the selected key
if ( keyCodeList [ selectedKeyIndex ] ! = 0 )
{
keys . push_back ( keyCodeList [ selectedKeyIndex ] ) ;
}
}
}
return keys ;
}
2020-04-16 15:17:57 -07:00
}