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