~gerchanovsky/xorg-server/xenial

« back to all changes in this revision

Viewing changes to hw/xwin/winkeybd.c

  • Committer: Alex Gerchanovsky
  • Date: 2016-05-05 01:15:19 UTC
  • Revision ID: alex@ubuntu-20160505011519-ew0mn8tsloglbowd
InitialĀ 1.18.3-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
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
 
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
 
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
 
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
 
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
 
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)
 
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
 
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
 
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
 
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
 
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
 
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
}