/*
 * 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 "IEventQueue.h"
#include "TMethodEventJob.h"
#include "CArchMiscWindows.h"

// extended mouse buttons
#if !defined(VK_XBUTTON1)
#define VK_XBUTTON1				0x05
#define VK_XBUTTON2				0x06
#endif

//
// CMSWindowsKeyState
//

// map virtual keys to synergy key enumeration
const KeyID				CMSWindowsKeyState::s_virtualKey[] =
{
	/* 0x000 */ { kKeyNone },		// reserved
	/* 0x001 */ { kKeyNone },		// VK_LBUTTON
	/* 0x002 */ { kKeyNone },		// VK_RBUTTON
	/* 0x003 */ { kKeyNone },		// VK_CANCEL
	/* 0x004 */ { kKeyNone },		// VK_MBUTTON
	/* 0x005 */ { kKeyNone },		// VK_XBUTTON1
	/* 0x006 */ { kKeyNone },		// VK_XBUTTON2
	/* 0x007 */ { kKeyNone },		// undefined
	/* 0x008 */ { kKeyBackSpace },	// VK_BACK
	/* 0x009 */ { kKeyTab },		// VK_TAB
	/* 0x00a */ { kKeyNone },		// undefined
	/* 0x00b */ { kKeyNone },		// undefined
	/* 0x00c */ { kKeyClear },		// VK_CLEAR
	/* 0x00d */ { kKeyReturn },		// VK_RETURN
	/* 0x00e */ { kKeyNone },		// undefined
	/* 0x00f */ { kKeyNone },		// undefined
	/* 0x010 */ { kKeyShift_L },	// VK_SHIFT
	/* 0x011 */ { kKeyControl_L },	// VK_CONTROL
	/* 0x012 */ { kKeyAlt_L },		// VK_MENU
	/* 0x013 */ { kKeyPause },		// VK_PAUSE
	/* 0x014 */ { kKeyCapsLock },	// VK_CAPITAL
	/* 0x015 */ { kKeyHangulKana },	// VK_HANGUL, VK_KANA
	/* 0x016 */ { kKeyNone },		// undefined
	/* 0x017 */ { kKeyNone },		// VK_JUNJA
	/* 0x018 */ { kKeyNone },		// VK_FINAL
	/* 0x019 */ { kKeyHanjaKanzi },	// VK_KANJI
	/* 0x01a */ { kKeyNone },		// undefined
	/* 0x01b */ { kKeyEscape },		// VK_ESCAPE
	/* 0x01c */ { kKeyHenkan },		// VK_CONVERT		
	/* 0x01d */ { kKeyNone },		// VK_NONCONVERT	
	/* 0x01e */ { kKeyNone },		// VK_ACCEPT		
	/* 0x01f */ { kKeyNone },		// VK_MODECHANGE	
	/* 0x020 */ { kKeyNone },		// VK_SPACE
	/* 0x021 */ { kKeyKP_PageUp },	// VK_PRIOR
	/* 0x022 */ { kKeyKP_PageDown },// VK_NEXT
	/* 0x023 */ { kKeyKP_End },		// VK_END
	/* 0x024 */ { kKeyKP_Home },	// VK_HOME
	/* 0x025 */ { kKeyKP_Left },	// VK_LEFT
	/* 0x026 */ { kKeyKP_Up },		// VK_UP
	/* 0x027 */ { kKeyKP_Right },	// VK_RIGHT
	/* 0x028 */ { kKeyKP_Down },	// VK_DOWN
	/* 0x029 */ { kKeySelect },		// VK_SELECT
	/* 0x02a */ { kKeyNone },		// VK_PRINT
	/* 0x02b */ { kKeyExecute },	// VK_EXECUTE
	/* 0x02c */ { kKeyPrint },		// VK_SNAPSHOT
	/* 0x02d */ { kKeyKP_Insert },	// VK_INSERT
	/* 0x02e */ { kKeyKP_Delete },	// VK_DELETE
	/* 0x02f */ { kKeyHelp },		// VK_HELP
	/* 0x030 */ { kKeyNone },		// VK_0
	/* 0x031 */ { kKeyNone },		// VK_1
	/* 0x032 */ { kKeyNone },		// VK_2
	/* 0x033 */ { kKeyNone },		// VK_3
	/* 0x034 */ { kKeyNone },		// VK_4
	/* 0x035 */ { kKeyNone },		// VK_5
	/* 0x036 */ { kKeyNone },		// VK_6
	/* 0x037 */ { kKeyNone },		// VK_7
	/* 0x038 */ { kKeyNone },		// VK_8
	/* 0x039 */ { kKeyNone },		// VK_9
	/* 0x03a */ { kKeyNone },		// undefined
	/* 0x03b */ { kKeyNone },		// undefined
	/* 0x03c */ { kKeyNone },		// undefined
	/* 0x03d */ { kKeyNone },		// undefined
	/* 0x03e */ { kKeyNone },		// undefined
	/* 0x03f */ { kKeyNone },		// undefined
	/* 0x040 */ { kKeyNone },		// undefined
	/* 0x041 */ { kKeyNone },		// VK_A
	/* 0x042 */ { kKeyNone },		// VK_B
	/* 0x043 */ { kKeyNone },		// VK_C
	/* 0x044 */ { kKeyNone },		// VK_D
	/* 0x045 */ { kKeyNone },		// VK_E
	/* 0x046 */ { kKeyNone },		// VK_F
	/* 0x047 */ { kKeyNone },		// VK_G
	/* 0x048 */ { kKeyNone },		// VK_H
	/* 0x049 */ { kKeyNone },		// VK_I
	/* 0x04a */ { kKeyNone },		// VK_J
	/* 0x04b */ { kKeyNone },		// VK_K
	/* 0x04c */ { kKeyNone },		// VK_L
	/* 0x04d */ { kKeyNone },		// VK_M
	/* 0x04e */ { kKeyNone },		// VK_N
	/* 0x04f */ { kKeyNone },		// VK_O
	/* 0x050 */ { kKeyNone },		// VK_P
	/* 0x051 */ { kKeyNone },		// VK_Q
	/* 0x052 */ { kKeyNone },		// VK_R
	/* 0x053 */ { kKeyNone },		// VK_S
	/* 0x054 */ { kKeyNone },		// VK_T
	/* 0x055 */ { kKeyNone },		// VK_U
	/* 0x056 */ { kKeyNone },		// VK_V
	/* 0x057 */ { kKeyNone },		// VK_W
	/* 0x058 */ { kKeyNone },		// VK_X
	/* 0x059 */ { kKeyNone },		// VK_Y
	/* 0x05a */ { kKeyNone },		// VK_Z
	/* 0x05b */ { kKeySuper_L },	// VK_LWIN
	/* 0x05c */ { kKeySuper_R },	// VK_RWIN
	/* 0x05d */ { kKeyMenu },		// VK_APPS
	/* 0x05e */ { kKeyNone },		// undefined
	/* 0x05f */ { kKeySleep },		// VK_SLEEP
	/* 0x060 */ { kKeyKP_0 },		// VK_NUMPAD0
	/* 0x061 */ { kKeyKP_1 },		// VK_NUMPAD1
	/* 0x062 */ { kKeyKP_2 },		// VK_NUMPAD2
	/* 0x063 */ { kKeyKP_3 },		// VK_NUMPAD3
	/* 0x064 */ { kKeyKP_4 },		// VK_NUMPAD4
	/* 0x065 */ { kKeyKP_5 },		// VK_NUMPAD5
	/* 0x066 */ { kKeyKP_6 },		// VK_NUMPAD6
	/* 0x067 */ { kKeyKP_7 },		// VK_NUMPAD7
	/* 0x068 */ { kKeyKP_8 },		// VK_NUMPAD8
	/* 0x069 */ { kKeyKP_9 },		// VK_NUMPAD9
	/* 0x06a */ { kKeyKP_Multiply },// VK_MULTIPLY
	/* 0x06b */ { kKeyKP_Add },		// VK_ADD
	/* 0x06c */ { kKeyKP_Separator },// VK_SEPARATOR
	/* 0x06d */ { kKeyKP_Subtract },// VK_SUBTRACT
	/* 0x06e */ { kKeyKP_Decimal },	// VK_DECIMAL
	/* 0x06f */ { kKeyNone },		// VK_DIVIDE
	/* 0x070 */ { kKeyF1 },			// VK_F1
	/* 0x071 */ { kKeyF2 },			// VK_F2
	/* 0x072 */ { kKeyF3 },			// VK_F3
	/* 0x073 */ { kKeyF4 },			// VK_F4
	/* 0x074 */ { kKeyF5 },			// VK_F5
	/* 0x075 */ { kKeyF6 },			// VK_F6
	/* 0x076 */ { kKeyF7 },			// VK_F7
	/* 0x077 */ { kKeyF8 },			// VK_F8
	/* 0x078 */ { kKeyF9 },			// VK_F9
	/* 0x079 */ { kKeyF10 },		// VK_F10
	/* 0x07a */ { kKeyF11 },		// VK_F11
	/* 0x07b */ { kKeyF12 },		// VK_F12
	/* 0x07c */ { kKeyF13 },		// VK_F13
	/* 0x07d */ { kKeyF14 },		// VK_F14
	/* 0x07e */ { kKeyF15 },		// VK_F15
	/* 0x07f */ { kKeyF16 },		// VK_F16
	/* 0x080 */ { kKeyF17 },		// VK_F17
	/* 0x081 */ { kKeyF18 },		// VK_F18
	/* 0x082 */ { kKeyF19 },		// VK_F19
	/* 0x083 */ { kKeyF20 },		// VK_F20
	/* 0x084 */ { kKeyF21 },		// VK_F21
	/* 0x085 */ { kKeyF22 },		// VK_F22
	/* 0x086 */ { kKeyF23 },		// VK_F23
	/* 0x087 */ { kKeyF24 },		// VK_F24
	/* 0x088 */ { kKeyNone },		// unassigned
	/* 0x089 */ { kKeyNone },		// unassigned
	/* 0x08a */ { kKeyNone },		// unassigned
	/* 0x08b */ { kKeyNone },		// unassigned
	/* 0x08c */ { kKeyNone },		// unassigned
	/* 0x08d */ { kKeyNone },		// unassigned
	/* 0x08e */ { kKeyNone },		// unassigned
	/* 0x08f */ { kKeyNone },		// unassigned
	/* 0x090 */ { kKeyNumLock },	// VK_NUMLOCK
	/* 0x091 */ { kKeyScrollLock },	// VK_SCROLL
	/* 0x092 */ { kKeyNone },		// unassigned
	/* 0x093 */ { kKeyNone },		// unassigned
	/* 0x094 */ { kKeyNone },		// unassigned
	/* 0x095 */ { kKeyNone },		// unassigned
	/* 0x096 */ { kKeyNone },		// unassigned
	/* 0x097 */ { kKeyNone },		// unassigned
	/* 0x098 */ { kKeyNone },		// unassigned
	/* 0x099 */ { kKeyNone },		// unassigned
	/* 0x09a */ { kKeyNone },		// unassigned
	/* 0x09b */ { kKeyNone },		// unassigned
	/* 0x09c */ { kKeyNone },		// unassigned
	/* 0x09d */ { kKeyNone },		// unassigned
	/* 0x09e */ { kKeyNone },		// unassigned
	/* 0x09f */ { kKeyNone },		// unassigned
	/* 0x0a0 */ { kKeyShift_L },	// VK_LSHIFT
	/* 0x0a1 */ { kKeyShift_R },	// VK_RSHIFT
	/* 0x0a2 */ { kKeyControl_L },	// VK_LCONTROL
	/* 0x0a3 */ { kKeyControl_R },	// VK_RCONTROL
	/* 0x0a4 */ { kKeyAlt_L },		// VK_LMENU
	/* 0x0a5 */ { kKeyAlt_R },		// VK_RMENU
	/* 0x0a6 */ { kKeyNone },		// VK_BROWSER_BACK
	/* 0x0a7 */ { kKeyNone },		// VK_BROWSER_FORWARD
	/* 0x0a8 */ { kKeyNone },		// VK_BROWSER_REFRESH
	/* 0x0a9 */ { kKeyNone },		// VK_BROWSER_STOP
	/* 0x0aa */ { kKeyNone },		// VK_BROWSER_SEARCH
	/* 0x0ab */ { kKeyNone },		// VK_BROWSER_FAVORITES
	/* 0x0ac */ { kKeyNone },		// VK_BROWSER_HOME
	/* 0x0ad */ { kKeyNone },		// VK_VOLUME_MUTE
	/* 0x0ae */ { kKeyNone },		// VK_VOLUME_DOWN
	/* 0x0af */ { kKeyNone },		// VK_VOLUME_UP
	/* 0x0b0 */ { kKeyNone },		// VK_MEDIA_NEXT_TRACK
	/* 0x0b1 */ { kKeyNone },		// VK_MEDIA_PREV_TRACK
	/* 0x0b2 */ { kKeyNone },		// VK_MEDIA_STOP
	/* 0x0b3 */ { kKeyNone },		// VK_MEDIA_PLAY_PAUSE
	/* 0x0b4 */ { kKeyNone },		// VK_LAUNCH_MAIL
	/* 0x0b5 */ { kKeyNone },		// VK_LAUNCH_MEDIA_SELECT
	/* 0x0b6 */ { kKeyNone },		// VK_LAUNCH_APP1
	/* 0x0b7 */ { kKeyNone },		// VK_LAUNCH_APP2
	/* 0x0b8 */ { kKeyNone },		// unassigned
	/* 0x0b9 */ { kKeyNone },		// unassigned
	/* 0x0ba */ { kKeyNone },		// OEM specific
	/* 0x0bb */ { kKeyNone },		// OEM specific
	/* 0x0bc */ { kKeyNone },		// OEM specific
	/* 0x0bd */ { kKeyNone },		// OEM specific
	/* 0x0be */ { kKeyNone },		// OEM specific
	/* 0x0bf */ { kKeyNone },		// OEM specific
	/* 0x0c0 */ { kKeyNone },		// OEM specific
	/* 0x0c1 */ { kKeyNone },		// unassigned
	/* 0x0c2 */ { kKeyNone },		// unassigned
	/* 0x0c3 */ { kKeyNone },		// unassigned
	/* 0x0c4 */ { kKeyNone },		// unassigned
	/* 0x0c5 */ { kKeyNone },		// unassigned
	/* 0x0c6 */ { kKeyNone },		// unassigned
	/* 0x0c7 */ { kKeyNone },		// unassigned
	/* 0x0c8 */ { kKeyNone },		// unassigned
	/* 0x0c9 */ { kKeyNone },		// unassigned
	/* 0x0ca */ { kKeyNone },		// unassigned
	/* 0x0cb */ { kKeyNone },		// unassigned
	/* 0x0cc */ { kKeyNone },		// unassigned
	/* 0x0cd */ { kKeyNone },		// unassigned
	/* 0x0ce */ { kKeyNone },		// unassigned
	/* 0x0cf */ { kKeyNone },		// unassigned
	/* 0x0d0 */ { kKeyNone },		// unassigned
	/* 0x0d1 */ { kKeyNone },		// unassigned
	/* 0x0d2 */ { kKeyNone },		// unassigned
	/* 0x0d3 */ { kKeyNone },		// unassigned
	/* 0x0d4 */ { kKeyNone },		// unassigned
	/* 0x0d5 */ { kKeyNone },		// unassigned
	/* 0x0d6 */ { kKeyNone },		// unassigned
	/* 0x0d7 */ { kKeyNone },		// unassigned
	/* 0x0d8 */ { kKeyNone },		// unassigned
	/* 0x0d9 */ { kKeyNone },		// unassigned
	/* 0x0da */ { kKeyNone },		// unassigned
	/* 0x0db */ { kKeyNone },		// OEM specific
	/* 0x0dc */ { kKeyNone },		// OEM specific
	/* 0x0dd */ { kKeyNone },		// OEM specific
	/* 0x0de */ { kKeyNone },		// OEM specific
	/* 0x0df */ { kKeyNone },		// OEM specific
	/* 0x0e0 */ { kKeyNone },		// OEM specific
	/* 0x0e1 */ { kKeyNone },		// OEM specific
	/* 0x0e2 */ { kKeyNone },		// OEM specific
	/* 0x0e3 */ { kKeyNone },		// OEM specific
	/* 0x0e4 */ { kKeyNone },		// OEM specific
	/* 0x0e5 */ { kKeyNone },		// unassigned
	/* 0x0e6 */ { kKeyNone },		// OEM specific
	/* 0x0e7 */ { kKeyNone },		// unassigned
	/* 0x0e8 */ { kKeyNone },		// unassigned
	/* 0x0e9 */ { kKeyNone },		// OEM specific
	/* 0x0ea */ { kKeyNone },		// OEM specific
	/* 0x0eb */ { kKeyNone },		// OEM specific
	/* 0x0ec */ { kKeyNone },		// OEM specific
	/* 0x0ed */ { kKeyNone },		// OEM specific
	/* 0x0ee */ { kKeyNone },		// OEM specific
	/* 0x0ef */ { kKeyNone },		// OEM specific
	/* 0x0f0 */ { kKeyNone },		// OEM specific
	/* 0x0f1 */ { kKeyNone },		// OEM specific
	/* 0x0f2 */ { kKeyHiraganaKatakana },	// VK_OEM_COPY
	/* 0x0f3 */ { kKeyZenkaku },	// VK_OEM_AUTO
	/* 0x0f4 */ { kKeyZenkaku },	// VK_OEM_ENLW
	/* 0x0f5 */ { kKeyNone },		// OEM specific
	/* 0x0f6 */ { kKeyNone },		// VK_ATTN			
	/* 0x0f7 */ { kKeyNone },		// VK_CRSEL			
	/* 0x0f8 */ { kKeyNone },		// VK_EXSEL			
	/* 0x0f9 */ { kKeyNone },		// VK_EREOF			
	/* 0x0fa */ { kKeyNone },		// VK_PLAY			
	/* 0x0fb */ { kKeyNone },		// VK_ZOOM			
	/* 0x0fc */ { kKeyNone },		// reserved
	/* 0x0fd */ { kKeyNone },		// VK_PA1			
	/* 0x0fe */ { kKeyNone },		// VK_OEM_CLEAR		
	/* 0x0ff */ { kKeyNone },		// reserved

	/* 0x100 */ { kKeyNone },		// reserved
	/* 0x101 */ { kKeyNone },		// VK_LBUTTON
	/* 0x102 */ { kKeyNone },		// VK_RBUTTON
	/* 0x103 */ { kKeyBreak },		// VK_CANCEL
	/* 0x104 */ { kKeyNone },		// VK_MBUTTON
	/* 0x105 */ { kKeyNone },		// VK_XBUTTON1
	/* 0x106 */ { kKeyNone },		// VK_XBUTTON2
	/* 0x107 */ { kKeyNone },		// undefined
	/* 0x108 */ { kKeyNone },		// VK_BACK
	/* 0x109 */ { kKeyNone },		// VK_TAB
	/* 0x10a */ { kKeyNone },		// undefined
	/* 0x10b */ { kKeyNone },		// undefined
	/* 0x10c */ { kKeyClear },		// VK_CLEAR
	/* 0x10d */ { kKeyKP_Enter },	// VK_RETURN
	/* 0x10e */ { kKeyNone },		// undefined
	/* 0x10f */ { kKeyNone },		// undefined
	/* 0x110 */ { kKeyShift_R },	// VK_SHIFT
	/* 0x111 */ { kKeyControl_R },	// VK_CONTROL
	/* 0x112 */ { kKeyAlt_R },		// VK_MENU
	/* 0x113 */ { kKeyNone },		// VK_PAUSE
	/* 0x114 */ { kKeyNone },		// VK_CAPITAL
	/* 0x115 */ { kKeyNone },		// VK_KANA			
	/* 0x116 */ { kKeyNone },		// VK_HANGUL		
	/* 0x117 */ { kKeyNone },		// VK_JUNJA			
	/* 0x118 */ { kKeyNone },		// VK_FINAL			
	/* 0x119 */ { kKeyNone },		// VK_KANJI			
	/* 0x11a */ { kKeyNone },		// undefined
	/* 0x11b */ { kKeyNone },		// VK_ESCAPE
	/* 0x11c */ { kKeyNone },		// VK_CONVERT		
	/* 0x11d */ { kKeyNone },		// VK_NONCONVERT	
	/* 0x11e */ { kKeyNone },		// VK_ACCEPT		
	/* 0x11f */ { kKeyNone },		// VK_MODECHANGE	
	/* 0x120 */ { kKeyNone },		// VK_SPACE
	/* 0x121 */ { kKeyPageUp },		// VK_PRIOR
	/* 0x122 */ { kKeyPageDown },	// VK_NEXT
	/* 0x123 */ { kKeyEnd },		// VK_END
	/* 0x124 */ { kKeyHome },		// VK_HOME
	/* 0x125 */ { kKeyLeft },		// VK_LEFT
	/* 0x126 */ { kKeyUp },			// VK_UP
	/* 0x127 */ { kKeyRight },		// VK_RIGHT
	/* 0x128 */ { kKeyDown },		// VK_DOWN
	/* 0x129 */ { kKeySelect },		// VK_SELECT
	/* 0x12a */ { kKeyNone },		// VK_PRINT
	/* 0x12b */ { kKeyExecute },	// VK_EXECUTE
	/* 0x12c */ { kKeyPrint },		// VK_SNAPSHOT
	/* 0x12d */ { kKeyInsert },		// VK_INSERT
	/* 0x12e */ { kKeyDelete },		// VK_DELETE
	/* 0x12f */ { kKeyHelp },		// VK_HELP
	/* 0x130 */ { kKeyNone },		// VK_0
	/* 0x131 */ { kKeyNone },		// VK_1
	/* 0x132 */ { kKeyNone },		// VK_2
	/* 0x133 */ { kKeyNone },		// VK_3
	/* 0x134 */ { kKeyNone },		// VK_4
	/* 0x135 */ { kKeyNone },		// VK_5
	/* 0x136 */ { kKeyNone },		// VK_6
	/* 0x137 */ { kKeyNone },		// VK_7
	/* 0x138 */ { kKeyNone },		// VK_8
	/* 0x139 */ { kKeyNone },		// VK_9
	/* 0x13a */ { kKeyNone },		// undefined
	/* 0x13b */ { kKeyNone },		// undefined
	/* 0x13c */ { kKeyNone },		// undefined
	/* 0x13d */ { kKeyNone },		// undefined
	/* 0x13e */ { kKeyNone },		// undefined
	/* 0x13f */ { kKeyNone },		// undefined
	/* 0x140 */ { kKeyNone },		// undefined
	/* 0x141 */ { kKeyNone },		// VK_A
	/* 0x142 */ { kKeyNone },		// VK_B
	/* 0x143 */ { kKeyNone },		// VK_C
	/* 0x144 */ { kKeyNone },		// VK_D
	/* 0x145 */ { kKeyNone },		// VK_E
	/* 0x146 */ { kKeyNone },		// VK_F
	/* 0x147 */ { kKeyNone },		// VK_G
	/* 0x148 */ { kKeyNone },		// VK_H
	/* 0x149 */ { kKeyNone },		// VK_I
	/* 0x14a */ { kKeyNone },		// VK_J
	/* 0x14b */ { kKeyNone },		// VK_K
	/* 0x14c */ { kKeyNone },		// VK_L
	/* 0x14d */ { kKeyNone },		// VK_M
	/* 0x14e */ { kKeyNone },		// VK_N
	/* 0x14f */ { kKeyNone },		// VK_O
	/* 0x150 */ { kKeyNone },		// VK_P
	/* 0x151 */ { kKeyNone },		// VK_Q
	/* 0x152 */ { kKeyNone },		// VK_R
	/* 0x153 */ { kKeyNone },		// VK_S
	/* 0x154 */ { kKeyNone },		// VK_T
	/* 0x155 */ { kKeyNone },		// VK_U
	/* 0x156 */ { kKeyNone },		// VK_V
	/* 0x157 */ { kKeyNone },		// VK_W
	/* 0x158 */ { kKeyNone },		// VK_X
	/* 0x159 */ { kKeyNone },		// VK_Y
	/* 0x15a */ { kKeyNone },		// VK_Z
	/* 0x15b */ { kKeySuper_L },	// VK_LWIN
	/* 0x15c */ { kKeySuper_R },	// VK_RWIN
	/* 0x15d */ { kKeyMenu },		// VK_APPS
	/* 0x15e */ { kKeyNone },		// undefined
	/* 0x15f */ { kKeyNone },		// VK_SLEEP
	/* 0x160 */ { kKeyNone },		// VK_NUMPAD0
	/* 0x161 */ { kKeyNone },		// VK_NUMPAD1
	/* 0x162 */ { kKeyNone },		// VK_NUMPAD2
	/* 0x163 */ { kKeyNone },		// VK_NUMPAD3
	/* 0x164 */ { kKeyNone },		// VK_NUMPAD4
	/* 0x165 */ { kKeyNone },		// VK_NUMPAD5
	/* 0x166 */ { kKeyNone },		// VK_NUMPAD6
	/* 0x167 */ { kKeyNone },		// VK_NUMPAD7
	/* 0x168 */ { kKeyNone },		// VK_NUMPAD8
	/* 0x169 */ { kKeyNone },		// VK_NUMPAD9
	/* 0x16a */ { kKeyNone },		// VK_MULTIPLY
	/* 0x16b */ { kKeyNone },		// VK_ADD
	/* 0x16c */ { kKeyKP_Separator },// VK_SEPARATOR
	/* 0x16d */ { kKeyNone },		// VK_SUBTRACT
	/* 0x16e */ { kKeyNone },		// VK_DECIMAL
	/* 0x16f */ { kKeyKP_Divide },	// VK_DIVIDE
	/* 0x170 */ { kKeyNone },		// VK_F1
	/* 0x171 */ { kKeyNone },		// VK_F2
	/* 0x172 */ { kKeyNone },		// VK_F3
	/* 0x173 */ { kKeyNone },		// VK_F4
	/* 0x174 */ { kKeyNone },		// VK_F5
	/* 0x175 */ { kKeyNone },		// VK_F6
	/* 0x176 */ { kKeyNone },		// VK_F7
	/* 0x177 */ { kKeyNone },		// VK_F8
	/* 0x178 */ { kKeyNone },		// VK_F9
	/* 0x179 */ { kKeyNone },		// VK_F10
	/* 0x17a */ { kKeyNone },		// VK_F11
	/* 0x17b */ { kKeyNone },		// VK_F12
	/* 0x17c */ { kKeyF13 },		// VK_F13
	/* 0x17d */ { kKeyF14 },		// VK_F14
	/* 0x17e */ { kKeyF15 },		// VK_F15
	/* 0x17f */ { kKeyF16 },		// VK_F16
	/* 0x180 */ { kKeyF17 },		// VK_F17
	/* 0x181 */ { kKeyF18 },		// VK_F18
	/* 0x182 */ { kKeyF19 },		// VK_F19
	/* 0x183 */ { kKeyF20 },		// VK_F20
	/* 0x184 */ { kKeyF21 },		// VK_F21
	/* 0x185 */ { kKeyF22 },		// VK_F22
	/* 0x186 */ { kKeyF23 },		// VK_F23
	/* 0x187 */ { kKeyF24 },		// VK_F24
	/* 0x188 */ { kKeyNone },		// unassigned
	/* 0x189 */ { kKeyNone },		// unassigned
	/* 0x18a */ { kKeyNone },		// unassigned
	/* 0x18b */ { kKeyNone },		// unassigned
	/* 0x18c */ { kKeyNone },		// unassigned
	/* 0x18d */ { kKeyNone },		// unassigned
	/* 0x18e */ { kKeyNone },		// unassigned
	/* 0x18f */ { kKeyNone },		// unassigned
	/* 0x190 */ { kKeyNumLock },	// VK_NUMLOCK
	/* 0x191 */ { kKeyNone },		// VK_SCROLL
	/* 0x192 */ { kKeyNone },		// unassigned
	/* 0x193 */ { kKeyNone },		// unassigned
	/* 0x194 */ { kKeyNone },		// unassigned
	/* 0x195 */ { kKeyNone },		// unassigned
	/* 0x196 */ { kKeyNone },		// unassigned
	/* 0x197 */ { kKeyNone },		// unassigned
	/* 0x198 */ { kKeyNone },		// unassigned
	/* 0x199 */ { kKeyNone },		// unassigned
	/* 0x19a */ { kKeyNone },		// unassigned
	/* 0x19b */ { kKeyNone },		// unassigned
	/* 0x19c */ { kKeyNone },		// unassigned
	/* 0x19d */ { kKeyNone },		// unassigned
	/* 0x19e */ { kKeyNone },		// unassigned
	/* 0x19f */ { kKeyNone },		// unassigned
	/* 0x1a0 */ { kKeyShift_L },	// VK_LSHIFT
	/* 0x1a1 */ { kKeyShift_R },	// VK_RSHIFT
	/* 0x1a2 */ { kKeyControl_L },	// VK_LCONTROL
	/* 0x1a3 */ { kKeyControl_R },	// VK_RCONTROL
	/* 0x1a4 */ { kKeyAlt_L },		// VK_LMENU
	/* 0x1a5 */ { kKeyAlt_R },		// VK_RMENU
	/* 0x1a6 */ { kKeyWWWBack },	// VK_BROWSER_BACK
	/* 0x1a7 */ { kKeyWWWForward },	// VK_BROWSER_FORWARD
	/* 0x1a8 */ { kKeyWWWRefresh },	// VK_BROWSER_REFRESH
	/* 0x1a9 */ { kKeyWWWStop },	// VK_BROWSER_STOP
	/* 0x1aa */ { kKeyWWWSearch },	// VK_BROWSER_SEARCH
	/* 0x1ab */ { kKeyWWWFavorites },// VK_BROWSER_FAVORITES
	/* 0x1ac */ { kKeyWWWHome },	// VK_BROWSER_HOME
	/* 0x1ad */ { kKeyAudioMute },	// VK_VOLUME_MUTE
	/* 0x1ae */ { kKeyAudioDown },	// VK_VOLUME_DOWN
	/* 0x1af */ { kKeyAudioUp },	// VK_VOLUME_UP
	/* 0x1b0 */ { kKeyAudioNext },	// VK_MEDIA_NEXT_TRACK
	/* 0x1b1 */ { kKeyAudioPrev },	// VK_MEDIA_PREV_TRACK
	/* 0x1b2 */ { kKeyAudioStop },	// VK_MEDIA_STOP
	/* 0x1b3 */ { kKeyAudioPlay },	// VK_MEDIA_PLAY_PAUSE
	/* 0x1b4 */ { kKeyAppMail },	// VK_LAUNCH_MAIL
	/* 0x1b5 */ { kKeyAppMedia },	// VK_LAUNCH_MEDIA_SELECT
	/* 0x1b6 */ { kKeyAppUser1 },	// VK_LAUNCH_APP1
	/* 0x1b7 */ { kKeyAppUser2 },	// VK_LAUNCH_APP2
	/* 0x1b8 */ { kKeyNone },		// unassigned
	/* 0x1b9 */ { kKeyNone },		// unassigned
	/* 0x1ba */ { kKeyNone },		// OEM specific
	/* 0x1bb */ { kKeyNone },		// OEM specific
	/* 0x1bc */ { kKeyNone },		// OEM specific
	/* 0x1bd */ { kKeyNone },		// OEM specific
	/* 0x1be */ { kKeyNone },		// OEM specific
	/* 0x1bf */ { kKeyNone },		// OEM specific
	/* 0x1c0 */ { kKeyNone },		// OEM specific
	/* 0x1c1 */ { kKeyNone },		// unassigned
	/* 0x1c2 */ { kKeyNone },		// unassigned
	/* 0x1c3 */ { kKeyNone },		// unassigned
	/* 0x1c4 */ { kKeyNone },		// unassigned
	/* 0x1c5 */ { kKeyNone },		// unassigned
	/* 0x1c6 */ { kKeyNone },		// unassigned
	/* 0x1c7 */ { kKeyNone },		// unassigned
	/* 0x1c8 */ { kKeyNone },		// unassigned
	/* 0x1c9 */ { kKeyNone },		// unassigned
	/* 0x1ca */ { kKeyNone },		// unassigned
	/* 0x1cb */ { kKeyNone },		// unassigned
	/* 0x1cc */ { kKeyNone },		// unassigned
	/* 0x1cd */ { kKeyNone },		// unassigned
	/* 0x1ce */ { kKeyNone },		// unassigned
	/* 0x1cf */ { kKeyNone },		// unassigned
	/* 0x1d0 */ { kKeyNone },		// unassigned
	/* 0x1d1 */ { kKeyNone },		// unassigned
	/* 0x1d2 */ { kKeyNone },		// unassigned
	/* 0x1d3 */ { kKeyNone },		// unassigned
	/* 0x1d4 */ { kKeyNone },		// unassigned
	/* 0x1d5 */ { kKeyNone },		// unassigned
	/* 0x1d6 */ { kKeyNone },		// unassigned
	/* 0x1d7 */ { kKeyNone },		// unassigned
	/* 0x1d8 */ { kKeyNone },		// unassigned
	/* 0x1d9 */ { kKeyNone },		// unassigned
	/* 0x1da */ { kKeyNone },		// unassigned
	/* 0x1db */ { kKeyNone },		// OEM specific
	/* 0x1dc */ { kKeyNone },		// OEM specific
	/* 0x1dd */ { kKeyNone },		// OEM specific
	/* 0x1de */ { kKeyNone },		// OEM specific
	/* 0x1df */ { kKeyNone },		// OEM specific
	/* 0x1e0 */ { kKeyNone },		// OEM specific
	/* 0x1e1 */ { kKeyNone },		// OEM specific
	/* 0x1e2 */ { kKeyNone },		// OEM specific
	/* 0x1e3 */ { kKeyNone },		// OEM specific
	/* 0x1e4 */ { kKeyNone },		// OEM specific
	/* 0x1e5 */ { kKeyNone },		// unassigned
	/* 0x1e6 */ { kKeyNone },		// OEM specific
	/* 0x1e7 */ { kKeyNone },		// unassigned
	/* 0x1e8 */ { kKeyNone },		// unassigned
	/* 0x1e9 */ { kKeyNone },		// OEM specific
	/* 0x1ea */ { kKeyNone },		// OEM specific
	/* 0x1eb */ { kKeyNone },		// OEM specific
	/* 0x1ec */ { kKeyNone },		// OEM specific
	/* 0x1ed */ { kKeyNone },		// OEM specific
	/* 0x1ee */ { kKeyNone },		// OEM specific
	/* 0x1ef */ { kKeyNone },		// OEM specific
	/* 0x1f0 */ { kKeyNone },		// OEM specific
	/* 0x1f1 */ { kKeyNone },		// OEM specific
	/* 0x1f2 */ { kKeyNone },		// VK_OEM_COPY
	/* 0x1f3 */ { kKeyNone },		// VK_OEM_AUTO
	/* 0x1f4 */ { kKeyNone },		// VK_OEM_ENLW
	/* 0x1f5 */ { kKeyNone },		// OEM specific
	/* 0x1f6 */ { kKeyNone },		// VK_ATTN			
	/* 0x1f7 */ { kKeyNone },		// VK_CRSEL			
	/* 0x1f8 */ { kKeyNone },		// VK_EXSEL			
	/* 0x1f9 */ { kKeyNone },		// VK_EREOF			
	/* 0x1fa */ { kKeyNone },		// VK_PLAY			
	/* 0x1fb */ { kKeyNone },		// VK_ZOOM			
	/* 0x1fc */ { kKeyNone },		// reserved
	/* 0x1fd */ { kKeyNone },		// VK_PA1			
	/* 0x1fe */ { kKeyNone },		// VK_OEM_CLEAR		
	/* 0x1ff */ { kKeyNone }		// reserved
};

struct CWin32Modifiers {
public:
	UINT				m_vk;
	KeyModifierMask		m_mask;
};

static const CWin32Modifiers s_modifiers[] =
{
	{ VK_SHIFT,    KeyModifierShift   },
	{ VK_LSHIFT,   KeyModifierShift   },
	{ VK_RSHIFT,   KeyModifierShift   },
	{ VK_CONTROL,  KeyModifierControl },
	{ VK_LCONTROL, KeyModifierControl },
	{ VK_RCONTROL, KeyModifierControl },
	{ VK_MENU,     KeyModifierAlt     },
	{ VK_LMENU,    KeyModifierAlt     },
	{ VK_RMENU,    KeyModifierAlt     },
	{ VK_LWIN,     KeyModifierSuper   },
	{ VK_RWIN,     KeyModifierSuper   }
};

CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks,
				void* eventTarget) :
	m_is95Family(CArchMiscWindows::isWindows95Family()),
	m_eventTarget(eventTarget),
	m_desks(desks),
	m_keyLayout(GetKeyboardLayout(0)),
	m_fixTimer(NULL),
	m_lastDown(0),
	m_useSavedModifiers(false),
	m_savedModifiers(0),
	m_originalSavedModifiers(0)
{
	// look up symbol that's available on winNT family but not win95
	HMODULE userModule = GetModuleHandle("user32.dll");
	m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx");
}

CMSWindowsKeyState::~CMSWindowsKeyState()
{
	disable();
}

void
CMSWindowsKeyState::disable()
{
	if (m_fixTimer != NULL) {
		EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
		EVENTQUEUE->deleteTimer(m_fixTimer);
		m_fixTimer = NULL;
	}
	m_lastDown = 0;
}

KeyButton
CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const
{
	return m_virtualKeyToButton[virtualKey & 0xffu];
}

void
CMSWindowsKeyState::setKeyLayout(HKL keyLayout)
{
	m_keyLayout = keyLayout;
}

bool
CMSWindowsKeyState::testAutoRepeat(bool press, bool isRepeat, KeyButton button)
{
	if (!isRepeat) {
		isRepeat = (press && m_lastDown != 0 && button == m_lastDown);
	}
	if (press) {
		m_lastDown = button;
	}
	else {
		m_lastDown = 0;
	}
	return isRepeat;
}

void
CMSWindowsKeyState::saveModifiers()
{
	m_savedModifiers         = getActiveModifiers();
	m_originalSavedModifiers = m_savedModifiers;
}

void
CMSWindowsKeyState::useSavedModifiers(bool enable)
{
	if (enable != m_useSavedModifiers) {
		m_useSavedModifiers = enable;
		if (!m_useSavedModifiers) {
			// transfer any modifier state changes to CKeyState's state
			KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers;
			getActiveModifiersRValue() =
				(getActiveModifiers() & ~mask) | (m_savedModifiers & mask);
		}
	}
}

KeyID
CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey,
				LPARAM info, KeyModifierMask* maskOut) const
{
	static const KeyModifierMask s_controlAlt =
		KeyModifierControl | KeyModifierAlt;

	// extract character, virtual key, and if we didn't use AltGr
	char c       = (char)((charAndVirtKey & 0xff00u) >> 8);
	UINT vkCode  = (charAndVirtKey & 0xffu);
	bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0);

	// handle some keys via table lookup
	KeyID id     = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu));

	// 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 modifier mask
	if (maskOut != NULL) {
		KeyModifierMask active = getActiveModifiers();
		if (!noAltGr && (active & s_controlAlt) == s_controlAlt) {
			// if !noAltGr then we're only interested in matching the
			// key, not the AltGr.  AltGr is down (i.e. control and alt
			// are down) but we don't want the client to have to match
			// that so we clear it.
			active &= ~s_controlAlt;
		}
		*maskOut = active;
	}

	return id;
}

bool
CMSWindowsKeyState::didGroupsChange() const
{
	GroupList groups;
	return (getGroups(groups) && groups != m_groups);
}

UINT
CMSWindowsKeyState::mapKeyToVirtualKey(KeyID key) const
{
	if (key == kKeyNone) {
		return 0;
	}
	KeyToVKMap::const_iterator i = m_keyToVKMap.find(key);
	if (i == m_keyToVKMap.end()) {
		return 0;
	}
	else {
		return i->second;
	}
}

void
CMSWindowsKeyState::onKey(KeyButton button, bool down, KeyModifierMask newState)
{
	// handle win32 brokenness and forward to superclass
	fixKeys();
	CKeyState::onKey(button, down, newState);
	fixKeys();
}

void
CMSWindowsKeyState::sendKeyEvent(void* target,
							bool press, bool isAutoRepeat,
							KeyID key, KeyModifierMask mask,
							SInt32 count, KeyButton button)
{
	if (press || isAutoRepeat) {
		// send key
		if (press && !isAutoRepeat) {
			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);
		}
	}
	else {
		// do key up
		CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
	}
}

void
CMSWindowsKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask,
				KeyButton button)
{
	CKeyState::fakeKeyDown(id, mask, button);
}

void
CMSWindowsKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
				SInt32 count, KeyButton button)
{
	CKeyState::fakeKeyRepeat(id, mask, count, 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,
							virtualKeyToButton(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()));
	}
}

KeyModifierMask
CMSWindowsKeyState::pollActiveModifiers() const
{
	KeyModifierMask state = 0;

	// get non-toggle modifiers from our own shadow key state
	for (size_t i = 0; i < sizeof(s_modifiers) / sizeof(s_modifiers[0]); ++i) {
		KeyButton button = virtualKeyToButton(s_modifiers[i].m_vk);
		if (button != 0 && isKeyDown(button)) {
			state |= s_modifiers[i].m_mask;
		}
	}

	// we can get toggle modifiers from the system
	if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) {
		state |= KeyModifierCapsLock;
	}
	if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) {
		state |= KeyModifierNumLock;
	}
	if ((GetKeyState(VK_SCROLL) & 0x01) != 0) {
		state |= KeyModifierScrollLock;
	}

	return state;
}

SInt32
CMSWindowsKeyState::pollActiveGroup() const
{
	// determine the thread that'll receive this event
	HWND  targetWindow = GetForegroundWindow();
	DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL);

	// get keyboard layout for the thread
	HKL hkl            = GetKeyboardLayout(targetThread);

	if (!hkl) {
		// GetKeyboardLayout failed. Maybe targetWindow is a console window.
		// We're getting the keyboard layout of the desktop instead.
		targetWindow = GetDesktopWindow();
		targetThread = GetWindowThreadProcessId(targetWindow, NULL);
		hkl          = GetKeyboardLayout(targetThread);
	}

	// get group
	GroupMap::const_iterator i = m_groupMap.find(hkl);
	if (i == m_groupMap.end()) {
		LOG((CLOG_DEBUG1 "can't find keyboard layout %08x", hkl));
		return 0;
	}

	return i->second;
}

void
CMSWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
{
	BYTE keyState[256];
	if (!GetKeyboardState(keyState)) {
		LOG((CLOG_ERR "GetKeyboardState returned false on pollPressedKeys"));
		return;
	}
	for (KeyButton i = 1; i < 256; ++i) {
		if ((keyState[i] & 0x80) != 0) {
			pressedKeys.insert(i);
		}
	}
}

void
CMSWindowsKeyState::getKeyMap(CKeyMap& keyMap)
{
	// update keyboard groups
	if (getGroups(m_groups)) {
		m_groupMap.clear();
		SInt32 numGroups = (SInt32)m_groups.size();
		for (SInt32 g = 0; g < numGroups; ++g) {
			m_groupMap[m_groups[g]] = g;
		}
	}
	HKL activeLayout = GetKeyboardLayout(0);

	// clear table
	memset(m_virtualKeyToButton, 0, sizeof(m_virtualKeyToButton));
	m_keyToVKMap.clear();

	CKeyMap::KeyItem item;
	SInt32 numGroups = (SInt32)m_groups.size();
	for (SInt32 g = 0; g < numGroups; ++g) {
		item.m_group = g;
		ActivateKeyboardLayout(m_groups[g], 0);

		// clear tables
		memset(m_buttonToVK, 0, sizeof(m_buttonToVK));
		memset(m_buttonToNumpadVK, 0, sizeof(m_buttonToNumpadVK));

		// map buttons (scancodes) to virtual keys
		for (KeyButton i = 1; i < 256; ++i) {
			UINT vk = MapVirtualKey(i, 1);
			if (vk == 0) {
				// unmapped
				continue;
			}

			// deal with certain virtual keys specially
			switch (vk) {
			case VK_SHIFT:
				if (MapVirtualKey(VK_RSHIFT, 0) == i) {
					vk = VK_RSHIFT;
				}
				else {
					vk = VK_LSHIFT;
				}
				break;

			case VK_CONTROL:
				vk = VK_LCONTROL;
				break;

			case VK_MENU:
				vk = VK_LMENU;
				break;

			case VK_NUMLOCK:
				vk = VK_PAUSE;
				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_DECIMAL:
				// numpad keys are saved in their own table
				m_buttonToNumpadVK[i] = vk;
				continue;

			case VK_LWIN:
			case VK_RWIN:
				// add extended key only for these on 95 family
				if (m_is95Family) {
					m_buttonToVK[i | 0x100u] = vk;
					continue;
				}
				break;

			case VK_RETURN:
			case VK_PRIOR:
			case VK_NEXT:
			case VK_END:
			case VK_HOME:
			case VK_LEFT:
			case VK_UP:
			case VK_RIGHT:
			case VK_DOWN:
			case VK_INSERT:
			case VK_DELETE:
				// also add extended key for these
				m_buttonToVK[i | 0x100u] = vk;
				break;
			}

			if (m_buttonToVK[i] == 0) {
				m_buttonToVK[i] = vk;
			}
		}

		// now map virtual keys to buttons.  multiple virtual keys may map
		// to a single button.  if the virtual key matches the one in
		// m_buttonToVK then we use the button as is.  if not then it's
		// either a numpad key and we use the button as is or it's an
		// extended button.
		for (UINT i = 1; i < 255; ++i) {
			// skip virtual keys we don't want
			switch (i) {
			case VK_LBUTTON:
			case VK_RBUTTON:
			case VK_MBUTTON:
			case VK_XBUTTON1:
			case VK_XBUTTON2:
			case VK_SHIFT:
			case VK_CONTROL:
			case VK_MENU:
				continue;
			}

			// get the button
			KeyButton button = static_cast<KeyButton>(MapVirtualKey(i, 0));
			if (button == 0) {
				continue;
			}

			// deal with certain virtual keys specially
			switch (i) {
			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_DECIMAL:
				m_buttonToNumpadVK[button] = i;
				break;

			default:
				// add extended key if virtual keys don't match
				if (m_buttonToVK[button] != i) {
					m_buttonToVK[button | 0x100u] = i;
				}
				break;
			}
		}

		// add alt+printscreen
		if (m_buttonToVK[0x54u] == 0) {
			m_buttonToVK[0x54u] = VK_SNAPSHOT;
		}

		// set virtual key to button table
		if (GetKeyboardLayout(0) == m_groups[g]) {
			for (KeyButton i = 0; i < 512; ++i) {
				if (m_buttonToVK[i] != 0) {
					if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) {
						m_virtualKeyToButton[m_buttonToVK[i]] = i;
					}
				}
				if (m_buttonToNumpadVK[i] != 0) {
					if (m_virtualKeyToButton[m_buttonToNumpadVK[i]] == 0) {
						m_virtualKeyToButton[m_buttonToNumpadVK[i]] = i;
					}
				}
			}
		}

		// add numpad keys
		for (KeyButton i = 0; i < 512; ++i) {
			if (m_buttonToNumpadVK[i] != 0) {
				item.m_id        = getKeyID(m_buttonToNumpadVK[i], i);
				item.m_button    = i;
				item.m_required  = KeyModifierNumLock;
				item.m_sensitive = KeyModifierNumLock | KeyModifierShift;
				item.m_generates = 0;
				item.m_client    = m_buttonToNumpadVK[i];
				addKeyEntry(keyMap, item);
			}
		}

		// add other keys
		BYTE keys[256];
		memset(keys, 0, sizeof(keys));
		for (KeyButton i = 0; i < 512; ++i) {
			if (m_buttonToVK[i] != 0) {
				// initialize item
				item.m_id        = getKeyID(m_buttonToVK[i], i);
				item.m_button    = i;
				item.m_required  = 0;
				item.m_sensitive = 0;
				item.m_client    = m_buttonToVK[i];

				// get flags for modifier keys
				CKeyMap::initModifierKey(item);

				if (item.m_id == 0) {
					// translate virtual key to a character with and without
					// shift, caps lock, and AltGr.
					struct Modifier {
						UINT			m_vk1;
						UINT			m_vk2;
						BYTE			m_state;
						KeyModifierMask	m_mask;
					};
					static const Modifier modifiers[] = {
						{ VK_SHIFT,   VK_SHIFT,   0x80u, KeyModifierShift    },
						{ VK_CAPITAL, VK_CAPITAL, 0x01u, KeyModifierCapsLock },
						{ VK_CONTROL, VK_MENU,    0x80u, KeyModifierControl |
														 KeyModifierAlt      }
					};
					static const size_t s_numModifiers =
						sizeof(modifiers) / sizeof(modifiers[0]);
					static const size_t s_numCombinations = 1 << s_numModifiers;
					KeyID id[s_numCombinations];

					bool anyFound = false;
					KeyButton button = static_cast<KeyButton>(i & 0xffu);
					for (size_t j = 0; j < s_numCombinations; ++j) {
						for (size_t k = 0; k < s_numModifiers; ++k) {
							//if ((j & (1 << k)) != 0) {
							// http://msdn.microsoft.com/en-us/library/ke55d167.aspx
							if ((j & (1i64 << k)) != 0) {
								keys[modifiers[k].m_vk1] = modifiers[k].m_state;
								keys[modifiers[k].m_vk2] = modifiers[k].m_state;
							}
							else {
								keys[modifiers[k].m_vk1] = 0;
								keys[modifiers[k].m_vk2] = 0;
							}
						}
						id[j] = getIDForKey(item, button,
										m_buttonToVK[i], keys, m_groups[g]);
						if (id[j] != 0) {
							anyFound = true;
						}
					}

					if (anyFound) {
						// determine what modifiers we're sensitive to.
						// we're sensitive if the KeyID changes when the
						// modifier does.
						item.m_sensitive = 0;
						for (size_t k = 0; k < s_numModifiers; ++k) {
							for (size_t j = 0; j < s_numCombinations; ++j) {
								//if (id[j] != id[j ^ (1u << k)]) {
								// http://msdn.microsoft.com/en-us/library/ke55d167.aspx
								if (id[j] != id[j ^ (1ui64 << k)]) {
									item.m_sensitive |= modifiers[k].m_mask;
									break;
								}
							}
						}
						
						// save each key.  the map will automatically discard
						// duplicates, like an unshift and shifted version of
						// a key that's insensitive to shift.
						for (size_t j = 0; j < s_numCombinations; ++j) {
							item.m_id       = id[j];
							item.m_required = 0;
							for (size_t k = 0; k < s_numModifiers; ++k) {
								if ((j & (1i64 << k)) != 0) {
									item.m_required |= modifiers[k].m_mask;
								}
							}
							addKeyEntry(keyMap, item);
						}
					}
				}
				else {
					// found in table
					switch (m_buttonToVK[i]) {
					case VK_TAB:
						// add kKeyLeftTab, too
						item.m_id         = kKeyLeftTab;
						item.m_required  |= KeyModifierShift;
						item.m_sensitive |= KeyModifierShift;
						addKeyEntry(keyMap, item);
						item.m_id         = kKeyTab;
						item.m_required  &= ~KeyModifierShift;
						break;

					case VK_CANCEL:
						item.m_required  |= KeyModifierControl;
						item.m_sensitive |= KeyModifierControl;
						break;

					case VK_SNAPSHOT:
						item.m_sensitive |= KeyModifierAlt;
						if ((i & 0x100u) == 0) {
							// non-extended snapshot key requires alt
							item.m_required |= KeyModifierAlt;
						}
						break;
					}
					addKeyEntry(keyMap, item);
				}
			}
		}
	}

	// restore keyboard layout
	ActivateKeyboardLayout(activeLayout, 0);
}

void
CMSWindowsKeyState::fakeKey(const Keystroke& keystroke)
{
	switch (keystroke.m_type) {
	case Keystroke::kButton: {
		LOG((CLOG_DEBUG1 "  %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
		KeyButton button = keystroke.m_data.m_button.m_button;

		// windows doesn't send key ups for key repeats
		if (keystroke.m_data.m_button.m_repeat &&
			!keystroke.m_data.m_button.m_press) {
			LOG((CLOG_DEBUG1 "  discard key repeat release"));
			break;
		}

		// get the virtual key for the button
		UINT vk = keystroke.m_data.m_button.m_client;

		// special handling of VK_SNAPSHOT
		if (vk == VK_SNAPSHOT) {
			if ((getActiveModifiers() & KeyModifierAlt) != 0) {
				// snapshot active window
				button = 1;
			}
			else {
				// snapshot full screen
				button = 0;
			}
		}

		// synthesize event
		m_desks->fakeKeyEvent(button, vk,
								keystroke.m_data.m_button.m_press,
								keystroke.m_data.m_button.m_repeat);
		break;
	}

	case Keystroke::kGroup:
		// we don't restore the group.  we'd like to but we can't be
		// sure the restoring group change will be processed after the
		// key events.
		if (!keystroke.m_data.m_group.m_restore) {
			if (keystroke.m_data.m_group.m_absolute) {
				LOG((CLOG_DEBUG1 "  group %d", keystroke.m_data.m_group.m_group));
				setWindowGroup(keystroke.m_data.m_group.m_group);
			}
			else {
				LOG((CLOG_DEBUG1 "  group %+d", keystroke.m_data.m_group.m_group));
				setWindowGroup(getEffectiveGroup(pollActiveGroup(),
									keystroke.m_data.m_group.m_group));
			}
		}
		break;
	}
}

KeyModifierMask&
CMSWindowsKeyState::getActiveModifiersRValue()
{
	if (m_useSavedModifiers) {
		return m_savedModifiers;
	}
	else {
		return CKeyState::getActiveModifiersRValue();
	}
}

bool
CMSWindowsKeyState::getGroups(GroupList& groups) const
{
	// get keyboard layouts
	UInt32 newNumLayouts = GetKeyboardLayoutList(0, NULL);
	if (newNumLayouts == 0) {
		LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
		return false;
	}
	HKL* newLayouts = new HKL[newNumLayouts];
	newNumLayouts = GetKeyboardLayoutList(newNumLayouts, newLayouts);
	if (newNumLayouts == 0) {
		LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
		delete[] newLayouts;
		return false;
	}

	groups.clear();
	groups.insert(groups.end(), newLayouts, newLayouts + newNumLayouts);
	delete[] newLayouts;
	return true;
}

void
CMSWindowsKeyState::setWindowGroup(SInt32 group)
{
	HWND targetWindow = GetForegroundWindow();

	bool sysCharSet = true;
	// XXX -- determine if m_groups[group] can be used with the system
	// character set.

	PostMessage(targetWindow, WM_INPUTLANGCHANGEREQUEST,
								sysCharSet ? 1 : 0, (LPARAM)m_groups[group]);

	// XXX -- use a short delay to let the target window process the message
	// before it sees the keyboard events.  i'm not sure why this is
	// necessary since the messages should arrive in order.  if we don't
	// delay, though, some of our keyboard events may disappear.
	Sleep(100);
}

void
CMSWindowsKeyState::fixKeys()
{
	// fake key releases for the windows keys if we think they're
	// down but they're really up.  we have to do this because if the
	// user presses and releases a windows key without pressing any
	// other key while it's down then the system will eat the key
	// release.  if we don't detect that and synthesize the release
	// then the client won't take the usual windows key release action
	// (which on windows is to show the start menu).
	//
	// only check on the windows 95 family since the NT family reports
	// the key releases as usual.
	if (!m_is95Family) {
		return;
	}

	KeyButton leftButton  = virtualKeyToButton(VK_LWIN);
	KeyButton rightButton = virtualKeyToButton(VK_RWIN);
	bool leftDown         = isKeyDown(leftButton);
	bool rightDown        = isKeyDown(rightButton);
	bool fix              = (leftDown || rightDown);
	if (fix) {
		// check if either button is not really down
		bool leftAsyncDown  = ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0);
		bool rightAsyncDown = ((GetAsyncKeyState(VK_RWIN) & 0x8000) != 0);

		if (leftAsyncDown != leftDown || rightAsyncDown != rightDown) {
			KeyModifierMask state = getActiveModifiers();
			if (!leftAsyncDown && !rightAsyncDown) {
				// no win keys are down so remove super modifier
				state &= ~KeyModifierSuper;
			}

			// report up events
			if (leftDown  && !leftAsyncDown) {
				LOG((CLOG_DEBUG1 "event: fake key release left windows key (0x%03x)", leftButton));
				CKeyState::onKey(leftButton, false, state);
				CKeyState::sendKeyEvent(m_eventTarget, false, false,
								kKeySuper_L, state, 1, leftButton);
			}
			if (rightDown && !rightAsyncDown) {
				LOG((CLOG_DEBUG1 "event: fake key release right windows key (0x%03x)", rightButton));
				CKeyState::onKey(rightButton, false, state);
				CKeyState::sendKeyEvent(m_eventTarget, false, false,
								kKeySuper_R, state, 1, rightButton);
			}
		}
	}

	if (fix && m_fixTimer == NULL) {
		// schedule check
		m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL);
		EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer,
							new TMethodEventJob<CMSWindowsKeyState>(
								this, &CMSWindowsKeyState::handleFixKeys));
	}
	else if (!fix && m_fixTimer != NULL) {
		// remove scheduled check
		EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
		EVENTQUEUE->deleteTimer(m_fixTimer);
		m_fixTimer = NULL;
	}
}

void
CMSWindowsKeyState::handleFixKeys(const CEvent&, void*)
{
	fixKeys();
}

KeyID
CMSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button)
{
	if ((button & 0x100u) != 0) {
		virtualKey += 0x100u;
	}
	return s_virtualKey[virtualKey];
}

KeyID
CMSWindowsKeyState::getIDForKey(CKeyMap::KeyItem& item,
				KeyButton button, UINT virtualKey,
				PBYTE keyState, HKL hkl) const
{
	int n;
	KeyID id;
	if (m_is95Family) {
		// XXX -- how do we get characters not in Latin-1?
		WORD ascii;
		n  = ToAsciiEx(virtualKey, button, keyState, &ascii, 0, hkl);
		id = static_cast<KeyID>(ascii & 0xffu);
	}
	else {
		WCHAR unicode[2];
		n  = m_ToUnicodeEx(virtualKey, button, keyState,
								unicode, sizeof(unicode) / sizeof(unicode[0]),
								0, hkl);
		id = static_cast<KeyID>(unicode[0]);
	}
	switch (n) {
	case -1:
		return CKeyMap::getDeadKey(id);

	default:
	case 0:
		// unmapped
		return kKeyNone;

	case 1:
		return id;

	case 2:
		// left over dead key in buffer;  oops.
		return getIDForKey(item, button, virtualKey, keyState, hkl);
	}
}

void
CMSWindowsKeyState::addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item)
{
	keyMap.addKeyEntry(item);
	if (item.m_group == 0) {
		m_keyToVKMap[item.m_id] = static_cast<UINT>(item.m_client);
	}
}