2020-05-28 14:47:32 -07:00
# include "pch.h"
# include "KeyboardEventHandlers.h"
2021-04-26 22:01:38 +03:00
2020-12-15 15:16:09 +03:00
# include <common/interop/shared_constants.h>
2021-04-26 22:01:38 +03:00
2020-07-13 11:49:09 -07:00
# include <keyboardmanager/common/InputInterface.h>
2020-07-23 16:43:49 -07:00
# include <keyboardmanager/common/Helpers.h>
2021-04-26 22:01:38 +03:00
# include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
2020-05-28 14:47:32 -07:00
namespace KeyboardEventHandlers
{
// Function to a handle a single key remap
2021-05-07 11:16:31 +03:00
intptr_t HandleSingleKeyRemapEvent ( KeyboardManagerInput : : InputInterface & ii , LowlevelKeyboardEvent * data , State & state ) noexcept
2020-05-28 14:47:32 -07:00
{
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if ( ! ( data - > lParam - > dwExtraInfo & CommonSharedConstants : : KEYBOARDMANAGER_INJECTED_FLAG ) )
{
2021-05-07 11:16:31 +03:00
const auto remapping = state . GetSingleKeyRemap ( data - > lParam - > vkCode ) ;
2020-10-08 11:28:24 -07:00
if ( remapping )
2020-05-28 14:47:32 -07:00
{
2020-10-08 11:28:24 -07:00
auto it = remapping . value ( ) ;
2020-07-23 16:43:49 -07:00
// Check if the remap is to a key or a shortcut
bool remapToKey = ( it - > second . index ( ) = = 0 ) ;
2020-10-02 15:36:36 +03:00
// If mapped to VK_DISABLED then the key is disabled
2020-07-23 16:43:49 -07:00
if ( remapToKey )
2020-05-28 14:47:32 -07:00
{
2020-10-02 15:36:36 +03:00
if ( std : : get < DWORD > ( it - > second ) = = CommonSharedConstants : : VK_DISABLED )
2020-07-23 16:43:49 -07:00
{
return 1 ;
}
2020-05-28 14:47:32 -07:00
}
2020-07-23 16:43:49 -07:00
int key_count ;
if ( remapToKey )
{
key_count = 1 ;
}
else
{
2020-10-15 08:53:43 -07:00
key_count = std : : get < Shortcut > ( it - > second ) . Size ( ) ;
2020-07-23 16:43:49 -07:00
}
2021-04-26 22:01:38 +03:00
2020-05-28 14:47:32 -07:00
LPINPUT keyEventList = new INPUT [ size_t ( key_count ) ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
// Handle remaps to VK_WIN_BOTH
2020-07-23 16:43:49 -07:00
DWORD target ;
if ( remapToKey )
{
2021-05-07 11:16:31 +03:00
target = Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second ) ) ;
2020-07-23 16:43:49 -07:00
}
else
2020-05-28 14:47:32 -07:00
{
2021-05-07 11:16:31 +03:00
target = Helpers : : FilterArtificialKeys ( std : : get < Shortcut > ( it - > second ) . GetActionKey ( ) ) ;
2020-05-28 14:47:32 -07:00
}
2020-06-15 16:48:00 -07:00
// 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
2020-07-23 16:43:49 -07:00
if ( data - > wParam = = WM_KEYDOWN | | data - > wParam = = WM_SYSKEYDOWN )
2020-06-15 16:48:00 -07:00
{
2020-07-23 16:43:49 -07:00
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , it - > first , target ) ;
2020-06-15 16:48:00 -07:00
}
2020-07-23 16:43:49 -07:00
if ( remapToKey )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
if ( data - > wParam = = WM_KEYUP | | data - > wParam = = WM_SYSKEYUP )
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) target , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
2020-07-23 16:43:49 -07:00
}
else
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) target , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
2020-07-23 16:43:49 -07:00
}
2020-05-28 14:47:32 -07:00
}
else
{
2020-07-23 16:43:49 -07:00
int i = 0 ;
Shortcut targetShortcut = std : : get < Shortcut > ( it - > second ) ;
if ( data - > wParam = = WM_KEYUP | | data - > wParam = = WM_SYSKEYUP )
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) targetShortcut . GetActionKey ( ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
2020-07-23 16:43:49 -07:00
i + + ;
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( targetShortcut , ModifierKey : : Disabled , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
2020-10-15 08:53:43 -07:00
// 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
2020-07-23 16:43:49 -07:00
}
else
{
2020-10-15 08:53:43 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( targetShortcut , ModifierKey : : Disabled , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) targetShortcut . GetActionKey ( ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
2020-07-23 16:43:49 -07:00
i + + ;
}
2020-05-28 14:47:32 -07:00
}
2020-06-11 13:07:46 -07:00
UINT res = ii . SendVirtualInput ( key_count , keyEventList , sizeof ( INPUT ) ) ;
2020-05-28 14:47:32 -07:00
delete [ ] keyEventList ;
2020-06-15 16:48:00 -07:00
2020-07-23 16:43:49 -07:00
if ( data - > wParam = = WM_KEYDOWN | | data - > wParam = = WM_SYSKEYDOWN )
2020-06-15 16:48:00 -07:00
{
2020-09-08 14:40:02 -07:00
// 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
2020-07-23 16:43:49 -07:00
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 ) ;
}
}
2020-06-15 16:48:00 -07:00
}
2020-05-28 14:47:32 -07:00
return 1 ;
}
}
return 0 ;
}
2020-10-08 11:28:24 -07:00
/* This feature has not been enabled (code from proof of concept stage)
*
2020-05-28 14:47:32 -07:00
// Function to a change a key's behavior from toggle to modifier
2021-05-07 11:16:31 +03:00
__declspec ( dllexport ) intptr_t HandleSingleKeyToggleToModEvent ( InputInterface & ii , LowlevelKeyboardEvent * data , State & State ) noexcept
2020-05-28 14:47:32 -07:00
{
// 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
2021-05-07 11:16:31 +03:00
std : : unique_lock < std : : mutex > lock ( State . singleKeyToggleToMod_mutex ) ;
auto it = State . singleKeyToggleToMod . find ( data - > lParam - > vkCode ) ;
if ( it ! = State . singleKeyToggleToMod . end ( ) )
2020-05-28 14:47:32 -07:00
{
// 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 )
{
2021-05-07 11:16:31 +03:00
State . singleKeyToggleToMod [ data - > lParam - > vkCode ] = true ;
2020-05-28 14:47:32 -07:00
}
else
{
lock . unlock ( ) ;
return 1 ;
}
}
int key_count = 2 ;
LPINPUT keyEventList = new INPUT [ size_t ( key_count ) ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) data - > lParam - > vkCode , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
Helpers : : SetKeyEvent ( keyEventList , 1 , INPUT_KEYBOARD , ( WORD ) data - > lParam - > vkCode , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SINGLEKEY_FLAG ) ;
2020-05-28 14:47:32 -07:00
lock . unlock ( ) ;
2020-06-11 13:07:46 -07:00
UINT res = ii . SendVirtualInput ( key_count , keyEventList , sizeof ( INPUT ) ) ;
2020-05-28 14:47:32 -07:00
delete [ ] keyEventList ;
// Reset the long press flag when the key has been lifted.
if ( data - > wParam = = WM_KEYUP | | data - > wParam = = WM_SYSKEYUP )
{
lock . lock ( ) ;
2021-05-07 11:16:31 +03:00
State . singleKeyToggleToMod [ data - > lParam - > vkCode ] = false ;
2020-05-28 14:47:32 -07:00
lock . unlock ( ) ;
}
return 1 ;
}
}
return 0 ;
}
2020-10-08 11:28:24 -07:00
*/
2020-05-28 14:47:32 -07:00
// Function to a handle a shortcut remap
2021-05-07 11:16:31 +03:00
intptr_t HandleShortcutRemapEvent ( KeyboardManagerInput : : InputInterface & ii , LowlevelKeyboardEvent * data , State & state , const std : : optional < std : : wstring > & activatedApp ) noexcept
2020-05-28 14:47:32 -07:00
{
2020-06-05 11:39:38 -07:00
// Check if any shortcut is currently in the invoked state
2021-05-07 11:16:31 +03:00
bool isShortcutInvoked = state . CheckShortcutRemapInvoked ( activatedApp ) ;
2020-10-08 11:28:24 -07:00
// Get shortcut table for given activatedApp
2021-05-07 11:16:31 +03:00
ShortcutRemapTable & reMap = state . GetShortcutRemapTable ( activatedApp ) ;
2020-06-05 11:39:38 -07:00
// Iterate through the shortcut remaps and apply whichever has been pressed
2021-05-07 11:16:31 +03:00
for ( auto & itShortcut : state . GetSortedShortcutRemapVector ( activatedApp ) )
2020-05-28 14:47:32 -07:00
{
2020-11-13 15:57:01 +03:00
const auto it = reMap . find ( itShortcut ) ;
2020-07-23 16:43:49 -07:00
2020-06-05 11:39:38 -07:00
// If a shortcut is currently in the invoked state then skip till the shortcut that is currently invoked
2020-07-23 16:43:49 -07:00
if ( isShortcutInvoked & & ! it - > second . isShortcutInvoked )
2020-06-05 11:39:38 -07:00
{
continue ;
}
2021-04-26 22:01:38 +03:00
2020-07-23 16:43:49 -07:00
// Check if the remap is to a key or a shortcut
bool remapToShortcut = ( it - > second . targetShortcut . index ( ) = = 1 ) ;
2020-06-05 11:39:38 -07:00
2020-07-23 16:43:49 -07:00
const size_t src_size = it - > first . Size ( ) ;
const size_t dest_size = remapToShortcut ? std : : get < Shortcut > ( it - > second . targetShortcut ) . Size ( ) : 1 ;
2020-05-28 14:47:32 -07:00
// If the shortcut has been pressed down
2020-07-23 16:43:49 -07:00
if ( ! it - > second . isShortcutInvoked & & it - > first . CheckModifiersKeyboardState ( ii ) )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
if ( data - > lParam - > vkCode = = it - > first . GetActionKey ( ) & & ( data - > wParam = = WM_KEYDOWN | | data - > wParam = = WM_SYSKEYDOWN ) )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
// 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
2020-10-02 15:36:36 +03:00
if ( ! it - > first . IsKeyboardStateClearExceptShortcut ( ii ) & & ( remapToShortcut | | std : : get < DWORD > ( it - > second . targetShortcut ) = = CommonSharedConstants : : VK_DISABLED ) )
2020-05-28 14:47:32 -07:00
{
continue ;
}
size_t key_count ;
LPINPUT keyEventList ;
// Remember which win key was pressed initially
2020-06-11 13:07:46 -07:00
if ( ii . GetVirtualKeyState ( VK_RWIN ) )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
it - > second . winKeyInvoked = ModifierKey : : Right ;
2020-05-28 14:47:32 -07:00
}
2020-06-11 13:07:46 -07:00
else if ( ii . GetVirtualKeyState ( VK_LWIN ) )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
it - > second . winKeyInvoked = ModifierKey : : Left ;
2020-05-28 14:47:32 -07:00
}
2020-07-23 16:43:49 -07:00
if ( remapToShortcut )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
// Get the common keys between the two shortcuts
int commonKeys = it - > first . GetCommonModifiersCount ( std : : get < Shortcut > ( it - > second . targetShortcut ) ) ;
2020-05-28 14:47:32 -07:00
2020-07-23 16:43:49 -07:00
// If the original shortcut modifiers are a subset of the new shortcut
if ( commonKeys = = src_size - 1 )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
// 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 ;
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( std : : get < Shortcut > ( it - > second . targetShortcut ) , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , it - > first ) ;
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-05-28 14:47:32 -07:00
i + + ;
}
2020-07-23 16:43:49 -07:00
else
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
// 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
2020-10-08 11:28:37 -07:00
key_count = KeyboardManagerConstants : : DUMMY_KEY_EVENT_SIZE + ( src_size - 1 ) + ( dest_size ) - ( 2 * ( size_t ) commonKeys ) ;
2020-07-23 16:43:49 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2020-10-15 08:53:43 -07:00
// 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
2020-07-23 16:43:49 -07:00
int i = 0 ;
2021-05-07 11:16:31 +03:00
Helpers : : SetDummyKeyEvent ( keyEventList , i , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
// Release original shortcut state (release in reverse order of shortcut to be accurate)
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , std : : get < Shortcut > ( it - > second . targetShortcut ) ) ;
2020-07-23 16:43:49 -07:00
// Set new shortcut key down state
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( std : : get < Shortcut > ( it - > second . targetShortcut ) , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , it - > first ) ;
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-05-28 14:47:32 -07:00
i + + ;
}
2020-07-23 16:43:49 -07:00
// 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 )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
Shortcut temp = std : : get < Shortcut > ( it - > second . targetShortcut ) ;
for ( auto keys : temp . GetKeyCodes ( ) )
{
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , keys , data - > lParam - > vkCode ) ;
}
2020-05-28 14:47:32 -07:00
}
}
else
{
2020-07-23 16:43:49 -07:00
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
2020-10-08 11:28:37 -07:00
key_count = KeyboardManagerConstants : : DUMMY_KEY_EVENT_SIZE + ( src_size - 1 ) + dest_size ;
2021-04-26 22:01:38 +03:00
2020-10-02 15:36:36 +03:00
// Do not send Disable key
if ( std : : get < DWORD > ( it - > second . targetShortcut ) = = CommonSharedConstants : : VK_DISABLED )
{
key_count - - ;
2020-10-15 08:53:43 -07:00
// Since the original shortcut's action key is pressed, set it to true
it - > second . isOriginalActionKeyPressed = true ;
2020-10-02 15:36:36 +03:00
}
2020-05-28 14:47:32 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2020-10-15 08:53:43 -07:00
// 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
2020-05-28 14:47:32 -07:00
int i = 0 ;
2021-05-07 11:16:31 +03:00
Helpers : : SetDummyKeyEvent ( keyEventList , i , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
2020-05-28 14:47:32 -07:00
// Release original shortcut state (release in reverse order of shortcut to be accurate)
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-05-28 14:47:32 -07:00
2020-07-23 16:43:49 -07:00
// Set target key down state
2020-10-02 15:36:36 +03:00
if ( std : : get < DWORD > ( it - > second . targetShortcut ) ! = CommonSharedConstants : : VK_DISABLED )
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-10-02 15:36:36 +03:00
i + + ;
}
2020-07-23 16:43:49 -07:00
// 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 )
2020-05-28 14:47:32 -07:00
{
2021-05-07 11:16:31 +03:00
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , ( WORD ) Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) , data - > lParam - > vkCode ) ;
2020-05-28 14:47:32 -07:00
}
}
2020-07-23 16:43:49 -07:00
it - > second . isShortcutInvoked = true ;
2020-07-10 17:53:41 -07:00
// If app specific shortcut is invoked, store the target application
2020-10-08 11:28:24 -07:00
if ( activatedApp )
2020-07-10 17:53:41 -07:00
{
2021-05-07 11:16:31 +03:00
state . SetActivatedApp ( * activatedApp ) ;
2020-07-10 17:53:41 -07:00
}
2020-10-08 11:28:24 -07:00
2020-06-11 13:07:46 -07:00
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
2020-05-28 14:47:32 -07:00
delete [ ] keyEventList ;
2020-09-08 14:40:02 -07:00
2020-05-28 14:47:32 -07:00
return 1 ;
}
}
2020-07-23 16:43:49 -07:00
else if ( it - > second . isShortcutInvoked )
2020-05-28 14:47:32 -07:00
{
2021-04-26 22:01:38 +03:00
// 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
2020-05-28 14:47:32 -07:00
// Get the common keys between the two shortcuts
2020-07-23 16:43:49 -07:00
int commonKeys = remapToShortcut ? it - > first . GetCommonModifiersCount ( std : : get < Shortcut > ( it - > second . targetShortcut ) ) : 0 ;
2020-05-28 14:47:32 -07:00
2020-07-23 16:43:49 -07:00
// 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 ) )
2020-05-28 14:47:32 -07:00
{
// Release new shortcut, and set original shortcut keys except the one released
size_t key_count ;
2020-07-23 16:43:49 -07:00
LPINPUT keyEventList ;
if ( remapToShortcut )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
// 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 ) )
{
2020-10-15 08:53:43 -07:00
// 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 ;
2020-07-23 16:43:49 -07:00
}
else
{
2020-10-15 08:53:43 -07:00
// 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 ;
2020-07-23 16:43:49 -07:00
}
2020-06-05 11:39:38 -07:00
2020-07-23 16:43:49 -07:00
// If the target shortcut's action key is pressed, then it should be released
bool isActionKeyPressed = false ;
2020-10-15 08:53:43 -07:00
if ( ii . GetVirtualKeyState ( ( std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) ) ) )
2020-07-23 16:43:49 -07:00
{
isActionKeyPressed = true ;
key_count + = 1 ;
}
2020-06-05 11:39:38 -07:00
2020-07-23 16:43:49 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2020-05-28 14:47:32 -07:00
2020-07-23 16:43:49 -07:00
// Release new shortcut state (release in reverse order of shortcut to be accurate)
int i = 0 ;
if ( isActionKeyPressed )
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
i + + ;
}
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( std : : get < Shortcut > ( it - > second . targetShortcut ) , it - > second . winKeyInvoked , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , it - > first , data - > lParam - > vkCode ) ;
2020-05-28 14:47:32 -07:00
2020-07-23 16:43:49 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , std : : get < Shortcut > ( it - > second . targetShortcut ) , data - > lParam - > vkCode ) ;
2020-10-15 08:53:43 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetDummyKeyEvent ( keyEventList , i , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-05-28 14:47:32 -07:00
}
2020-07-23 16:43:49 -07:00
else
2020-05-28 14:47:32 -07:00
{
2020-10-15 08:53:43 -07:00
// 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 ;
2020-10-21 10:24:43 -07:00
bool isTargetKeyPressed = false ;
2020-10-02 15:36:36 +03:00
// Do not send Disable key up
if ( std : : get < DWORD > ( it - > second . targetShortcut ) = = CommonSharedConstants : : VK_DISABLED )
{
key_count - - ;
}
2021-05-07 11:16:31 +03:00
else if ( ii . GetVirtualKeyState ( Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) ) )
2020-10-21 10:24:43 -07:00
{
isTargetKeyPressed = true ;
}
else
{
isTargetKeyPressed = false ;
key_count - - ;
}
2020-10-02 15:36:36 +03:00
2020-07-23 16:43:49 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
// Release new key state
int i = 0 ;
2020-10-21 10:24:43 -07:00
if ( std : : get < DWORD > ( it - > second . targetShortcut ) ! = CommonSharedConstants : : VK_DISABLED & & isTargetKeyPressed )
2020-10-02 15:36:36 +03:00
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-10-08 11:28:37 -07:00
i + + ;
2020-10-02 15:36:36 +03:00
}
2020-07-23 16:43:49 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , Shortcut ( ) , data - > lParam - > vkCode ) ;
2020-10-15 08:53:43 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetDummyKeyEvent ( keyEventList , i , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-05-28 14:47:32 -07:00
}
2020-10-15 08:53:43 -07:00
// Reset the remap state
2020-07-23 16:43:49 -07:00
it - > second . isShortcutInvoked = false ;
it - > second . winKeyInvoked = ModifierKey : : Disabled ;
2020-10-15 08:53:43 -07:00
it - > second . isOriginalActionKeyPressed = false ;
2021-04-26 22:01:38 +03:00
2020-07-10 17:53:41 -07:00
// If app specific shortcut has finished invoking, reset the target application
2020-10-08 11:28:24 -07:00
if ( activatedApp )
2020-07-10 17:53:41 -07:00
{
2021-05-07 11:16:31 +03:00
state . SetActivatedApp ( KeyboardManagerConstants : : NoActivatedApp ) ;
2020-07-10 17:53:41 -07:00
}
2020-06-05 11:39:38 -07:00
// 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 )
{
2020-06-11 13:07:46 -07:00
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
2020-06-05 11:39:38 -07:00
delete [ ] keyEventList ;
}
2020-05-28 14:47:32 -07:00
return 1 ;
}
// The system will see the modifiers of the new shortcut as being held down because of the shortcut remap
2020-07-23 16:43:49 -07:00
if ( ! remapToShortcut | | std : : get < Shortcut > ( it - > second . targetShortcut ) . CheckModifiersKeyboardState ( ii ) )
2020-05-28 14:47:32 -07:00
{
// 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)
2020-07-23 16:43:49 -07:00
if ( data - > lParam - > vkCode = = it - > first . GetActionKey ( ) & & ( data - > wParam = = WM_KEYDOWN | | data - > wParam = = WM_SYSKEYDOWN ) )
2020-05-28 14:47:32 -07:00
{
2020-10-02 15:36:36 +03:00
// In case of mapping to disable do not send anything
if ( ! remapToShortcut & & std : : get < DWORD > ( it - > second . targetShortcut ) = = CommonSharedConstants : : VK_DISABLED )
{
2020-10-15 08:53:43 -07:00
// Since the original shortcut's action key is pressed, set it to true
it - > second . isOriginalActionKeyPressed = true ;
2020-10-02 15:36:36 +03:00
return 1 ;
}
2020-05-28 14:47:32 -07:00
size_t key_count = 1 ;
LPINPUT keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2020-07-23 16:43:49 -07:00
if ( remapToShortcut )
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
}
else
{
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
}
2020-05-28 14:47:32 -07:00
2020-06-11 13:07:46 -07:00
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
2020-05-28 14:47:32 -07:00
delete [ ] keyEventList ;
return 1 ;
}
2020-10-15 08:53:43 -07:00
// 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
2020-07-23 16:43:49 -07:00
if ( data - > lParam - > vkCode = = it - > first . GetActionKey ( ) & & ( data - > wParam = = WM_KEYUP | | data - > wParam = = WM_SYSKEYUP ) )
2020-06-05 11:39:38 -07:00
{
size_t key_count = 1 ;
2020-05-28 14:47:32 -07:00
LPINPUT keyEventList ;
2020-07-23 16:43:49 -07:00
if ( remapToShortcut )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
}
2020-10-15 08:53:43 -07:00
else if ( std : : get < DWORD > ( it - > second . targetShortcut ) = = CommonSharedConstants : : VK_DISABLED )
{
2021-04-26 22:01:38 +03:00
// If remapped to disable, do nothing and suppress the key event
2020-10-15 08:53:43 -07:00
// Since the original shortcut's action key is released, set it to false
it - > second . isOriginalActionKeyPressed = false ;
return 1 ;
}
2020-07-23 16:43:49 -07:00
else
{
2020-10-15 08:53:43 -07:00
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
2021-05-07 11:16:31 +03:00
bool isKeyboardStateClear = Shortcut ( std : : vector < int32_t > ( { Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) } ) ) . IsKeyboardStateClearExceptShortcut ( ii ) ;
2021-04-26 22:01:38 +03:00
2020-10-15 08:53:43 -07:00
// If the keyboard state is clear, we release the target key but do not reset the remap state
if ( isKeyboardStateClear )
2020-10-02 15:36:36 +03:00
{
2020-10-15 08:53:43 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-10-02 15:36:36 +03:00
}
2020-10-15 08:53:43 -07:00
else
{
2021-04-26 22:01:38 +03:00
// 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
2020-10-15 08:53:43 -07:00
// 1 for releasing new key and original shortcut modifiers, and dummy key
key_count = dest_size + ( src_size - 1 ) + KeyboardManagerConstants : : DUMMY_KEY_EVENT_SIZE ;
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2020-05-28 14:47:32 -07:00
2020-10-15 08:53:43 -07:00
// Release new key state
int i = 0 ;
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-10-08 11:28:37 -07:00
i + + ;
2020-06-05 11:39:38 -07:00
2020-10-21 10:24:43 -07:00
// Set original shortcut key down state except the action key
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-07-23 16:43:49 -07:00
2020-10-15 08:53:43 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetDummyKeyEvent ( keyEventList , i , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-06-05 11:39:38 -07:00
2020-10-15 08:53:43 -07:00
// Reset the remap state
it - > second . isShortcutInvoked = false ;
it - > second . winKeyInvoked = ModifierKey : : Disabled ;
it - > second . isOriginalActionKeyPressed = false ;
2021-04-26 22:01:38 +03:00
2020-10-15 08:53:43 -07:00
// If app specific shortcut has finished invoking, reset the target application
if ( activatedApp ! = KeyboardManagerConstants : : NoActivatedApp )
{
2021-05-07 11:16:31 +03:00
state . SetActivatedApp ( KeyboardManagerConstants : : NoActivatedApp ) ;
2020-10-15 08:53:43 -07:00
}
2020-06-05 11:39:38 -07:00
}
2020-07-23 16:43:49 -07:00
}
2020-06-05 11:39:38 -07:00
2020-07-23 16:43:49 -07:00
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
delete [ ] keyEventList ;
return 1 ;
}
2020-05-28 14:47:32 -07:00
2020-07-23 16:43:49 -07:00
// 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 )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , data - > lParam - > vkCode , std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) ) ;
2020-05-28 14:47:32 -07:00
}
2020-07-23 16:43:49 -07:00
}
2020-10-15 08:53:43 -07:00
else if ( std : : get < DWORD > ( it - > second . targetShortcut ) ! = CommonSharedConstants : : VK_DISABLED )
2020-10-02 15:36:36 +03:00
{
2021-04-26 22:01:38 +03:00
// If it is not remapped to Disable
2020-10-15 08:53:43 -07:00
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps
2021-05-07 11:16:31 +03:00
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , data - > lParam - > vkCode , Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) ) ;
2020-10-02 15:36:36 +03:00
}
2020-10-15 08:53:43 -07:00
// Suppress the modifier as it is already physically pressed
return 1 ;
2020-07-23 16:43:49 -07:00
}
// 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 )
2020-05-28 14:47:32 -07:00
{
2020-07-23 16:43:49 -07:00
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , data - > lParam - > vkCode , std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) ) ;
2020-05-28 14:47:32 -07:00
}
2020-07-23 16:43:49 -07:00
size_t key_count ;
2021-09-04 08:49:13 +03:00
LPINPUT keyEventList = nullptr ;
// Check if a new remapping should be applied
Shortcut currentlyPressed = it - > first ;
currentlyPressed . actionKey = data - > lParam - > vkCode ;
auto newRemappingIter = reMap . find ( currentlyPressed ) ;
if ( newRemappingIter ! = reMap . end ( ) )
{
auto & newRemapping = newRemappingIter - > second ;
Shortcut from = std : : get < Shortcut > ( it - > second . targetShortcut ) ;
if ( newRemapping . RemapToKey ( ) )
{
DWORD to = std : : get < 0 > ( newRemapping . targetShortcut ) ;
2022-05-27 14:01:26 +01:00
bool isLastKeyStillPressed = ii . GetVirtualKeyState ( ( WORD ) from . actionKey ) ;
2022-10-25 17:33:23 +01:00
key_count = static_cast < size_t > ( from . Size ( ) ) - 1 + 1 + ( isLastKeyStillPressed ? 1 : 0 ) ;
2021-09-04 08:49:13 +03:00
keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
int i = 0 ;
Helpers : : SetModifierKeyEvents ( from , it - > second . winKeyInvoked , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2022-05-27 14:01:26 +01:00
if ( ii . GetVirtualKeyState ( ( WORD ) from . actionKey ) )
{
// If the action key from the last shortcut is still being pressed, release it.
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) from . actionKey , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
i + + ;
}
2021-09-04 08:49:13 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) to , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
} else
{
Shortcut to = std : : get < Shortcut > ( newRemapping . targetShortcut ) ;
2022-05-27 14:01:26 +01:00
bool isLastKeyStillPressed = ii . GetVirtualKeyState ( ( WORD ) from . actionKey ) ;
2022-10-25 17:33:23 +01:00
size_t temp_key_count_calculation = static_cast < size_t > ( from . Size ( ) ) - 1 ;
temp_key_count_calculation + = static_cast < size_t > ( to . Size ( ) ) - 1 ;
temp_key_count_calculation - = static_cast < size_t > ( 2 ) * from . GetCommonModifiersCount ( to ) ;
key_count = temp_key_count_calculation + 1 + ( isLastKeyStillPressed ? 1 : 0 ) ;
2021-09-04 08:49:13 +03:00
keyEventList = new INPUT [ key_count ] ( ) ;
2020-05-28 14:47:32 -07:00
2021-09-04 08:49:13 +03:00
int i = 0 ;
Helpers : : SetModifierKeyEvents ( from , it - > second . winKeyInvoked , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , to ) ;
2022-05-27 14:01:26 +01:00
if ( ii . GetVirtualKeyState ( ( WORD ) from . actionKey ) )
{
// If the action key from the last shortcut is still being pressed, release it.
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) from . actionKey , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
i + + ;
}
2021-09-04 08:49:13 +03:00
Helpers : : SetModifierKeyEvents ( to , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , from ) ;
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) to . actionKey , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
newRemapping . isShortcutInvoked = true ;
}
// Remember which win key was pressed initially
if ( ii . GetVirtualKeyState ( VK_RWIN ) )
{
newRemapping . winKeyInvoked = ModifierKey : : Right ;
}
else if ( ii . GetVirtualKeyState ( VK_LWIN ) )
{
newRemapping . winKeyInvoked = ModifierKey : : Left ;
}
2021-08-31 12:43:04 +03:00
}
2021-09-04 08:49:13 +03:00
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 ) ;
2020-07-23 16:43:49 -07:00
2021-09-04 08:49:13 +03:00
// 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 ;
}
2020-07-23 16:43:49 -07:00
2021-09-04 08:49:13 +03:00
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 )
{
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) std : : get < Shortcut > ( it - > second . targetShortcut ) . GetActionKey ( ) , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
i + + ;
}
Helpers : : SetModifierKeyEvents ( std : : get < Shortcut > ( it - > second . targetShortcut ) , it - > second . winKeyInvoked , keyEventList , i , false , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , it - > first ) ;
2020-07-23 16:43:49 -07:00
2021-09-04 08:49:13 +03:00
// Set old shortcut key down state
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG , std : : get < Shortcut > ( it - > second . targetShortcut ) ) ;
2020-07-23 16:43:49 -07:00
2021-09-04 08:49:13 +03:00
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
if ( isActionKeyPressed )
{
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) it - > first . GetActionKey ( ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
i + + ;
}
2020-05-28 14:47:32 -07:00
2021-09-04 08:49:13 +03:00
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) data - > lParam - > vkCode , 0 , 0 ) ;
i + + ;
2021-08-31 12:43:04 +03:00
2021-09-04 08:49:13 +03:00
// 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
}
2021-08-31 12:43:04 +03:00
2020-10-15 08:53:43 -07:00
// Reset the remap state
2020-07-23 16:43:49 -07:00
it - > second . isShortcutInvoked = false ;
it - > second . winKeyInvoked = ModifierKey : : Disabled ;
2020-10-15 08:53:43 -07:00
it - > second . isOriginalActionKeyPressed = false ;
2021-04-26 22:01:38 +03:00
2020-07-23 16:43:49 -07:00
// If app specific shortcut has finished invoking, reset the target application
2020-10-08 11:28:24 -07:00
if ( activatedApp )
2020-10-02 15:36:36 +03:00
{
2021-05-07 11:16:31 +03:00
state . SetActivatedApp ( KeyboardManagerConstants : : NoActivatedApp ) ;
2020-10-02 15:36:36 +03:00
}
2020-10-08 11:28:24 -07:00
2020-10-02 15:36:36 +03:00
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
delete [ ] keyEventList ;
return 1 ;
}
2020-10-15 08:53:43 -07:00
else
2020-10-02 15:36:36 +03:00
{
2021-04-26 22:01:38 +03:00
// 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
2020-10-15 08:53:43 -07:00
// 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
2021-05-07 11:16:31 +03:00
ResetIfModifierKeyForLowerLevelKeyHandlers ( ii , data - > lParam - > vkCode , Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) ) ;
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
// 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 ;
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
if ( ! isRemapToDisable )
{
// If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys
2021-05-07 11:16:31 +03:00
if ( ii . GetVirtualKeyState ( ( Helpers : : FilterArtificialKeys ( std : : get < DWORD > ( it - > second . targetShortcut ) ) ) ) )
2020-10-15 08:53:43 -07:00
{
isOriginalActionKeyPressed = true ;
}
}
else
{
isOriginalActionKeyPressed = it - > second . isOriginalActionKeyPressed ;
}
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
if ( isRemapToDisable | | ! isOriginalActionKeyPressed )
{
// Key down for original shortcut modifiers and action key, and current key press
size_t key_count = src_size + 1 ;
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
LPINPUT keyEventList = new INPUT [ key_count ] ( ) ;
memset ( keyEventList , 0 , sizeof ( keyEventList ) ) ;
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
// Set original shortcut key down state
int i = 0 ;
2021-05-07 11:16:31 +03:00
Helpers : : SetModifierKeyEvents ( it - > first , it - > second . winKeyInvoked , keyEventList , i , true , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-10-02 15:36:36 +03:00
2020-10-15 08:53:43 -07:00
// 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
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) it - > first . GetActionKey ( ) , 0 , KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG ) ;
2020-10-15 08:53:43 -07:00
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
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , i , INPUT_KEYBOARD , ( WORD ) data - > lParam - > vkCode , 0 , 0 ) ;
2020-10-15 08:53:43 -07:00
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 ;
2021-04-26 22:01:38 +03:00
2020-10-15 08:53:43 -07:00
// If app specific shortcut has finished invoking, reset the target application
if ( activatedApp ! = KeyboardManagerConstants : : NoActivatedApp )
{
2021-05-07 11:16:31 +03:00
state . SetActivatedApp ( KeyboardManagerConstants : : NoActivatedApp ) ;
2020-10-15 08:53:43 -07:00
}
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
delete [ ] keyEventList ;
return 1 ;
}
else
2020-06-05 11:39:38 -07:00
{
2020-10-15 08:53:43 -07:00
return 0 ;
2020-06-05 11:39:38 -07:00
}
2020-07-10 17:53:41 -07:00
}
2020-05-28 14:47:32 -07:00
}
2020-07-23 16:43:49 -07:00
// 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
2020-07-10 17:53:41 -07:00
}
2020-05-28 14:47:32 -07:00
}
}
return 0 ;
}
// Function to a handle an os-level shortcut remap
2021-05-07 11:16:31 +03:00
intptr_t HandleOSLevelShortcutRemapEvent ( KeyboardManagerInput : : InputInterface & ii , LowlevelKeyboardEvent * data , State & state ) noexcept
2020-05-28 14:47:32 -07:00
{
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if ( data - > lParam - > dwExtraInfo ! = KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG )
{
2021-05-07 11:16:31 +03:00
bool result = HandleShortcutRemapEvent ( ii , data , state ) ;
2020-05-28 14:47:32 -07:00
return result ;
}
return 0 ;
}
// Function to a handle an app-specific shortcut remap
2021-05-07 11:16:31 +03:00
intptr_t HandleAppSpecificShortcutRemapEvent ( KeyboardManagerInput : : InputInterface & ii , LowlevelKeyboardEvent * data , State & state ) noexcept
2020-05-28 14:47:32 -07:00
{
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if ( data - > lParam - > dwExtraInfo ! = KeyboardManagerConstants : : KEYBOARDMANAGER_SHORTCUT_FLAG )
{
2020-07-06 16:45:53 -07:00
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 ( ) ) ;
2020-05-28 14:47:32 -07:00
if ( process_name . empty ( ) )
{
return 0 ;
}
2020-07-06 16:45:53 -07:00
// Convert process name to lower case
std : : transform ( process_name . begin ( ) , process_name . end ( ) , process_name . begin ( ) , towlower ) ;
2020-07-10 17:53:41 -07:00
std : : wstring query_string ;
2020-10-08 11:28:24 -07:00
AppSpecificShortcutRemapTable : : iterator it ;
2021-04-26 22:01:38 +03:00
2020-07-10 17:53:41 -07:00
// Check if an app-specific shortcut is already activated
2021-05-07 11:16:31 +03:00
if ( state . GetActivatedApp ( ) = = KeyboardManagerConstants : : NoActivatedApp )
2020-07-10 17:53:41 -07:00
{
query_string = process_name ;
2021-05-07 11:16:31 +03:00
it = state . appSpecificShortcutReMap . find ( query_string ) ;
2020-07-06 16:45:53 -07:00
2020-07-10 17:53:41 -07:00
// If no entry is found, search for the process name without it's file extension
2021-05-07 11:16:31 +03:00
if ( it = = state . appSpecificShortcutReMap . end ( ) )
2020-07-10 17:53:41 -07:00
{
// Find index of the file extension
size_t extensionIndex = process_name . find_last_of ( L " . " ) ;
query_string = process_name . substr ( 0 , extensionIndex ) ;
2021-05-07 11:16:31 +03:00
it = state . appSpecificShortcutReMap . find ( query_string ) ;
2020-07-10 17:53:41 -07:00
}
}
else
2020-07-06 16:45:53 -07:00
{
2021-05-07 11:16:31 +03:00
query_string = state . GetActivatedApp ( ) ;
it = state . appSpecificShortcutReMap . find ( query_string ) ;
2020-07-06 16:45:53 -07:00
}
2021-05-07 11:16:31 +03:00
if ( it ! = state . appSpecificShortcutReMap . end ( ) )
2020-05-28 14:47:32 -07:00
{
2021-05-07 11:16:31 +03:00
bool result = HandleShortcutRemapEvent ( ii , data , state , query_string ) ;
2020-05-28 14:47:32 -07:00
return result ;
}
}
return 0 ;
}
2020-06-05 12:54:52 -07:00
2020-07-23 16:43:49 -07:00
// 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
2021-04-26 22:01:38 +03:00
void ResetIfModifierKeyForLowerLevelKeyHandlers ( KeyboardManagerInput : : InputInterface & ii , DWORD key , DWORD target )
2020-06-15 16:48:00 -07:00
{
2020-07-23 16:43:49 -07:00
// 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 )
2020-06-15 16:48:00 -07:00
{
2020-07-23 16:43:49 -07:00
// If the argument is either of the Ctrl/Shift/Alt modifier key codes
2021-05-07 11:16:31 +03:00
if ( Helpers : : IsModifierKey ( key ) & & ! ( key = = VK_LWIN | | key = = VK_RWIN | | key = = CommonSharedConstants : : VK_WIN_BOTH ) )
2020-07-23 16:43:49 -07:00
{
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
2021-05-07 11:16:31 +03:00
Helpers : : SetKeyEvent ( keyEventList , 0 , INPUT_KEYBOARD , ( WORD ) key , KEYEVENTF_KEYUP , KeyboardManagerConstants : : KEYBOARDMANAGER_SUPPRESS_FLAG ) ;
2020-07-23 16:43:49 -07:00
UINT res = ii . SendVirtualInput ( ( UINT ) key_count , keyEventList , sizeof ( INPUT ) ) ;
delete [ ] keyEventList ;
}
2020-06-15 16:48:00 -07:00
}
}
2020-05-28 14:47:32 -07:00
}