xserver-multidpi/hw/xwin/winkeybd.c

591 lines
15 KiB
C
Raw Normal View History

2003-11-14 17:48:57 +01:00
/*
*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
*/
2004-04-23 21:54:30 +02:00
/* $XFree86: xc/programs/Xserver/hw/xwin/winkeybd.c,v 1.12 2002/10/17 08:18:22 alanh Exp $ */
2003-11-14 17:48:57 +01:00
#include "win.h"
#include "winkeybd.h"
#include "winconfig.h"
2004-06-21 15:19:32 +02:00
#include "winmsg.h"
2003-11-14 17:48:57 +01:00
#ifdef XKB
#define XKB_IN_SERVER
#include "XKBsrv.h"
#endif
static Bool g_winKeyState[NUM_KEYCODES];
2004-06-21 15:19:32 +02:00
/* Stored to get internal mode key states. Must be read-only. */
static unsigned short const *g_winInternalModeKeyStatesPtr = NULL;
2003-11-14 17:48:57 +01:00
2004-06-21 15:19:32 +02:00
/*
* Local prototypes
*/
2003-11-14 17:48:57 +01:00
2004-06-21 15:19:32 +02:00
static void
winGetKeyMappings (KeySymsPtr pKeySyms, CARD8 *pModMap);
2003-11-14 17:48:57 +01:00
2004-06-21 15:19:32 +02:00
static void
winKeybdBell (int iPercent, DeviceIntPtr pDeviceInt,
pointer pCtrl, int iClass);
2003-11-14 17:48:57 +01:00
2004-06-21 15:19:32 +02:00
static void
winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl);
2003-11-14 17:48:57 +01:00
/*
* 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];
/* Branch on special extended, special non-extended, or normal key */
if ((HIWORD (lParam) & KF_EXTENDED) && iKeyFixupEx)
*piScanCode = iKeyFixupEx;
else if (iKeyFixup)
*piScanCode = iKeyFixup;
else
*piScanCode = LOBYTE (HIWORD (lParam));
}
/*
* We call this function from winKeybdProc when we are
* initializing the keyboard.
*/
2004-06-21 15:19:32 +02:00
static void
2003-11-14 17:48:57 +01:00
winGetKeyMappings (KeySymsPtr pKeySyms, CARD8 *pModMap)
{
int i;
KeySym *pMap = map;
KeySym *pKeySym;
/*
* Initialize all key states to up... which may not be true
* but it is close enough.
*/
ZeroMemory (g_winKeyState, sizeof (g_winKeyState[0]) * NUM_KEYCODES);
/* MAP_LENGTH is defined in Xserver/include/input.h to be 256 */
for (i = 0; i < MAP_LENGTH; i++)
pModMap[i] = NoSymbol; /* make sure it is restored */
/* Loop through all valid entries in the key symbol table */
for (pKeySym = pMap, i = MIN_KEYCODE;
i < (MIN_KEYCODE + NUM_KEYCODES);
i++, pKeySym += GLYPHS_PER_KEY)
{
switch (*pKeySym)
{
case XK_Shift_L:
case XK_Shift_R:
pModMap[i] = ShiftMask;
break;
case XK_Control_L:
case XK_Control_R:
pModMap[i] = ControlMask;
break;
case XK_Caps_Lock:
pModMap[i] = LockMask;
break;
case XK_Alt_L:
case XK_Alt_R:
pModMap[i] = AltMask;
break;
case XK_Num_Lock:
pModMap[i] = NumLockMask;
break;
case XK_Scroll_Lock:
pModMap[i] = ScrollLockMask;
break;
2004-06-21 15:19:32 +02:00
#if 0
case XK_Super_L:
case XK_Super_R:
pModMap[i] = Mod4Mask;
break;
#else
2003-11-14 17:48:57 +01:00
/* Hirigana/Katakana toggle */
case XK_Kana_Lock:
case XK_Kana_Shift:
pModMap[i] = KanaMask;
break;
#endif
/* alternate toggle for multinational support */
case XK_Mode_switch:
pModMap[i] = AltLangMask;
break;
}
}
pKeySyms->map = (KeySym *) pMap;
pKeySyms->mapWidth = GLYPHS_PER_KEY;
pKeySyms->minKeyCode = MIN_KEYCODE;
pKeySyms->maxKeyCode = MAX_KEYCODE;
}
/* Ring the keyboard bell (system speaker on PCs) */
2004-06-21 15:19:32 +02:00
static void
2003-11-14 17:48:57 +01:00
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 */
2004-06-21 15:19:32 +02:00
static void
2003-11-14 17:48:57 +01:00
winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl)
{
2004-06-21 15:19:32 +02:00
g_winInternalModeKeyStatesPtr = &(pDevice->key->state);
2003-11-14 17:48:57 +01:00
}
/*
* See Porting Layer Definition - p. 18
* winKeybdProc is known as a DeviceProc.
*/
int
winKeybdProc (DeviceIntPtr pDeviceInt, int iState)
{
KeySymsRec keySyms;
CARD8 modMap[MAP_LENGTH];
DevicePtr pDevice = (DevicePtr) pDeviceInt;
#ifdef XKB
XkbComponentNamesRec names;
2004-06-21 15:19:32 +02:00
XkbSrvInfoPtr xkbi;
XkbControlsPtr ctrl;
2003-11-14 17:48:57 +01:00
#endif
switch (iState)
{
case DEVICE_INIT:
winConfigKeyboard (pDeviceInt);
winGetKeyMappings (&keySyms, modMap);
#ifdef XKB
/* FIXME: Maybe we should use winGetKbdLeds () here? */
defaultKeyboardControl.leds = g_winInfo.keyboard.leds;
#else
defaultKeyboardControl.leds = g_winInfo.keyboard.leds;
#endif
#ifdef XKB
if (g_winInfo.xkb.disable)
{
#endif
InitKeyboardDeviceStruct (pDevice,
&keySyms,
modMap,
winKeybdBell,
winKeybdCtrl);
#ifdef XKB
}
else
{
if (XkbInitialMap)
{
names.keymap = XkbInitialMap;
names.keycodes = NULL;
names.types = NULL;
names.compat = NULL;
names.symbols = NULL;
names.geometry = NULL;
}
else
{
names.keymap = g_winInfo.xkb.keymap;
names.keycodes = g_winInfo.xkb.keycodes;
names.types = g_winInfo.xkb.types;
names.compat = g_winInfo.xkb.compat;
names.symbols = g_winInfo.xkb.symbols;
names.geometry = g_winInfo.xkb.geometry;
}
2004-06-21 15:19:32 +02:00
winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\""
2003-11-14 17:48:57 +01:00
" Variant = \"%s\" Options = \"%s\"\n",
g_winInfo.xkb.rules, g_winInfo.xkb.model,
g_winInfo.xkb.layout, g_winInfo.xkb.variant,
g_winInfo.xkb.options);
XkbSetRulesDflts (g_winInfo.xkb.rules, g_winInfo.xkb.model,
g_winInfo.xkb.layout, g_winInfo.xkb.variant,
g_winInfo.xkb.options);
XkbInitKeyboardDeviceStruct (pDeviceInt, &names, &keySyms,
modMap, winKeybdBell, winKeybdCtrl);
}
#endif
2004-06-21 15:19:32 +02:00
#ifdef XKB
if (!g_winInfo.xkb.disable)
{
xkbi = pDeviceInt->key->xkbInfo;
if (xkbi != 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 (No XKB)\n");
}
}
#endif
g_winInternalModeKeyStatesPtr = &(pDeviceInt->key->state);
2003-11-14 17:48:57 +01:00
break;
2004-06-21 15:19:32 +02:00
2003-11-14 17:48:57 +01:00
case DEVICE_ON:
pDevice->on = TRUE;
2004-06-21 15:19:32 +02:00
g_winInternalModeKeyStatesPtr = &(pDeviceInt->key->state);
2003-11-14 17:48:57 +01:00
break;
case DEVICE_CLOSE:
case DEVICE_OFF:
pDevice->on = FALSE;
2004-06-21 15:19:32 +02:00
g_winInternalModeKeyStatesPtr = NULL;
2003-11-14 17:48:57 +01:00
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
2004-06-21 15:19:32 +02:00
winRestoreModeKeyStates ()
2003-11-14 17:48:57 +01:00
{
DWORD dwKeyState;
2004-06-21 15:19:32 +02:00
BOOL processEvents = TRUE;
unsigned short internalKeyStates;
/* X server is being initialized */
if (!g_winInternalModeKeyStatesPtr)
return;
/* Only process events if the rootwindow is mapped. The keyboard events
* will cause segfaults otherwise */
if (WindowTable && WindowTable[0] && WindowTable[0]->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 */
internalKeyStates = *g_winInternalModeKeyStatesPtr;
2003-11-14 17:48:57 +01:00
/*
* 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;
2004-06-21 15:19:32 +02:00
if (WIN_XOR (internalKeyStates & NumLockMask, dwKeyState))
2003-11-14 17:48:57 +01:00
{
winSendKeyEvent (KEY_NumLock, TRUE);
winSendKeyEvent (KEY_NumLock, FALSE);
}
/* Has the key state changed? */
dwKeyState = GetKeyState (VK_CAPITAL) & 0x0001;
2004-06-21 15:19:32 +02:00
if (WIN_XOR (internalKeyStates & LockMask, dwKeyState))
2003-11-14 17:48:57 +01:00
{
winSendKeyEvent (KEY_CapsLock, TRUE);
winSendKeyEvent (KEY_CapsLock, FALSE);
}
/* Has the key state changed? */
dwKeyState = GetKeyState (VK_SCROLL) & 0x0001;
2004-06-21 15:19:32 +02:00
if (WIN_XOR (internalKeyStates & ScrollLockMask, dwKeyState))
2003-11-14 17:48:57 +01:00
{
winSendKeyEvent (KEY_ScrollLock, TRUE);
winSendKeyEvent (KEY_ScrollLock, FALSE);
}
/* Has the key state changed? */
dwKeyState = GetKeyState (VK_KANA) & 0x0001;
2004-06-21 15:19:32 +02:00
if (WIN_XOR (internalKeyStates & KanaMask, dwKeyState))
2003-11-14 17:48:57 +01:00
{
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
&& wParam == VK_CONTROL
&& (HIWORD (lParam) & KF_EXTENDED) == 0)
{
/* Got a Ctrl_L press */
/* Get time of current message */
lTime = GetMessageTime ();
2004-06-21 15:19:32 +02:00
2003-11-14 17:48:57 +01:00
/* Look for fake Ctrl_L preceeding an Alt_R press. */
fReturn = PeekMessage (&msgNext, NULL,
WM_KEYDOWN, WM_KEYDOWN,
PM_NOREMOVE);
2004-06-21 15:19:32 +02:00
/*
* 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_KEYDOWN,
PM_NOREMOVE);
}
2003-11-14 17:48:57 +01:00
/* 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);
2004-06-21 15:19:32 +02:00
/*
* 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);
}
2003-11-14 17:48:57 +01:00
/* 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 ()
{
int i;
#ifdef HAS_DEVWINDOWS
2003-11-14 17:48:57 +01:00
/* Verify that the mi input system has been initialized */
if (g_fdMessageQueue == WIN_FD_INVALID)
return;
#endif
2003-11-14 17:48:57 +01:00
/* Loop through all keys */
2003-11-14 17:48:57 +01:00
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;
2003-11-14 17:48:57 +01:00
}
}
/*
* 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)
{
xEvent xCurrentEvent;
/*
* 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;
ZeroMemory (&xCurrentEvent, sizeof (xCurrentEvent));
xCurrentEvent.u.u.type = fDown ? KeyPress : KeyRelease;
xCurrentEvent.u.keyButtonPointer.time =
g_c32LastInputEventTime = GetTickCount ();
xCurrentEvent.u.u.detail = dwKey + MIN_KEYCODE;
mieqEnqueue (&xCurrentEvent);
}