diff options
Diffstat (limited to 'tk8.6/win/tkWinKey.c')
-rw-r--r-- | tk8.6/win/tkWinKey.c | 750 |
1 files changed, 750 insertions, 0 deletions
diff --git a/tk8.6/win/tkWinKey.c b/tk8.6/win/tkWinKey.c new file mode 100644 index 0000000..8db34af --- /dev/null +++ b/tk8.6/win/tkWinKey.c @@ -0,0 +1,750 @@ +/* + * tkWinKey.c -- + * + * This file contains X emulation routines for keyboard related + * functions. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * The keymap table holds mappings of Windows keycodes to X keysyms. If + * Windows ever comes along and changes the value of their keycodes, this will + * break all kinds of things. However, this table lookup is much faster than + * the alternative, in which we walked a list of keycodes looking for a match. + * Since this lookup is performed for every Windows keypress event, it seems + * like a worthwhile improvement to use the table. + */ + +#define MAX_KEYCODE 179 /* VK_MEDIA_PLAY_PAUSE is the last entry in our table below */ +/* cf. https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx */ + +static const KeySym keymap[] = { + NoSymbol, NoSymbol, NoSymbol, XK_Cancel, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, XK_BackSpace, XK_Tab, + NoSymbol, NoSymbol, XK_Clear, XK_Return, NoSymbol, + NoSymbol, XK_Shift_L, XK_Control_L, XK_Alt_L, XK_Pause, + XK_Caps_Lock, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, XK_Escape, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, XK_space, XK_Prior, XK_Next, + XK_End, XK_Home, XK_Left, XK_Up, XK_Right, + XK_Down, XK_Select, XK_Print, XK_Execute, NoSymbol, + XK_Insert, XK_Delete, XK_Help, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, XK_Win_L, XK_Win_R, XK_App, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, XK_F1, XK_F2, XK_F3, + XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, + XK_F9, XK_F10, XK_F11, XK_F12, XK_F13, + XK_F14, XK_F15, XK_F16, XK_F17, XK_F18, + XK_F19, XK_F20, XK_F21, XK_F22, XK_F23, + XK_F24, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_Num_Lock, + XK_Scroll_Lock, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*150 0x96*/ + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*155 0x9b*/ + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*160 0xa0*/ + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*165 0xa5*/ + NoSymbol, NoSymbol, NoSymbol, XK_XF86AudioMute, XK_XF86AudioLowerVolume, /*170 0xaa*/ + XK_XF86AudioRaiseVolume, XK_XF86AudioNext, XK_XF86AudioPrev, XK_XF86AudioStop, XK_XF86AudioPlay /*175 0xaf*/ +}; + +/* + * Prototypes for local functions defined in this file: + */ + +static KeySym KeycodeToKeysym(unsigned int keycode, + int state, int noascii); + +/* + *---------------------------------------------------------------------- + * + * TkpGetString -- + * + * Retrieve the UTF string equivalent for the given keyboard event. + * + * Results: + * Returns the UTF string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +const char * +TkpGetString( + TkWindow *winPtr, /* Window where event occurred: needed to get + * input context. */ + XEvent *eventPtr, /* X keyboard event. */ + Tcl_DString *dsPtr) /* Uninitialized or empty string to hold + * result. */ +{ + XKeyEvent *keyEv = &eventPtr->xkey; + char buf[6]; + int len; + + Tcl_DStringInit(dsPtr); + if (keyEv->send_event == -1) { + if (keyEv->nbytes > 0) { + Tcl_ExternalToUtfDString(TkWinGetKeyInputEncoding(), + keyEv->trans_chars, keyEv->nbytes, dsPtr); + } + } else if (keyEv->send_event == -3) { + + /* + * Special case for WM_UNICHAR and win2000 multi-lingal IME input + */ + + len = TkUniCharToUtf(keyEv->keycode, buf); + Tcl_DStringAppend(dsPtr, buf, len); + } else { + /* + * This is an event generated from generic code. It has no nchars or + * trans_chars members. + */ + + KeySym keysym = KeycodeToKeysym(keyEv->keycode, keyEv->state, 0); + + if (((keysym != NoSymbol) && (keysym > 0) && (keysym < 256)) + || (keysym == XK_Return) || (keysym == XK_Tab)) { + len = Tcl_UniCharToUtf(keysym & 255, buf); + Tcl_DStringAppend(dsPtr, buf, len); + } + } + return Tcl_DStringValue(dsPtr); +} + +/* + *---------------------------------------------------------------------- + * + * XKeycodeToKeysym -- + * + * Translate from a system-dependent keycode to a system-independent + * keysym. + * + * Results: + * Returns the translated keysym, or NoSymbol on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +KeySym +XKeycodeToKeysym( + Display *display, + unsigned int keycode, + int index) +{ + int state = 0; + + if (index & 0x01) { + state |= ShiftMask; + } + return KeycodeToKeysym(keycode, state, 0); +} + +/* + *---------------------------------------------------------------------- + * + * KeycodeToKeysym -- + * + * Translate from a system-dependent keycode to a system-independent + * keysym. + * + * Results: + * Returns the translated keysym, or NoSymbol on failure. + * + * Side effects: + * It may affect the internal state of the keyboard, such as remembered + * dead key or lock indicator lamps. + * + *---------------------------------------------------------------------- + */ + +static KeySym +KeycodeToKeysym( + unsigned int keycode, + int state, + int noascii) +{ + BYTE keys[256]; + int result, deadkey, shift; + TCHAR buf[4]; + unsigned int scancode = MapVirtualKey(keycode, 0); + + /* + * Do not run keycodes of lock keys through ToUnicode(). One of ToUnicode()'s + * side effects is to handle the lights on the keyboard, and we don't want + * to mess that up. + */ + + if (noascii || keycode == VK_CAPITAL || keycode == VK_SCROLL || + keycode == VK_NUMLOCK) { + goto skipToUnicode; + } + + /* + * Use MapVirtualKey() to detect some dead keys. + */ + + if (MapVirtualKey(keycode, 2) > 0x7fffUL) { + return XK_Multi_key; + } + + /* + * Set up a keyboard with correct modifiers + */ + + memset(keys, 0, 256); + if (state & ShiftMask) { + keys[VK_SHIFT] = 0x80; + } + if (state & ControlMask) { + keys[VK_CONTROL] = 0x80; + } + if (state & Mod2Mask) { + keys[VK_MENU] = 0x80; + } + + /* + * Make sure all lock button info is correct so we don't mess up the + * lights. + */ + + if (state & LockMask) { + keys[VK_CAPITAL] = 1; + } + if (state & Mod3Mask) { + keys[VK_SCROLL] = 1; + } + if (state & Mod1Mask) { + keys[VK_NUMLOCK] = 1; + } + + result = ToUnicode(keycode, scancode, keys, buf, 4, 0); + + if (result < 0) { + /* + * Win95/98: This was a dead char, which is now remembered by the + * keyboard. Call ToUnicode() again to forget it. + * WinNT: This was a dead char, overwriting any previously remembered + * key. Calling ToUnicode() again does not affect anything. + */ + + ToUnicode(keycode, scancode, keys, buf, 4, 0); + return XK_Multi_key; + } + + if (result == 2) { + /* + * This was a dead char, and there were one previously remembered by + * the keyboard. Call ToUnicode() again with proper parameters to + * restore it. + * + * Get information about the old char + */ + + deadkey = VkKeyScan(buf[0]); + shift = deadkey >> 8; + deadkey &= 255; + scancode = MapVirtualKey(deadkey, 0); + + /* + * Set up a keyboard with proper modifier keys + */ + + memset(keys, 0, 256); + if (shift & 1) { + keys[VK_SHIFT] = 0x80; + } + if (shift & 2) { + keys[VK_CONTROL] = 0x80; + } + if (shift & 4) { + keys[VK_MENU] = 0x80; + } + ToUnicode(deadkey, scancode, keys, buf, 4, 0); + return XK_Multi_key; + } + + /* + * Keycode mapped to a valid Unicode character. Since the keysyms for + * alphanumeric characters map onto Unicode, we just return it. + * + * We treat 0x7F as a special case mostly for backwards compatibility. In + * versions of Tk<=8.2, Control-Backspace returned "XK_BackSpace" as the X + * Keysym. This was due to the fact that we did not initialize the keys + * array properly when we passed it to ToUnicode, above. We had previously + * not been setting the state bit for the Control key. When we fixed that, + * we found that Control-Backspace on Windows is interpreted as ASCII-127 + * (0x7F), which corresponds to the Delete key. + * + * Upon discovering this, we realized we had two choices: return XK_Delete + * or return XK_BackSpace. If we returned XK_Delete, that could be + * considered "more correct" (although the correctness would be dependent + * on whether you believe that ToUnicode is doing the right thing in that + * case); however, this would break backwards compatibility, and worse, it + * would limit application programmers; they would effectively be unable + * to bind to <Control-Backspace> on Windows. We therefore chose instead + * to return XK_BackSpace (handled here by letting the code "fall-through" + * to the return statement below, which works because the keycode for this + * event is VK_BACKSPACE, and the keymap table maps that keycode to + * XK_BackSpace). + */ + + if (result == 1 && buf[0] >= 0x20 && buf[0] != 0x7F) { + return (KeySym) buf[0]; + } + + /* + * Keycode is a non-alphanumeric key, so we have to do the lookup. + */ + + skipToUnicode: + if (keycode > MAX_KEYCODE) { + return NoSymbol; + } + switch (keycode) { + /* + * Windows only gives us an undifferentiated VK_CONTROL code (for + * example) when either Control key is pressed. To distinguish between + * left and right, we use the Extended flag. Indeed, the right Control + * and Alt (aka Menu) keys are such extended keys (which their left + * counterparts are not). + * Regarding the shift case, Windows does not set the Extended flag for + * the neither the left nor the right shift key. As a consequence another + * way to distinguish between the two keys is to query the state of one + * of the two to determine which was actually pressed. So if the keycode + * indicates Shift, do this extra test. If the right-side key was + * pressed, return the appropriate keycode. Otherwise, we fall through + * and rely on the keymap table to hold the correct keysym value. + * Note: this little trick only works for KeyPress, not for KeyRelease, + * for reasons stated in bug [2945130] + */ + + case VK_CONTROL: + if (state & EXTENDED_MASK) { + return XK_Control_R; + } + break; + case VK_SHIFT: + if (GetKeyState(VK_RSHIFT) & 0x80) { + return XK_Shift_R; + } + break; + case VK_MENU: + if (state & EXTENDED_MASK) { + return XK_Alt_R; + } + break; + } + return keymap[keycode]; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetKeySym -- + * + * Given an X KeyPress or KeyRelease event, map the keycode in the event + * into a KeySym. + * + * Results: + * The return value is the KeySym corresponding to eventPtr, or NoSymbol + * if no matching Keysym could be found. + * + * Side effects: + * In the first call for a given display, keycode-to-KeySym maps get + * loaded. + * + *---------------------------------------------------------------------- + */ + +KeySym +TkpGetKeySym( + TkDisplay *dispPtr, /* Display in which to map keycode. */ + XEvent *eventPtr) /* Description of X event. */ +{ + KeySym sym; + int state = eventPtr->xkey.state; + + /* + * Refresh the mapping information if it's stale + */ + + if (dispPtr->bindInfoStale) { + TkpInitKeymapInfo(dispPtr); + } + + sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0); + + /* + * Special handling: if this is a ctrl-alt or shifted key, and there is no + * keysym defined, try without the modifiers. + */ + + if ((sym == NoSymbol) && ((state & ControlMask) || (state & Mod2Mask))) { + state &= ~(ControlMask | Mod2Mask); + sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0); + } + if ((sym == NoSymbol) && (state & ShiftMask)) { + state &= ~ShiftMask; + sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0); + } + return sym; +} + +/* + *-------------------------------------------------------------- + * + * TkpInitKeymapInfo -- + * + * This function is invoked to scan keymap information to recompute stuff + * that's important for binding, such as the modifier key (if any) that + * corresponds to "mode switch". + * + * Results: + * None. + * + * Side effects: + * Keymap-related information in dispPtr is updated. + * + *-------------------------------------------------------------- + */ + +void +TkpInitKeymapInfo( + TkDisplay *dispPtr) /* Display for which to recompute keymap + * information. */ +{ + XModifierKeymap *modMapPtr; + KeyCode *codePtr; + KeySym keysym; + int count, i, j, max, arraySize; +#define KEYCODE_ARRAY_SIZE 20 + + dispPtr->bindInfoStale = 0; + modMapPtr = XGetModifierMapping(dispPtr->display); + + /* + * Check the keycodes associated with the Lock modifier. If any of them is + * associated with the XK_Shift_Lock modifier, then Lock has to be + * interpreted as Shift Lock, not Caps Lock. + */ + + dispPtr->lockUsage = LU_IGNORE; + codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex; + for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) { + if (*codePtr == 0) { + continue; + } + keysym = KeycodeToKeysym(*codePtr, 0, 1); + if (keysym == XK_Shift_Lock) { + dispPtr->lockUsage = LU_SHIFT; + break; + } + if (keysym == XK_Caps_Lock) { + dispPtr->lockUsage = LU_CAPS; + break; + } + } + + /* + * Look through the keycodes associated with modifiers to see if the the + * "mode switch", "meta", or "alt" keysyms are associated with any + * modifiers. If so, remember their modifier mask bits. + */ + + dispPtr->modeModMask = 0; + dispPtr->metaModMask = 0; + dispPtr->altModMask = 0; + codePtr = modMapPtr->modifiermap; + max = 8*modMapPtr->max_keypermod; + for (i = 0; i < max; i++, codePtr++) { + if (*codePtr == 0) { + continue; + } + keysym = KeycodeToKeysym(*codePtr, 0, 1); + if (keysym == XK_Mode_switch) { + dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod); + } + if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) { + dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod); + } + if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) { + dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod); + } + } + + /* + * Create an array of the keycodes for all modifier keys. + */ + + if (dispPtr->modKeyCodes != NULL) { + ckfree(dispPtr->modKeyCodes); + } + dispPtr->numModKeyCodes = 0; + arraySize = KEYCODE_ARRAY_SIZE; + dispPtr->modKeyCodes = ckalloc(KEYCODE_ARRAY_SIZE * sizeof(KeyCode)); + for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) { + if (*codePtr == 0) { + continue; + } + + /* + * Make sure that the keycode isn't already in the array. + */ + + for (j = 0; j < dispPtr->numModKeyCodes; j++) { + if (dispPtr->modKeyCodes[j] == *codePtr) { + goto nextModCode; + } + } + if (dispPtr->numModKeyCodes >= arraySize) { + KeyCode *new; + + /* + * Ran out of space in the array; grow it. + */ + + arraySize *= 2; + new = ckalloc(arraySize * sizeof(KeyCode)); + memcpy(new, dispPtr->modKeyCodes, + dispPtr->numModKeyCodes * sizeof(KeyCode)); + ckfree(dispPtr->modKeyCodes); + dispPtr->modKeyCodes = new; + } + dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr; + dispPtr->numModKeyCodes++; + nextModCode: continue; + } + XFreeModifiermap(modMapPtr); +} + +/* + * When mapping from a keysym to a keycode, need information about the + * modifier state that should be used so that when they call XKeycodeToKeysym + * taking into account the xkey.state, they will get back the original keysym. + */ + +void +TkpSetKeycodeAndState( + Tk_Window tkwin, + KeySym keySym, + XEvent *eventPtr) +{ + int i; + SHORT result; + int shift; + + eventPtr->xkey.keycode = 0; + if (keySym == NoSymbol) { + return; + } + + /* + * We check our private map first for a virtual keycode, as VkKeyScan will + * return values that don't map to X for the "extended" Syms. This may be + * due to just casting problems below, but this works. + */ + + for (i = 0; i <= MAX_KEYCODE; i++) { + if (keymap[i] == keySym) { + eventPtr->xkey.keycode = i; + return; + } + } + if (keySym >= 0x20) { + result = VkKeyScan((TCHAR) keySym); + if (result != -1) { + shift = result >> 8; + if (shift & 1) + eventPtr->xkey.state |= ShiftMask; + if (shift & 2) + eventPtr->xkey.state |= ControlMask; + if (shift & 4) + eventPtr->xkey.state |= Mod2Mask; + eventPtr->xkey.keycode = (KeyCode) (result & 0xff); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * XKeysymToKeycode -- + * + * Translate a keysym back into a keycode. + * + * Results: + * Returns the keycode that would generate the specified keysym. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +KeyCode +XKeysymToKeycode( + Display *display, + KeySym keysym) +{ + int i; + SHORT result; + + /* + * We check our private map first for a virtual keycode, as VkKeyScan will + * return values that don't map to X for the "extended" Syms. This may be + * due to just casting problems below, but this works. + */ + + if (keysym == NoSymbol) { + return 0; + } + for (i = 0; i <= MAX_KEYCODE; i++) { + if (keymap[i] == keysym) { + return ((KeyCode) i); + } + } + if (keysym >= 0x20) { + result = VkKeyScan((TCHAR) keysym); + if (result != -1) { + return (KeyCode) (result & 0xff); + } + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * XGetModifierMapping -- + * + * Fetch the current keycodes used as modifiers. + * + * Results: + * Returns a new modifier map. + * + * Side effects: + * Allocates a new modifier map data structure. + * + *---------------------------------------------------------------------- + */ + +XModifierKeymap * +XGetModifierMapping( + Display *display) +{ + XModifierKeymap *map = ckalloc(sizeof(XModifierKeymap)); + + map->max_keypermod = 1; + map->modifiermap = ckalloc(sizeof(KeyCode) * 8); + map->modifiermap[ShiftMapIndex] = VK_SHIFT; + map->modifiermap[LockMapIndex] = VK_CAPITAL; + map->modifiermap[ControlMapIndex] = VK_CONTROL; + map->modifiermap[Mod1MapIndex] = VK_NUMLOCK; + map->modifiermap[Mod2MapIndex] = VK_MENU; + map->modifiermap[Mod3MapIndex] = VK_SCROLL; + map->modifiermap[Mod4MapIndex] = 0; + map->modifiermap[Mod5MapIndex] = 0; + return map; +} + +/* + *---------------------------------------------------------------------- + * + * XFreeModifiermap -- + * + * Deallocate a modifier map that was created by XGetModifierMapping. + * + * Results: + * None. + * + * Side effects: + * Frees the datastructure referenced by modmap. + * + *---------------------------------------------------------------------- + */ + +int +XFreeModifiermap( + XModifierKeymap *modmap) +{ + ckfree(modmap->modifiermap); + ckfree(modmap); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XStringToKeysym -- + * + * Translate a keysym name to the matching keysym. + * + * Results: + * Returns the keysym. Since this is already handled by Tk's + * StringToKeysym function, we just return NoSymbol. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +KeySym +XStringToKeysym( + _Xconst char *string) +{ + return NoSymbol; +} + +/* + *---------------------------------------------------------------------- + * + * XKeysymToString -- + * + * Convert a keysym to character form. + * + * Results: + * Returns NULL, since Tk will have handled this already. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +XKeysymToString( + KeySym keysym) +{ + return NULL; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |