2020-03-23 10:44:02 -07:00
# include "pch.h"
# include "Helpers.h"
2021-04-27 00:37:44 -07:00
# include <sstream>
2020-12-15 15:16:09 +03:00
# include <common/interop/shared_constants.h>
# include <common/utils/process_path.h>
2020-10-08 11:28:37 -07:00
# include "KeyboardManagerConstants.h"
2020-03-23 10:44:02 -07:00
2021-05-07 11:16:31 +03:00
namespace Helpers
2020-03-23 10:44:02 -07:00
{
2023-08-23 18:56:40 +02:00
DWORD EncodeKeyNumpadOrigin ( const DWORD key , const bool extended )
{
bool numpad_originated = false ;
switch ( key )
{
case VK_LEFT :
case VK_RIGHT :
case VK_UP :
case VK_DOWN :
case VK_INSERT :
case VK_DELETE :
case VK_PRIOR :
case VK_NEXT :
case VK_HOME :
case VK_END :
numpad_originated = ! extended ;
break ;
case VK_RETURN :
case VK_DIVIDE :
numpad_originated = extended ;
break ;
}
if ( numpad_originated )
return key | GetNumpadOriginEncodingBit ( ) ;
else
return key ;
}
DWORD ClearKeyNumpadOrigin ( const DWORD key )
{
return ( key & ~ GetNumpadOriginEncodingBit ( ) ) ;
}
bool IsNumpadOriginated ( const DWORD key )
{
return ! ! ( key & GetNumpadOriginEncodingBit ( ) ) ;
}
DWORD GetNumpadOriginEncodingBit ( )
{
// Intentionally do not mimic KF_EXTENDED to avoid confusion, because it's not the same thing
// See EncodeKeyNumpadOrigin.
return 1ull < < 31 ;
}
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
2021-09-03 16:19:16 +01:00
// Function to get the combined key for modifier keys
DWORD GetCombinedKey ( DWORD key )
{
2023-08-23 18:56:40 +02:00
switch ( key )
{
2021-09-03 16:19:16 +01:00
case VK_LWIN :
case VK_RWIN :
return CommonSharedConstants : : VK_WIN_BOTH ;
case VK_LCONTROL :
case VK_RCONTROL :
return VK_CONTROL ;
case VK_LMENU :
case VK_RMENU :
return VK_MENU ;
case VK_LSHIFT :
case VK_RSHIFT :
return VK_SHIFT ;
default :
return key ;
}
}
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 :
2023-04-21 17:50:14 +01:00
case VK_SLEEP :
case VK_MEDIA_NEXT_TRACK :
case VK_MEDIA_PREV_TRACK :
case VK_MEDIA_STOP :
case VK_MEDIA_PLAY_PAUSE :
case VK_VOLUME_MUTE :
case VK_VOLUME_UP :
case VK_VOLUME_DOWN :
case VK_LAUNCH_MEDIA_SELECT :
case VK_LAUNCH_MAIL :
case VK_LAUNCH_APP1 :
case VK_LAUNCH_APP2 :
case VK_BROWSER_SEARCH :
case VK_BROWSER_HOME :
case VK_BROWSER_BACK :
case VK_BROWSER_FORWARD :
case VK_BROWSER_STOP :
case VK_BROWSER_REFRESH :
case VK_BROWSER_FAVORITES :
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-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-10-08 17:52:19 -07:00
2020-12-15 15:16:09 +03:00
// Set wScan to the value from MapVirtualKey as some applications may use the scan code for handling input, for instance, Windows Terminal ignores non-character input which has scancode set to 0.
2020-10-08 17:52:19 -07:00
// MapVirtualKey returns 0 if the key code does not correspond to a physical key (such as unassigned/reserved keys). More details at https://github.com/microsoft/PowerToys/pull/7143#issue-498877747
2023-02-08 11:01:13 +00:00
keyEventArray [ index ] . ki . wScan = static_cast < WORD > ( MapVirtualKey ( keyCode , MAPVK_VK_TO_VSC ) ) ;
2020-05-28 14:47:32 -07:00
}
2020-10-08 11:28:37 -07:00
// Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar)
void SetDummyKeyEvent ( LPINPUT keyEventArray , int & index , ULONG_PTR extraInfo )
{
2023-02-08 11:01:13 +00:00
SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( KeyboardManagerConstants : : DUMMY_KEY ) , 0 , extraInfo ) ;
2020-10-08 11:28:37 -07:00
index + + ;
2023-02-08 11:01:13 +00:00
SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( KeyboardManagerConstants : : DUMMY_KEY ) , KEYEVENTF_KEYUP , extraInfo ) ;
2020-10-08 11:28:37 -07:00
index + + ;
}
2020-05-28 14:47:32 -07:00
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
2020-10-30 14:41:37 -04: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 modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
2020-07-23 16:43:49 -07:00
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 )
{
2020-10-30 14:41:37 -04:00
// 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 modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
2020-07-23 16:43:49 -07:00
if ( shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = shortcutToCompare . GetWinKey ( winKeyInvoked ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckWinKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetWinKey ( winKeyInvoked ) ) , 0 , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
if ( shortcutToBeSent . GetCtrlKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetCtrlKey ( ) ! = shortcutToCompare . GetCtrlKey ( ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckCtrlKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetCtrlKey ( ) ) , 0 , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
if ( shortcutToBeSent . GetAltKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetAltKey ( ) ! = shortcutToCompare . GetAltKey ( ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckAltKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetAltKey ( ) ) , 0 , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
if ( shortcutToBeSent . GetShiftKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetShiftKey ( ) ! = shortcutToCompare . GetShiftKey ( ) ) & & ( keyToBeReleased = = NULL | | ! shortcutToBeSent . CheckShiftKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetShiftKey ( ) ) , 0 , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
}
// If key up is to be sent, send in the order Shift, Alt, Ctrl, Win
else
{
2020-10-30 14:41:37 -04:00
// 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 modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
2020-07-23 16:43:49 -07:00
if ( shortcutToBeSent . GetShiftKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetShiftKey ( ) ! = shortcutToCompare . GetShiftKey ( ) | | shortcutToBeSent . CheckShiftKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetShiftKey ( ) ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
if ( shortcutToBeSent . GetAltKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetAltKey ( ) ! = shortcutToCompare . GetAltKey ( ) | | shortcutToBeSent . CheckAltKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetAltKey ( ) ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
if ( shortcutToBeSent . GetCtrlKey ( ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetCtrlKey ( ) ! = shortcutToCompare . GetCtrlKey ( ) | | shortcutToBeSent . CheckCtrlKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetCtrlKey ( ) ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
if ( shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = NULL & & ( shortcutToCompare . IsEmpty ( ) | | shortcutToBeSent . GetWinKey ( winKeyInvoked ) ! = shortcutToCompare . GetWinKey ( winKeyInvoked ) | | shortcutToBeSent . CheckWinKey ( keyToBeReleased ) ) )
{
2023-02-08 11:01:13 +00:00
Helpers : : SetKeyEvent ( keyEventArray , index , INPUT_KEYBOARD , static_cast < WORD > ( shortcutToBeSent . GetWinKey ( winKeyInvoked ) ) , KEYEVENTF_KEYUP , extraInfoFlag ) ;
2020-07-23 16:43:49 -07:00
index + + ;
}
}
}
// Function to filter the key codes for artificial key codes
2020-10-19 12:27:47 +03:00
int32_t FilterArtificialKeys ( const int32_t & key )
2020-07-23 16:43:49 -07:00
{
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-04-16 15:17:57 -07:00
}