532 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			532 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
 | |
|  *
 | |
|  *Permission is hereby granted, free of charge, to any person obtaining
 | |
|  * a copy of this software and associated documentation files (the
 | |
|  *"Software"), to deal in the Software without restriction, including
 | |
|  *without limitation the rights to use, copy, modify, merge, publish,
 | |
|  *distribute, sublicense, and/or sell copies of the Software, and to
 | |
|  *permit persons to whom the Software is furnished to do so, subject to
 | |
|  *the following conditions:
 | |
|  *
 | |
|  *The above copyright notice and this permission notice shall be
 | |
|  *included in all copies or substantial portions of the Software.
 | |
|  *
 | |
|  *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|  *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|  *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
|  *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
 | |
|  *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 | |
|  *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | |
|  *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  *Except as contained in this notice, the name of the XFree86 Project
 | |
|  *shall not be used in advertising or otherwise to promote the sale, use
 | |
|  *or other dealings in this Software without prior written authorization
 | |
|  *from the XFree86 Project.
 | |
|  *
 | |
|  * Authors:	Dakshinamurthy Karra
 | |
|  *		Suhaib M Siddiqi
 | |
|  *		Peter Busch
 | |
|  *		Harold L Hunt II
 | |
|  */
 | |
| 
 | |
| 
 | |
| #ifdef HAVE_XWIN_CONFIG_H
 | |
| #include <xwin-config.h>
 | |
| #endif
 | |
| #include "win.h"
 | |
| #include "winkeybd.h"
 | |
| #include "winconfig.h"
 | |
| #include "winmsg.h"
 | |
| 
 | |
| #include "xkbsrv.h"
 | |
| 
 | |
| static Bool g_winKeyState[NUM_KEYCODES];
 | |
| 
 | |
| /*
 | |
|  * Local prototypes
 | |
|  */
 | |
| 
 | |
| static void
 | |
| winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt,
 | |
| 	      pointer pCtrl, int iClass);
 | |
| 
 | |
| static void
 | |
| winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl);
 | |
| 
 | |
| 
 | |
| /* 
 | |
|  * Translate a Windows WM_[SYS]KEY(UP/DOWN) message
 | |
|  * into an ASCII scan code.
 | |
|  *
 | |
|  * We do this ourselves, rather than letting Windows handle it,
 | |
|  * because Windows tends to munge the handling of special keys,
 | |
|  * like AltGr on European keyboards.
 | |
|  */
 | |
| 
 | |
| void
 | |
| winTranslateKey (WPARAM wParam, LPARAM lParam, int *piScanCode)
 | |
| {
 | |
|   int		iKeyFixup = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 1];
 | |
|   int		iKeyFixupEx = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 2];
 | |
|   int		iParam = HIWORD (lParam);
 | |
|   int		iParamScanCode = LOBYTE (iParam);
 | |
| 
 | |
| /* WM_ key messages faked by Vista speech recognition (WSR) don't have a
 | |
|  * scan code.
 | |
|  *
 | |
|  * Vocola 3 (Rick Mohr's supplement to WSR) uses
 | |
|  * System.Windows.Forms.SendKeys.SendWait(), which appears always to give a
 | |
|  * scan code of 1
 | |
|  */
 | |
|   if (iParamScanCode <= 1)
 | |
|     {
 | |
|       if (VK_PRIOR <= wParam && wParam <= VK_DOWN)
 | |
|         /* Trigger special case table to translate to extended
 | |
|          * keycode, otherwise if num_lock is on, we can get keypad
 | |
|          * numbers instead of navigation keys. */
 | |
|         iParam |= KF_EXTENDED;
 | |
|       else
 | |
|         iParamScanCode = MapVirtualKeyEx(wParam,
 | |
|                          /*MAPVK_VK_TO_VSC*/0,
 | |
|                          GetKeyboardLayout(0));
 | |
|     }
 | |
| 
 | |
|   /* Branch on special extended, special non-extended, or normal key */
 | |
|   if ((iParam & KF_EXTENDED) && iKeyFixupEx)
 | |
|     *piScanCode = iKeyFixupEx;
 | |
|   else if (iKeyFixup)
 | |
|     *piScanCode = iKeyFixup;
 | |
|   else if (wParam == 0 && iParamScanCode == 0x70)
 | |
|     *piScanCode = KEY_HKTG;
 | |
|   else
 | |
|     switch (iParamScanCode)
 | |
|     {
 | |
|       case 0x70:
 | |
|         *piScanCode = KEY_HKTG;
 | |
|         break;
 | |
|       case 0x73:
 | |
|         *piScanCode = KEY_BSlash2;
 | |
|         break;
 | |
|       default: 
 | |
|         *piScanCode = iParamScanCode;
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Ring the keyboard bell (system speaker on PCs) */
 | |
| static void
 | |
| winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt,
 | |
| 	      pointer pCtrl, int iClass)
 | |
| {
 | |
|   /*
 | |
|    * We can't use Beep () here because it uses the PC speaker
 | |
|    * on NT/2000.  MessageBeep (MB_OK) will play the default system
 | |
|    * sound on systems with a sound card or it will beep the PC speaker
 | |
|    * on systems that do not have a sound card.
 | |
|    */
 | |
|   MessageBeep (MB_OK);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Change some keyboard configuration parameters */
 | |
| static void
 | |
| winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl)
 | |
| {
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 
 | |
|  * See Porting Layer Definition - p. 18
 | |
|  * winKeybdProc is known as a DeviceProc.
 | |
|  */
 | |
| 
 | |
| int
 | |
| winKeybdProc (DeviceIntPtr pDeviceInt, int iState)
 | |
| {
 | |
|   DevicePtr		pDevice = (DevicePtr) pDeviceInt;
 | |
|   XkbSrvInfoPtr       xkbi;
 | |
|   XkbControlsPtr      ctrl;
 | |
| 
 | |
|   switch (iState)
 | |
|     {
 | |
|     case DEVICE_INIT:
 | |
|       winConfigKeyboard (pDeviceInt);
 | |
| 
 | |
|       /* FIXME: Maybe we should use winGetKbdLeds () here? */
 | |
|       defaultKeyboardControl.leds = g_winInfo.keyboard.leds;
 | |
| 
 | |
|       winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\""
 | |
|                     " Variant = \"%s\" Options = \"%s\"\n",
 | |
|                     g_winInfo.xkb.rules ? g_winInfo.xkb.rules : "none",
 | |
|                     g_winInfo.xkb.model ? g_winInfo.xkb.model : "none",
 | |
|                     g_winInfo.xkb.layout ? g_winInfo.xkb.layout : "none",
 | |
|                     g_winInfo.xkb.variant ? g_winInfo.xkb.variant : "none",
 | |
|                     g_winInfo.xkb.options ? g_winInfo.xkb.options : "none");
 | |
| 
 | |
|       InitKeyboardDeviceStruct (pDeviceInt,
 | |
|                                 &g_winInfo.xkb,
 | |
|                                 winKeybdBell,
 | |
|                                 winKeybdCtrl);
 | |
| 
 | |
|       xkbi = pDeviceInt->key->xkbInfo;
 | |
|       if ((xkbi != NULL) && (xkbi->desc != NULL))
 | |
|         {
 | |
|           ctrl = xkbi->desc->ctrls;
 | |
|           ctrl->repeat_delay = g_winInfo.keyboard.delay;
 | |
|           ctrl->repeat_interval = 1000/g_winInfo.keyboard.rate;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           winErrorFVerb (1, "winKeybdProc - Error initializing keyboard AutoRepeat\n");
 | |
|         }
 | |
| 
 | |
|       break;
 | |
|       
 | |
|     case DEVICE_ON: 
 | |
|       pDevice->on = TRUE;
 | |
| 
 | |
|       // immediately copy the state of this keyboard device to the VCK
 | |
|       // (which otherwise happens lazily after the first keypress)
 | |
|       CopyKeyClass(pDeviceInt, inputInfo.keyboard);
 | |
|       break;
 | |
| 
 | |
|     case DEVICE_CLOSE:
 | |
|     case DEVICE_OFF: 
 | |
|       pDevice->on = FALSE;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   return Success;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Detect current mode key states upon server startup.
 | |
|  *
 | |
|  * Simulate a press and release of any key that is currently
 | |
|  * toggled.
 | |
|  */
 | |
| 
 | |
| void
 | |
| winInitializeModeKeyStates (void)
 | |
| {
 | |
|   /* Restore NumLock */
 | |
|   if (GetKeyState (VK_NUMLOCK) & 0x0001)
 | |
|     {
 | |
|       winSendKeyEvent (KEY_NumLock, TRUE);
 | |
|       winSendKeyEvent (KEY_NumLock, FALSE);
 | |
|     }
 | |
| 
 | |
|   /* Restore CapsLock */
 | |
|   if (GetKeyState (VK_CAPITAL) & 0x0001)
 | |
|     {
 | |
|       winSendKeyEvent (KEY_CapsLock, TRUE);
 | |
|       winSendKeyEvent (KEY_CapsLock, FALSE);
 | |
|     }
 | |
| 
 | |
|   /* Restore ScrollLock */
 | |
|   if (GetKeyState (VK_SCROLL) & 0x0001)
 | |
|     {
 | |
|       winSendKeyEvent (KEY_ScrollLock, TRUE);
 | |
|       winSendKeyEvent (KEY_ScrollLock, FALSE);
 | |
|     }
 | |
| 
 | |
|   /* Restore KanaLock */
 | |
|   if (GetKeyState (VK_KANA) & 0x0001)
 | |
|     {
 | |
|       winSendKeyEvent (KEY_HKTG, TRUE);
 | |
|       winSendKeyEvent (KEY_HKTG, FALSE);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Upon regaining the keyboard focus we must
 | |
|  * resynchronize our internal mode key states
 | |
|  * with the actual state of the keys.
 | |
|  */
 | |
| 
 | |
| void
 | |
| winRestoreModeKeyStates (void)
 | |
| {
 | |
|   DWORD			dwKeyState;
 | |
|   BOOL			processEvents = TRUE;
 | |
|   unsigned short	internalKeyStates;
 | |
| 
 | |
|   /* X server is being initialized */
 | |
|   if (!inputInfo.keyboard)
 | |
|     return;
 | |
| 
 | |
|   /* Only process events if the rootwindow is mapped. The keyboard events
 | |
|    * will cause segfaults otherwise */
 | |
|   if (screenInfo.screens[0]->root && screenInfo.screens[0]->root->mapped == FALSE)
 | |
|     processEvents = FALSE;    
 | |
|   
 | |
|   /* Force to process all pending events in the mi event queue */
 | |
|   if (processEvents)
 | |
|     mieqProcessInputEvents ();
 | |
|   
 | |
|   /* Read the mode key states of our X server */
 | |
|   /* (stored in the virtual core keyboard) */
 | |
|   internalKeyStates = XkbStateFieldFromRec(&inputInfo.keyboard->key->xkbInfo->state);
 | |
|   winDebug("winRestoreModeKeyStates: state %d\n", internalKeyStates);
 | |
| 
 | |
|   /* 
 | |
|    * NOTE: The C XOR operator, ^, will not work here because it is
 | |
|    * a bitwise operator, not a logical operator.  C does not
 | |
|    * have a logical XOR operator, so we use a macro instead.
 | |
|    */
 | |
| 
 | |
|   /* Has the key state changed? */
 | |
|   dwKeyState = GetKeyState (VK_NUMLOCK) & 0x0001;
 | |
|   if (WIN_XOR (internalKeyStates & NumLockMask, dwKeyState))
 | |
|     {
 | |
|       winSendKeyEvent (KEY_NumLock, TRUE);
 | |
|       winSendKeyEvent (KEY_NumLock, FALSE);
 | |
|     }
 | |
| 
 | |
|   /* Has the key state changed? */
 | |
|   dwKeyState = GetKeyState (VK_CAPITAL) & 0x0001;
 | |
|   if (WIN_XOR (internalKeyStates & LockMask, dwKeyState))
 | |
|     {
 | |
|       winSendKeyEvent (KEY_CapsLock, TRUE);
 | |
|       winSendKeyEvent (KEY_CapsLock, FALSE);
 | |
|     }
 | |
| 
 | |
|   /* Has the key state changed? */
 | |
|   dwKeyState = GetKeyState (VK_SCROLL) & 0x0001;
 | |
|   if (WIN_XOR (internalKeyStates & ScrollLockMask, dwKeyState))
 | |
|     {
 | |
|       winSendKeyEvent (KEY_ScrollLock, TRUE);
 | |
|       winSendKeyEvent (KEY_ScrollLock, FALSE);
 | |
|     }
 | |
| 
 | |
|   /* Has the key state changed? */
 | |
|   dwKeyState = GetKeyState (VK_KANA) & 0x0001;
 | |
|   if (WIN_XOR (internalKeyStates & KanaMask, dwKeyState))
 | |
|     {
 | |
|       winSendKeyEvent (KEY_HKTG, TRUE);
 | |
|       winSendKeyEvent (KEY_HKTG, FALSE);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Look for the lovely fake Control_L press/release generated by Windows
 | |
|  * when AltGr is pressed/released on a non-U.S. keyboard.
 | |
|  */
 | |
| 
 | |
| Bool
 | |
| winIsFakeCtrl_L (UINT message, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   MSG		msgNext;
 | |
|   LONG		lTime;
 | |
|   Bool		fReturn;
 | |
| 
 | |
|   /*
 | |
|    * Fake Ctrl_L presses will be followed by an Alt_R keypress
 | |
|    * with the same timestamp as the Ctrl_L press.
 | |
|    */
 | |
|   if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
 | |
|       && wParam == VK_CONTROL
 | |
|       && (HIWORD (lParam) & KF_EXTENDED) == 0)
 | |
|     {
 | |
|       /* Got a Ctrl_L press */
 | |
| 
 | |
|       /* Get time of current message */
 | |
|       lTime = GetMessageTime ();
 | |
| 
 | |
|       /* Look for fake Ctrl_L preceeding an Alt_R press. */
 | |
|       fReturn = PeekMessage (&msgNext, NULL,
 | |
| 			     WM_KEYDOWN, WM_SYSKEYDOWN,
 | |
| 			     PM_NOREMOVE);
 | |
| 
 | |
|       /*
 | |
|        * Try again if the first call fails.
 | |
|        * NOTE: This usually happens when TweakUI is enabled.
 | |
|        */
 | |
|       if (!fReturn)
 | |
| 	{
 | |
| 	  /* Voodoo to make sure that the Alt_R message has posted */
 | |
| 	  Sleep (0);
 | |
| 
 | |
| 	  /* Look for fake Ctrl_L preceeding an Alt_R press. */
 | |
| 	  fReturn = PeekMessage (&msgNext, NULL,
 | |
| 				 WM_KEYDOWN, WM_SYSKEYDOWN,
 | |
| 				 PM_NOREMOVE);
 | |
| 	}
 | |
|       if (msgNext.message != WM_KEYDOWN && msgNext.message != WM_SYSKEYDOWN)
 | |
|           fReturn = 0;
 | |
| 
 | |
|       /* Is next press an Alt_R with the same timestamp? */
 | |
|       if (fReturn && msgNext.wParam == VK_MENU
 | |
| 	  && msgNext.time == lTime
 | |
| 	  && (HIWORD (msgNext.lParam) & KF_EXTENDED))
 | |
| 	{
 | |
| 	  /* 
 | |
| 	   * Next key press is Alt_R with same timestamp as current
 | |
| 	   * Ctrl_L message.  Therefore, this Ctrl_L press is a fake
 | |
| 	   * event, so discard it.
 | |
| 	   */
 | |
| 	  return TRUE;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* 
 | |
|    * Fake Ctrl_L releases will be followed by an Alt_R release
 | |
|    * with the same timestamp as the Ctrl_L release.
 | |
|    */
 | |
|   if ((message == WM_KEYUP || message == WM_SYSKEYUP)
 | |
|       && wParam == VK_CONTROL
 | |
|       && (HIWORD (lParam) & KF_EXTENDED) == 0)
 | |
|     {
 | |
|       /* Got a Ctrl_L release */
 | |
| 
 | |
|       /* Get time of current message */
 | |
|       lTime = GetMessageTime ();
 | |
| 
 | |
|       /* Look for fake Ctrl_L release preceeding an Alt_R release. */
 | |
|       fReturn = PeekMessage (&msgNext, NULL,
 | |
| 			     WM_KEYUP, WM_SYSKEYUP, 
 | |
| 			     PM_NOREMOVE);
 | |
| 
 | |
|       /*
 | |
|        * Try again if the first call fails.
 | |
|        * NOTE: This usually happens when TweakUI is enabled.
 | |
|        */
 | |
|       if (!fReturn)
 | |
| 	{
 | |
| 	  /* Voodoo to make sure that the Alt_R message has posted */
 | |
| 	  Sleep (0);
 | |
| 
 | |
| 	  /* Look for fake Ctrl_L release preceeding an Alt_R release. */
 | |
| 	  fReturn = PeekMessage (&msgNext, NULL,
 | |
| 				 WM_KEYUP, WM_SYSKEYUP, 
 | |
| 				 PM_NOREMOVE);
 | |
| 	}
 | |
| 
 | |
|       if (msgNext.message != WM_KEYUP && msgNext.message != WM_SYSKEYUP)
 | |
|           fReturn = 0;
 | |
|       
 | |
|       /* Is next press an Alt_R with the same timestamp? */
 | |
|       if (fReturn
 | |
| 	  && (msgNext.message == WM_KEYUP
 | |
| 	      || msgNext.message == WM_SYSKEYUP)
 | |
| 	  && msgNext.wParam == VK_MENU
 | |
| 	  && msgNext.time == lTime
 | |
| 	  && (HIWORD (msgNext.lParam) & KF_EXTENDED))
 | |
| 	{
 | |
| 	  /*
 | |
| 	   * Next key release is Alt_R with same timestamp as current
 | |
| 	   * Ctrl_L message. Therefore, this Ctrl_L release is a fake
 | |
| 	   * event, so discard it.
 | |
| 	   */
 | |
| 	  return TRUE;
 | |
| 	}
 | |
|     }
 | |
|   
 | |
|   /* Not a fake control left press/release */
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Lift any modifier keys that are pressed
 | |
|  */
 | |
| 
 | |
| void
 | |
| winKeybdReleaseKeys (void)
 | |
| {
 | |
|   int				i;
 | |
| 
 | |
| #ifdef HAS_DEVWINDOWS
 | |
|   /* Verify that the mi input system has been initialized */
 | |
|   if (g_fdMessageQueue == WIN_FD_INVALID)
 | |
|     return;
 | |
| #endif
 | |
| 
 | |
|   /* Loop through all keys */
 | |
|   for (i = 0; i < NUM_KEYCODES; ++i)
 | |
|     {
 | |
|       /* Pop key if pressed */
 | |
|       if (g_winKeyState[i])
 | |
| 	winSendKeyEvent (i, FALSE);
 | |
| 
 | |
|       /* Reset pressed flag for keys */
 | |
|       g_winKeyState[i] = FALSE;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Take a raw X key code and send an up or down event for it.
 | |
|  *
 | |
|  * Thanks to VNC for inspiration, though it is a simple function.
 | |
|  */
 | |
| 
 | |
| void
 | |
| winSendKeyEvent (DWORD dwKey, Bool fDown)
 | |
| {
 | |
|   EventListPtr events;
 | |
|   int i, nevents;
 | |
| 
 | |
|   /*
 | |
|    * When alt-tabing between screens we can get phantom key up messages
 | |
|    * Here we only pass them through it we think we should!
 | |
|    */
 | |
|   if (g_winKeyState[dwKey] == FALSE && fDown == FALSE) return;
 | |
| 
 | |
|   /* Update the keyState map */
 | |
|   g_winKeyState[dwKey] = fDown;
 | |
| 
 | |
|   GetEventList(&events);
 | |
|   nevents = GetKeyboardEvents(events, g_pwinKeyboard, fDown ? KeyPress : KeyRelease, dwKey + MIN_KEYCODE);
 | |
| 
 | |
|   for (i = 0; i < nevents; i++)
 | |
|     mieqEnqueue(g_pwinKeyboard, events[i].event);
 | |
| 
 | |
| #if CYGDEBUG
 | |
|   ErrorF("winSendKeyEvent: dwKey: %d, fDown: %d, nEvents %d\n",
 | |
|           dwKey, fDown, nevents);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| BOOL winCheckKeyPressed(WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   switch (wParam)
 | |
|   {
 | |
|     case VK_CONTROL:
 | |
|       if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl])
 | |
|         return TRUE;
 | |
|       if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl])
 | |
|         return TRUE;
 | |
|       break;
 | |
|     case VK_SHIFT:
 | |
|       if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR])
 | |
|         return TRUE;
 | |
|       if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL])
 | |
|         return TRUE;
 | |
|       break;
 | |
|     default:
 | |
|       return TRUE;
 | |
|   }
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /* Only on shift release message is sent even if both are pressed.
 | |
|  * Fix this here 
 | |
|  */
 | |
| void winFixShiftKeys (int iScanCode)
 | |
| {
 | |
|   if (GetKeyState (VK_SHIFT) & 0x8000)
 | |
|     return;
 | |
| 
 | |
|   if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR])
 | |
|     winSendKeyEvent (KEY_ShiftR, FALSE);
 | |
|   if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL])
 | |
|     winSendKeyEvent (KEY_ShiftL, FALSE);
 | |
| }
 |