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