winkeybd.c revision 6747b715
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#include "xkbsrv.h" 44 45static Bool g_winKeyState[NUM_KEYCODES]; 46 47/* 48 * Local prototypes 49 */ 50 51static void 52winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt, 53 pointer pCtrl, int iClass); 54 55static void 56winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl); 57 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 68void 69winTranslateKey (WPARAM wParam, LPARAM lParam, int *piScanCode) 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 76/* WM_ key messages faked by Vista speech recognition (WSR) don't have a 77 * scan code. 78 * 79 * Vocola 3 (Rick Mohr's supplement to WSR) uses 80 * System.Windows.Forms.SendKeys.SendWait(), which appears always to give a 81 * scan code of 1 82 */ 83 if (iParamScanCode <= 1) 84 { 85 if (VK_PRIOR <= wParam && wParam <= VK_DOWN) 86 /* Trigger special case table to translate to extended 87 * keycode, otherwise if num_lock is on, we can get keypad 88 * numbers instead of navigation keys. */ 89 iParam |= KF_EXTENDED; 90 else 91 iParamScanCode = MapVirtualKeyEx(wParam, 92 /*MAPVK_VK_TO_VSC*/0, 93 GetKeyboardLayout(0)); 94 } 95 96 /* Branch on special extended, special non-extended, or normal key */ 97 if ((iParam & KF_EXTENDED) && iKeyFixupEx) 98 *piScanCode = iKeyFixupEx; 99 else if (iKeyFixup) 100 *piScanCode = iKeyFixup; 101 else if (wParam == 0 && iParamScanCode == 0x70) 102 *piScanCode = KEY_HKTG; 103 else 104 switch (iParamScanCode) 105 { 106 case 0x70: 107 *piScanCode = KEY_HKTG; 108 break; 109 case 0x73: 110 *piScanCode = KEY_BSlash2; 111 break; 112 default: 113 *piScanCode = iParamScanCode; 114 break; 115 } 116} 117 118 119/* Ring the keyboard bell (system speaker on PCs) */ 120static void 121winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt, 122 pointer pCtrl, int iClass) 123{ 124 /* 125 * We can't use Beep () here because it uses the PC speaker 126 * on NT/2000. MessageBeep (MB_OK) will play the default system 127 * sound on systems with a sound card or it will beep the PC speaker 128 * on systems that do not have a sound card. 129 */ 130 MessageBeep (MB_OK); 131} 132 133 134/* Change some keyboard configuration parameters */ 135static void 136winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl) 137{ 138} 139 140 141/* 142 * See Porting Layer Definition - p. 18 143 * winKeybdProc is known as a DeviceProc. 144 */ 145 146int 147winKeybdProc (DeviceIntPtr pDeviceInt, int iState) 148{ 149 DevicePtr pDevice = (DevicePtr) pDeviceInt; 150 XkbSrvInfoPtr xkbi; 151 XkbControlsPtr ctrl; 152 153 switch (iState) 154 { 155 case DEVICE_INIT: 156 winConfigKeyboard (pDeviceInt); 157 158 /* FIXME: Maybe we should use winGetKbdLeds () here? */ 159 defaultKeyboardControl.leds = g_winInfo.keyboard.leds; 160 161 winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\"" 162 " Variant = \"%s\" Options = \"%s\"\n", 163 g_winInfo.xkb.rules ? g_winInfo.xkb.rules : "none", 164 g_winInfo.xkb.model ? g_winInfo.xkb.model : "none", 165 g_winInfo.xkb.layout ? g_winInfo.xkb.layout : "none", 166 g_winInfo.xkb.variant ? g_winInfo.xkb.variant : "none", 167 g_winInfo.xkb.options ? g_winInfo.xkb.options : "none"); 168 169 InitKeyboardDeviceStruct (pDeviceInt, 170 &g_winInfo.xkb, 171 winKeybdBell, 172 winKeybdCtrl); 173 174 xkbi = pDeviceInt->key->xkbInfo; 175 if ((xkbi != NULL) && (xkbi->desc != NULL)) 176 { 177 ctrl = xkbi->desc->ctrls; 178 ctrl->repeat_delay = g_winInfo.keyboard.delay; 179 ctrl->repeat_interval = 1000/g_winInfo.keyboard.rate; 180 } 181 else 182 { 183 winErrorFVerb (1, "winKeybdProc - Error initializing keyboard AutoRepeat\n"); 184 } 185 186 break; 187 188 case DEVICE_ON: 189 pDevice->on = TRUE; 190 191 // immediately copy the state of this keyboard device to the VCK 192 // (which otherwise happens lazily after the first keypress) 193 CopyKeyClass(pDeviceInt, inputInfo.keyboard); 194 break; 195 196 case DEVICE_CLOSE: 197 case DEVICE_OFF: 198 pDevice->on = FALSE; 199 break; 200 } 201 202 return Success; 203} 204 205 206/* 207 * Detect current mode key states upon server startup. 208 * 209 * Simulate a press and release of any key that is currently 210 * toggled. 211 */ 212 213void 214winInitializeModeKeyStates (void) 215{ 216 /* Restore NumLock */ 217 if (GetKeyState (VK_NUMLOCK) & 0x0001) 218 { 219 winSendKeyEvent (KEY_NumLock, TRUE); 220 winSendKeyEvent (KEY_NumLock, FALSE); 221 } 222 223 /* Restore CapsLock */ 224 if (GetKeyState (VK_CAPITAL) & 0x0001) 225 { 226 winSendKeyEvent (KEY_CapsLock, TRUE); 227 winSendKeyEvent (KEY_CapsLock, FALSE); 228 } 229 230 /* Restore ScrollLock */ 231 if (GetKeyState (VK_SCROLL) & 0x0001) 232 { 233 winSendKeyEvent (KEY_ScrollLock, TRUE); 234 winSendKeyEvent (KEY_ScrollLock, FALSE); 235 } 236 237 /* Restore KanaLock */ 238 if (GetKeyState (VK_KANA) & 0x0001) 239 { 240 winSendKeyEvent (KEY_HKTG, TRUE); 241 winSendKeyEvent (KEY_HKTG, FALSE); 242 } 243} 244 245 246/* 247 * Upon regaining the keyboard focus we must 248 * resynchronize our internal mode key states 249 * with the actual state of the keys. 250 */ 251 252void 253winRestoreModeKeyStates (void) 254{ 255 DWORD dwKeyState; 256 BOOL processEvents = TRUE; 257 unsigned short internalKeyStates; 258 259 /* X server is being initialized */ 260 if (!inputInfo.keyboard) 261 return; 262 263 /* Only process events if the rootwindow is mapped. The keyboard events 264 * will cause segfaults otherwise */ 265 if (screenInfo.screens[0]->root && screenInfo.screens[0]->root->mapped == FALSE) 266 processEvents = FALSE; 267 268 /* Force to process all pending events in the mi event queue */ 269 if (processEvents) 270 mieqProcessInputEvents (); 271 272 /* Read the mode key states of our X server */ 273 /* (stored in the virtual core keyboard) */ 274 internalKeyStates = XkbStateFieldFromRec(&inputInfo.keyboard->key->xkbInfo->state); 275 winDebug("winRestoreModeKeyStates: state %d\n", internalKeyStates); 276 277 /* 278 * NOTE: The C XOR operator, ^, will not work here because it is 279 * a bitwise operator, not a logical operator. C does not 280 * have a logical XOR operator, so we use a macro instead. 281 */ 282 283 /* Has the key state changed? */ 284 dwKeyState = GetKeyState (VK_NUMLOCK) & 0x0001; 285 if (WIN_XOR (internalKeyStates & NumLockMask, dwKeyState)) 286 { 287 winSendKeyEvent (KEY_NumLock, TRUE); 288 winSendKeyEvent (KEY_NumLock, FALSE); 289 } 290 291 /* Has the key state changed? */ 292 dwKeyState = GetKeyState (VK_CAPITAL) & 0x0001; 293 if (WIN_XOR (internalKeyStates & LockMask, dwKeyState)) 294 { 295 winSendKeyEvent (KEY_CapsLock, TRUE); 296 winSendKeyEvent (KEY_CapsLock, FALSE); 297 } 298 299 /* Has the key state changed? */ 300 dwKeyState = GetKeyState (VK_SCROLL) & 0x0001; 301 if (WIN_XOR (internalKeyStates & ScrollLockMask, dwKeyState)) 302 { 303 winSendKeyEvent (KEY_ScrollLock, TRUE); 304 winSendKeyEvent (KEY_ScrollLock, FALSE); 305 } 306 307 /* Has the key state changed? */ 308 dwKeyState = GetKeyState (VK_KANA) & 0x0001; 309 if (WIN_XOR (internalKeyStates & KanaMask, dwKeyState)) 310 { 311 winSendKeyEvent (KEY_HKTG, TRUE); 312 winSendKeyEvent (KEY_HKTG, FALSE); 313 } 314} 315 316 317/* 318 * Look for the lovely fake Control_L press/release generated by Windows 319 * when AltGr is pressed/released on a non-U.S. keyboard. 320 */ 321 322Bool 323winIsFakeCtrl_L (UINT message, WPARAM wParam, LPARAM lParam) 324{ 325 MSG msgNext; 326 LONG lTime; 327 Bool fReturn; 328 329 /* 330 * Fake Ctrl_L presses will be followed by an Alt_R keypress 331 * with the same timestamp as the Ctrl_L press. 332 */ 333 if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN) 334 && wParam == VK_CONTROL 335 && (HIWORD (lParam) & KF_EXTENDED) == 0) 336 { 337 /* Got a Ctrl_L press */ 338 339 /* Get time of current message */ 340 lTime = GetMessageTime (); 341 342 /* Look for fake Ctrl_L preceeding an Alt_R press. */ 343 fReturn = PeekMessage (&msgNext, NULL, 344 WM_KEYDOWN, WM_SYSKEYDOWN, 345 PM_NOREMOVE); 346 347 /* 348 * Try again if the first call fails. 349 * NOTE: This usually happens when TweakUI is enabled. 350 */ 351 if (!fReturn) 352 { 353 /* Voodoo to make sure that the Alt_R message has posted */ 354 Sleep (0); 355 356 /* Look for fake Ctrl_L preceeding an Alt_R press. */ 357 fReturn = PeekMessage (&msgNext, NULL, 358 WM_KEYDOWN, WM_SYSKEYDOWN, 359 PM_NOREMOVE); 360 } 361 if (msgNext.message != WM_KEYDOWN && msgNext.message != WM_SYSKEYDOWN) 362 fReturn = 0; 363 364 /* Is next press an Alt_R with the same timestamp? */ 365 if (fReturn && msgNext.wParam == VK_MENU 366 && msgNext.time == lTime 367 && (HIWORD (msgNext.lParam) & KF_EXTENDED)) 368 { 369 /* 370 * Next key press is Alt_R with same timestamp as current 371 * Ctrl_L message. Therefore, this Ctrl_L press is a fake 372 * event, so discard it. 373 */ 374 return TRUE; 375 } 376 } 377 378 /* 379 * Fake Ctrl_L releases will be followed by an Alt_R release 380 * with the same timestamp as the Ctrl_L release. 381 */ 382 if ((message == WM_KEYUP || message == WM_SYSKEYUP) 383 && wParam == VK_CONTROL 384 && (HIWORD (lParam) & KF_EXTENDED) == 0) 385 { 386 /* Got a Ctrl_L release */ 387 388 /* Get time of current message */ 389 lTime = GetMessageTime (); 390 391 /* Look for fake Ctrl_L release preceeding an Alt_R release. */ 392 fReturn = PeekMessage (&msgNext, NULL, 393 WM_KEYUP, WM_SYSKEYUP, 394 PM_NOREMOVE); 395 396 /* 397 * Try again if the first call fails. 398 * NOTE: This usually happens when TweakUI is enabled. 399 */ 400 if (!fReturn) 401 { 402 /* Voodoo to make sure that the Alt_R message has posted */ 403 Sleep (0); 404 405 /* Look for fake Ctrl_L release preceeding an Alt_R release. */ 406 fReturn = PeekMessage (&msgNext, NULL, 407 WM_KEYUP, WM_SYSKEYUP, 408 PM_NOREMOVE); 409 } 410 411 if (msgNext.message != WM_KEYUP && msgNext.message != WM_SYSKEYUP) 412 fReturn = 0; 413 414 /* Is next press an Alt_R with the same timestamp? */ 415 if (fReturn 416 && (msgNext.message == WM_KEYUP 417 || msgNext.message == WM_SYSKEYUP) 418 && msgNext.wParam == VK_MENU 419 && msgNext.time == lTime 420 && (HIWORD (msgNext.lParam) & KF_EXTENDED)) 421 { 422 /* 423 * Next key release is Alt_R with same timestamp as current 424 * Ctrl_L message. Therefore, this Ctrl_L release is a fake 425 * event, so discard it. 426 */ 427 return TRUE; 428 } 429 } 430 431 /* Not a fake control left press/release */ 432 return FALSE; 433} 434 435 436/* 437 * Lift any modifier keys that are pressed 438 */ 439 440void 441winKeybdReleaseKeys (void) 442{ 443 int i; 444 445#ifdef HAS_DEVWINDOWS 446 /* Verify that the mi input system has been initialized */ 447 if (g_fdMessageQueue == WIN_FD_INVALID) 448 return; 449#endif 450 451 /* Loop through all keys */ 452 for (i = 0; i < NUM_KEYCODES; ++i) 453 { 454 /* Pop key if pressed */ 455 if (g_winKeyState[i]) 456 winSendKeyEvent (i, FALSE); 457 458 /* Reset pressed flag for keys */ 459 g_winKeyState[i] = FALSE; 460 } 461} 462 463 464/* 465 * Take a raw X key code and send an up or down event for it. 466 * 467 * Thanks to VNC for inspiration, though it is a simple function. 468 */ 469 470void 471winSendKeyEvent (DWORD dwKey, Bool fDown) 472{ 473 EventListPtr events; 474 int i, nevents; 475 476 /* 477 * When alt-tabing between screens we can get phantom key up messages 478 * Here we only pass them through it we think we should! 479 */ 480 if (g_winKeyState[dwKey] == FALSE && fDown == FALSE) return; 481 482 /* Update the keyState map */ 483 g_winKeyState[dwKey] = fDown; 484 485 GetEventList(&events); 486 nevents = GetKeyboardEvents(events, g_pwinKeyboard, fDown ? KeyPress : KeyRelease, dwKey + MIN_KEYCODE); 487 488 for (i = 0; i < nevents; i++) 489 mieqEnqueue(g_pwinKeyboard, events[i].event); 490 491#if CYGDEBUG 492 ErrorF("winSendKeyEvent: dwKey: %d, fDown: %d, nEvents %d\n", 493 dwKey, fDown, nevents); 494#endif 495} 496 497BOOL winCheckKeyPressed(WPARAM wParam, LPARAM lParam) 498{ 499 switch (wParam) 500 { 501 case VK_CONTROL: 502 if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl]) 503 return TRUE; 504 if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl]) 505 return TRUE; 506 break; 507 case VK_SHIFT: 508 if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR]) 509 return TRUE; 510 if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL]) 511 return TRUE; 512 break; 513 default: 514 return TRUE; 515 } 516 return FALSE; 517} 518 519/* Only on shift release message is sent even if both are pressed. 520 * Fix this here 521 */ 522void winFixShiftKeys (int iScanCode) 523{ 524 if (GetKeyState (VK_SHIFT) & 0x8000) 525 return; 526 527 if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR]) 528 winSendKeyEvent (KEY_ShiftR, FALSE); 529 if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL]) 530 winSendKeyEvent (KEY_ShiftL, FALSE); 531} 532