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 
47 static Bool g_winKeyState[NUM_KEYCODES];
48 
49 /*
50  * Local prototypes
51  */
52 
53 static void
54  winKeybdBell(int iPercent, DeviceIntPtr pDeviceInt, void *pCtrl, int iClass);
55 
56 static 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 
68 int
winTranslateKey(WPARAM wParam,LPARAM lParam)69 winTranslateKey(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) */
122 static void
winKeybdBell(int iPercent,DeviceIntPtr pDeviceInt,void * pCtrl,int iClass)123 winKeybdBell(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 */
135 static void
winKeybdCtrl(DeviceIntPtr pDevice,KeybdCtrl * pCtrl)136 winKeybdCtrl(DeviceIntPtr pDevice, KeybdCtrl * pCtrl)
137 {
138 }
139 
140 /*
141  * See Porting Layer Definition - p. 18
142  * winKeybdProc is known as a DeviceProc.
143  */
144 
145 int
winKeybdProc(DeviceIntPtr pDeviceInt,int iState)146 winKeybdProc(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 
207 void
winInitializeModeKeyStates(void)208 winInitializeModeKeyStates(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 
241 void
winRestoreModeKeyStates(void)242 winRestoreModeKeyStates(void)
243 {
244     DWORD dwKeyState;
245     BOOL processEvents = TRUE;
246     unsigned short internalKeyStates;
247 
248     /* X server is being initialized */
249     if (!inputInfo.keyboard || !inputInfo.keyboard->key)
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 
345 Bool
winIsFakeCtrl_L(UINT message,WPARAM wParam,LPARAM lParam)346 winIsFakeCtrl_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 
463 void
winKeybdReleaseKeys(void)464 winKeybdReleaseKeys(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 
491 void
winSendKeyEvent(DWORD dwKey,Bool fDown)492 winSendKeyEvent(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 
510 BOOL
winCheckKeyPressed(WPARAM wParam,LPARAM lParam)511 winCheckKeyPressed(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  */
535 void
winFixShiftKeys(int iScanCode)536 winFixShiftKeys(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