winkeybd.c revision 35c4bbdf
1/* 2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 *"Software"), to deal in the Software without restriction, including 7 *without limitation the rights to use, copy, modify, merge, publish, 8 *distribute, sublicense, and/or sell copies of the Software, and to 9 *permit persons to whom the Software is furnished to do so, subject to 10 *the following conditions: 11 * 12 *The above copyright notice and this permission notice shall be 13 *included in all copies or substantial portions of the Software. 14 * 15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR 19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 *Except as contained in this notice, the name of the XFree86 Project 24 *shall not be used in advertising or otherwise to promote the sale, use 25 *or other dealings in this Software without prior written authorization 26 *from the XFree86 Project. 27 * 28 * Authors: Dakshinamurthy Karra 29 * Suhaib M Siddiqi 30 * Peter Busch 31 * Harold L Hunt II 32 */ 33 34#ifdef HAVE_XWIN_CONFIG_H 35#include <xwin-config.h> 36#endif 37#include "win.h" 38#include "winkeybd.h" 39#include "winconfig.h" 40#include "winmsg.h" 41 42#include "xkbsrv.h" 43 44/* C does not have a logical XOR operator, so we use a macro instead */ 45#define LOGICAL_XOR(a,b) ((!(a) && (b)) || ((a) && !(b))) 46 47static Bool g_winKeyState[NUM_KEYCODES]; 48 49/* 50 * Local prototypes 51 */ 52 53static void 54 winKeybdBell(int iPercent, DeviceIntPtr pDeviceInt, void *pCtrl, int iClass); 55 56static void 57 winKeybdCtrl(DeviceIntPtr pDevice, KeybdCtrl * pCtrl); 58 59/* 60 * Translate a Windows WM_[SYS]KEY(UP/DOWN) message 61 * into an ASCII scan code. 62 * 63 * We do this ourselves, rather than letting Windows handle it, 64 * because Windows tends to munge the handling of special keys, 65 * like AltGr on European keyboards. 66 */ 67 68int 69winTranslateKey(WPARAM wParam, LPARAM lParam) 70{ 71 int iKeyFixup = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 1]; 72 int iKeyFixupEx = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 2]; 73 int iParam = HIWORD(lParam); 74 int iParamScanCode = LOBYTE(iParam); 75 int iScanCode; 76 77 winDebug("winTranslateKey: wParam %08x lParam %08x\n", (int)wParam, (int)lParam); 78 79/* WM_ key messages faked by Vista speech recognition (WSR) don't have a 80 * scan code. 81 * 82 * Vocola 3 (Rick Mohr's supplement to WSR) uses 83 * System.Windows.Forms.SendKeys.SendWait(), which appears always to give a 84 * scan code of 1 85 */ 86 if (iParamScanCode <= 1) { 87 if (VK_PRIOR <= wParam && wParam <= VK_DOWN) 88 /* Trigger special case table to translate to extended 89 * keycode, otherwise if num_lock is on, we can get keypad 90 * numbers instead of navigation keys. */ 91 iParam |= KF_EXTENDED; 92 else 93 iParamScanCode = MapVirtualKeyEx(wParam, 94 /*MAPVK_VK_TO_VSC */ 0, 95 GetKeyboardLayout(0)); 96 } 97 98 /* Branch on special extended, special non-extended, or normal key */ 99 if ((iParam & KF_EXTENDED) && iKeyFixupEx) 100 iScanCode = iKeyFixupEx; 101 else if (iKeyFixup) 102 iScanCode = iKeyFixup; 103 else if (wParam == 0 && iParamScanCode == 0x70) 104 iScanCode = KEY_HKTG; 105 else 106 switch (iParamScanCode) { 107 case 0x70: 108 iScanCode = KEY_HKTG; 109 break; 110 case 0x73: 111 iScanCode = KEY_BSlash2; 112 break; 113 default: 114 iScanCode = iParamScanCode; 115 break; 116 } 117 118 return iScanCode; 119} 120 121/* Ring the keyboard bell (system speaker on PCs) */ 122static void 123winKeybdBell(int iPercent, DeviceIntPtr pDeviceInt, void *pCtrl, int iClass) 124{ 125 /* 126 * We can't use Beep () here because it uses the PC speaker 127 * on NT/2000. MessageBeep (MB_OK) will play the default system 128 * sound on systems with a sound card or it will beep the PC speaker 129 * on systems that do not have a sound card. 130 */ 131 if (iPercent > 0) MessageBeep(MB_OK); 132} 133 134/* Change some keyboard configuration parameters */ 135static void 136winKeybdCtrl(DeviceIntPtr pDevice, KeybdCtrl * pCtrl) 137{ 138} 139 140/* 141 * See Porting Layer Definition - p. 18 142 * winKeybdProc is known as a DeviceProc. 143 */ 144 145int 146winKeybdProc(DeviceIntPtr pDeviceInt, int iState) 147{ 148 DevicePtr pDevice = (DevicePtr) pDeviceInt; 149 XkbSrvInfoPtr xkbi; 150 XkbControlsPtr ctrl; 151 152 switch (iState) { 153 case DEVICE_INIT: 154 winConfigKeyboard(pDeviceInt); 155 156 /* FIXME: Maybe we should use winGetKbdLeds () here? */ 157 defaultKeyboardControl.leds = g_winInfo.keyboard.leds; 158 159 winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\"" 160 " Variant = \"%s\" Options = \"%s\"\n", 161 g_winInfo.xkb.rules ? g_winInfo.xkb.rules : "none", 162 g_winInfo.xkb.model ? g_winInfo.xkb.model : "none", 163 g_winInfo.xkb.layout ? g_winInfo.xkb.layout : "none", 164 g_winInfo.xkb.variant ? g_winInfo.xkb.variant : "none", 165 g_winInfo.xkb.options ? g_winInfo.xkb.options : "none"); 166 167 InitKeyboardDeviceStruct(pDeviceInt, 168 &g_winInfo.xkb, winKeybdBell, winKeybdCtrl); 169 170 xkbi = pDeviceInt->key->xkbInfo; 171 if ((xkbi != NULL) && (xkbi->desc != NULL)) { 172 ctrl = xkbi->desc->ctrls; 173 ctrl->repeat_delay = g_winInfo.keyboard.delay; 174 ctrl->repeat_interval = 1000 / g_winInfo.keyboard.rate; 175 } 176 else { 177 winErrorFVerb(1, 178 "winKeybdProc - Error initializing keyboard AutoRepeat\n"); 179 } 180 181 break; 182 183 case DEVICE_ON: 184 pDevice->on = TRUE; 185 186 // immediately copy the state of this keyboard device to the VCK 187 // (which otherwise happens lazily after the first keypress) 188 CopyKeyClass(pDeviceInt, inputInfo.keyboard); 189 break; 190 191 case DEVICE_CLOSE: 192 case DEVICE_OFF: 193 pDevice->on = FALSE; 194 break; 195 } 196 197 return Success; 198} 199 200/* 201 * Detect current mode key states upon server startup. 202 * 203 * Simulate a press and release of any key that is currently 204 * toggled. 205 */ 206 207void 208winInitializeModeKeyStates(void) 209{ 210 /* Restore NumLock */ 211 if (GetKeyState(VK_NUMLOCK) & 0x0001) { 212 winSendKeyEvent(KEY_NumLock, TRUE); 213 winSendKeyEvent(KEY_NumLock, FALSE); 214 } 215 216 /* Restore CapsLock */ 217 if (GetKeyState(VK_CAPITAL) & 0x0001) { 218 winSendKeyEvent(KEY_CapsLock, TRUE); 219 winSendKeyEvent(KEY_CapsLock, FALSE); 220 } 221 222 /* Restore ScrollLock */ 223 if (GetKeyState(VK_SCROLL) & 0x0001) { 224 winSendKeyEvent(KEY_ScrollLock, TRUE); 225 winSendKeyEvent(KEY_ScrollLock, FALSE); 226 } 227 228 /* Restore KanaLock */ 229 if (GetKeyState(VK_KANA) & 0x0001) { 230 winSendKeyEvent(KEY_HKTG, TRUE); 231 winSendKeyEvent(KEY_HKTG, FALSE); 232 } 233} 234 235/* 236 * Upon regaining the keyboard focus we must 237 * resynchronize our internal mode key states 238 * with the actual state of the keys. 239 */ 240 241void 242winRestoreModeKeyStates(void) 243{ 244 DWORD dwKeyState; 245 BOOL processEvents = TRUE; 246 unsigned short internalKeyStates; 247 248 /* X server is being initialized */ 249 if (!inputInfo.keyboard) 250 return; 251 252 /* Only process events if the rootwindow is mapped. The keyboard events 253 * will cause segfaults otherwise */ 254 if (screenInfo.screens[0]->root && 255 screenInfo.screens[0]->root->mapped == FALSE) 256 processEvents = FALSE; 257 258 /* Force to process all pending events in the mi event queue */ 259 if (processEvents) 260 mieqProcessInputEvents(); 261 262 /* Read the mode key states of our X server */ 263 /* (stored in the virtual core keyboard) */ 264 internalKeyStates = 265 XkbStateFieldFromRec(&inputInfo.keyboard->key->xkbInfo->state); 266 winDebug("winRestoreModeKeyStates: state %d\n", internalKeyStates); 267 268 /* Check if modifier keys are pressed, and if so, fake a press */ 269 { 270 271 BOOL lctrl = (GetAsyncKeyState(VK_LCONTROL) < 0); 272 BOOL rctrl = (GetAsyncKeyState(VK_RCONTROL) < 0); 273 BOOL lshift = (GetAsyncKeyState(VK_LSHIFT) < 0); 274 BOOL rshift = (GetAsyncKeyState(VK_RSHIFT) < 0); 275 BOOL alt = (GetAsyncKeyState(VK_LMENU) < 0); 276 BOOL altgr = (GetAsyncKeyState(VK_RMENU) < 0); 277 278 /* 279 If AltGr and CtrlL appear to be pressed, assume the 280 CtrL is a fake one 281 */ 282 if (lctrl && altgr) 283 lctrl = FALSE; 284 285 if (lctrl) 286 winSendKeyEvent(KEY_LCtrl, TRUE); 287 288 if (rctrl) 289 winSendKeyEvent(KEY_RCtrl, TRUE); 290 291 if (lshift) 292 winSendKeyEvent(KEY_ShiftL, TRUE); 293 294 if (rshift) 295 winSendKeyEvent(KEY_ShiftL, TRUE); 296 297 if (alt) 298 winSendKeyEvent(KEY_Alt, TRUE); 299 300 if (altgr) 301 winSendKeyEvent(KEY_AltLang, TRUE); 302 } 303 304 /* 305 Check if latching modifier key states have changed, and if so, 306 fake a press and a release to toggle the modifier to the correct 307 state 308 */ 309 dwKeyState = GetKeyState(VK_NUMLOCK) & 0x0001; 310 if (LOGICAL_XOR(internalKeyStates & NumLockMask, dwKeyState)) { 311 winSendKeyEvent(KEY_NumLock, TRUE); 312 winSendKeyEvent(KEY_NumLock, FALSE); 313 } 314 315 dwKeyState = GetKeyState(VK_CAPITAL) & 0x0001; 316 if (LOGICAL_XOR(internalKeyStates & LockMask, dwKeyState)) { 317 winSendKeyEvent(KEY_CapsLock, TRUE); 318 winSendKeyEvent(KEY_CapsLock, FALSE); 319 } 320 321 dwKeyState = GetKeyState(VK_SCROLL) & 0x0001; 322 if (LOGICAL_XOR(internalKeyStates & ScrollLockMask, dwKeyState)) { 323 winSendKeyEvent(KEY_ScrollLock, TRUE); 324 winSendKeyEvent(KEY_ScrollLock, FALSE); 325 } 326 327 dwKeyState = GetKeyState(VK_KANA) & 0x0001; 328 if (LOGICAL_XOR(internalKeyStates & KanaMask, dwKeyState)) { 329 winSendKeyEvent(KEY_HKTG, TRUE); 330 winSendKeyEvent(KEY_HKTG, FALSE); 331 } 332 333 /* 334 For strict correctness, we should also press any non-modifier keys 335 which are already down when we gain focus, but nobody has complained 336 yet :-) 337 */ 338} 339 340/* 341 * Look for the lovely fake Control_L press/release generated by Windows 342 * when AltGr is pressed/released on a non-U.S. keyboard. 343 */ 344 345Bool 346winIsFakeCtrl_L(UINT message, WPARAM wParam, LPARAM lParam) 347{ 348 MSG msgNext; 349 LONG lTime; 350 Bool fReturn; 351 352 static Bool lastWasControlL = FALSE; 353 static LONG lastTime; 354 355 /* 356 * Fake Ctrl_L presses will be followed by an Alt_R press 357 * with the same timestamp as the Ctrl_L press. 358 */ 359 if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN) 360 && wParam == VK_CONTROL && (HIWORD(lParam) & KF_EXTENDED) == 0) { 361 /* Got a Ctrl_L press */ 362 363 /* Get time of current message */ 364 lTime = GetMessageTime(); 365 366 /* Look for next press message */ 367 fReturn = PeekMessage(&msgNext, NULL, 368 WM_KEYDOWN, WM_SYSKEYDOWN, PM_NOREMOVE); 369 370 if (fReturn && msgNext.message != WM_KEYDOWN && 371 msgNext.message != WM_SYSKEYDOWN) 372 fReturn = 0; 373 374 if (!fReturn) { 375 lastWasControlL = TRUE; 376 lastTime = lTime; 377 } 378 else { 379 lastWasControlL = FALSE; 380 } 381 382 /* Is next press an Alt_R with the same timestamp? */ 383 if (fReturn && msgNext.wParam == VK_MENU 384 && msgNext.time == lTime 385 && (HIWORD(msgNext.lParam) & KF_EXTENDED)) { 386 /* 387 * Next key press is Alt_R with same timestamp as current 388 * Ctrl_L message. Therefore, this Ctrl_L press is a fake 389 * event, so discard it. 390 */ 391 return TRUE; 392 } 393 } 394 /* 395 * Sometimes, the Alt_R press message is not yet posted when the 396 * fake Ctrl_L press message arrives (even though it has the 397 * same timestamp), so check for an Alt_R press message that has 398 * arrived since the last Ctrl_L message. 399 */ 400 else if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN) 401 && wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) { 402 /* Got a Alt_R press */ 403 404 if (lastWasControlL) { 405 lTime = GetMessageTime(); 406 407 if (lastTime == lTime) { 408 /* Undo the fake Ctrl_L press by sending a fake Ctrl_L release */ 409 winSendKeyEvent(KEY_LCtrl, FALSE); 410 } 411 lastWasControlL = FALSE; 412 } 413 } 414 /* 415 * Fake Ctrl_L releases will be followed by an Alt_R release 416 * with the same timestamp as the Ctrl_L release. 417 */ 418 else if ((message == WM_KEYUP || message == WM_SYSKEYUP) 419 && wParam == VK_CONTROL && (HIWORD(lParam) & KF_EXTENDED) == 0) { 420 /* Got a Ctrl_L release */ 421 422 /* Get time of current message */ 423 lTime = GetMessageTime(); 424 425 /* Look for next release message */ 426 fReturn = PeekMessage(&msgNext, NULL, 427 WM_KEYUP, WM_SYSKEYUP, PM_NOREMOVE); 428 429 if (fReturn && msgNext.message != WM_KEYUP && 430 msgNext.message != WM_SYSKEYUP) 431 fReturn = 0; 432 433 lastWasControlL = FALSE; 434 435 /* Is next press an Alt_R with the same timestamp? */ 436 if (fReturn 437 && (msgNext.message == WM_KEYUP || msgNext.message == WM_SYSKEYUP) 438 && msgNext.wParam == VK_MENU 439 && msgNext.time == lTime 440 && (HIWORD(msgNext.lParam) & KF_EXTENDED)) { 441 /* 442 * Next key release is Alt_R with same timestamp as current 443 * Ctrl_L message. Therefore, this Ctrl_L release is a fake 444 * event, so discard it. 445 */ 446 return TRUE; 447 } 448 } 449 else { 450 /* On any other press or release message, we don't have a 451 potentially fake Ctrl_L to worry about anymore... */ 452 lastWasControlL = FALSE; 453 } 454 455 /* Not a fake control left press/release */ 456 return FALSE; 457} 458 459/* 460 * Lift any modifier keys that are pressed 461 */ 462 463void 464winKeybdReleaseKeys(void) 465{ 466 int i; 467 468#ifdef HAS_DEVWINDOWS 469 /* Verify that the mi input system has been initialized */ 470 if (g_fdMessageQueue == WIN_FD_INVALID) 471 return; 472#endif 473 474 /* Loop through all keys */ 475 for (i = 0; i < NUM_KEYCODES; ++i) { 476 /* Pop key if pressed */ 477 if (g_winKeyState[i]) 478 winSendKeyEvent(i, FALSE); 479 480 /* Reset pressed flag for keys */ 481 g_winKeyState[i] = FALSE; 482 } 483} 484 485/* 486 * Take a raw X key code and send an up or down event for it. 487 * 488 * Thanks to VNC for inspiration, though it is a simple function. 489 */ 490 491void 492winSendKeyEvent(DWORD dwKey, Bool fDown) 493{ 494 /* 495 * When alt-tabing between screens we can get phantom key up messages 496 * Here we only pass them through it we think we should! 497 */ 498 if (g_winKeyState[dwKey] == FALSE && fDown == FALSE) 499 return; 500 501 /* Update the keyState map */ 502 g_winKeyState[dwKey] = fDown; 503 504 QueueKeyboardEvents(g_pwinKeyboard, fDown ? KeyPress : KeyRelease, 505 dwKey + MIN_KEYCODE); 506 507 winDebug("winSendKeyEvent: dwKey: %u, fDown: %u\n", (unsigned int)dwKey, fDown); 508} 509 510BOOL 511winCheckKeyPressed(WPARAM wParam, LPARAM lParam) 512{ 513 switch (wParam) { 514 case VK_CONTROL: 515 if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl]) 516 return TRUE; 517 if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl]) 518 return TRUE; 519 break; 520 case VK_SHIFT: 521 if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR]) 522 return TRUE; 523 if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL]) 524 return TRUE; 525 break; 526 default: 527 return TRUE; 528 } 529 return FALSE; 530} 531 532/* Only one shift release message is sent even if both are pressed. 533 * Fix this here 534 */ 535void 536winFixShiftKeys(int iScanCode) 537{ 538 if (GetKeyState(VK_SHIFT) & 0x8000) 539 return; 540 541 if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR]) 542 winSendKeyEvent(KEY_ShiftR, FALSE); 543 if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL]) 544 winSendKeyEvent(KEY_ShiftL, FALSE); 545} 546