428261197a
Tested by ajacoutot@, krw@, shadchin@ and jasper@ on various configurations including multihead with both zaphod and xrandr.
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);
|
|
}
|