winkeybd.c revision 05b261ec
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#ifdef XKB
44#ifndef XKB_IN_SERVER
45#define XKB_IN_SERVER
46#endif
47#include <xkbsrv.h>
48#endif
49
50static Bool g_winKeyState[NUM_KEYCODES];
51
52/* Stored to get internal mode key states.  Must be read-only.  */
53static unsigned short const *g_winInternalModeKeyStatesPtr = NULL;
54
55
56/*
57 * Local prototypes
58 */
59
60static void
61winGetKeyMappings (KeySymsPtr pKeySyms, CARD8 *pModMap);
62
63static void
64winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt,
65	      pointer pCtrl, int iClass);
66
67static void
68winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl);
69
70
71/*
72 * Translate a Windows WM_[SYS]KEY(UP/DOWN) message
73 * into an ASCII scan code.
74 *
75 * We do this ourselves, rather than letting Windows handle it,
76 * because Windows tends to munge the handling of special keys,
77 * like AltGr on European keyboards.
78 */
79
80void
81winTranslateKey (WPARAM wParam, LPARAM lParam, int *piScanCode)
82{
83  int		iKeyFixup = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 1];
84  int		iKeyFixupEx = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 2];
85  int		iParamScanCode = LOBYTE (HIWORD (lParam));
86
87  /* Branch on special extended, special non-extended, or normal key */
88  if ((HIWORD (lParam) & KF_EXTENDED) && iKeyFixupEx)
89    *piScanCode = iKeyFixupEx;
90  else if (iKeyFixup)
91    *piScanCode = iKeyFixup;
92  else if (wParam == 0 && iParamScanCode == 0x70)
93    *piScanCode = KEY_HKTG;
94  else
95    switch (iParamScanCode)
96    {
97      case 0x70:
98        *piScanCode = KEY_HKTG;
99        break;
100      case 0x73:
101        *piScanCode = KEY_BSlash2;
102        break;
103      default:
104        *piScanCode = iParamScanCode;
105        break;
106    }
107}
108
109
110/*
111 * We call this function from winKeybdProc when we are
112 * initializing the keyboard.
113 */
114
115static void
116winGetKeyMappings (KeySymsPtr pKeySyms, CARD8 *pModMap)
117{
118  int			i;
119  KeySym		*pMap = map;
120  KeySym		*pKeySym;
121
122  /*
123   * Initialize all key states to up... which may not be true
124   * but it is close enough.
125   */
126  ZeroMemory (g_winKeyState, sizeof (g_winKeyState[0]) * NUM_KEYCODES);
127
128  /* MAP_LENGTH is defined in Xserver/include/input.h to be 256 */
129  for (i = 0; i < MAP_LENGTH; i++)
130    pModMap[i] = NoSymbol;  /* make sure it is restored */
131
132  /* Loop through all valid entries in the key symbol table */
133  for (pKeySym = pMap, i = MIN_KEYCODE;
134       i < (MIN_KEYCODE + NUM_KEYCODES);
135       i++, pKeySym += GLYPHS_PER_KEY)
136    {
137      switch (*pKeySym)
138	{
139	case XK_Shift_L:
140	case XK_Shift_R:
141	  pModMap[i] = ShiftMask;
142	  break;
143
144	case XK_Control_L:
145	case XK_Control_R:
146	  pModMap[i] = ControlMask;
147	  break;
148
149	case XK_Caps_Lock:
150	  pModMap[i] = LockMask;
151	  break;
152
153	case XK_Alt_L:
154	case XK_Alt_R:
155	  pModMap[i] = AltMask;
156	  break;
157
158	case XK_Num_Lock:
159	  pModMap[i] = NumLockMask;
160	  break;
161
162	case XK_Scroll_Lock:
163	  pModMap[i] = ScrollLockMask;
164	  break;
165
166#if 0
167	case XK_Super_L:
168	case XK_Super_R:
169	  pModMap[i] = Mod4Mask;
170	  break;
171#else
172	/* Hirigana/Katakana toggle */
173	case XK_Kana_Lock:
174	case XK_Kana_Shift:
175	  pModMap[i] = KanaMask;
176	  break;
177#endif
178
179	/* alternate toggle for multinational support */
180	case XK_Mode_switch:
181	  pModMap[i] = AltLangMask;
182	  break;
183	}
184    }
185
186  pKeySyms->map        = (KeySym *) pMap;
187  pKeySyms->mapWidth   = GLYPHS_PER_KEY;
188  pKeySyms->minKeyCode = MIN_KEYCODE;
189  pKeySyms->maxKeyCode = MAX_KEYCODE;
190}
191
192
193/* Ring the keyboard bell (system speaker on PCs) */
194static void
195winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt,
196	      pointer pCtrl, int iClass)
197{
198  /*
199   * We can't use Beep () here because it uses the PC speaker
200   * on NT/2000.  MessageBeep (MB_OK) will play the default system
201   * sound on systems with a sound card or it will beep the PC speaker
202   * on systems that do not have a sound card.
203   */
204  MessageBeep (MB_OK);
205}
206
207
208/* Change some keyboard configuration parameters */
209static void
210winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl)
211{
212  g_winInternalModeKeyStatesPtr = &(pDevice->key->state);
213}
214
215
216/*
217 * See Porting Layer Definition - p. 18
218 * winKeybdProc is known as a DeviceProc.
219 */
220
221int
222winKeybdProc (DeviceIntPtr pDeviceInt, int iState)
223{
224  KeySymsRec		keySyms;
225  CARD8 		modMap[MAP_LENGTH];
226  DevicePtr		pDevice = (DevicePtr) pDeviceInt;
227#ifdef XKB
228  XkbComponentNamesRec names;
229  XkbSrvInfoPtr       xkbi;
230  XkbControlsPtr      ctrl;
231#endif
232
233  switch (iState)
234    {
235    case DEVICE_INIT:
236      winConfigKeyboard (pDeviceInt);
237
238      winGetKeyMappings (&keySyms, modMap);
239
240#ifdef XKB
241      /* FIXME: Maybe we should use winGetKbdLeds () here? */
242      defaultKeyboardControl.leds = g_winInfo.keyboard.leds;
243#else
244      defaultKeyboardControl.leds = g_winInfo.keyboard.leds;
245#endif
246
247#ifdef XKB
248      if (g_winInfo.xkb.disable)
249	{
250#endif
251	  InitKeyboardDeviceStruct (pDevice,
252				    &keySyms,
253				    modMap,
254				    winKeybdBell,
255				    winKeybdCtrl);
256#ifdef XKB
257	}
258      else
259	{
260
261          names.keymap = g_winInfo.xkb.keymap;
262          names.keycodes = g_winInfo.xkb.keycodes;
263          names.types = g_winInfo.xkb.types;
264          names.compat = g_winInfo.xkb.compat;
265          names.symbols = g_winInfo.xkb.symbols;
266          names.geometry = g_winInfo.xkb.geometry;
267
268	  winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\""
269		 " Variant = \"%s\" Options = \"%s\"\n",
270		 g_winInfo.xkb.rules, g_winInfo.xkb.model,
271		 g_winInfo.xkb.layout, g_winInfo.xkb.variant,
272		 g_winInfo.xkb.options);
273
274	  XkbSetRulesDflts (g_winInfo.xkb.rules, g_winInfo.xkb.model,
275			    g_winInfo.xkb.layout, g_winInfo.xkb.variant,
276			    g_winInfo.xkb.options);
277	  XkbInitKeyboardDeviceStruct (pDeviceInt, &names, &keySyms,
278				       modMap, winKeybdBell, winKeybdCtrl);
279	}
280#endif
281
282#ifdef XKB
283      if (!g_winInfo.xkb.disable)
284        {
285          xkbi = pDeviceInt->key->xkbInfo;
286          if (xkbi != NULL)
287            {
288              ctrl = xkbi->desc->ctrls;
289              ctrl->repeat_delay = g_winInfo.keyboard.delay;
290              ctrl->repeat_interval = 1000/g_winInfo.keyboard.rate;
291            }
292          else
293            {
294              winErrorFVerb (1, "winKeybdProc - Error initializing keyboard AutoRepeat (No XKB)\n");
295            }
296        }
297#endif
298
299      g_winInternalModeKeyStatesPtr = &(pDeviceInt->key->state);
300      break;
301
302    case DEVICE_ON:
303      pDevice->on = TRUE;
304      g_winInternalModeKeyStatesPtr = &(pDeviceInt->key->state);
305      break;
306
307    case DEVICE_CLOSE:
308    case DEVICE_OFF:
309      pDevice->on = FALSE;
310      g_winInternalModeKeyStatesPtr = NULL;
311      break;
312    }
313
314  return Success;
315}
316
317
318/*
319 * Detect current mode key states upon server startup.
320 *
321 * Simulate a press and release of any key that is currently
322 * toggled.
323 */
324
325void
326winInitializeModeKeyStates (void)
327{
328  /* Restore NumLock */
329  if (GetKeyState (VK_NUMLOCK) & 0x0001)
330    {
331      winSendKeyEvent (KEY_NumLock, TRUE);
332      winSendKeyEvent (KEY_NumLock, FALSE);
333    }
334
335  /* Restore CapsLock */
336  if (GetKeyState (VK_CAPITAL) & 0x0001)
337    {
338      winSendKeyEvent (KEY_CapsLock, TRUE);
339      winSendKeyEvent (KEY_CapsLock, FALSE);
340    }
341
342  /* Restore ScrollLock */
343  if (GetKeyState (VK_SCROLL) & 0x0001)
344    {
345      winSendKeyEvent (KEY_ScrollLock, TRUE);
346      winSendKeyEvent (KEY_ScrollLock, FALSE);
347    }
348
349  /* Restore KanaLock */
350  if (GetKeyState (VK_KANA) & 0x0001)
351    {
352      winSendKeyEvent (KEY_HKTG, TRUE);
353      winSendKeyEvent (KEY_HKTG, FALSE);
354    }
355}
356
357
358/*
359 * Upon regaining the keyboard focus we must
360 * resynchronize our internal mode key states
361 * with the actual state of the keys.
362 */
363
364void
365winRestoreModeKeyStates ()
366{
367  DWORD			dwKeyState;
368  BOOL			processEvents = TRUE;
369  unsigned short	internalKeyStates;
370
371  /* X server is being initialized */
372  if (!g_winInternalModeKeyStatesPtr)
373    return;
374
375  /* Only process events if the rootwindow is mapped. The keyboard events
376   * will cause segfaults otherwise */
377  if (WindowTable && WindowTable[0] && WindowTable[0]->mapped == FALSE)
378    processEvents = FALSE;
379
380  /* Force to process all pending events in the mi event queue */
381  if (processEvents)
382    mieqProcessInputEvents ();
383
384  /* Read the mode key states of our X server */
385  internalKeyStates = *g_winInternalModeKeyStatesPtr;
386
387  /*
388   * NOTE: The C XOR operator, ^, will not work here because it is
389   * a bitwise operator, not a logical operator.  C does not
390   * have a logical XOR operator, so we use a macro instead.
391   */
392
393  /* Has the key state changed? */
394  dwKeyState = GetKeyState (VK_NUMLOCK) & 0x0001;
395  if (WIN_XOR (internalKeyStates & NumLockMask, dwKeyState))
396    {
397      winSendKeyEvent (KEY_NumLock, TRUE);
398      winSendKeyEvent (KEY_NumLock, FALSE);
399    }
400
401  /* Has the key state changed? */
402  dwKeyState = GetKeyState (VK_CAPITAL) & 0x0001;
403  if (WIN_XOR (internalKeyStates & LockMask, dwKeyState))
404    {
405      winSendKeyEvent (KEY_CapsLock, TRUE);
406      winSendKeyEvent (KEY_CapsLock, FALSE);
407    }
408
409  /* Has the key state changed? */
410  dwKeyState = GetKeyState (VK_SCROLL) & 0x0001;
411  if (WIN_XOR (internalKeyStates & ScrollLockMask, dwKeyState))
412    {
413      winSendKeyEvent (KEY_ScrollLock, TRUE);
414      winSendKeyEvent (KEY_ScrollLock, FALSE);
415    }
416
417  /* Has the key state changed? */
418  dwKeyState = GetKeyState (VK_KANA) & 0x0001;
419  if (WIN_XOR (internalKeyStates & KanaMask, dwKeyState))
420    {
421      winSendKeyEvent (KEY_HKTG, TRUE);
422      winSendKeyEvent (KEY_HKTG, FALSE);
423    }
424}
425
426
427/*
428 * Look for the lovely fake Control_L press/release generated by Windows
429 * when AltGr is pressed/released on a non-U.S. keyboard.
430 */
431
432Bool
433winIsFakeCtrl_L (UINT message, WPARAM wParam, LPARAM lParam)
434{
435  MSG		msgNext;
436  LONG		lTime;
437  Bool		fReturn;
438
439  /*
440   * Fake Ctrl_L presses will be followed by an Alt_R keypress
441   * with the same timestamp as the Ctrl_L press.
442   */
443  if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
444      && wParam == VK_CONTROL
445      && (HIWORD (lParam) & KF_EXTENDED) == 0)
446    {
447      /* Got a Ctrl_L press */
448
449      /* Get time of current message */
450      lTime = GetMessageTime ();
451
452      /* Look for fake Ctrl_L preceeding an Alt_R press. */
453      fReturn = PeekMessage (&msgNext, NULL,
454			     WM_KEYDOWN, WM_SYSKEYDOWN,
455			     PM_NOREMOVE);
456
457      /*
458       * Try again if the first call fails.
459       * NOTE: This usually happens when TweakUI is enabled.
460       */
461      if (!fReturn)
462	{
463	  /* Voodoo to make sure that the Alt_R message has posted */
464	  Sleep (0);
465
466	  /* Look for fake Ctrl_L preceeding an Alt_R press. */
467	  fReturn = PeekMessage (&msgNext, NULL,
468				 WM_KEYDOWN, WM_SYSKEYDOWN,
469				 PM_NOREMOVE);
470	}
471      if (msgNext.message != WM_KEYDOWN && msgNext.message != WM_SYSKEYDOWN)
472          fReturn = 0;
473
474      /* Is next press an Alt_R with the same timestamp? */
475      if (fReturn && msgNext.wParam == VK_MENU
476	  && msgNext.time == lTime
477	  && (HIWORD (msgNext.lParam) & KF_EXTENDED))
478	{
479	  /*
480	   * Next key press is Alt_R with same timestamp as current
481	   * Ctrl_L message.  Therefore, this Ctrl_L press is a fake
482	   * event, so discard it.
483	   */
484	  return TRUE;
485	}
486    }
487
488  /*
489   * Fake Ctrl_L releases will be followed by an Alt_R release
490   * with the same timestamp as the Ctrl_L release.
491   */
492  if ((message == WM_KEYUP || message == WM_SYSKEYUP)
493      && wParam == VK_CONTROL
494      && (HIWORD (lParam) & KF_EXTENDED) == 0)
495    {
496      /* Got a Ctrl_L release */
497
498      /* Get time of current message */
499      lTime = GetMessageTime ();
500
501      /* Look for fake Ctrl_L release preceeding an Alt_R release. */
502      fReturn = PeekMessage (&msgNext, NULL,
503			     WM_KEYUP, WM_SYSKEYUP,
504			     PM_NOREMOVE);
505
506      /*
507       * Try again if the first call fails.
508       * NOTE: This usually happens when TweakUI is enabled.
509       */
510      if (!fReturn)
511	{
512	  /* Voodoo to make sure that the Alt_R message has posted */
513	  Sleep (0);
514
515	  /* Look for fake Ctrl_L release preceeding an Alt_R release. */
516	  fReturn = PeekMessage (&msgNext, NULL,
517				 WM_KEYUP, WM_SYSKEYUP,
518				 PM_NOREMOVE);
519	}
520
521      if (msgNext.message != WM_KEYUP && msgNext.message != WM_SYSKEYUP)
522          fReturn = 0;
523
524      /* Is next press an Alt_R with the same timestamp? */
525      if (fReturn
526	  && (msgNext.message == WM_KEYUP
527	      || msgNext.message == WM_SYSKEYUP)
528	  && msgNext.wParam == VK_MENU
529	  && msgNext.time == lTime
530	  && (HIWORD (msgNext.lParam) & KF_EXTENDED))
531	{
532	  /*
533	   * Next key release is Alt_R with same timestamp as current
534	   * Ctrl_L message. Therefore, this Ctrl_L release is a fake
535	   * event, so discard it.
536	   */
537	  return TRUE;
538	}
539    }
540
541  /* Not a fake control left press/release */
542  return FALSE;
543}
544
545
546/*
547 * Lift any modifier keys that are pressed
548 */
549
550void
551winKeybdReleaseKeys ()
552{
553  int				i;
554
555#ifdef HAS_DEVWINDOWS
556  /* Verify that the mi input system has been initialized */
557  if (g_fdMessageQueue == WIN_FD_INVALID)
558    return;
559#endif
560
561  /* Loop through all keys */
562  for (i = 0; i < NUM_KEYCODES; ++i)
563    {
564      /* Pop key if pressed */
565      if (g_winKeyState[i])
566	winSendKeyEvent (i, FALSE);
567
568      /* Reset pressed flag for keys */
569      g_winKeyState[i] = FALSE;
570    }
571}
572
573
574/*
575 * Take a raw X key code and send an up or down event for it.
576 *
577 * Thanks to VNC for inspiration, though it is a simple function.
578 */
579
580void
581winSendKeyEvent (DWORD dwKey, Bool fDown)
582{
583  xEvent			xCurrentEvent;
584
585  /*
586   * When alt-tabing between screens we can get phantom key up messages
587   * Here we only pass them through it we think we should!
588   */
589  if (g_winKeyState[dwKey] == FALSE && fDown == FALSE) return;
590
591  /* Update the keyState map */
592  g_winKeyState[dwKey] = fDown;
593
594  ZeroMemory (&xCurrentEvent, sizeof (xCurrentEvent));
595
596  xCurrentEvent.u.u.type = fDown ? KeyPress : KeyRelease;
597  xCurrentEvent.u.keyButtonPointer.time =
598    g_c32LastInputEventTime = GetTickCount ();
599  xCurrentEvent.u.u.detail = dwKey + MIN_KEYCODE;
600  mieqEnqueue (&xCurrentEvent);
601}
602
603BOOL winCheckKeyPressed(WPARAM wParam, LPARAM lParam)
604{
605  switch (wParam)
606  {
607    case VK_CONTROL:
608      if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl])
609        return TRUE;
610      if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl])
611        return TRUE;
612      break;
613    case VK_SHIFT:
614      if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR])
615        return TRUE;
616      if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL])
617        return TRUE;
618      break;
619    default:
620      return TRUE;
621  }
622  return FALSE;
623}
624
625/* Only on shift release message is sent even if both are pressed.
626 * Fix this here
627 */
628void winFixShiftKeys (int iScanCode)
629{
630  if (GetKeyState (VK_SHIFT) & 0x8000)
631    return;
632
633  if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR])
634    winSendKeyEvent (KEY_ShiftR, FALSE);
635  if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL])
636    winSendKeyEvent (KEY_ShiftL, FALSE);
637}
638