winkeybd.c revision 05b261ec
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 35#ifdef HAVE_XWIN_CONFIG_H 36#include <xwin-config.h> 37#endif 38#include "win.h" 39#include "winkeybd.h" 40#include "winconfig.h" 41#include "winmsg.h" 42 43#ifdef XKB 44#ifndef XKB_IN_SERVER 45#define XKB_IN_SERVER 46#endif 47#include <xkbsrv.h> 48#endif 49 50static Bool g_winKeyState[NUM_KEYCODES]; 51 52/* Stored to get internal mode key states. Must be read-only. */ 53static unsigned short const *g_winInternalModeKeyStatesPtr = NULL; 54 55 56/* 57 * Local prototypes 58 */ 59 60static void 61winGetKeyMappings (KeySymsPtr pKeySyms, CARD8 *pModMap); 62 63static void 64winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt, 65 pointer pCtrl, int iClass); 66 67static void 68winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl); 69 70 71/* 72 * Translate a Windows WM_[SYS]KEY(UP/DOWN) message 73 * into an ASCII scan code. 74 * 75 * We do this ourselves, rather than letting Windows handle it, 76 * because Windows tends to munge the handling of special keys, 77 * like AltGr on European keyboards. 78 */ 79 80void 81winTranslateKey (WPARAM wParam, LPARAM lParam, int *piScanCode) 82{ 83 int iKeyFixup = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 1]; 84 int iKeyFixupEx = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 2]; 85 int iParamScanCode = LOBYTE (HIWORD (lParam)); 86 87 /* Branch on special extended, special non-extended, or normal key */ 88 if ((HIWORD (lParam) & KF_EXTENDED) && iKeyFixupEx) 89 *piScanCode = iKeyFixupEx; 90 else if (iKeyFixup) 91 *piScanCode = iKeyFixup; 92 else if (wParam == 0 && iParamScanCode == 0x70) 93 *piScanCode = KEY_HKTG; 94 else 95 switch (iParamScanCode) 96 { 97 case 0x70: 98 *piScanCode = KEY_HKTG; 99 break; 100 case 0x73: 101 *piScanCode = KEY_BSlash2; 102 break; 103 default: 104 *piScanCode = iParamScanCode; 105 break; 106 } 107} 108 109 110/* 111 * We call this function from winKeybdProc when we are 112 * initializing the keyboard. 113 */ 114 115static void 116winGetKeyMappings (KeySymsPtr pKeySyms, CARD8 *pModMap) 117{ 118 int i; 119 KeySym *pMap = map; 120 KeySym *pKeySym; 121 122 /* 123 * Initialize all key states to up... which may not be true 124 * but it is close enough. 125 */ 126 ZeroMemory (g_winKeyState, sizeof (g_winKeyState[0]) * NUM_KEYCODES); 127 128 /* MAP_LENGTH is defined in Xserver/include/input.h to be 256 */ 129 for (i = 0; i < MAP_LENGTH; i++) 130 pModMap[i] = NoSymbol; /* make sure it is restored */ 131 132 /* Loop through all valid entries in the key symbol table */ 133 for (pKeySym = pMap, i = MIN_KEYCODE; 134 i < (MIN_KEYCODE + NUM_KEYCODES); 135 i++, pKeySym += GLYPHS_PER_KEY) 136 { 137 switch (*pKeySym) 138 { 139 case XK_Shift_L: 140 case XK_Shift_R: 141 pModMap[i] = ShiftMask; 142 break; 143 144 case XK_Control_L: 145 case XK_Control_R: 146 pModMap[i] = ControlMask; 147 break; 148 149 case XK_Caps_Lock: 150 pModMap[i] = LockMask; 151 break; 152 153 case XK_Alt_L: 154 case XK_Alt_R: 155 pModMap[i] = AltMask; 156 break; 157 158 case XK_Num_Lock: 159 pModMap[i] = NumLockMask; 160 break; 161 162 case XK_Scroll_Lock: 163 pModMap[i] = ScrollLockMask; 164 break; 165 166#if 0 167 case XK_Super_L: 168 case XK_Super_R: 169 pModMap[i] = Mod4Mask; 170 break; 171#else 172 /* Hirigana/Katakana toggle */ 173 case XK_Kana_Lock: 174 case XK_Kana_Shift: 175 pModMap[i] = KanaMask; 176 break; 177#endif 178 179 /* alternate toggle for multinational support */ 180 case XK_Mode_switch: 181 pModMap[i] = AltLangMask; 182 break; 183 } 184 } 185 186 pKeySyms->map = (KeySym *) pMap; 187 pKeySyms->mapWidth = GLYPHS_PER_KEY; 188 pKeySyms->minKeyCode = MIN_KEYCODE; 189 pKeySyms->maxKeyCode = MAX_KEYCODE; 190} 191 192 193/* Ring the keyboard bell (system speaker on PCs) */ 194static void 195winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt, 196 pointer pCtrl, int iClass) 197{ 198 /* 199 * We can't use Beep () here because it uses the PC speaker 200 * on NT/2000. MessageBeep (MB_OK) will play the default system 201 * sound on systems with a sound card or it will beep the PC speaker 202 * on systems that do not have a sound card. 203 */ 204 MessageBeep (MB_OK); 205} 206 207 208/* Change some keyboard configuration parameters */ 209static void 210winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl) 211{ 212 g_winInternalModeKeyStatesPtr = &(pDevice->key->state); 213} 214 215 216/* 217 * See Porting Layer Definition - p. 18 218 * winKeybdProc is known as a DeviceProc. 219 */ 220 221int 222winKeybdProc (DeviceIntPtr pDeviceInt, int iState) 223{ 224 KeySymsRec keySyms; 225 CARD8 modMap[MAP_LENGTH]; 226 DevicePtr pDevice = (DevicePtr) pDeviceInt; 227#ifdef XKB 228 XkbComponentNamesRec names; 229 XkbSrvInfoPtr xkbi; 230 XkbControlsPtr ctrl; 231#endif 232 233 switch (iState) 234 { 235 case DEVICE_INIT: 236 winConfigKeyboard (pDeviceInt); 237 238 winGetKeyMappings (&keySyms, modMap); 239 240#ifdef XKB 241 /* FIXME: Maybe we should use winGetKbdLeds () here? */ 242 defaultKeyboardControl.leds = g_winInfo.keyboard.leds; 243#else 244 defaultKeyboardControl.leds = g_winInfo.keyboard.leds; 245#endif 246 247#ifdef XKB 248 if (g_winInfo.xkb.disable) 249 { 250#endif 251 InitKeyboardDeviceStruct (pDevice, 252 &keySyms, 253 modMap, 254 winKeybdBell, 255 winKeybdCtrl); 256#ifdef XKB 257 } 258 else 259 { 260 261 names.keymap = g_winInfo.xkb.keymap; 262 names.keycodes = g_winInfo.xkb.keycodes; 263 names.types = g_winInfo.xkb.types; 264 names.compat = g_winInfo.xkb.compat; 265 names.symbols = g_winInfo.xkb.symbols; 266 names.geometry = g_winInfo.xkb.geometry; 267 268 winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\"" 269 " Variant = \"%s\" Options = \"%s\"\n", 270 g_winInfo.xkb.rules, g_winInfo.xkb.model, 271 g_winInfo.xkb.layout, g_winInfo.xkb.variant, 272 g_winInfo.xkb.options); 273 274 XkbSetRulesDflts (g_winInfo.xkb.rules, g_winInfo.xkb.model, 275 g_winInfo.xkb.layout, g_winInfo.xkb.variant, 276 g_winInfo.xkb.options); 277 XkbInitKeyboardDeviceStruct (pDeviceInt, &names, &keySyms, 278 modMap, winKeybdBell, winKeybdCtrl); 279 } 280#endif 281 282#ifdef XKB 283 if (!g_winInfo.xkb.disable) 284 { 285 xkbi = pDeviceInt->key->xkbInfo; 286 if (xkbi != NULL) 287 { 288 ctrl = xkbi->desc->ctrls; 289 ctrl->repeat_delay = g_winInfo.keyboard.delay; 290 ctrl->repeat_interval = 1000/g_winInfo.keyboard.rate; 291 } 292 else 293 { 294 winErrorFVerb (1, "winKeybdProc - Error initializing keyboard AutoRepeat (No XKB)\n"); 295 } 296 } 297#endif 298 299 g_winInternalModeKeyStatesPtr = &(pDeviceInt->key->state); 300 break; 301 302 case DEVICE_ON: 303 pDevice->on = TRUE; 304 g_winInternalModeKeyStatesPtr = &(pDeviceInt->key->state); 305 break; 306 307 case DEVICE_CLOSE: 308 case DEVICE_OFF: 309 pDevice->on = FALSE; 310 g_winInternalModeKeyStatesPtr = NULL; 311 break; 312 } 313 314 return Success; 315} 316 317 318/* 319 * Detect current mode key states upon server startup. 320 * 321 * Simulate a press and release of any key that is currently 322 * toggled. 323 */ 324 325void 326winInitializeModeKeyStates (void) 327{ 328 /* Restore NumLock */ 329 if (GetKeyState (VK_NUMLOCK) & 0x0001) 330 { 331 winSendKeyEvent (KEY_NumLock, TRUE); 332 winSendKeyEvent (KEY_NumLock, FALSE); 333 } 334 335 /* Restore CapsLock */ 336 if (GetKeyState (VK_CAPITAL) & 0x0001) 337 { 338 winSendKeyEvent (KEY_CapsLock, TRUE); 339 winSendKeyEvent (KEY_CapsLock, FALSE); 340 } 341 342 /* Restore ScrollLock */ 343 if (GetKeyState (VK_SCROLL) & 0x0001) 344 { 345 winSendKeyEvent (KEY_ScrollLock, TRUE); 346 winSendKeyEvent (KEY_ScrollLock, FALSE); 347 } 348 349 /* Restore KanaLock */ 350 if (GetKeyState (VK_KANA) & 0x0001) 351 { 352 winSendKeyEvent (KEY_HKTG, TRUE); 353 winSendKeyEvent (KEY_HKTG, FALSE); 354 } 355} 356 357 358/* 359 * Upon regaining the keyboard focus we must 360 * resynchronize our internal mode key states 361 * with the actual state of the keys. 362 */ 363 364void 365winRestoreModeKeyStates () 366{ 367 DWORD dwKeyState; 368 BOOL processEvents = TRUE; 369 unsigned short internalKeyStates; 370 371 /* X server is being initialized */ 372 if (!g_winInternalModeKeyStatesPtr) 373 return; 374 375 /* Only process events if the rootwindow is mapped. The keyboard events 376 * will cause segfaults otherwise */ 377 if (WindowTable && WindowTable[0] && WindowTable[0]->mapped == FALSE) 378 processEvents = FALSE; 379 380 /* Force to process all pending events in the mi event queue */ 381 if (processEvents) 382 mieqProcessInputEvents (); 383 384 /* Read the mode key states of our X server */ 385 internalKeyStates = *g_winInternalModeKeyStatesPtr; 386 387 /* 388 * NOTE: The C XOR operator, ^, will not work here because it is 389 * a bitwise operator, not a logical operator. C does not 390 * have a logical XOR operator, so we use a macro instead. 391 */ 392 393 /* Has the key state changed? */ 394 dwKeyState = GetKeyState (VK_NUMLOCK) & 0x0001; 395 if (WIN_XOR (internalKeyStates & NumLockMask, dwKeyState)) 396 { 397 winSendKeyEvent (KEY_NumLock, TRUE); 398 winSendKeyEvent (KEY_NumLock, FALSE); 399 } 400 401 /* Has the key state changed? */ 402 dwKeyState = GetKeyState (VK_CAPITAL) & 0x0001; 403 if (WIN_XOR (internalKeyStates & LockMask, dwKeyState)) 404 { 405 winSendKeyEvent (KEY_CapsLock, TRUE); 406 winSendKeyEvent (KEY_CapsLock, FALSE); 407 } 408 409 /* Has the key state changed? */ 410 dwKeyState = GetKeyState (VK_SCROLL) & 0x0001; 411 if (WIN_XOR (internalKeyStates & ScrollLockMask, dwKeyState)) 412 { 413 winSendKeyEvent (KEY_ScrollLock, TRUE); 414 winSendKeyEvent (KEY_ScrollLock, FALSE); 415 } 416 417 /* Has the key state changed? */ 418 dwKeyState = GetKeyState (VK_KANA) & 0x0001; 419 if (WIN_XOR (internalKeyStates & KanaMask, dwKeyState)) 420 { 421 winSendKeyEvent (KEY_HKTG, TRUE); 422 winSendKeyEvent (KEY_HKTG, FALSE); 423 } 424} 425 426 427/* 428 * Look for the lovely fake Control_L press/release generated by Windows 429 * when AltGr is pressed/released on a non-U.S. keyboard. 430 */ 431 432Bool 433winIsFakeCtrl_L (UINT message, WPARAM wParam, LPARAM lParam) 434{ 435 MSG msgNext; 436 LONG lTime; 437 Bool fReturn; 438 439 /* 440 * Fake Ctrl_L presses will be followed by an Alt_R keypress 441 * with the same timestamp as the Ctrl_L press. 442 */ 443 if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN) 444 && wParam == VK_CONTROL 445 && (HIWORD (lParam) & KF_EXTENDED) == 0) 446 { 447 /* Got a Ctrl_L press */ 448 449 /* Get time of current message */ 450 lTime = GetMessageTime (); 451 452 /* Look for fake Ctrl_L preceeding an Alt_R press. */ 453 fReturn = PeekMessage (&msgNext, NULL, 454 WM_KEYDOWN, WM_SYSKEYDOWN, 455 PM_NOREMOVE); 456 457 /* 458 * Try again if the first call fails. 459 * NOTE: This usually happens when TweakUI is enabled. 460 */ 461 if (!fReturn) 462 { 463 /* Voodoo to make sure that the Alt_R message has posted */ 464 Sleep (0); 465 466 /* Look for fake Ctrl_L preceeding an Alt_R press. */ 467 fReturn = PeekMessage (&msgNext, NULL, 468 WM_KEYDOWN, WM_SYSKEYDOWN, 469 PM_NOREMOVE); 470 } 471 if (msgNext.message != WM_KEYDOWN && msgNext.message != WM_SYSKEYDOWN) 472 fReturn = 0; 473 474 /* Is next press an Alt_R with the same timestamp? */ 475 if (fReturn && msgNext.wParam == VK_MENU 476 && msgNext.time == lTime 477 && (HIWORD (msgNext.lParam) & KF_EXTENDED)) 478 { 479 /* 480 * Next key press is Alt_R with same timestamp as current 481 * Ctrl_L message. Therefore, this Ctrl_L press is a fake 482 * event, so discard it. 483 */ 484 return TRUE; 485 } 486 } 487 488 /* 489 * Fake Ctrl_L releases will be followed by an Alt_R release 490 * with the same timestamp as the Ctrl_L release. 491 */ 492 if ((message == WM_KEYUP || message == WM_SYSKEYUP) 493 && wParam == VK_CONTROL 494 && (HIWORD (lParam) & KF_EXTENDED) == 0) 495 { 496 /* Got a Ctrl_L release */ 497 498 /* Get time of current message */ 499 lTime = GetMessageTime (); 500 501 /* Look for fake Ctrl_L release preceeding an Alt_R release. */ 502 fReturn = PeekMessage (&msgNext, NULL, 503 WM_KEYUP, WM_SYSKEYUP, 504 PM_NOREMOVE); 505 506 /* 507 * Try again if the first call fails. 508 * NOTE: This usually happens when TweakUI is enabled. 509 */ 510 if (!fReturn) 511 { 512 /* Voodoo to make sure that the Alt_R message has posted */ 513 Sleep (0); 514 515 /* Look for fake Ctrl_L release preceeding an Alt_R release. */ 516 fReturn = PeekMessage (&msgNext, NULL, 517 WM_KEYUP, WM_SYSKEYUP, 518 PM_NOREMOVE); 519 } 520 521 if (msgNext.message != WM_KEYUP && msgNext.message != WM_SYSKEYUP) 522 fReturn = 0; 523 524 /* Is next press an Alt_R with the same timestamp? */ 525 if (fReturn 526 && (msgNext.message == WM_KEYUP 527 || msgNext.message == WM_SYSKEYUP) 528 && msgNext.wParam == VK_MENU 529 && msgNext.time == lTime 530 && (HIWORD (msgNext.lParam) & KF_EXTENDED)) 531 { 532 /* 533 * Next key release is Alt_R with same timestamp as current 534 * Ctrl_L message. Therefore, this Ctrl_L release is a fake 535 * event, so discard it. 536 */ 537 return TRUE; 538 } 539 } 540 541 /* Not a fake control left press/release */ 542 return FALSE; 543} 544 545 546/* 547 * Lift any modifier keys that are pressed 548 */ 549 550void 551winKeybdReleaseKeys () 552{ 553 int i; 554 555#ifdef HAS_DEVWINDOWS 556 /* Verify that the mi input system has been initialized */ 557 if (g_fdMessageQueue == WIN_FD_INVALID) 558 return; 559#endif 560 561 /* Loop through all keys */ 562 for (i = 0; i < NUM_KEYCODES; ++i) 563 { 564 /* Pop key if pressed */ 565 if (g_winKeyState[i]) 566 winSendKeyEvent (i, FALSE); 567 568 /* Reset pressed flag for keys */ 569 g_winKeyState[i] = FALSE; 570 } 571} 572 573 574/* 575 * Take a raw X key code and send an up or down event for it. 576 * 577 * Thanks to VNC for inspiration, though it is a simple function. 578 */ 579 580void 581winSendKeyEvent (DWORD dwKey, Bool fDown) 582{ 583 xEvent xCurrentEvent; 584 585 /* 586 * When alt-tabing between screens we can get phantom key up messages 587 * Here we only pass them through it we think we should! 588 */ 589 if (g_winKeyState[dwKey] == FALSE && fDown == FALSE) return; 590 591 /* Update the keyState map */ 592 g_winKeyState[dwKey] = fDown; 593 594 ZeroMemory (&xCurrentEvent, sizeof (xCurrentEvent)); 595 596 xCurrentEvent.u.u.type = fDown ? KeyPress : KeyRelease; 597 xCurrentEvent.u.keyButtonPointer.time = 598 g_c32LastInputEventTime = GetTickCount (); 599 xCurrentEvent.u.u.detail = dwKey + MIN_KEYCODE; 600 mieqEnqueue (&xCurrentEvent); 601} 602 603BOOL winCheckKeyPressed(WPARAM wParam, LPARAM lParam) 604{ 605 switch (wParam) 606 { 607 case VK_CONTROL: 608 if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl]) 609 return TRUE; 610 if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl]) 611 return TRUE; 612 break; 613 case VK_SHIFT: 614 if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR]) 615 return TRUE; 616 if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL]) 617 return TRUE; 618 break; 619 default: 620 return TRUE; 621 } 622 return FALSE; 623} 624 625/* Only on shift release message is sent even if both are pressed. 626 * Fix this here 627 */ 628void winFixShiftKeys (int iScanCode) 629{ 630 if (GetKeyState (VK_SHIFT) & 0x8000) 631 return; 632 633 if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR]) 634 winSendKeyEvent (KEY_ShiftR, FALSE); 635 if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL]) 636 winSendKeyEvent (KEY_ShiftL, FALSE); 637} 638