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