1721 lines
53 KiB
C++
1721 lines
53 KiB
C++
/*
|
|
* synergy -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2003 Chris Schoeneman
|
|
*
|
|
* This package is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* found in the file COPYING that should have accompanied this file.
|
|
*
|
|
* This package is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "CMSWindowsKeyState.h"
|
|
#include "CMSWindowsDesks.h"
|
|
#include "CThread.h"
|
|
#include "CFunctionJob.h"
|
|
#include "CLog.h"
|
|
#include "CStringUtil.h"
|
|
#include "CArchMiscWindows.h"
|
|
|
|
// extended mouse buttons
|
|
#if !defined(VK_XBUTTON1)
|
|
#define VK_XBUTTON1 0x05
|
|
#define VK_XBUTTON2 0x06
|
|
#endif
|
|
|
|
// multimedia keys
|
|
#if !defined(VK_BROWSER_BACK)
|
|
#define VK_BROWSER_BACK 0xA6
|
|
#define VK_BROWSER_FORWARD 0xA7
|
|
#define VK_BROWSER_REFRESH 0xA8
|
|
#define VK_BROWSER_STOP 0xA9
|
|
#define VK_BROWSER_SEARCH 0xAA
|
|
#define VK_BROWSER_FAVORITES 0xAB
|
|
#define VK_BROWSER_HOME 0xAC
|
|
#define VK_VOLUME_MUTE 0xAD
|
|
#define VK_VOLUME_DOWN 0xAE
|
|
#define VK_VOLUME_UP 0xAF
|
|
#define VK_MEDIA_NEXT_TRACK 0xB0
|
|
#define VK_MEDIA_PREV_TRACK 0xB1
|
|
#define VK_MEDIA_STOP 0xB2
|
|
#define VK_MEDIA_PLAY_PAUSE 0xB3
|
|
#define VK_LAUNCH_MAIL 0xB4
|
|
#define VK_LAUNCH_MEDIA_SELECT 0xB5
|
|
#define VK_LAUNCH_APP1 0xB6
|
|
#define VK_LAUNCH_APP2 0xB7
|
|
#endif
|
|
|
|
//
|
|
// CMSWindowsKeyState
|
|
//
|
|
|
|
const char* CMSWindowsKeyState::s_vkToName[] =
|
|
{
|
|
"vk 0x00",
|
|
"Left Button",
|
|
"Right Button",
|
|
"VK_CANCEL",
|
|
"Middle Button",
|
|
"vk 0x05",
|
|
"vk 0x06",
|
|
"vk 0x07",
|
|
"VK_BACK",
|
|
"VK_TAB",
|
|
"vk 0x0a",
|
|
"vk 0x0b",
|
|
"VK_CLEAR",
|
|
"VK_RETURN",
|
|
"vk 0x0e",
|
|
"vk 0x0f",
|
|
"VK_SHIFT",
|
|
"VK_CONTROL",
|
|
"VK_MENU",
|
|
"VK_PAUSE",
|
|
"VK_CAPITAL",
|
|
"VK_KANA",
|
|
"vk 0x16",
|
|
"VK_JUNJA",
|
|
"VK_FINAL",
|
|
"VK_KANJI",
|
|
"vk 0x1a",
|
|
"VK_ESCAPE",
|
|
"VK_CONVERT",
|
|
"VK_NONCONVERT",
|
|
"VK_ACCEPT",
|
|
"VK_MODECHANGE",
|
|
"VK_SPACE",
|
|
"VK_PRIOR",
|
|
"VK_NEXT",
|
|
"VK_END",
|
|
"VK_HOME",
|
|
"VK_LEFT",
|
|
"VK_UP",
|
|
"VK_RIGHT",
|
|
"VK_DOWN",
|
|
"VK_SELECT",
|
|
"VK_PRINT",
|
|
"VK_EXECUTE",
|
|
"VK_SNAPSHOT",
|
|
"VK_INSERT",
|
|
"VK_DELETE",
|
|
"VK_HELP",
|
|
"VK_0",
|
|
"VK_1",
|
|
"VK_2",
|
|
"VK_3",
|
|
"VK_4",
|
|
"VK_5",
|
|
"VK_6",
|
|
"VK_7",
|
|
"VK_8",
|
|
"VK_9",
|
|
"vk 0x3a",
|
|
"vk 0x3b",
|
|
"vk 0x3c",
|
|
"vk 0x3d",
|
|
"vk 0x3e",
|
|
"vk 0x3f",
|
|
"vk 0x40",
|
|
"VK_A",
|
|
"VK_B",
|
|
"VK_C",
|
|
"VK_D",
|
|
"VK_E",
|
|
"VK_F",
|
|
"VK_G",
|
|
"VK_H",
|
|
"VK_I",
|
|
"VK_J",
|
|
"VK_K",
|
|
"VK_L",
|
|
"VK_M",
|
|
"VK_N",
|
|
"VK_O",
|
|
"VK_P",
|
|
"VK_Q",
|
|
"VK_R",
|
|
"VK_S",
|
|
"VK_T",
|
|
"VK_U",
|
|
"VK_V",
|
|
"VK_W",
|
|
"VK_X",
|
|
"VK_Y",
|
|
"VK_Z",
|
|
"VK_LWIN",
|
|
"VK_RWIN",
|
|
"VK_APPS",
|
|
"vk 0x5e",
|
|
"vk 0x5f",
|
|
"VK_NUMPAD0",
|
|
"VK_NUMPAD1",
|
|
"VK_NUMPAD2",
|
|
"VK_NUMPAD3",
|
|
"VK_NUMPAD4",
|
|
"VK_NUMPAD5",
|
|
"VK_NUMPAD6",
|
|
"VK_NUMPAD7",
|
|
"VK_NUMPAD8",
|
|
"VK_NUMPAD9",
|
|
"VK_MULTIPLY",
|
|
"VK_ADD",
|
|
"VK_SEPARATOR",
|
|
"VK_SUBTRACT",
|
|
"VK_DECIMAL",
|
|
"VK_DIVIDE",
|
|
"VK_F1",
|
|
"VK_F2",
|
|
"VK_F3",
|
|
"VK_F4",
|
|
"VK_F5",
|
|
"VK_F6",
|
|
"VK_F7",
|
|
"VK_F8",
|
|
"VK_F9",
|
|
"VK_F10",
|
|
"VK_F11",
|
|
"VK_F12",
|
|
"VK_F13",
|
|
"VK_F14",
|
|
"VK_F15",
|
|
"VK_F16",
|
|
"VK_F17",
|
|
"VK_F18",
|
|
"VK_F19",
|
|
"VK_F20",
|
|
"VK_F21",
|
|
"VK_F22",
|
|
"VK_F23",
|
|
"VK_F24",
|
|
"vk 0x88",
|
|
"vk 0x89",
|
|
"vk 0x8a",
|
|
"vk 0x8b",
|
|
"vk 0x8c",
|
|
"vk 0x8d",
|
|
"vk 0x8e",
|
|
"vk 0x8f",
|
|
"VK_NUMLOCK",
|
|
"VK_SCROLL",
|
|
"vk 0x92",
|
|
"vk 0x93",
|
|
"vk 0x94",
|
|
"vk 0x95",
|
|
"vk 0x96",
|
|
"vk 0x97",
|
|
"vk 0x98",
|
|
"vk 0x99",
|
|
"vk 0x9a",
|
|
"vk 0x9b",
|
|
"vk 0x9c",
|
|
"vk 0x9d",
|
|
"vk 0x9e",
|
|
"vk 0x9f",
|
|
"VK_LSHIFT",
|
|
"VK_RSHIFT",
|
|
"VK_LCONTROL",
|
|
"VK_RCONTROL",
|
|
"VK_LMENU",
|
|
"VK_RMENU",
|
|
"VK_BROWSER_BACK",
|
|
"VK_BROWSER_FORWARD",
|
|
"VK_BROWSER_REFRESH",
|
|
"VK_BROWSER_STOP",
|
|
"VK_BROWSER_SEARCH",
|
|
"VK_BROWSER_FAVORITES",
|
|
"VK_BROWSER_HOME",
|
|
"VK_VOLUME_MUTE",
|
|
"VK_VOLUME_DOWN",
|
|
"VK_VOLUME_UP",
|
|
"VK_MEDIA_NEXT_TRACK",
|
|
"VK_MEDIA_PREV_TRACK",
|
|
"VK_MEDIA_STOP",
|
|
"VK_MEDIA_PLAY_PAUSE",
|
|
"VK_LAUNCH_MAIL",
|
|
"VK_LAUNCH_MEDIA_SELECT",
|
|
"VK_LAUNCH_APP1",
|
|
"VK_LAUNCH_APP2",
|
|
"vk 0xb8",
|
|
"vk 0xb9",
|
|
"vk 0xba",
|
|
"vk 0xbb",
|
|
"vk 0xbc",
|
|
"vk 0xbd",
|
|
"vk 0xbe",
|
|
"vk 0xbf",
|
|
"vk 0xc0",
|
|
"vk 0xc1",
|
|
"vk 0xc2",
|
|
"vk 0xc3",
|
|
"vk 0xc4",
|
|
"vk 0xc5",
|
|
"vk 0xc6",
|
|
"vk 0xc7",
|
|
"vk 0xc8",
|
|
"vk 0xc9",
|
|
"vk 0xca",
|
|
"vk 0xcb",
|
|
"vk 0xcc",
|
|
"vk 0xcd",
|
|
"vk 0xce",
|
|
"vk 0xcf",
|
|
"vk 0xd0",
|
|
"vk 0xd1",
|
|
"vk 0xd2",
|
|
"vk 0xd3",
|
|
"vk 0xd4",
|
|
"vk 0xd5",
|
|
"vk 0xd6",
|
|
"vk 0xd7",
|
|
"vk 0xd8",
|
|
"vk 0xd9",
|
|
"vk 0xda",
|
|
"vk 0xdb",
|
|
"vk 0xdc",
|
|
"vk 0xdd",
|
|
"vk 0xde",
|
|
"vk 0xdf",
|
|
"vk 0xe0",
|
|
"vk 0xe1",
|
|
"vk 0xe2",
|
|
"vk 0xe3",
|
|
"vk 0xe4",
|
|
"VK_PROCESSKEY",
|
|
"vk 0xe6",
|
|
"vk 0xe7",
|
|
"vk 0xe8",
|
|
"vk 0xe9",
|
|
"vk 0xea",
|
|
"vk 0xeb",
|
|
"vk 0xec",
|
|
"vk 0xed",
|
|
"vk 0xee",
|
|
"vk 0xef",
|
|
"vk 0xf0",
|
|
"vk 0xf1",
|
|
"vk 0xf2",
|
|
"vk 0xf3",
|
|
"vk 0xf4",
|
|
"vk 0xf5",
|
|
"VK_ATTN",
|
|
"VK_CRSEL",
|
|
"VK_EXSEL",
|
|
"VK_EREOF",
|
|
"VK_PLAY",
|
|
"VK_ZOOM",
|
|
"VK_NONAME",
|
|
"VK_PA1",
|
|
"VK_OEM_CLEAR",
|
|
"vk 0xff"
|
|
};
|
|
|
|
// map virtual keys to synergy key enumeration
|
|
const KeyID CMSWindowsKeyState::s_virtualKey[][2] =
|
|
{
|
|
/* 0x00 */ { kKeyNone, kKeyNone }, // reserved
|
|
/* 0x01 */ { kKeyNone, kKeyNone }, // VK_LBUTTON
|
|
/* 0x02 */ { kKeyNone, kKeyNone }, // VK_RBUTTON
|
|
/* 0x03 */ { kKeyNone, kKeyBreak }, // VK_CANCEL
|
|
/* 0x04 */ { kKeyNone, kKeyNone }, // VK_MBUTTON
|
|
/* 0x05 */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x06 */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x07 */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x08 */ { kKeyBackSpace, kKeyNone }, // VK_BACK
|
|
/* 0x09 */ { kKeyTab, kKeyNone }, // VK_TAB
|
|
/* 0x0a */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x0b */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x0c */ { kKeyClear, kKeyClear }, // VK_CLEAR
|
|
/* 0x0d */ { kKeyReturn, kKeyKP_Enter }, // VK_RETURN
|
|
/* 0x0e */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x0f */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x10 */ { kKeyShift_L, kKeyShift_R }, // VK_SHIFT
|
|
/* 0x11 */ { kKeyControl_L, kKeyControl_R },// VK_CONTROL
|
|
/* 0x12 */ { kKeyAlt_L, kKeyAlt_R }, // VK_MENU
|
|
/* 0x13 */ { kKeyPause, kKeyNone }, // VK_PAUSE
|
|
/* 0x14 */ { kKeyCapsLock, kKeyNone }, // VK_CAPITAL
|
|
/* 0x15 */ { kKeyNone, kKeyNone }, // VK_KANA
|
|
/* 0x16 */ { kKeyNone, kKeyNone }, // VK_HANGUL
|
|
/* 0x17 */ { kKeyNone, kKeyNone }, // VK_JUNJA
|
|
/* 0x18 */ { kKeyNone, kKeyNone }, // VK_FINAL
|
|
/* 0x19 */ { kKeyZenkaku, kKeyNone }, // VK_KANJI
|
|
/* 0x1a */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x1b */ { kKeyEscape, kKeyNone }, // VK_ESCAPE
|
|
/* 0x1c */ { kKeyNone, kKeyNone }, // VK_CONVERT
|
|
/* 0x1d */ { kKeyNone, kKeyNone }, // VK_NONCONVERT
|
|
/* 0x1e */ { kKeyNone, kKeyNone }, // VK_ACCEPT
|
|
/* 0x1f */ { kKeyNone, kKeyNone }, // VK_MODECHANGE
|
|
/* 0x20 */ { kKeyNone, kKeyNone }, // VK_SPACE
|
|
/* 0x21 */ { kKeyKP_PageUp, kKeyPageUp }, // VK_PRIOR
|
|
/* 0x22 */ { kKeyKP_PageDown, kKeyPageDown }, // VK_NEXT
|
|
/* 0x23 */ { kKeyKP_End, kKeyEnd }, // VK_END
|
|
/* 0x24 */ { kKeyKP_Home, kKeyHome }, // VK_HOME
|
|
/* 0x25 */ { kKeyKP_Left, kKeyLeft }, // VK_LEFT
|
|
/* 0x26 */ { kKeyKP_Up, kKeyUp }, // VK_UP
|
|
/* 0x27 */ { kKeyKP_Right, kKeyRight }, // VK_RIGHT
|
|
/* 0x28 */ { kKeyKP_Down, kKeyDown }, // VK_DOWN
|
|
/* 0x29 */ { kKeySelect, kKeySelect }, // VK_SELECT
|
|
/* 0x2a */ { kKeyNone, kKeyNone }, // VK_PRINT
|
|
/* 0x2b */ { kKeyExecute, kKeyExecute }, // VK_EXECUTE
|
|
/* 0x2c */ { kKeyPrint, kKeyPrint }, // VK_SNAPSHOT
|
|
/* 0x2d */ { kKeyKP_Insert, kKeyInsert }, // VK_INSERT
|
|
/* 0x2e */ { kKeyKP_Delete, kKeyDelete }, // VK_DELETE
|
|
/* 0x2f */ { kKeyHelp, kKeyHelp }, // VK_HELP
|
|
/* 0x30 */ { kKeyNone, kKeyNone }, // VK_0
|
|
/* 0x31 */ { kKeyNone, kKeyNone }, // VK_1
|
|
/* 0x32 */ { kKeyNone, kKeyNone }, // VK_2
|
|
/* 0x33 */ { kKeyNone, kKeyNone }, // VK_3
|
|
/* 0x34 */ { kKeyNone, kKeyNone }, // VK_4
|
|
/* 0x35 */ { kKeyNone, kKeyNone }, // VK_5
|
|
/* 0x36 */ { kKeyNone, kKeyNone }, // VK_6
|
|
/* 0x37 */ { kKeyNone, kKeyNone }, // VK_7
|
|
/* 0x38 */ { kKeyNone, kKeyNone }, // VK_8
|
|
/* 0x39 */ { kKeyNone, kKeyNone }, // VK_9
|
|
/* 0x3a */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x3b */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x3c */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x3d */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x3e */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x3f */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x40 */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x41 */ { kKeyNone, kKeyNone }, // VK_A
|
|
/* 0x42 */ { kKeyNone, kKeyNone }, // VK_B
|
|
/* 0x43 */ { kKeyNone, kKeyNone }, // VK_C
|
|
/* 0x44 */ { kKeyNone, kKeyNone }, // VK_D
|
|
/* 0x45 */ { kKeyNone, kKeyNone }, // VK_E
|
|
/* 0x46 */ { kKeyNone, kKeyNone }, // VK_F
|
|
/* 0x47 */ { kKeyNone, kKeyNone }, // VK_G
|
|
/* 0x48 */ { kKeyNone, kKeyNone }, // VK_H
|
|
/* 0x49 */ { kKeyNone, kKeyNone }, // VK_I
|
|
/* 0x4a */ { kKeyNone, kKeyNone }, // VK_J
|
|
/* 0x4b */ { kKeyNone, kKeyNone }, // VK_K
|
|
/* 0x4c */ { kKeyNone, kKeyNone }, // VK_L
|
|
/* 0x4d */ { kKeyNone, kKeyNone }, // VK_M
|
|
/* 0x4e */ { kKeyNone, kKeyNone }, // VK_N
|
|
/* 0x4f */ { kKeyNone, kKeyNone }, // VK_O
|
|
/* 0x50 */ { kKeyNone, kKeyNone }, // VK_P
|
|
/* 0x51 */ { kKeyNone, kKeyNone }, // VK_Q
|
|
/* 0x52 */ { kKeyNone, kKeyNone }, // VK_R
|
|
/* 0x53 */ { kKeyNone, kKeyNone }, // VK_S
|
|
/* 0x54 */ { kKeyNone, kKeyNone }, // VK_T
|
|
/* 0x55 */ { kKeyNone, kKeyNone }, // VK_U
|
|
/* 0x56 */ { kKeyNone, kKeyNone }, // VK_V
|
|
/* 0x57 */ { kKeyNone, kKeyNone }, // VK_W
|
|
/* 0x58 */ { kKeyNone, kKeyNone }, // VK_X
|
|
/* 0x59 */ { kKeyNone, kKeyNone }, // VK_Y
|
|
/* 0x5a */ { kKeyNone, kKeyNone }, // VK_Z
|
|
/* 0x5b */ { kKeyNone, kKeySuper_L }, // VK_LWIN
|
|
/* 0x5c */ { kKeyNone, kKeySuper_R }, // VK_RWIN
|
|
/* 0x5d */ { kKeyMenu, kKeyMenu }, // VK_APPS
|
|
/* 0x5e */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x5f */ { kKeyNone, kKeyNone }, // undefined
|
|
/* 0x60 */ { kKeyKP_0, kKeyNone }, // VK_NUMPAD0
|
|
/* 0x61 */ { kKeyKP_1, kKeyNone }, // VK_NUMPAD1
|
|
/* 0x62 */ { kKeyKP_2, kKeyNone }, // VK_NUMPAD2
|
|
/* 0x63 */ { kKeyKP_3, kKeyNone }, // VK_NUMPAD3
|
|
/* 0x64 */ { kKeyKP_4, kKeyNone }, // VK_NUMPAD4
|
|
/* 0x65 */ { kKeyKP_5, kKeyNone }, // VK_NUMPAD5
|
|
/* 0x66 */ { kKeyKP_6, kKeyNone }, // VK_NUMPAD6
|
|
/* 0x67 */ { kKeyKP_7, kKeyNone }, // VK_NUMPAD7
|
|
/* 0x68 */ { kKeyKP_8, kKeyNone }, // VK_NUMPAD8
|
|
/* 0x69 */ { kKeyKP_9, kKeyNone }, // VK_NUMPAD9
|
|
/* 0x6a */ { kKeyKP_Multiply, kKeyNone }, // VK_MULTIPLY
|
|
/* 0x6b */ { kKeyKP_Add, kKeyNone }, // VK_ADD
|
|
/* 0x6c */ { kKeyKP_Separator, kKeyKP_Separator },// VK_SEPARATOR
|
|
/* 0x6d */ { kKeyKP_Subtract, kKeyNone }, // VK_SUBTRACT
|
|
/* 0x6e */ { kKeyKP_Decimal, kKeyNone }, // VK_DECIMAL
|
|
/* 0x6f */ { kKeyNone, kKeyKP_Divide },// VK_DIVIDE
|
|
/* 0x70 */ { kKeyF1, kKeyNone }, // VK_F1
|
|
/* 0x71 */ { kKeyF2, kKeyNone }, // VK_F2
|
|
/* 0x72 */ { kKeyF3, kKeyNone }, // VK_F3
|
|
/* 0x73 */ { kKeyF4, kKeyNone }, // VK_F4
|
|
/* 0x74 */ { kKeyF5, kKeyNone }, // VK_F5
|
|
/* 0x75 */ { kKeyF6, kKeyNone }, // VK_F6
|
|
/* 0x76 */ { kKeyF7, kKeyNone }, // VK_F7
|
|
/* 0x77 */ { kKeyF8, kKeyNone }, // VK_F8
|
|
/* 0x78 */ { kKeyF9, kKeyNone }, // VK_F9
|
|
/* 0x79 */ { kKeyF10, kKeyNone }, // VK_F10
|
|
/* 0x7a */ { kKeyF11, kKeyNone }, // VK_F11
|
|
/* 0x7b */ { kKeyF12, kKeyNone }, // VK_F12
|
|
/* 0x7c */ { kKeyF13, kKeyF13 }, // VK_F13
|
|
/* 0x7d */ { kKeyF14, kKeyF14 }, // VK_F14
|
|
/* 0x7e */ { kKeyF15, kKeyF15 }, // VK_F15
|
|
/* 0x7f */ { kKeyF16, kKeyF16 }, // VK_F16
|
|
/* 0x80 */ { kKeyF17, kKeyF17 }, // VK_F17
|
|
/* 0x81 */ { kKeyF18, kKeyF18 }, // VK_F18
|
|
/* 0x82 */ { kKeyF19, kKeyF19 }, // VK_F19
|
|
/* 0x83 */ { kKeyF20, kKeyF20 }, // VK_F20
|
|
/* 0x84 */ { kKeyF21, kKeyF21 }, // VK_F21
|
|
/* 0x85 */ { kKeyF22, kKeyF22 }, // VK_F22
|
|
/* 0x86 */ { kKeyF23, kKeyF23 }, // VK_F23
|
|
/* 0x87 */ { kKeyF24, kKeyF24 }, // VK_F24
|
|
/* 0x88 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x89 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x8a */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x8b */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x8c */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x8d */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x8e */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x8f */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x90 */ { kKeyNumLock, kKeyNumLock }, // VK_NUMLOCK
|
|
/* 0x91 */ { kKeyScrollLock, kKeyNone }, // VK_SCROLL
|
|
/* 0x92 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x93 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x94 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x95 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x96 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x97 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x98 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x99 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x9a */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x9b */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x9c */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x9d */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x9e */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0x9f */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xa0 */ { kKeyShift_L, kKeyShift_L }, // VK_LSHIFT
|
|
/* 0xa1 */ { kKeyShift_R, kKeyShift_R }, // VK_RSHIFT
|
|
/* 0xa2 */ { kKeyControl_L, kKeyControl_L },// VK_LCONTROL
|
|
/* 0xa3 */ { kKeyControl_R, kKeyControl_R },// VK_RCONTROL
|
|
/* 0xa4 */ { kKeyAlt_L, kKeyAlt_L }, // VK_LMENU
|
|
/* 0xa5 */ { kKeyAlt_R, kKeyAlt_R }, // VK_RMENU
|
|
/* 0xa6 */ { kKeyNone, kKeyWWWBack }, // VK_BROWSER_BACK
|
|
/* 0xa7 */ { kKeyNone, kKeyWWWForward },// VK_BROWSER_FORWARD
|
|
/* 0xa8 */ { kKeyNone, kKeyWWWRefresh },// VK_BROWSER_REFRESH
|
|
/* 0xa9 */ { kKeyNone, kKeyWWWStop }, // VK_BROWSER_STOP
|
|
/* 0xaa */ { kKeyNone, kKeyWWWSearch },// VK_BROWSER_SEARCH
|
|
/* 0xab */ { kKeyNone, kKeyWWWFavorites },// VK_BROWSER_FAVORITES
|
|
/* 0xac */ { kKeyNone, kKeyWWWHome }, // VK_BROWSER_HOME
|
|
/* 0xad */ { kKeyNone, kKeyAudioMute },// VK_VOLUME_MUTE
|
|
/* 0xae */ { kKeyNone, kKeyAudioDown },// VK_VOLUME_DOWN
|
|
/* 0xaf */ { kKeyNone, kKeyAudioUp }, // VK_VOLUME_UP
|
|
/* 0xb0 */ { kKeyNone, kKeyAudioNext },// VK_MEDIA_NEXT_TRACK
|
|
/* 0xb1 */ { kKeyNone, kKeyAudioPrev },// VK_MEDIA_PREV_TRACK
|
|
/* 0xb2 */ { kKeyNone, kKeyAudioStop },// VK_MEDIA_STOP
|
|
/* 0xb3 */ { kKeyNone, kKeyAudioPlay },// VK_MEDIA_PLAY_PAUSE
|
|
/* 0xb4 */ { kKeyNone, kKeyAppMail }, // VK_LAUNCH_MAIL
|
|
/* 0xb5 */ { kKeyNone, kKeyAppMedia }, // VK_LAUNCH_MEDIA_SELECT
|
|
/* 0xb6 */ { kKeyNone, kKeyAppUser1 }, // VK_LAUNCH_APP1
|
|
/* 0xb7 */ { kKeyNone, kKeyAppUser2 }, // VK_LAUNCH_APP2
|
|
/* 0xb8 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xb9 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xba */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xbb */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xbc */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xbd */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xbe */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xbf */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xc0 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xc1 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc2 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc3 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc4 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc5 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc6 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc7 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc8 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xc9 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xca */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xcb */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xcc */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xcd */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xce */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xcf */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd0 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd1 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd2 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd3 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd4 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd5 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd6 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd7 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd8 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xd9 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xda */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xdb */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xdc */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xdd */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xde */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xdf */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe0 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe1 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe2 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe3 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe4 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe5 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xe6 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xe7 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xe8 */ { kKeyNone, kKeyNone }, // unassigned
|
|
/* 0xe9 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xea */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xeb */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xec */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xed */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xee */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xef */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf0 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf1 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf2 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf3 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf4 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf5 */ { kKeyNone, kKeyNone }, // OEM specific
|
|
/* 0xf6 */ { kKeyNone, kKeyNone }, // VK_ATTN
|
|
/* 0xf7 */ { kKeyNone, kKeyNone }, // VK_CRSEL
|
|
/* 0xf8 */ { kKeyNone, kKeyNone }, // VK_EXSEL
|
|
/* 0xf9 */ { kKeyNone, kKeyNone }, // VK_EREOF
|
|
/* 0xfa */ { kKeyNone, kKeyNone }, // VK_PLAY
|
|
/* 0xfb */ { kKeyNone, kKeyNone }, // VK_ZOOM
|
|
/* 0xfc */ { kKeyNone, kKeyNone }, // reserved
|
|
/* 0xfd */ { kKeyNone, kKeyNone }, // VK_PA1
|
|
/* 0xfe */ { kKeyNone, kKeyNone }, // VK_OEM_CLEAR
|
|
/* 0xff */ { kKeyNone, kKeyNone } // reserved
|
|
};
|
|
|
|
// map special KeyID keys to virtual key codes plus whether or not
|
|
// the key maps to an extended scan code
|
|
const UINT CMSWindowsKeyState::s_mapE000[] =
|
|
{
|
|
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xa0 */ 0, 0, 0, 0,
|
|
/* 0xa4 */ 0, 0, VK_BROWSER_BACK | 0x100u, VK_BROWSER_FORWARD | 0x100u,
|
|
/* 0xa8 */ VK_BROWSER_REFRESH | 0x100u, VK_BROWSER_STOP | 0x100u,
|
|
/* 0xaa */ VK_BROWSER_SEARCH | 0x100u, VK_BROWSER_FAVORITES | 0x100u,
|
|
/* 0xac */ VK_BROWSER_HOME | 0x100u, VK_VOLUME_MUTE | 0x100u,
|
|
/* 0xae */ VK_VOLUME_DOWN | 0x100u, VK_VOLUME_UP | 0x100u,
|
|
/* 0xb0 */ VK_MEDIA_NEXT_TRACK | 0x100u, VK_MEDIA_PREV_TRACK | 0x100u,
|
|
/* 0xb2 */ VK_MEDIA_STOP | 0x100u, VK_MEDIA_PLAY_PAUSE | 0x100u,
|
|
/* 0xb4 */ VK_LAUNCH_MAIL | 0x100u, VK_LAUNCH_MEDIA_SELECT | 0x100u,
|
|
/* 0xb6 */ VK_LAUNCH_APP1 | 0x100u, VK_LAUNCH_APP2 | 0x100u,
|
|
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
const UINT CMSWindowsKeyState::s_mapEE00[] =
|
|
{
|
|
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
/* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32
|
|
* doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to
|
|
* the same meaning. */
|
|
const UINT CMSWindowsKeyState::s_mapEF00[] =
|
|
{
|
|
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0,
|
|
/* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0,
|
|
/* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0,
|
|
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x28 */ 0, 0, VK_KANJI, 0, 0, 0, 0, 0,
|
|
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x50 */ VK_HOME | 0x100u, VK_LEFT | 0x100u,
|
|
/* 0x52 */ VK_UP | 0x100u, VK_RIGHT | 0x100u,
|
|
/* 0x54 */ VK_DOWN | 0x100u, VK_PRIOR | 0x100u,
|
|
/* 0x56 */ VK_NEXT | 0x100u, VK_END | 0x100u,
|
|
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT | 0x100u,
|
|
/* 0x64 */ 0, 0, 0, VK_APPS | 0x100u,
|
|
/* 0x68 */ 0, 0, VK_HELP, VK_CANCEL | 0x100u, 0, 0, 0, 0,
|
|
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK | 0x100u,
|
|
/* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN | 0x100u, 0, 0,
|
|
/* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP,
|
|
/* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT,
|
|
/* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE,
|
|
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD,
|
|
/* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE | 0x100u,
|
|
/* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3,
|
|
/* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
|
/* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2,
|
|
/* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10,
|
|
/* 0xc8 */ VK_F11, VK_F12, VK_F13 | 0x100u, VK_F14 | 0x100u,
|
|
/* 0xcc */ VK_F15 | 0x100u, VK_F16 | 0x100u,
|
|
/* 0xce */ VK_F17 | 0x100u, VK_F18 | 0x100u,
|
|
/* 0xd0 */ VK_F19 | 0x100u, VK_F20 | 0x100u,
|
|
/* 0xd2 */ VK_F21 | 0x100u, VK_F22 | 0x100u,
|
|
/* 0xd4 */ VK_F23 | 0x100u, VK_F24 | 0x100u, 0, 0,
|
|
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT | 0x100u, VK_LCONTROL,
|
|
/* 0xe4 */ VK_RCONTROL | 0x100u, VK_CAPITAL, 0, 0,
|
|
/* 0xe8 */ 0, VK_LMENU, VK_RMENU | 0x100u, VK_LWIN | 0x100u,
|
|
/* 0xec */ VK_RWIN | 0x100u, 0, 0, 0,
|
|
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE | 0x100u
|
|
};
|
|
|
|
CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) :
|
|
m_is95Family(CArchMiscWindows::isWindows95Family()),
|
|
m_desks(desks),
|
|
m_keyLayout(GetKeyboardLayout(0))
|
|
{
|
|
}
|
|
|
|
CMSWindowsKeyState::~CMSWindowsKeyState()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void
|
|
CMSWindowsKeyState::setKeyLayout(HKL keyLayout)
|
|
{
|
|
m_keyLayout = keyLayout;
|
|
}
|
|
|
|
void
|
|
CMSWindowsKeyState::fixKey(void* target, UINT virtualKey)
|
|
{
|
|
// check if virtualKey is up but we think it's down. if so then
|
|
// synthesize a key release for it.
|
|
//
|
|
// we use GetAsyncKeyState() to check the state of the keys even
|
|
// though we might not be in sync with that yet.
|
|
KeyButton button = m_virtKeyToScanCode[virtualKey];
|
|
if (isKeyDown(button) && (GetAsyncKeyState(virtualKey) & 0x8000) == 0) {
|
|
// compute appropriate parameters for fake event
|
|
LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16);
|
|
|
|
// process as if it were a key up
|
|
KeyModifierMask mask;
|
|
KeyID key = mapKeyFromEvent(virtualKey, lParam, &mask);
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
|
|
setKeyDown(button, false);
|
|
}
|
|
}
|
|
|
|
KeyID
|
|
CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey,
|
|
LPARAM info, KeyModifierMask* maskOut) const
|
|
{
|
|
// FIXME -- look into this
|
|
// note: known microsoft bugs
|
|
// Q72583 -- MapVirtualKey() maps keypad keys incorrectly
|
|
// 95,98: num pad vk code -> invalid scan code
|
|
// 95,98,NT4: num pad scan code -> bad vk code except
|
|
// SEPARATOR, MULTIPLY, SUBTRACT, ADD
|
|
|
|
// extract character and virtual key
|
|
char c = (char)((charAndVirtKey & 0xff00u) >> 8);
|
|
UINT vkCode = (charAndVirtKey & 0xffu);
|
|
|
|
// handle some keys via table lookup
|
|
int extended = ((info >> 24) & 1);
|
|
KeyID id = s_virtualKey[vkCode][extended];
|
|
|
|
// check if not in table; map character to key id
|
|
if (id == kKeyNone && c != 0) {
|
|
if ((c & 0x80u) == 0) {
|
|
// ASCII
|
|
id = static_cast<KeyID>(c) & 0xffu;
|
|
}
|
|
else {
|
|
// character is not really ASCII. instead it's some
|
|
// character in the current ANSI code page. try to
|
|
// convert that to a Unicode character. if we fail
|
|
// then use the single byte character as is.
|
|
char src = c;
|
|
wchar_t unicode;
|
|
if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
|
|
&src, 1, &unicode, 1) > 0) {
|
|
id = static_cast<KeyID>(unicode);
|
|
}
|
|
else {
|
|
id = static_cast<KeyID>(c) & 0xffu;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set mask
|
|
KeyModifierMask activeMask = getActiveModifiers();
|
|
bool needAltGr = false;
|
|
if (id != kKeyNone && c != 0) {
|
|
// note if key requires AltGr. VkKeyScan() can have a problem
|
|
// with some characters. there are two problems in particular.
|
|
// first, typing a dead key then pressing space will cause
|
|
// VkKeyScan() to return 0xffff. second, certain characters
|
|
// may map to multiple virtual keys and we might get the wrong
|
|
// one. if that happens then we might not get the right
|
|
// modifier mask. AltGr+9 on the french keyboard layout (^)
|
|
// has this problem. in the first case, we'll assume AltGr is
|
|
// required (only because it solves the problems we've seen
|
|
// so far). in the second, we'll use whatever the keyboard
|
|
// state says.
|
|
WORD virtualKeyAndModifierState = VkKeyScanEx(c, m_keyLayout);
|
|
if (virtualKeyAndModifierState == 0xffff) {
|
|
// there is no mapping. assume AltGr.
|
|
LOG((CLOG_DEBUG1 "no VkKeyScan() mapping"));
|
|
needAltGr = true;
|
|
}
|
|
else if (LOBYTE(virtualKeyAndModifierState) != vkCode) {
|
|
// we didn't get the key that was actually pressed
|
|
LOG((CLOG_DEBUG1 "VkKeyScan() mismatch"));
|
|
if ((activeMask & (KeyModifierControl | KeyModifierAlt)) ==
|
|
(KeyModifierControl | KeyModifierAlt)) {
|
|
needAltGr = true;
|
|
}
|
|
}
|
|
else {
|
|
BYTE modifierState = HIBYTE(virtualKeyAndModifierState);
|
|
if ((modifierState & 6) == 6) {
|
|
// key requires ctrl and alt == AltGr
|
|
needAltGr = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// map modifier key
|
|
if (maskOut != NULL) {
|
|
if (needAltGr) {
|
|
activeMask |= KeyModifierModeSwitch;
|
|
activeMask &= ~(KeyModifierControl | KeyModifierAlt);
|
|
}
|
|
else {
|
|
activeMask &= ~KeyModifierModeSwitch;
|
|
}
|
|
*maskOut = activeMask;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
KeyButton
|
|
CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const
|
|
{
|
|
return m_virtKeyToScanCode[virtualKey & 0xffu];
|
|
}
|
|
|
|
void
|
|
CMSWindowsKeyState::sendKeyEvent(void* target,
|
|
bool press, bool isAutoRepeat,
|
|
KeyID key, KeyModifierMask mask,
|
|
SInt32 count, KeyButton button)
|
|
{
|
|
if (press || isAutoRepeat) {
|
|
// if AltGr is required for this key then make sure
|
|
// the ctrl and alt keys are *not* down on the
|
|
// client. windows simulates AltGr with ctrl and
|
|
// alt for some inexplicable reason and clients
|
|
// will get confused if they see mode switch and
|
|
// ctrl and alt. we'll also need to put ctrl and
|
|
// alt back the way they were after we simulate
|
|
// the key.
|
|
bool ctrlL = isKeyDown(m_virtKeyToScanCode[VK_LCONTROL]);
|
|
bool ctrlR = isKeyDown(m_virtKeyToScanCode[VK_RCONTROL]);
|
|
bool altL = isKeyDown(m_virtKeyToScanCode[VK_LMENU]);
|
|
bool altR = isKeyDown(m_virtKeyToScanCode[VK_RMENU]);
|
|
if ((mask & KeyModifierModeSwitch) != 0) {
|
|
KeyModifierMask mask2 = (mask &
|
|
~(KeyModifierControl |
|
|
KeyModifierAlt |
|
|
KeyModifierModeSwitch));
|
|
if (ctrlL) {
|
|
CKeyState::sendKeyEvent(target, false, false,
|
|
kKeyControl_L, mask2, 1,
|
|
m_virtKeyToScanCode[VK_LCONTROL]);
|
|
}
|
|
if (ctrlR) {
|
|
CKeyState::sendKeyEvent(target, false, false,
|
|
kKeyControl_R, mask2, 1,
|
|
m_virtKeyToScanCode[VK_RCONTROL]);
|
|
}
|
|
if (altL) {
|
|
CKeyState::sendKeyEvent(target, false, false,
|
|
kKeyAlt_L, mask2, 1,
|
|
m_virtKeyToScanCode[VK_LMENU]);
|
|
}
|
|
if (altR) {
|
|
CKeyState::sendKeyEvent(target, false, false,
|
|
kKeyAlt_R, mask2, 1,
|
|
m_virtKeyToScanCode[VK_RMENU]);
|
|
}
|
|
}
|
|
|
|
// send key
|
|
if (press) {
|
|
CKeyState::sendKeyEvent(target, true, false,
|
|
key, mask, 1, button);
|
|
if (count > 0) {
|
|
--count;
|
|
}
|
|
}
|
|
if (count >= 1) {
|
|
CKeyState::sendKeyEvent(target, true, true,
|
|
key, mask, count, button);
|
|
}
|
|
|
|
// restore ctrl and alt state
|
|
if ((mask & KeyModifierModeSwitch) != 0) {
|
|
KeyModifierMask mask2 = (mask &
|
|
~(KeyModifierControl |
|
|
KeyModifierAlt |
|
|
KeyModifierModeSwitch));
|
|
if (ctrlL) {
|
|
CKeyState::sendKeyEvent(target, true, false,
|
|
kKeyControl_L, mask2, 1,
|
|
m_virtKeyToScanCode[VK_LCONTROL]);
|
|
mask2 |= KeyModifierControl;
|
|
}
|
|
if (ctrlR) {
|
|
CKeyState::sendKeyEvent(target, true, false,
|
|
kKeyControl_R, mask2, 1,
|
|
m_virtKeyToScanCode[VK_RCONTROL]);
|
|
mask2 |= KeyModifierControl;
|
|
}
|
|
if (altL) {
|
|
CKeyState::sendKeyEvent(target, true, false,
|
|
kKeyAlt_L, mask2, 1,
|
|
m_virtKeyToScanCode[VK_LMENU]);
|
|
mask2 |= KeyModifierAlt;
|
|
}
|
|
if (altR) {
|
|
CKeyState::sendKeyEvent(target, true, false,
|
|
kKeyAlt_R, mask2, 1,
|
|
m_virtKeyToScanCode[VK_RMENU]);
|
|
mask2 |= KeyModifierAlt;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// do key up
|
|
CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
|
|
}
|
|
}
|
|
|
|
bool
|
|
CMSWindowsKeyState::fakeCtrlAltDel()
|
|
{
|
|
if (!m_is95Family) {
|
|
// to fake ctrl+alt+del on the NT family we broadcast a suitable
|
|
// hotkey to all windows on the winlogon desktop. however, the
|
|
// current thread must be on that desktop to do the broadcast
|
|
// and we can't switch just any thread because some own windows
|
|
// or hooks. so start a new thread to do the real work.
|
|
CThread cad(new CFunctionJob(&CMSWindowsKeyState::ctrlAltDelThread));
|
|
cad.wait();
|
|
}
|
|
else {
|
|
// simulate ctrl+alt+del
|
|
fakeKeyDown(kKeyDelete, KeyModifierControl | KeyModifierAlt,
|
|
m_virtKeyToScanCode[VK_DELETE]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CMSWindowsKeyState::ctrlAltDelThread(void*)
|
|
{
|
|
// get the Winlogon desktop at whatever privilege we can
|
|
HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED);
|
|
if (desk != NULL) {
|
|
if (SetThreadDesktop(desk)) {
|
|
PostMessage(HWND_BROADCAST, WM_HOTKEY, 0,
|
|
MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE));
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError()));
|
|
}
|
|
CloseDesktop(desk);
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError()));
|
|
}
|
|
}
|
|
|
|
const char*
|
|
CMSWindowsKeyState::getKeyName(KeyButton button) const
|
|
{
|
|
char keyName[100];
|
|
char keyName2[100];
|
|
CMSWindowsKeyState* self = const_cast<CMSWindowsKeyState*>(this);
|
|
if (GetKeyNameText(button << 16, keyName, sizeof(keyName)) != 0) {
|
|
// get the extended name of the key if button is not extended
|
|
// or vice versa. if the names are different then report both.
|
|
button ^= 0x100u;
|
|
if (GetKeyNameText(button << 16, keyName2, sizeof(keyName2)) != 0 &&
|
|
strcmp(keyName, keyName2) != 0) {
|
|
self->m_keyName = CStringUtil::print("%s or %s", keyName, keyName2);
|
|
}
|
|
else {
|
|
self->m_keyName = keyName;
|
|
}
|
|
}
|
|
else if (m_scanCodeToVirtKey[button] != 0) {
|
|
self->m_keyName = s_vkToName[m_scanCodeToVirtKey[button]];
|
|
}
|
|
else {
|
|
self->m_keyName = CStringUtil::print("scan code 0x%03x", button);
|
|
}
|
|
return m_keyName.c_str();
|
|
}
|
|
|
|
void
|
|
CMSWindowsKeyState::doUpdateKeys()
|
|
{
|
|
// clear scan code to/from virtual key mapping
|
|
memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey));
|
|
memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode));
|
|
|
|
// add modifiers. note that ModeSwitch is mapped to VK_RMENU and
|
|
// that it's mapped *before* the Alt modifier. we must map it so
|
|
// KeyModifierModeSwitch mask can be converted to keystrokes. it
|
|
// must be mapped before the Alt modifier so that the Alt modifier
|
|
// takes precedence when mapping keystrokes to modifier masks.
|
|
KeyButtons keys;
|
|
keys.push_back(mapVirtKeyToButton(VK_RMENU));
|
|
addModifier(KeyModifierModeSwitch, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_LSHIFT));
|
|
keys.push_back(mapVirtKeyToButton(VK_RSHIFT));
|
|
addModifier(KeyModifierShift, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_LCONTROL));
|
|
keys.push_back(mapVirtKeyToButton(VK_RCONTROL));
|
|
addModifier(KeyModifierControl, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_LMENU));
|
|
keys.push_back(mapVirtKeyToButton(VK_RMENU));
|
|
addModifier(KeyModifierAlt, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_LWIN));
|
|
keys.push_back(mapVirtKeyToButton(VK_RWIN));
|
|
addModifier(KeyModifierSuper, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_CAPITAL));
|
|
addModifier(KeyModifierCapsLock, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_NUMLOCK));
|
|
addModifier(KeyModifierNumLock, keys);
|
|
keys.clear();
|
|
keys.push_back(mapVirtKeyToButton(VK_SCROLL));
|
|
addModifier(KeyModifierScrollLock, keys);
|
|
|
|
BYTE keyState[256];
|
|
GetKeyboardState(keyState);
|
|
for (UINT i = 1; i < 256; ++i) {
|
|
// skip mouse button virtual keys
|
|
switch (i) {
|
|
case VK_LBUTTON:
|
|
case VK_RBUTTON:
|
|
case VK_MBUTTON:
|
|
case VK_XBUTTON1:
|
|
case VK_XBUTTON2:
|
|
continue;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// map to a scancode and back to a virtual key
|
|
KeyButton button2;
|
|
KeyButton button = mapVirtKeyToButton(i, button2);
|
|
if (button == 0) {
|
|
continue;
|
|
}
|
|
|
|
// okay, now we have the scan code for the virtual key.
|
|
m_scanCodeToVirtKey[button] = i;
|
|
m_scanCodeToVirtKey[button2] = i;
|
|
m_virtKeyToScanCode[i] = button;
|
|
|
|
// if the virtual key is VK_DELETE then use the extended
|
|
// scan code. this is important for simulating ctrl+alt+del
|
|
// which only works with the extended key.
|
|
if (i == VK_DELETE) {
|
|
m_virtKeyToScanCode[i] |= 0x100u;
|
|
}
|
|
|
|
// save the key state
|
|
if ((keyState[i] & 0x80) != 0) {
|
|
setKeyDown(button, true);
|
|
}
|
|
|
|
// toggle state applies to all keys but we only want it for
|
|
// the modifier keys with corresponding lights.
|
|
if ((keyState[i] & 0x01) != 0) {
|
|
switch (i) {
|
|
case VK_CAPITAL:
|
|
setToggled(KeyModifierCapsLock);
|
|
break;
|
|
|
|
case VK_NUMLOCK:
|
|
setToggled(KeyModifierNumLock);
|
|
break;
|
|
|
|
case VK_SCROLL:
|
|
setToggled(KeyModifierScrollLock);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CMSWindowsKeyState::doFakeKeyEvent(KeyButton button,
|
|
bool press, bool isAutoRepeat)
|
|
{
|
|
UINT vk = m_scanCodeToVirtKey[button];
|
|
m_desks->fakeKeyEvent(button, vk, press, isAutoRepeat);
|
|
}
|
|
|
|
KeyButton
|
|
CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id,
|
|
KeyModifierMask mask, bool isAutoRepeat) const
|
|
{
|
|
UINT extVirtualKey = 0;
|
|
|
|
// check for special keys
|
|
if ((id & 0xfffff000u) == 0xe000u) {
|
|
if ((id & 0xff00u) == 0xe000u) {
|
|
extVirtualKey = s_mapE000[id & 0xffu];
|
|
}
|
|
else if ((id & 0xff00) == 0xee00) {
|
|
extVirtualKey = s_mapEE00[id & 0xffu];
|
|
}
|
|
else if ((id & 0xff00) == 0xef00) {
|
|
extVirtualKey = s_mapEF00[id & 0xffu];
|
|
}
|
|
if (extVirtualKey == 0) {
|
|
LOG((CLOG_DEBUG2 "unknown special key"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// special handling of VK_SNAPSHOT
|
|
if (extVirtualKey == VK_SNAPSHOT) {
|
|
// ignore key repeats on print screen
|
|
if (!isAutoRepeat) {
|
|
// active window (with alt) or fullscreen (without alt)?
|
|
BYTE scan = 0;
|
|
if ((mask & KeyModifierAlt) != 0) {
|
|
scan = 1;
|
|
}
|
|
|
|
// send events
|
|
keybd_event(VK_SNAPSHOT, scan, 0, 0);
|
|
keybd_event(VK_SNAPSHOT, scan, KEYEVENTF_KEYUP, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// handle other special keys
|
|
if (extVirtualKey != 0) {
|
|
// compute required modifiers
|
|
KeyModifierMask requiredMask = 0;
|
|
KeyModifierMask outMask = 0;
|
|
|
|
// check numeric keypad. note that virtual keys do not distinguish
|
|
// between the keypad and non-keypad movement keys. however, the
|
|
// virtual keys do distinguish between keypad numbers and operators
|
|
// (e.g. add, multiply) and their main keyboard counterparts.
|
|
// therefore, we can ignore the num-lock state for movement virtual
|
|
// keys but not for numeric keys.
|
|
UINT virtualKey = (extVirtualKey & 0xffu);
|
|
if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) {
|
|
requiredMask |= KeyModifierNumLock;
|
|
if ((getActiveModifiers() & KeyModifierNumLock) != 0) {
|
|
LOG((CLOG_DEBUG2 "turn on num lock for keypad key"));
|
|
outMask |= KeyModifierNumLock;
|
|
}
|
|
}
|
|
|
|
// check for left tab. that requires the shift key.
|
|
if (id == kKeyLeftTab) {
|
|
requiredMask |= KeyModifierShift;
|
|
outMask |= KeyModifierShift;
|
|
}
|
|
|
|
// now generate the keystrokes and return the resulting modifier mask
|
|
KeyButton button = m_virtKeyToScanCode[virtualKey];
|
|
if ((extVirtualKey & 0x100u) != 0) {
|
|
button |= 0x100u;
|
|
}
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%03x mask 0x%04x", id, virtualKey, button, outMask));
|
|
return mapToKeystrokes(keys, button,
|
|
outMask, requiredMask, isAutoRepeat);
|
|
}
|
|
|
|
// determine the thread that'll receive this event
|
|
// FIXME -- we can't be sure we'll get the right thread here
|
|
HWND targetWindow = GetForegroundWindow();
|
|
DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL);
|
|
|
|
// figure out the code page for the target thread. i'm just
|
|
// guessing here. get the target thread's keyboard layout,
|
|
// extract the language id from that, and choose the code page
|
|
// based on that language.
|
|
HKL hkl = GetKeyboardLayout(targetThread);
|
|
LANGID langID = static_cast<LANGID>(LOWORD(hkl));
|
|
UINT codePage = getCodePageFromLangID(langID);
|
|
LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread));
|
|
|
|
// regular characters are complicated by dead keys. it may not be
|
|
// possible to generate a desired character directly. we may need
|
|
// to generate a dead key first then some other character. the
|
|
// app receiving the events will compose these two characters into
|
|
// a single precomposed character.
|
|
//
|
|
// as best as i can tell this is the simplest way to convert a
|
|
// character into its uncomposed version. along the way we'll
|
|
// discover if the key cannot be handled at all. we convert
|
|
// from wide char to multibyte, then from multibyte to wide char
|
|
// forcing conversion to composite characters, then from wide
|
|
// char back to multibyte without making precomposed characters.
|
|
//
|
|
// after the first conversion to multibyte we see if we can map
|
|
// the key. if so then we don't bother trying to decompose dead
|
|
// keys.
|
|
BOOL error;
|
|
char multiByte[2 * MB_LEN_MAX];
|
|
wchar_t unicode[2];
|
|
unicode[0] = static_cast<wchar_t>(id & 0x0000ffffu);
|
|
int nChars = WideCharToMultiByte(codePage,
|
|
WC_COMPOSITECHECK | WC_DEFAULTCHAR,
|
|
unicode, 1,
|
|
multiByte, sizeof(multiByte),
|
|
NULL, &error);
|
|
if (nChars == 0 || error) {
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id));
|
|
return 0;
|
|
}
|
|
KeyButton button = mapCharacter(keys, multiByte[0], hkl, isAutoRepeat);
|
|
if (button != 0) {
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0]));
|
|
if (isDeadChar(multiByte[0], hkl, false)) {
|
|
// character mapped to a dead key but we want the
|
|
// character for real so send a space key afterwards.
|
|
LOG((CLOG_DEBUG2 "character mapped to dead key"));
|
|
Keystroke keystroke;
|
|
keystroke.m_key = m_virtKeyToScanCode[VK_SPACE];
|
|
keystroke.m_press = true;
|
|
keystroke.m_repeat = false;
|
|
keys.push_back(keystroke);
|
|
keystroke.m_press = false;
|
|
keys.push_back(keystroke);
|
|
|
|
// ignore the release of this key since we already
|
|
// handled it.
|
|
button = 0;
|
|
}
|
|
return button;
|
|
}
|
|
nChars = MultiByteToWideChar(codePage,
|
|
MB_COMPOSITE | MB_ERR_INVALID_CHARS,
|
|
multiByte, nChars,
|
|
unicode, 2);
|
|
if (nChars == 0) {
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id));
|
|
return 0;
|
|
}
|
|
nChars = WideCharToMultiByte(codePage,
|
|
0,
|
|
unicode, nChars,
|
|
multiByte, sizeof(multiByte),
|
|
NULL, &error);
|
|
if (nChars == 0 || error) {
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id));
|
|
return 0;
|
|
}
|
|
|
|
// we expect one or two characters in multiByte. if there are two
|
|
// then the *second* is a dead key. process the dead key if there.
|
|
// FIXME -- we assume each character is one byte here
|
|
if (nChars > 2) {
|
|
LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id));
|
|
return 0;
|
|
}
|
|
if (nChars == 2) {
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1]));
|
|
mapCharacter(keys, multiByte[1], hkl, isAutoRepeat);
|
|
}
|
|
|
|
// process character
|
|
LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0]));
|
|
return mapCharacter(keys, multiByte[0], hkl, isAutoRepeat);
|
|
}
|
|
|
|
UINT
|
|
CMSWindowsKeyState::getCodePageFromLangID(LANGID langid) const
|
|
{
|
|
// construct a locale id from the language id
|
|
LCID lcid = MAKELCID(langid, SORT_DEFAULT);
|
|
|
|
// get the ANSI code page for this locale
|
|
char data[6];
|
|
if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) {
|
|
// can't get code page
|
|
LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid));
|
|
return CP_ACP;
|
|
}
|
|
|
|
// convert stringified code page into a number
|
|
UINT codePage = static_cast<UINT>(atoi(data));
|
|
if (codePage == 0) {
|
|
// parse failed
|
|
LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid));
|
|
return CP_ACP;
|
|
}
|
|
|
|
return codePage;
|
|
}
|
|
|
|
KeyButton
|
|
CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey,
|
|
KeyButton& extended) const
|
|
{
|
|
// this method does what MapVirtualKey(virtualKey, 0) should do.
|
|
// we have to explicitly set the extended key flag for some
|
|
// modifiers because the win32 API is inadequate. we also find
|
|
// the unextended and the extended scancodes for those virtual
|
|
// keys that have both except for VK_SHIFT, VK_CONTROL, and VK_MENU.
|
|
//
|
|
// the windows 95 family doesn't map the side distinguishing virtual
|
|
// keys. but we know that VK_CONTROL maps to VK_LCONTROL and
|
|
// that VK_RCONTROL is the same scan code | 0x100. similarly for
|
|
// VK_MENU. but VK_RSHIFT cannot be determined that way so we
|
|
// search for it.
|
|
extended = 0;
|
|
KeyButton button;
|
|
if (m_is95Family) {
|
|
UINT scancode;
|
|
switch (virtualKey) {
|
|
case VK_LSHIFT:
|
|
button = (KeyButton)MapVirtualKey(VK_SHIFT, 0);
|
|
break;
|
|
|
|
case VK_RSHIFT:
|
|
// we have to search
|
|
scancode = MapVirtualKey(VK_SHIFT, 0);
|
|
for (UINT i = 1; i < 256; ++i) {
|
|
if (i != scancode && MapVirtualKey(i, 1) == VK_SHIFT) {
|
|
return (KeyButton)(i);
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
button = (KeyButton)MapVirtualKey(VK_CONTROL, 0);
|
|
break;
|
|
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
button = (KeyButton)MapVirtualKey(VK_MENU, 0);
|
|
break;
|
|
|
|
case VK_PAUSE:
|
|
// mapped to 0. i hope this works on all keyboards.
|
|
button = (KeyButton)0x45u;
|
|
break;
|
|
|
|
case VK_DIVIDE:
|
|
// mapped to 0. i hope this works on all keyboards.
|
|
button = (KeyButton)0x35u;
|
|
break;
|
|
|
|
default:
|
|
button = (KeyButton)MapVirtualKey(virtualKey, 0);
|
|
|
|
// okay, now we have the scan code for the virtual key. windows
|
|
// may map different virtual keys to the same button. for example,
|
|
// windows 95/98/me maps virtual keys 220 and 226 to scan code 86
|
|
// in the british english keyboard map. why? who knows. it
|
|
// doesn't make any sense since a button can't actually generate
|
|
// more than one virtual key. to avoid this stupidity, we map the
|
|
// button back to a virtual key to see if it matches the starting
|
|
// point.
|
|
if (button == 0 || MapVirtualKey(button, 1) != virtualKey) {
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (virtualKey) {
|
|
case VK_PAUSE:
|
|
// mapped to 0. i hope this works on all keyboards.
|
|
button = (KeyButton)0x45u;
|
|
break;
|
|
|
|
default:
|
|
button = (KeyButton)MapVirtualKey(virtualKey, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// map extended keys
|
|
switch (virtualKey) {
|
|
case VK_RETURN: // Return/numpad Enter
|
|
case VK_PRIOR: // numpad PageUp/PageUp
|
|
case VK_NEXT: // numpad PageDown/PageDown
|
|
case VK_END: // numpad End/End
|
|
case VK_HOME: // numpad Home/Home
|
|
case VK_LEFT: // numpad Left/Left
|
|
case VK_UP: // numpad Up/Up
|
|
case VK_RIGHT: // numpad Right/Right
|
|
case VK_DOWN: // numpad Down/Down
|
|
case VK_INSERT: // numpad Insert/Insert
|
|
case VK_DELETE: // numpad Delete/Delete
|
|
// case VK_SELECT:
|
|
// case VK_EXECUTE:
|
|
// case VK_HELP:
|
|
extended = (KeyButton)(button | 0x100u);
|
|
break;
|
|
}
|
|
|
|
// see if the win32 API can help us determine an extended key.
|
|
// if the remapped virtual key doesn't match the starting
|
|
// point then there's a really good chance that that virtual
|
|
// key is mapped to an extended key. however, this is not
|
|
// the case for modifiers that don't distinguish between left
|
|
// and right.
|
|
UINT virtualKey2 = MapVirtualKey(button, 3);
|
|
if (virtualKey2 != 0 && virtualKey2 != virtualKey) {
|
|
switch (virtualKey) {
|
|
case VK_SHIFT:
|
|
case VK_CONTROL:
|
|
case VK_MENU:
|
|
break;
|
|
|
|
case VK_NUMPAD0:
|
|
case VK_NUMPAD1:
|
|
case VK_NUMPAD2:
|
|
case VK_NUMPAD3:
|
|
case VK_NUMPAD4:
|
|
case VK_NUMPAD5:
|
|
case VK_NUMPAD6:
|
|
case VK_NUMPAD7:
|
|
case VK_NUMPAD8:
|
|
case VK_NUMPAD9:
|
|
case VK_MULTIPLY:
|
|
case VK_ADD:
|
|
case VK_SEPARATOR:
|
|
case VK_SUBTRACT:
|
|
case VK_DECIMAL:
|
|
break;
|
|
|
|
case VK_PAUSE:
|
|
break;
|
|
|
|
default:
|
|
button |= 0x100u;
|
|
extended = 0;
|
|
break;
|
|
}
|
|
return button;
|
|
}
|
|
|
|
// note other extended keys that the win32 API won't help us with.
|
|
// on the windows 95 family this is the only way to find extended
|
|
// keys since MapVirtualKey(N, 3) is unimplemented.
|
|
switch (virtualKey) {
|
|
case VK_CANCEL:
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
case VK_APPS:
|
|
// case VK_SEPARATOR:
|
|
case VK_DIVIDE:
|
|
case VK_F13:
|
|
case VK_F14:
|
|
case VK_F15:
|
|
case VK_F16:
|
|
case VK_F17:
|
|
case VK_F18:
|
|
case VK_F19:
|
|
case VK_F20:
|
|
case VK_F21:
|
|
case VK_F22:
|
|
case VK_F23:
|
|
case VK_F24:
|
|
case VK_NUMLOCK:
|
|
case VK_RSHIFT:
|
|
case VK_RCONTROL:
|
|
case VK_RMENU:
|
|
case VK_BROWSER_BACK:
|
|
case VK_BROWSER_FORWARD:
|
|
case VK_BROWSER_REFRESH:
|
|
case VK_BROWSER_STOP:
|
|
case VK_BROWSER_SEARCH:
|
|
case VK_BROWSER_FAVORITES:
|
|
case VK_BROWSER_HOME:
|
|
case VK_VOLUME_MUTE:
|
|
case VK_VOLUME_DOWN:
|
|
case VK_VOLUME_UP:
|
|
case VK_MEDIA_NEXT_TRACK:
|
|
case VK_MEDIA_PREV_TRACK:
|
|
case VK_MEDIA_STOP:
|
|
case VK_MEDIA_PLAY_PAUSE:
|
|
case VK_LAUNCH_MAIL:
|
|
case VK_LAUNCH_MEDIA_SELECT:
|
|
case VK_LAUNCH_APP1:
|
|
case VK_LAUNCH_APP2:
|
|
button |= 0x100u;
|
|
extended = 0;
|
|
break;
|
|
}
|
|
|
|
return button;
|
|
}
|
|
|
|
KeyButton
|
|
CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey) const
|
|
{
|
|
KeyButton dummy;
|
|
return mapVirtKeyToButton(virtualKey, dummy);
|
|
}
|
|
|
|
KeyButton
|
|
CMSWindowsKeyState::mapCharacter(Keystrokes& keys,
|
|
char c, HKL hkl, bool isAutoRepeat) const
|
|
{
|
|
KeyModifierMask activeMask = getActiveModifiers();
|
|
|
|
// translate the character into its virtual key and its required
|
|
// modifier state.
|
|
SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl);
|
|
|
|
// get virtual key
|
|
UINT virtualKey = LOBYTE(virtualKeyAndModifierState);
|
|
if (virtualKey == 0xffu) {
|
|
LOG((CLOG_DEBUG2 "cannot map character %d", static_cast<unsigned char>(c)));
|
|
return 0;
|
|
}
|
|
|
|
// get the required modifier state
|
|
BYTE modifierState = HIBYTE(virtualKeyAndModifierState);
|
|
|
|
// see what modifiers are needed. we only check for shift and
|
|
// AltGr. we must always match the desired shift state but only
|
|
// the desired AltGr state if AltGr is required. AltGr is actually
|
|
// ctrl + alt so we can't require that ctrl and alt not be pressed
|
|
// otherwise users couldn't do, say, ctrl+z.
|
|
//
|
|
// the space character (ascii 32) is special in that it's unaffected
|
|
// by shift and should match our stored shift state.
|
|
KeyModifierMask desiredMask = 0;
|
|
KeyModifierMask requiredMask = KeyModifierShift;
|
|
if (c == 32) {
|
|
desiredMask |= (activeMask & KeyModifierShift);
|
|
}
|
|
else if ((modifierState & 0x01u) == 1) {
|
|
desiredMask |= KeyModifierShift;
|
|
}
|
|
if ((modifierState & 0x06u) == 6) {
|
|
// add ctrl and alt, which must be matched. match alt via
|
|
// mode-switch, which uses the right alt key rather than
|
|
// the left. windows doesn't care which alt key so long
|
|
// as ctrl is also down but some apps do their own mapping
|
|
// and they do care. Emacs and PuTTY, for example.
|
|
desiredMask |= KeyModifierControl | KeyModifierModeSwitch;
|
|
requiredMask |= KeyModifierControl | KeyModifierModeSwitch;
|
|
}
|
|
|
|
// handle combination of caps-lock and shift. if caps-lock is
|
|
// off locally then use shift as necessary. if caps-lock is on
|
|
// locally then it reverses the meaning of shift for keys that
|
|
// are subject to case conversion.
|
|
if ((activeMask & KeyModifierCapsLock) != 0) {
|
|
// there doesn't seem to be a simple way to test if a
|
|
// character respects the caps lock key. for normal
|
|
// characters it's easy enough but CharLower() and
|
|
// CharUpper() don't map dead keys even though they
|
|
// do respect caps lock for some unfathomable reason.
|
|
// first check the easy way. if that doesn't work
|
|
// then see if it's a dead key.
|
|
unsigned char uc = static_cast<unsigned char>(c);
|
|
if (CharLower(reinterpret_cast<LPTSTR>(uc)) !=
|
|
CharUpper(reinterpret_cast<LPTSTR>(uc)) ||
|
|
(MapVirtualKeyEx(virtualKey, 2, hkl) & 0x80000000lu) != 0) {
|
|
LOG((CLOG_DEBUG2 "flip shift"));
|
|
desiredMask ^= KeyModifierShift;
|
|
}
|
|
}
|
|
|
|
// now generate the keystrokes. ignore the resulting modifier
|
|
// mask since it can't have changed (because we don't call this
|
|
// method for modifier keys).
|
|
KeyButton scanCode = m_virtKeyToScanCode[virtualKey];
|
|
LOG((CLOG_DEBUG2 "character %d to virtual key %d scan code 0x%04x mask 0x%08x", (unsigned char)c, virtualKey, scanCode, desiredMask));
|
|
return mapToKeystrokes(keys, scanCode,
|
|
desiredMask, requiredMask, isAutoRepeat);
|
|
}
|
|
|
|
KeyButton
|
|
CMSWindowsKeyState::mapToKeystrokes(Keystrokes& keys, KeyButton button,
|
|
KeyModifierMask desiredMask, KeyModifierMask requiredMask,
|
|
bool isAutoRepeat) const
|
|
{
|
|
// adjust the modifiers to match the desired modifiers
|
|
Keystrokes undo;
|
|
if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) {
|
|
LOG((CLOG_DEBUG2 "failed to adjust modifiers"));
|
|
keys.clear();
|
|
return 0;
|
|
}
|
|
|
|
// add the key event
|
|
Keystroke keystroke;
|
|
keystroke.m_key = button;
|
|
keystroke.m_press = true;
|
|
keystroke.m_repeat = isAutoRepeat;
|
|
keys.push_back(keystroke);
|
|
|
|
// put undo keystrokes at end of keystrokes in reverse order
|
|
while (!undo.empty()) {
|
|
keys.push_back(undo.back());
|
|
undo.pop_back();
|
|
}
|
|
|
|
return button;
|
|
}
|
|
|
|
bool
|
|
CMSWindowsKeyState::adjustModifiers(Keystrokes& keys,
|
|
Keystrokes& undo,
|
|
KeyModifierMask desiredMask,
|
|
KeyModifierMask requiredMask) const
|
|
{
|
|
// for each modifier in requiredMask make sure the current state
|
|
// of that modifier matches the bit in desiredMask.
|
|
for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) {
|
|
if ((mask & requiredMask) != 0) {
|
|
bool active = ((desiredMask & mask) != 0);
|
|
if (!mapModifier(keys, undo, mask, active)) {
|
|
return false;
|
|
}
|
|
requiredMask ^= mask;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int
|
|
CMSWindowsKeyState::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const
|
|
{
|
|
// ignore bogus character
|
|
if (c == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// translate the character into its virtual key and its required
|
|
// modifier state.
|
|
SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl);
|
|
|
|
// get virtual key
|
|
BYTE virtualKey = LOBYTE(virtualKeyAndModifierState);
|
|
if (virtualKey == 0xffu) {
|
|
return 0;
|
|
}
|
|
|
|
// get the required modifier state
|
|
BYTE modifierState = HIBYTE(virtualKeyAndModifierState);
|
|
|
|
// set shift state required to generate key
|
|
BYTE keys[256];
|
|
memset(keys, 0, sizeof(keys));
|
|
if (modifierState & 0x01u) {
|
|
keys[VK_SHIFT] = 0x80u;
|
|
}
|
|
if (modifierState & 0x02u) {
|
|
keys[VK_CONTROL] = 0x80u;
|
|
}
|
|
if (modifierState & 0x04u) {
|
|
keys[VK_MENU] = 0x80u;
|
|
}
|
|
|
|
// get the scan code for the key
|
|
UINT scanCode = MapVirtualKeyEx(virtualKey, 0, hkl);
|
|
|
|
// discard characters if chars is NULL
|
|
WORD dummy;
|
|
if (chars == NULL) {
|
|
chars = &dummy;
|
|
}
|
|
|
|
// put it back
|
|
return ToAsciiEx(virtualKey, scanCode, keys, chars, menu ? 1 : 0, hkl);
|
|
}
|
|
|
|
bool
|
|
CMSWindowsKeyState::isDeadChar(TCHAR c, HKL hkl, bool menu) const
|
|
{
|
|
// first clear out ToAsciiEx()'s internal buffer by sending it
|
|
// a space.
|
|
WORD ascii;
|
|
int old = toAscii(' ', hkl, 0, &ascii);
|
|
|
|
// now pass the character of interest
|
|
WORD dummy;
|
|
bool isDead = (toAscii(c, hkl, menu, &dummy) < 0);
|
|
|
|
// clear out internal buffer again
|
|
toAscii(' ', hkl, 0, &dummy);
|
|
|
|
// put old dead key back if there was one
|
|
if (old == 1 && ascii != ' ') {
|
|
toAscii(static_cast<TCHAR>(ascii & 0xffu), hkl, menu, &dummy);
|
|
}
|
|
|
|
return isDead;
|
|
}
|