diff options
Diffstat (limited to 'hw/xwin/winkeybd.c')
-rw-r--r-- | hw/xwin/winkeybd.c | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/hw/xwin/winkeybd.c b/hw/xwin/winkeybd.c new file mode 100644 index 000000000..20df053c2 --- /dev/null +++ b/hw/xwin/winkeybd.c @@ -0,0 +1,707 @@ +/* + *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 + */ +/* $XFree86: xc/programs/Xserver/hw/xwin/winkeybd.c,v 1.12 2002/10/17 08:18:22 alanh Exp $ */ + + +#include "win.h" +#include "winkeybd.h" +#include "winconfig.h" + +#ifdef XKB +#define XKB_IN_SERVER +#include "XKBsrv.h" +#endif + +static Bool g_winKeyState[NUM_KEYCODES]; + + +#if WIN_NEW_KEYBOARD_SUPPORT + +const unsigned int MaxKeysPerKey = 4; + +void +winProcessKeyEvent (DWORD dwVirtualKey, DWORD dwKeyData) +{ + Bool fDown = ((dwKeyData & 0x80000000) == 0); + winKeyEventsRec kerEvent; + int i; + + /* Get the key events */ + kerEvent = winTranslateKey (dwVirtualKey, dwKeyData); + + if (kerEvent.dwReleaseModifiers & WIN_MOD_LCONTROL) + winSendKeyEvent (XK_Control_L, FALSE); + if (kerEvent.dwReleaseModifiers & WIN_MOD_RCONTROL) + winSendKeyEvent (XK_Control_R, FALSE); + if (kerEvent.dwReleaseModifiers & WIN_MOD_LALT) + winSendKeyEvent (XK_Alt_L, FALSE); + if (kerEvent.dwReleaseModifiers & WIN_MOD_RALT) + winSendKeyEvent (XK_Alt_R, FALSE); + + for (i = 0; kerEvent.dwXKeycodes[i] != XK_VoidSymbol; ++i) + winSendKeyEvent (kerEvent.dwXKeycodes[i], fDown); + + if (kerEvent.dwReleaseModifiers & WIN_MOD_LCONTROL) + winSendKeyEvent (XK_Control_L, FALSE); + if (kerEvent.dwReleaseModifiers & WIN_MOD_RCONTROL) + winSendKeyEvent (XK_Control_R, TRUE); + if (kerEvent.dwReleaseModifiers & WIN_MOD_LALT) + winSendKeyEvent (XK_Alt_L, FALSE); + if (kerEvent.dwReleaseModifiers & WIN_MOD_RALT) + winSendKeyEvent (XK_Alt_R, TRUE); + +} + + +winKeyEventsRec +winTranslateKey (DWORD dwVirtualKey, DWORD dwKeyData) +{ + winKeyEventsRec kerEvents; + Bool fExtended = ((HIWORD (dwKeyData) & KF_EXTENDED) != 0); + int i; + DWORD dwNumEvents = 0; + BYTE bKeyboardState[256]; + int iReturn; + unsigned char cAscii[4]; + + /* Remap extended modifiers to the right version of that key. */ + if (fExtended) + { + switch (dwVirtualKey) + { + case VK_MENU: + dwVirtualKey = VK_RMENU; + break; + + case VK_CONTROL: + dwVirtualKey = VK_RCONTROL; + break; + } + } + + /* Initialize the modifiers to release flag */ + kerEvents.dwReleaseModifiers = 0; + + /* Look up the current virtual key code in the translation table */ + for (i = 0; i < g_winKeymapEntries; ++i) + { + /* Did we find a mapping? */ + if (winKeymap[i].dwVirtualKey == dwVirtualKey) + { + /* Mapping found, we have at least one event now */ + kerEvents.dwXKeycodes[dwNumEvents] = winKeymap[i].dwXKey; + break; + } + } + + + /* Break out early, if we found the key in the translation table */ + if (dwNumEvents != 0) + { + /* Terminate the last of the key events with a void symbol */ + kerEvents.dwXKeycodes[dwNumEvents] = XK_VoidSymbol; + return kerEvents; + } + + /* Get the state of all keyboard keys */ + GetKeyboardState (bKeyboardState); + + /* Try to convert the key to ASCII */ + iReturn = ToAscii (dwVirtualKey, 0, bKeyboardState, (WORD *) cAscii, 0); + + /* + * Left Control and Alt pressed, combined with a valid result + * from ToAscii means that we have found the Windows version of AltGr. + */ + if ((bKeyboardState[VK_MENU] & 0x80) && (bKeyboardState[VK_CONTROL] & 0x80) + && (iReturn >= 1) + && (((cAscii[0] >= 32) && (cAscii[0] <= 126)) + || (cAscii[0] >= 160))) + { + /* These three calls will return 0 on Windows 95/98/Me */ + if ((GetKeyState (VK_LCONTROL) & KF_UP)) + kerEvents.dwReleaseModifiers |= WIN_MOD_LCONTROL; + if ((GetKeyState (VK_LMENU) & KF_UP)) + kerEvents.dwReleaseModifiers |= WIN_MOD_LALT; + if ((GetKeyState (VK_RMENU) & KF_UP)) + kerEvents.dwReleaseModifiers |= WIN_MOD_RALT; + + /* Windows 95/98/Me handling - pop all of them */ + if (kerEvents.dwReleaseModifiers == 0) + kerEvents.dwReleaseModifiers + = WIN_MOD_LCONTROL | WIN_MOD_LALT | WIN_MOD_RALT; + + /* Copy the string of character events */ + for (i = 0; i < iReturn; ++i) + kerEvents.dwXKeycodes[dwNumEvents++] = cAscii[i]; + } + + /* Handle non Ctrl+Alt cases*/ + if (dwNumEvents == 0) + { + bKeyboardState[VK_CONTROL] = 0; + bKeyboardState[VK_LCONTROL] = 0; + bKeyboardState[VK_RCONTROL] = 0; + + iReturn = ToAscii (dwVirtualKey, 0, bKeyboardState, (WORD *)cAscii, 0); + if (iReturn < 0) + { + switch (cAscii[0]) + { + case '`': + kerEvents.dwXKeycodes[dwNumEvents++] = XK_dead_grave; + break; + + case '\'': + kerEvents.dwXKeycodes[dwNumEvents++] = XK_dead_acute; + break; + + case '~': + kerEvents.dwXKeycodes[dwNumEvents++] = XK_dead_tilde; + break; + + case '^': + kerEvents.dwXKeycodes[dwNumEvents++] = XK_dead_circumflex; + break; + } + } + + /* Send what we've got if its a printable character */ + if (iReturn >= 1) + for (i = 0; i < iReturn; ++i) + kerEvents.dwXKeycodes[dwNumEvents++] = cAscii[i]; + } + + + /* Terminate the last of the key events with a void symbol */ + kerEvents.dwXKeycodes[dwNumEvents] = XK_VoidSymbol; + return kerEvents; +} + + +#else /* WIN_NEW_KEYBOARD_SUPPORT */ + + +/* + * 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)); +} + +#endif /* WIN_NEW_KEYBOARD_SUPPORT */ + + +/* + * We call this function from winKeybdProc when we are + * initializing the keyboard. + */ + +void +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; + +#if !WIN_NEW_KEYBOARD_SUPPORT + case XK_Num_Lock: + pModMap[i] = NumLockMask; + break; + + case XK_Scroll_Lock: + pModMap[i] = ScrollLockMask; + break; + + /* 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) */ +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 */ +void +winKeybdCtrl (DeviceIntPtr pDevice, KeybdCtrl *pCtrl) +{ + +} + + +/* + * 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; +#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; + } + + ErrorF("Rules = \"%s\" Model = \"%s\" Layout = \"%s\"" + " 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 + break; + case DEVICE_ON: + pDevice->on = TRUE; + 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) +{ +#if !WIN_NEW_KEYBOARD_SUPPORT + /* 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); + } +#endif +} + + +/* + * We have to store the last state of each mode + * key before we lose the keyboard focus. + */ + +void +winStoreModeKeyStates (ScreenPtr pScreen) +{ +#if !WIN_NEW_KEYBOARD_SUPPORT + winScreenPriv(pScreen); + + /* Initialize all mode key states to off */ + pScreenPriv->dwModeKeyStates = 0x0L; + + pScreenPriv->dwModeKeyStates |= + (GetKeyState (VK_NUMLOCK) & 0x0001) << NumLockMapIndex; + + pScreenPriv->dwModeKeyStates |= + (GetKeyState (VK_SCROLL) & 0x0001) << ScrollLockMapIndex; + + pScreenPriv->dwModeKeyStates |= + (GetKeyState (VK_CAPITAL) & 0x0001) << LockMapIndex; + + pScreenPriv->dwModeKeyStates |= + (GetKeyState (VK_KANA) & 0x0001) << KanaMapIndex; +#endif +} + + +/* + * Upon regaining the keyboard focus we must + * resynchronize our internal mode key states + * with the actual state of the keys. + */ + +void +winRestoreModeKeyStates (ScreenPtr pScreen) +{ +#if !WIN_NEW_KEYBOARD_SUPPORT + winScreenPriv(pScreen); + DWORD dwKeyState; + + /* + * 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 (pScreenPriv->dwModeKeyStates & NumLockMask, dwKeyState)) + { + winSendKeyEvent (KEY_NumLock, TRUE); + winSendKeyEvent (KEY_NumLock, FALSE); + } + + /* Has the key state changed? */ + dwKeyState = GetKeyState (VK_CAPITAL) & 0x0001; + if (WIN_XOR (pScreenPriv->dwModeKeyStates & LockMask, dwKeyState)) + { + winSendKeyEvent (KEY_CapsLock, TRUE); + winSendKeyEvent (KEY_CapsLock, FALSE); + } + + /* Has the key state changed? */ + dwKeyState = GetKeyState (VK_SCROLL) & 0x0001; + if (WIN_XOR (pScreenPriv->dwModeKeyStates & ScrollLockMask, dwKeyState)) + { + winSendKeyEvent (KEY_ScrollLock, TRUE); + winSendKeyEvent (KEY_ScrollLock, FALSE); + } + + /* Has the key state changed? */ + dwKeyState = GetKeyState (VK_KANA) & 0x0001; + if (WIN_XOR (pScreenPriv->dwModeKeyStates & KanaMask, dwKeyState)) + { + winSendKeyEvent (KEY_HKTG, TRUE); + winSendKeyEvent (KEY_HKTG, FALSE); + } +#endif +} + + +#if !WIN_NEW_KEYBOARD_SUPPORT +/* + * 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 (); + + /* Look for fake Ctrl_L preceeding an Alt_R press. */ + fReturn = PeekMessage (&msgNext, NULL, + WM_KEYDOWN, WM_KEYDOWN, + PM_NOREMOVE); + + /* 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); + + /* 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; +} +#endif /* WIN_NEW_KEYBOARD_SUPPORT */ + + +/* + * Lift any modifier keys that are pressed + */ + +void +winKeybdReleaseKeys () +{ +#if !WIN_NEW_KEYBOARD_SUPPORT +#if 0 /* Old function that just pops modifiers */ + /* Verify that the mi input system has been initialized */ + if (g_fdMessageQueue == WIN_FD_INVALID) + return; + + winSendKeyEvent (KEY_Alt, FALSE); + winSendKeyEvent (KEY_AltLang, FALSE); + winSendKeyEvent (KEY_LCtrl, FALSE); + winSendKeyEvent (KEY_RCtrl, FALSE); + winSendKeyEvent (KEY_ShiftL, FALSE); + winSendKeyEvent (KEY_ShiftR, FALSE); +#else /* New function that pops all keys */ + int i; + + /* Verify that the mi input system has been initialized */ + if (g_fdMessageQueue == WIN_FD_INVALID) + return; + + /* Pop any pressed keys */ + for (i = 0; i < NUM_KEYCODES; ++i) + { + if (g_winKeyState[i]) winSendKeyEvent (i, FALSE); + } +#endif +#endif +} + + +/* + * 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); +} |