2020-04-21 12:01:21 +08:00
# include "pch.h"
2021-05-07 16:16:31 +08:00
# include <array>
# include <algorithm>
2020-04-21 12:01:21 +08:00
# include "keyboard_layout_impl.h"
2020-04-23 23:37:52 +08:00
# include "shared_constants.h"
2020-04-21 12:01:21 +08:00
2023-08-24 00:56:40 +08:00
constexpr DWORD numpadOriginBit = 1ull < < 31 ;
2020-04-21 12:01:21 +08:00
LayoutMap : : LayoutMap ( ) :
impl ( new LayoutMap : : LayoutMapImpl ( ) )
{
}
LayoutMap : : ~ LayoutMap ( )
{
delete impl ;
}
void LayoutMap : : UpdateLayout ( )
{
impl - > UpdateLayout ( ) ;
}
std : : wstring LayoutMap : : GetKeyName ( DWORD key )
{
return impl - > GetKeyName ( key ) ;
}
std : : vector < DWORD > LayoutMap : : GetKeyCodeList ( const bool isShortcut )
{
return impl - > GetKeyCodeList ( isShortcut ) ;
}
2020-10-19 17:27:47 +08:00
std : : vector < std : : pair < DWORD , std : : wstring > > LayoutMap : : GetKeyNameList ( const bool isShortcut )
2020-04-21 12:01:21 +08:00
{
return impl - > GetKeyNameList ( isShortcut ) ;
}
// Function to return the unicode string name of the key
std : : wstring LayoutMap : : LayoutMapImpl : : GetKeyName ( DWORD key )
{
std : : wstring result = L " Undefined " ;
std : : lock_guard < std : : mutex > lock ( keyboardLayoutMap_mutex ) ;
UpdateLayout ( ) ;
auto it = keyboardLayoutMap . find ( key ) ;
if ( it ! = keyboardLayoutMap . end ( ) )
{
result = it - > second ;
}
return result ;
}
2020-11-19 19:59:01 +08:00
bool mapKeycodeToUnicode ( const int vCode , HKL layout , const BYTE * keyState , std : : array < wchar_t , 3 > & outBuffer )
{
// Get the scan code from the virtual key code
const UINT scanCode = MapVirtualKeyExW ( vCode , MAPVK_VK_TO_VSC , layout ) ;
// Get the unicode representation from the virtual key code and scan code pair
2022-07-15 17:25:11 +08:00
const UINT wFlags = 1 < < 2 ; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)
2023-02-08 19:00:19 +08:00
const int result = ToUnicodeEx ( vCode , scanCode , keyState , outBuffer . data ( ) , static_cast < int > ( outBuffer . size ( ) ) , wFlags , layout ) ;
2020-11-19 19:59:01 +08:00
return result ! = 0 ;
}
2020-04-21 12:01:21 +08:00
// Update Keyboard layout according to input locale identifier
void LayoutMap : : LayoutMapImpl : : UpdateLayout ( )
{
// Get keyboard layout for current thread
2020-11-19 19:59:01 +08:00
const HKL layout = GetKeyboardLayout ( 0 ) ;
2020-04-21 12:01:21 +08:00
if ( layout = = previousLayout )
{
return ;
}
previousLayout = layout ;
if ( ! isKeyCodeListGenerated )
{
unicodeKeys . clear ( ) ;
unknownKeys . clear ( ) ;
}
2020-11-19 19:59:01 +08:00
std : : array < BYTE , 256 > btKeys = { 0 } ;
2020-04-22 04:43:52 +08:00
// Only set the Caps Lock key to on for the key names in uppercase
btKeys [ VK_CAPITAL ] = 1 ;
2020-04-21 12:01:21 +08:00
// Iterate over all the virtual key codes. virtual key 0 is not used
for ( int i = 1 ; i < 256 ; i + + )
{
2020-11-19 19:59:01 +08:00
std : : array < wchar_t , 3 > szBuffer = { 0 } ;
if ( mapKeycodeToUnicode ( i , layout , btKeys . data ( ) , szBuffer ) )
2020-04-21 12:01:21 +08:00
{
2020-11-19 19:59:01 +08:00
keyboardLayoutMap [ i ] = szBuffer . data ( ) ;
2020-04-21 12:01:21 +08:00
if ( ! isKeyCodeListGenerated )
{
2020-11-19 19:59:01 +08:00
unicodeKeys [ i ] = szBuffer . data ( ) ;
2020-04-21 12:01:21 +08:00
}
2020-11-19 19:59:01 +08:00
continue ;
2020-04-21 12:01:21 +08:00
}
2020-11-19 19:59:01 +08:00
// Store the virtual key code as string
std : : wstring vk = L " VK " ;
vk + = std : : to_wstring ( i ) ;
keyboardLayoutMap [ i ] = vk ;
if ( ! isKeyCodeListGenerated )
2020-04-21 12:01:21 +08:00
{
2020-11-19 19:59:01 +08:00
unknownKeys [ i ] = vk ;
2020-04-21 12:01:21 +08:00
}
}
// Override special key names like Shift, Ctrl etc because they don't have unicode mappings and key names like Enter, Space as they appear as "\r", " "
// To do: localization
keyboardLayoutMap [ VK_CANCEL ] = L " Break " ;
keyboardLayoutMap [ VK_BACK ] = L " Backspace " ;
keyboardLayoutMap [ VK_TAB ] = L " Tab " ;
keyboardLayoutMap [ VK_CLEAR ] = L " Clear " ;
keyboardLayoutMap [ VK_SHIFT ] = L " Shift " ;
keyboardLayoutMap [ VK_CONTROL ] = L " Ctrl " ;
keyboardLayoutMap [ VK_MENU ] = L " Alt " ;
keyboardLayoutMap [ VK_PAUSE ] = L " Pause " ;
keyboardLayoutMap [ VK_CAPITAL ] = L " Caps Lock " ;
keyboardLayoutMap [ VK_ESCAPE ] = L " Esc " ;
keyboardLayoutMap [ VK_SPACE ] = L " Space " ;
2023-08-24 00:56:40 +08:00
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_LEFT ] = L " Left " ;
keyboardLayoutMap [ VK_RIGHT ] = L " Right " ;
2023-08-24 00:56:40 +08:00
keyboardLayoutMap [ VK_UP ] = L " Up " ;
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_DOWN ] = L " Down " ;
2023-08-24 00:56:40 +08:00
keyboardLayoutMap [ VK_INSERT ] = L " Insert " ;
keyboardLayoutMap [ VK_DELETE ] = L " Delete " ;
keyboardLayoutMap [ VK_PRIOR ] = L " PgUp " ;
keyboardLayoutMap [ VK_NEXT ] = L " PgDn " ;
keyboardLayoutMap [ VK_HOME ] = L " Home " ;
keyboardLayoutMap [ VK_END ] = L " End " ;
keyboardLayoutMap [ VK_RETURN ] = L " Enter " ;
keyboardLayoutMap [ VK_LEFT | numpadOriginBit ] = L " Left (Numpad) " ;
keyboardLayoutMap [ VK_RIGHT | numpadOriginBit ] = L " Right (Numpad) " ;
keyboardLayoutMap [ VK_UP | numpadOriginBit ] = L " Up (Numpad) " ;
keyboardLayoutMap [ VK_DOWN | numpadOriginBit ] = L " Down (Numpad) " ;
keyboardLayoutMap [ VK_INSERT | numpadOriginBit ] = L " Insert (Numpad) " ;
keyboardLayoutMap [ VK_DELETE | numpadOriginBit ] = L " Delete (Numpad) " ;
keyboardLayoutMap [ VK_PRIOR | numpadOriginBit ] = L " PgUp (Numpad) " ;
keyboardLayoutMap [ VK_NEXT | numpadOriginBit ] = L " PgDn (Numpad) " ;
keyboardLayoutMap [ VK_HOME | numpadOriginBit ] = L " Home (Numpad) " ;
keyboardLayoutMap [ VK_END | numpadOriginBit ] = L " End (Numpad) " ;
keyboardLayoutMap [ VK_RETURN | numpadOriginBit ] = L " Enter (Numpad) " ;
keyboardLayoutMap [ VK_DIVIDE | numpadOriginBit ] = L " / (Numpad) " ;
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_SELECT ] = L " Select " ;
keyboardLayoutMap [ VK_PRINT ] = L " Print " ;
keyboardLayoutMap [ VK_EXECUTE ] = L " Execute " ;
keyboardLayoutMap [ VK_SNAPSHOT ] = L " Print Screen " ;
keyboardLayoutMap [ VK_HELP ] = L " Help " ;
2020-04-23 23:37:52 +08:00
keyboardLayoutMap [ VK_LWIN ] = L " Win (Left) " ;
keyboardLayoutMap [ VK_RWIN ] = L " Win (Right) " ;
2020-07-23 03:57:13 +08:00
keyboardLayoutMap [ VK_APPS ] = L " Apps/Menu " ;
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_SLEEP ] = L " Sleep " ;
keyboardLayoutMap [ VK_NUMPAD0 ] = L " NumPad 0 " ;
keyboardLayoutMap [ VK_NUMPAD1 ] = L " NumPad 1 " ;
keyboardLayoutMap [ VK_NUMPAD2 ] = L " NumPad 2 " ;
keyboardLayoutMap [ VK_NUMPAD3 ] = L " NumPad 3 " ;
keyboardLayoutMap [ VK_NUMPAD4 ] = L " NumPad 4 " ;
keyboardLayoutMap [ VK_NUMPAD5 ] = L " NumPad 5 " ;
keyboardLayoutMap [ VK_NUMPAD6 ] = L " NumPad 6 " ;
keyboardLayoutMap [ VK_NUMPAD7 ] = L " NumPad 7 " ;
keyboardLayoutMap [ VK_NUMPAD8 ] = L " NumPad 8 " ;
keyboardLayoutMap [ VK_NUMPAD9 ] = L " NumPad 9 " ;
keyboardLayoutMap [ VK_SEPARATOR ] = L " Separator " ;
keyboardLayoutMap [ VK_F1 ] = L " F1 " ;
keyboardLayoutMap [ VK_F2 ] = L " F2 " ;
keyboardLayoutMap [ VK_F3 ] = L " F3 " ;
keyboardLayoutMap [ VK_F4 ] = L " F4 " ;
keyboardLayoutMap [ VK_F5 ] = L " F5 " ;
keyboardLayoutMap [ VK_F6 ] = L " F6 " ;
keyboardLayoutMap [ VK_F7 ] = L " F7 " ;
keyboardLayoutMap [ VK_F8 ] = L " F8 " ;
keyboardLayoutMap [ VK_F9 ] = L " F9 " ;
keyboardLayoutMap [ VK_F10 ] = L " F10 " ;
keyboardLayoutMap [ VK_F11 ] = L " F11 " ;
keyboardLayoutMap [ VK_F12 ] = L " F12 " ;
keyboardLayoutMap [ VK_F13 ] = L " F13 " ;
keyboardLayoutMap [ VK_F14 ] = L " F14 " ;
keyboardLayoutMap [ VK_F15 ] = L " F15 " ;
keyboardLayoutMap [ VK_F16 ] = L " F16 " ;
keyboardLayoutMap [ VK_F17 ] = L " F17 " ;
keyboardLayoutMap [ VK_F18 ] = L " F18 " ;
keyboardLayoutMap [ VK_F19 ] = L " F19 " ;
keyboardLayoutMap [ VK_F20 ] = L " F20 " ;
keyboardLayoutMap [ VK_F21 ] = L " F21 " ;
keyboardLayoutMap [ VK_F22 ] = L " F22 " ;
keyboardLayoutMap [ VK_F23 ] = L " F23 " ;
keyboardLayoutMap [ VK_F24 ] = L " F24 " ;
keyboardLayoutMap [ VK_NUMLOCK ] = L " Num Lock " ;
keyboardLayoutMap [ VK_SCROLL ] = L " Scroll Lock " ;
2020-04-23 23:37:52 +08:00
keyboardLayoutMap [ VK_LSHIFT ] = L " Shift (Left) " ;
keyboardLayoutMap [ VK_RSHIFT ] = L " Shift (Right) " ;
keyboardLayoutMap [ VK_LCONTROL ] = L " Ctrl (Left) " ;
keyboardLayoutMap [ VK_RCONTROL ] = L " Ctrl (Right) " ;
keyboardLayoutMap [ VK_LMENU ] = L " Alt (Left) " ;
keyboardLayoutMap [ VK_RMENU ] = L " Alt (Right) " ;
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_BROWSER_BACK ] = L " Browser Back " ;
keyboardLayoutMap [ VK_BROWSER_FORWARD ] = L " Browser Forward " ;
keyboardLayoutMap [ VK_BROWSER_REFRESH ] = L " Browser Refresh " ;
keyboardLayoutMap [ VK_BROWSER_STOP ] = L " Browser Stop " ;
keyboardLayoutMap [ VK_BROWSER_SEARCH ] = L " Browser Search " ;
keyboardLayoutMap [ VK_BROWSER_FAVORITES ] = L " Browser Favorites " ;
2020-05-15 00:23:51 +08:00
keyboardLayoutMap [ VK_BROWSER_HOME ] = L " Browser Home " ;
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_VOLUME_MUTE ] = L " Volume Mute " ;
keyboardLayoutMap [ VK_VOLUME_DOWN ] = L " Volume Down " ;
keyboardLayoutMap [ VK_VOLUME_UP ] = L " Volume Up " ;
keyboardLayoutMap [ VK_MEDIA_NEXT_TRACK ] = L " Next Track " ;
keyboardLayoutMap [ VK_MEDIA_PREV_TRACK ] = L " Previous Track " ;
keyboardLayoutMap [ VK_MEDIA_STOP ] = L " Stop Media " ;
keyboardLayoutMap [ VK_MEDIA_PLAY_PAUSE ] = L " Play/Pause Media " ;
keyboardLayoutMap [ VK_LAUNCH_MAIL ] = L " Start Mail " ;
keyboardLayoutMap [ VK_LAUNCH_MEDIA_SELECT ] = L " Select Media " ;
2020-05-15 00:23:51 +08:00
keyboardLayoutMap [ VK_LAUNCH_APP1 ] = L " Start App 1 " ;
keyboardLayoutMap [ VK_LAUNCH_APP2 ] = L " Start App 2 " ;
2020-04-21 12:01:21 +08:00
keyboardLayoutMap [ VK_PACKET ] = L " Packet " ;
keyboardLayoutMap [ VK_ATTN ] = L " Attn " ;
keyboardLayoutMap [ VK_CRSEL ] = L " CrSel " ;
keyboardLayoutMap [ VK_EXSEL ] = L " ExSel " ;
keyboardLayoutMap [ VK_EREOF ] = L " Erase EOF " ;
keyboardLayoutMap [ VK_PLAY ] = L " Play " ;
keyboardLayoutMap [ VK_ZOOM ] = L " Zoom " ;
keyboardLayoutMap [ VK_PA1 ] = L " PA1 " ;
keyboardLayoutMap [ VK_OEM_CLEAR ] = L " Clear " ;
keyboardLayoutMap [ 0xFF ] = L " Undefined " ;
2020-04-23 23:37:52 +08:00
keyboardLayoutMap [ CommonSharedConstants : : VK_WIN_BOTH ] = L " Win " ;
2020-07-23 03:57:13 +08:00
keyboardLayoutMap [ VK_KANA ] = L " IME Kana " ;
keyboardLayoutMap [ VK_HANGEUL ] = L " IME Hangeul " ;
keyboardLayoutMap [ VK_HANGUL ] = L " IME Hangul " ;
keyboardLayoutMap [ VK_JUNJA ] = L " IME Junja " ;
keyboardLayoutMap [ VK_FINAL ] = L " IME Final " ;
keyboardLayoutMap [ VK_HANJA ] = L " IME Hanja " ;
keyboardLayoutMap [ VK_KANJI ] = L " IME Kanji " ;
keyboardLayoutMap [ VK_CONVERT ] = L " IME Convert " ;
keyboardLayoutMap [ VK_NONCONVERT ] = L " IME Non-Convert " ;
keyboardLayoutMap [ VK_ACCEPT ] = L " IME Kana " ;
keyboardLayoutMap [ VK_MODECHANGE ] = L " IME Mode Change " ;
2020-10-02 20:36:36 +08:00
keyboardLayoutMap [ CommonSharedConstants : : VK_DISABLED ] = L " Disable " ;
2020-04-21 12:01:21 +08:00
}
// Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist
std : : vector < DWORD > LayoutMap : : LayoutMapImpl : : GetKeyCodeList ( const bool isShortcut )
{
std : : lock_guard < std : : mutex > lock ( keyboardLayoutMap_mutex ) ;
UpdateLayout ( ) ;
std : : vector < DWORD > keyCodes ;
if ( ! isKeyCodeListGenerated )
{
// Add character keys
for ( auto & it : unicodeKeys )
{
// If it was not renamed with a special name
if ( it . second = = keyboardLayoutMap [ it . first ] )
{
keyCodes . push_back ( it . first ) ;
}
}
2020-04-27 06:09:40 +08:00
// Add modifier keys in alphabetical order
keyCodes . push_back ( VK_MENU ) ;
keyCodes . push_back ( VK_LMENU ) ;
keyCodes . push_back ( VK_RMENU ) ;
keyCodes . push_back ( VK_CONTROL ) ;
keyCodes . push_back ( VK_LCONTROL ) ;
keyCodes . push_back ( VK_RCONTROL ) ;
keyCodes . push_back ( VK_SHIFT ) ;
keyCodes . push_back ( VK_LSHIFT ) ;
keyCodes . push_back ( VK_RSHIFT ) ;
keyCodes . push_back ( CommonSharedConstants : : VK_WIN_BOTH ) ;
keyCodes . push_back ( VK_LWIN ) ;
keyCodes . push_back ( VK_RWIN ) ;
2020-04-21 12:01:21 +08:00
// Add all other special keys
2020-04-27 06:09:40 +08:00
std : : vector < DWORD > specialKeys ;
2020-04-21 12:01:21 +08:00
for ( int i = 1 ; i < 256 ; i + + )
{
// If it is not already been added (i.e. it was either a modifier or had a unicode representation)
if ( std : : find ( keyCodes . begin ( ) , keyCodes . end ( ) , i ) = = keyCodes . end ( ) )
{
// If it is any other key but it is not named as VK #
auto it = unknownKeys . find ( i ) ;
if ( it = = unknownKeys . end ( ) )
{
2020-04-27 06:09:40 +08:00
specialKeys . push_back ( i ) ;
2020-04-21 12:01:21 +08:00
}
else if ( unknownKeys [ i ] ! = keyboardLayoutMap [ i ] )
{
2020-04-27 06:09:40 +08:00
specialKeys . push_back ( i ) ;
2020-04-21 12:01:21 +08:00
}
}
}
2020-04-27 06:09:40 +08:00
2023-08-24 00:56:40 +08:00
// Add numpad keys
for ( auto it = keyboardLayoutMap . rbegin ( ) ; it - > first & numpadOriginBit ; + + it )
{
keyCodes . push_back ( it - > first ) ;
}
2020-04-27 06:09:40 +08:00
// Sort the special keys in alphabetical order
std : : sort ( specialKeys . begin ( ) , specialKeys . end ( ) , [ & ] ( const DWORD & lhs , const DWORD & rhs ) {
return keyboardLayoutMap [ lhs ] < keyboardLayoutMap [ rhs ] ;
} ) ;
for ( int i = 0 ; i < specialKeys . size ( ) ; i + + )
{
keyCodes . push_back ( specialKeys [ i ] ) ;
}
2020-04-21 12:01:21 +08:00
// Add unknown keys
for ( auto & it : unknownKeys )
{
// If it was not renamed with a special name
if ( it . second = = keyboardLayoutMap [ it . first ] )
{
keyCodes . push_back ( it . first ) ;
}
}
keyCodeList = keyCodes ;
isKeyCodeListGenerated = true ;
}
else
{
keyCodes = keyCodeList ;
}
// If it is a key list for the shortcut control then we add a "None" key at the start
if ( isShortcut )
{
keyCodes . insert ( keyCodes . begin ( ) , 0 ) ;
}
return keyCodes ;
}
2020-10-19 17:27:47 +08:00
std : : vector < std : : pair < DWORD , std : : wstring > > LayoutMap : : LayoutMapImpl : : GetKeyNameList ( const bool isShortcut )
2020-04-21 12:01:21 +08:00
{
2020-10-19 17:27:47 +08:00
std : : vector < std : : pair < DWORD , std : : wstring > > keyNames ;
2020-04-21 12:01:21 +08:00
std : : vector < DWORD > keyCodes = GetKeyCodeList ( isShortcut ) ;
2020-04-27 06:09:40 +08:00
std : : lock_guard < std : : mutex > lock ( keyboardLayoutMap_mutex ) ;
2020-04-21 12:01:21 +08:00
// If it is a key list for the shortcut control then we add a "None" key at the start
if ( isShortcut )
{
2020-10-19 17:27:47 +08:00
keyNames . push_back ( { 0 , L " None " } ) ;
2020-04-21 12:01:21 +08:00
for ( int i = 1 ; i < keyCodes . size ( ) ; i + + )
{
2020-10-19 17:27:47 +08:00
keyNames . push_back ( { keyCodes [ i ] , keyboardLayoutMap [ keyCodes [ i ] ] } ) ;
2020-04-21 12:01:21 +08:00
}
}
else
{
for ( int i = 0 ; i < keyCodes . size ( ) ; i + + )
{
2020-10-19 17:27:47 +08:00
keyNames . push_back ( { keyCodes [ i ] , keyboardLayoutMap [ keyCodes [ i ] ] } ) ;
2020-04-21 12:01:21 +08:00
}
}
return keyNames ;
}