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