diff options
author | marc_culler <marc.culler@gmail.com> | 2020-04-29 18:40:54 (GMT) |
---|---|---|
committer | marc_culler <marc.culler@gmail.com> | 2020-04-29 18:40:54 (GMT) |
commit | fb718bb804556da3691f5945793c4a0da75a85dd (patch) | |
tree | 943f6fc29f9bf372c143f657c79408d4917423e3 /macosx | |
parent | 373c3b707cea33b1eabb9e1d417d9003a2e8a051 (diff) | |
download | tk-fb718bb804556da3691f5945793c4a0da75a85dd.zip tk-fb718bb804556da3691f5945793c4a0da75a85dd.tar.gz tk-fb718bb804556da3691f5945793c4a0da75a85dd.tar.bz2 |
Deal with <Oslash> ~ <Shift-oslash> ~ <Shift-Option-o> ~ <Shift-Option-O> ~ <Option-O> ... and fix <BackSpace> and improve injection of key NSEvents.
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/tkMacOSXKeyEvent.c | 36 | ||||
-rw-r--r-- | macosx/tkMacOSXKeyboard.c | 195 | ||||
-rw-r--r-- | macosx/tkMacOSXKeysyms.h | 5 | ||||
-rw-r--r-- | macosx/tkMacOSXPrivate.h | 10 | ||||
-rw-r--r-- | macosx/tkMacOSXTest.c | 61 |
5 files changed, 195 insertions, 112 deletions
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index b25f2a5..c2445b9 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -17,9 +17,10 @@ #include "tkMacOSXInt.h" #include "tkMacOSXConstants.h" #include "tkMacOSXWm.h" -#define IS_PRINTABLE(keychar) ((keychar >= 0x20) && \ - (keychar != 0x7f) && \ - (keychar < 0xF700)) + +/* + * See tkMacOSXPrivate.h for the definition of IS_PRINTABLE. + */ /* #ifdef TK_MAC_DEBUG @@ -118,7 +119,7 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w); has_modifiers = xEvent.xkey.state & XEVENT_MOD_MASK; focusWinPtr = TkFocusKeyEvent(winPtr, &xEvent); if (focusWinPtr == NULL) { - + TKContentView *contentView = [w contentView]; /* * This NSEvent is being sent to a window which no longer has focus. * This has been observed to happen when the user deactivates the Tk @@ -127,7 +128,7 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w); * operation and discard the event. */ - [[w contentView] cancelComposingText]; + [contentView cancelComposingText]; return theEvent; } can_input_text = ((focusWinPtr->flags & TK_CAN_INPUT_TEXT) != 0); @@ -331,10 +332,12 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w); */ for (i = 0; i < len; i++) { - TkUtfAtIndex(str, i, xEvent.xkey.trans_chars, &xEvent.xkey.keycode); - if (xEvent.xkey.keycode > 0xffff){ + unsigned int code; + TkUtfAtIndex(str, i, xEvent.xkey.trans_chars, &code); + if (code > 0xFFFF){ i++; } + xEvent.xkey.keycode = code; xEvent.xany.type = KeyPress; Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); } @@ -421,15 +424,6 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w); return rng; } -- (void)cancelComposingText -{ - if (NS_KEYLOG) { - TKLog(@"cancelComposingText"); - } - [self deleteWorkingText]; - processingCompose = NO; -} - - (void)unmarkText { if (NS_KEYLOG) { @@ -552,6 +546,16 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w); } } } + +- (void)cancelComposingText +{ + if (NS_KEYLOG) { + TKLog(@"cancelComposingText"); + } + [self deleteWorkingText]; + processingCompose = NO; +} + @end /* diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index 6430484..4e04bae 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -17,13 +17,6 @@ #include "tkMacOSXConstants.h" #include "tkMacOSXKeysyms.h" -#define IS_PRINTABLE(keychar) ((keychar >= 0x20) && \ - (keychar != 0x7f) && \ - (keychar < 0xF700)) -#define ON_KEYPAD(virtual) ((virtual >= 0x41) && (virtual <= 0x5C)) -#define VIRTUAL_MAX 0x7F -#define MAC_KEYCHAR_MASK 0xFFFFFF - /* * About keyboards * --------------- @@ -97,7 +90,7 @@ * numerical X11 keysyms and macOS keychars; this file constructs Tcl hash * tables to do this job, using data defined in the file tkMacOSXKeysyms.h. * The code here adopts the convention that the keychar of any modifier key - * is 0xF8FF, the last value in the private-use range. + * is MOD_KEYCHAR. * * The macosx platform-specific scheme for generating a keycode when mapping an * NSEvent of type KeyUp, KeyDown or FlagsChanged to an XEvent of type KeyPress @@ -109,16 +102,43 @@ * requires 21 bits. So, in the future, when macs have emoji keyboards, no * change will be needed in how keycodes are generated. Second, the KeyCode * type for the keycode field in an XEvent is currently defined as unsigned - * long, which means that it is 64 bits on modern macOS systems. Finally, there - * is no obstruction to generating KeyPress events for keys that represent - * letters which do not exist on the current keyboard layout. And different - * keyboard layouts can assign a given letter to different keys. So we need a - * convention for what value to assign to "virtual" when computing the keycode - * for a generated event. The convention used here is as follows: - * If there is a key on the current keyboard which produces the keychar, - * use the virtual keycode of that key. Otherwise set virtual = 0. + * int, which was modified from the unsigned short used in X11 in order to + * accomodate macOS. Finally, there is no obstruction to generating KeyPress + * events for keys that represent letters which do not exist on the current + * keyboard layout. And different keyboard layouts can assign a given letter + * to different keys. So we need a convention for what value to assign to + * "virtual" when computing the keycode for a generated event. The convention + * used here is as follows: If there is a key on the current keyboard which + * produces the keychar, use the virtual keycode of that key. Otherwise set + * virtual = NO_VIRTUAL. + */ + + +/* + * See tkMacOSXPrivate.h for the definition of IS_PRINTABLE. */ +#define ON_KEYPAD(virtual) ((virtual >= 0x41) && (virtual <= 0x5C)) +#define VIRTUAL_MAX 0x7F +#define NO_VIRTUAL 0xFF +#define MAC_KEYCHAR_MASK 0xFFFFFF +#define MOD_KEYCHAR 0xF8FE + +/* + * An "index" is an int containing two modifier flags for Option and Shift + * in bits 0-1. It is used as an index when building the keymap hash + * tables. + */ + +#define INDEX_SHIFT 1 +#define INDEX_OPTION 2 +#define INDEX2STATE(index) ((index & INDEX_SHIFT ? ShiftMask : 0) | \ + (index & INDEX_OPTION ? Mod2Mask : 0)) +#define INDEX2CARBON(index) ((index & INDEX_SHIFT ? shiftKey : 0) | \ + (index & INDEX_OPTION ? optionKey : 0)) +#define STATE2INDEX(state) ((state & ShiftMask ? INDEX_SHIFT : 0) | \ + (state & Mod2Mask ? INDEX_OPTION : 0)) + /* * Hash tables used to translate between various key attributes. */ @@ -127,7 +147,8 @@ static Tcl_HashTable virtual2keysym; /* Special virtual keycode to keysym */ static Tcl_HashTable keysym2keycode; /* keysym to XEvent keycode */ static Tcl_HashTable keysym2unichar; /* keysym to unichar */ static Tcl_HashTable unichar2keysym; /* unichar to X11 keysym */ -static Tcl_HashTable unichar2virtual; /* unichar to virtual keycode */ +static Tcl_HashTable unichar2virtual; /* unichar to virtual with index */ +static Tcl_HashTable virtual2unichar; /* virtual with index to unichar */ /* * Flags. @@ -141,7 +162,7 @@ static BOOL keyboardChanged = YES; */ static void InitHashTables(void); -static void UpdateKeymap(void); +static void UpdateKeymaps(void); static int KeyDataToUnicode(UniChar *uniChars, int maxChars, UInt16 keyaction, UInt32 virtual, UInt32 modifiers, UInt32 * deadKeyStatePtr); @@ -155,7 +176,7 @@ static int KeyDataToUnicode(UniChar *uniChars, int maxChars, TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); #endif keyboardChanged = YES; - UpdateKeymap(); + UpdateKeymaps(); } @end @@ -205,14 +226,14 @@ InitHashTables(void) &dummy); Tcl_SetHashValue(hPtr, INT2PTR(ksPtr->keysym)); } - UpdateKeymap(); + UpdateKeymaps(); initialized = YES; } /* *---------------------------------------------------------------------- * - * UpdateKeymap -- + * UpdateKeymaps -- * * Called when the keyboard changes to update the hash table that * maps unicode characters to virtual keycodes with states. In order @@ -230,30 +251,41 @@ InitHashTables(void) */ static void -UpdateKeymap() +UpdateKeymaps() { static int keymapInitialized = 0; UniChar keychar; Tcl_HashEntry *hPtr; - int virtual, state, modifiers, dummy; + int virtual, index, modifiers, dummy; if (!keymapInitialized) { Tcl_InitHashTable(&unichar2virtual, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&virtual2unichar, TCL_ONE_WORD_KEYS); } else { Tcl_DeleteHashTable(&unichar2virtual); + Tcl_DeleteHashTable(&virtual2unichar); } - for (state = 4; state >= 0; state--) { + + /* + * This for loop goes backwards so that a unichar lookup will + * provide the minimal modifier mask. Simpler combinations + * will overwrite more complex ones. + */ + + for (index = 4; index >= 0; index--) { for (virtual = 0; virtual <= VIRTUAL_MAX; virtual++) { if (ON_KEYPAD(virtual)) { continue; } - modifiers = (state & 1 ? shiftKey : 0) | - (state & 2 ? optionKey : 0); + modifiers = INDEX2CARBON(index); KeyDataToUnicode(&keychar, 1, kUCKeyActionDown, virtual, modifiers, NULL); hPtr = Tcl_CreateHashEntry(&unichar2virtual, INT2PTR(keychar), &dummy); - Tcl_SetHashValue(hPtr, INT2PTR(state << 8 | virtual)); + Tcl_SetHashValue(hPtr, INT2PTR(index << 8 | virtual)); + hPtr = Tcl_CreateHashEntry(&virtual2unichar, + INT2PTR(index << 8 | virtual), &dummy); + Tcl_SetHashValue(hPtr, INT2PTR(keychar)); } } } @@ -392,6 +424,18 @@ XKeycodeToKeysym( } /* + * If there is no key with this keysym, try using the keychar to look up a + * keysym. + */ + + if (virtual == NO_VIRTUAL) + hPtr = Tcl_FindHashEntry(&unichar2keysym, + INT2PTR(keycode & MAC_KEYCHAR_MASK)); + if (hPtr != NULL) { + return (KeySym) Tcl_GetHashValue(hPtr); + } + + /* * If not, use the Carbon Framework to find the unicode character and * translate it to a keysym using the unicode2keysym hash table. */ @@ -550,10 +594,10 @@ XStringToKeysym( *---------------------------------------------------------------------- */ static KeyCode -XKeysymToKeycodeWithState( +XKeysymToKeycodeWithIndex( Display *display, KeySym keysym, - int *state) + int *index) { Tcl_HashEntry *hPtr; @@ -561,6 +605,19 @@ XKeysymToKeycodeWithState( InitHashTables(); } + /* + * First check for a special key. + */ + + hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym)); + if (hPtr != NULL) { + return (KeyCode) Tcl_GetHashValue(hPtr); + } + + /* + * Next check for printable keys. + */ + hPtr = Tcl_FindHashEntry(&keysym2unichar, INT2PTR(keysym)); if (hPtr != NULL) { KeyCode character = (KeyCode) Tcl_GetHashValue(hPtr); @@ -568,23 +625,23 @@ XKeysymToKeycodeWithState( if (hPtr != NULL) { KeyCode lookup = ((KeyCode) Tcl_GetHashValue(hPtr)); KeyCode virtual = lookup & 0xFF; - *state = lookup >> 8; + *index = lookup >> 8; return virtual << 24 | character; } else { - return character; - } - } - hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym)); - if (hPtr != NULL) { - return (KeyCode) Tcl_GetHashValue(hPtr); + /* + * The keysym does not exist on the current keyboard. + */ + + return (NO_VIRTUAL << 24) | character; + } } /* - * Could not construct a keycode. + * Would not construct a keycode. Set the keychar to 0. */ - return 0; + return (NO_VIRTUAL << 24) ; } KeyCode @@ -592,8 +649,8 @@ XKeysymToKeycode( Display *display, KeySym keysym) { - int state; - return XKeysymToKeycodeWithState(display, keysym, &state); + int index; + return XKeysymToKeycodeWithIndex(display, keysym, &index); } /* @@ -625,18 +682,36 @@ TkpSetKeycodeAndState( if (keysym == NoSymbol) { eventPtr->xkey.keycode = 0; } else { - int state; - UniChar keychar; + int index = 0, eventIndex = STATE2INDEX(eventPtr->xkey.state); Display *display = Tk_Display(tkwin); - eventPtr->xkey.keycode = XKeysymToKeycodeWithState(display, keysym, - &state); - eventPtr->xkey.state |= state; - keychar = eventPtr->xkey.keycode & MAC_KEYCHAR_MASK; + KeyCode keycode = XKeysymToKeycodeWithIndex(display, keysym, &index); + UniChar keychar = keycode & MAC_KEYCHAR_MASK; + int virtual = (keycode >> 24) & 0xFF; /* - * Set trans_chars for keychars outside of the private-use range. + * We now have a virtual keycode and a minimal choice of Shift and + * Option modifiers which which generates the keychar that corresponds + * to the specified keysym. But we might not have the correct keychar + * yet, because the xEvent may have specified modifiers beyond our + * minimal set. For example, the events described by <Oslash>, + * <Shift-oslash>, <Shift-Option-O> and <Shift-Option-o> should all + * produce the same uppercase Danish O. So we may need to add some + * extra modifiers and do another lookup for the keychar. */ + if (index != eventIndex) { + Tcl_HashEntry *hPtr; + eventIndex |= index; + hPtr = Tcl_FindHashEntry(&virtual2unichar, + INT2PTR(eventIndex << 8 | virtual)); + if (hPtr != NULL) { + keychar = ((UniChar) Tcl_GetHashValue(hPtr)); + } + keycode = virtual << 24 | keychar; + } + eventPtr->xkey.keycode = keycode; + eventPtr->xkey.state |= INDEX2STATE(eventIndex); + keychar = eventPtr->xkey.keycode & MAC_KEYCHAR_MASK; if (IS_PRINTABLE(keychar)) { int length = TkUniCharToUtf(keychar, eventPtr->xkey.trans_chars); eventPtr->xkey.trans_chars[length] = 0; @@ -684,7 +759,7 @@ TkpGetKeySym( * Modifier key events have a special mac keycode (see tkProcessKeyEvent). */ - if ((eventPtr->xkey.keycode & MAC_KEYCHAR_MASK) == 0xF8FF) { + if ((eventPtr->xkey.keycode & MAC_KEYCHAR_MASK) == MOD_KEYCHAR) { switch (eventPtr->xkey.keycode >> 24) { /* the virtual keyCode */ case 54: return XK_Meta_R; @@ -717,12 +792,8 @@ TkpGetKeySym( * works. */ - index = 0; - if (eventPtr->xkey.state & Mod2Mask) { /* Option */ - index |= 2; - } - if ((eventPtr->xkey.state & ShiftMask) || /* Shift or caps lock. */ - (eventPtr->xkey.state & LockMask)) { + index = STATE2INDEX(eventPtr->xkey.state); + if (eventPtr->xkey.state & LockMask) { index |= 1; } @@ -733,16 +804,14 @@ TkpGetKeySym( sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index); /* - * Special handling: If the key was shifted because of Lock, but lock is - * only caps lock, not shift lock, and the shifted keysym isn't upper-case - * alphabetic, then switch back to the unshifted keysym. + * Special handling: If the key was shifted because of Lock, which is only + * caps lock on macOS, not shift lock, and if the shifted keysym isn't + * upper-case alphabetic, then switch back to the unshifted keysym. */ - if ((index & 1) && !(eventPtr->xkey.state & ShiftMask) - /*&& (dispPtr->lockUsage == LU_CAPS)*/ ) { - + if ((index & INDEX_SHIFT) && !(eventPtr->xkey.state & ShiftMask)) { if ((sym == NoSymbol) || !Tcl_UniCharIsUpper(sym)) { - index &= ~1; + index &= ~INDEX_SHIFT; sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index); } @@ -753,9 +822,9 @@ TkpGetKeySym( * no keysym defined, then use the keysym for the unshifted key. */ - if ((index & 1) && (sym == NoSymbol)) { + if ((index & INDEX_SHIFT) && (sym == NoSymbol)) { sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, - index & ~1); + index & ~INDEX_SHIFT); } return sym; } diff --git a/macosx/tkMacOSXKeysyms.h b/macosx/tkMacOSXKeysyms.h index 91dd760..8e8dba6 100644 --- a/macosx/tkMacOSXKeysyms.h +++ b/macosx/tkMacOSXKeysyms.h @@ -39,7 +39,7 @@ typedef struct { static const KeyInfo keyArray[] = { {36, XK_Return, NSNewlineCharacter}, {48, XK_Tab, NSTabCharacter}, - {51, XK_BackSpace, NSBackspaceCharacter}, + {51, XK_BackSpace, NSDeleteCharacter}, {52, XK_Return, NSNewlineCharacter}, /* Used on some Powerbooks */ {53, XK_Escape, 0x1B}, {54, XK_Meta_R, XK_Meta_R}, @@ -105,7 +105,8 @@ static const KeyInfo keyArray[] = { }; /* - * X11 keysyms for modifier keys, in order. + * X11 keysyms for modifier keys, in order. This list includes keys which + * do not appear on Apple keyboards, such as Shift_Lock and Super_R. */ #define NUM_MOD_KEYCODES 14 diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 64a0cd3..77b6272 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -123,6 +123,15 @@ } /* + * Macro used in tkMacOSXKeyboard.c and tkMacOSXKeyEvent.c to check if an + * XEvent should have a non-empty trans_chars field. + */ + +#define IS_PRINTABLE(keychar) ((keychar >= 0x20) && \ + (keychar != 0x7f) && \ + (keychar < 0xF700)) + +/* * Structure encapsulating current drawing environment. */ @@ -356,6 +365,7 @@ VISIBILITY_HIDDEN @interface TKContentView(TKKeyEvent) - (void) deleteWorkingText; +- (void) cancelComposingText; @end @interface TKContentView(TKWindowEvent) diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c index 72a5996..a4809a4 100644 --- a/macosx/tkMacOSXTest.c +++ b/macosx/tkMacOSXTest.c @@ -229,18 +229,18 @@ InjectKeyEventObjCmd( int objc, Tcl_Obj *const objv[]) { + static const char *const optionStrings[] = { + "press", "release", "flagschanged", NULL}; + NSUInteger types[3] = {NSKeyDown, NSKeyUp, NSFlagsChanged}; + static const char *const argStrings[] = { + "-shift", "-control", "-option", "-command", "-x", "-y", NULL}; + enum args {KEYEVENT_SHIFT, KEYEVENT_CONTROL, KEYEVENT_OPTION, + KEYEVENT_COMMAND, KEYEVENT_X, KEYEVENT_Y}; int i, index, key, mods = 0, x = 0, y = 0; - const char *character, *unmod=nil; + NSString *keyChar, *unmod; NSEvent *keyEvent; - NSString *unmodChar = nil; NSUInteger type; - static const char *const optionStrings[] = { - "press", "release", "flagschanged"}; - NSUInteger types[3] = {NSKeyDown, NSKeyUp, NSFlagsChanged}; - static const char *const argStrings[] = { - "-unmod", "-modifiers", "-x", "-y"}; - enum args {KEYEVENT_UNMOD, KEYEVENT_MODIFIERS, KEYEVENT_X, KEYEVENT_Y}; if (objc < 4) { wrongArgs: Tcl_WrongNumArgs(interp, 1, objv, "option key char ?arg?"); @@ -263,34 +263,33 @@ InjectKeyEventObjCmd( Tcl_SetErrorCode(interp, "TK", "TEST", "INJECT", "KEY", NULL); return TCL_ERROR; } - character = Tcl_GetString(objv[3]); + keyChar = [[[NSString alloc] initWithUTF8String:Tcl_GetString(objv[3])] + autorelease]; + unmod = keyChar; for (i = 4; i < objc; i++) { - if (Tcl_GetIndexFromObjStruct(interp, objv[i], &argStrings, - sizeof(char *), "option", 0, &index) != TCL_OK) { + if (Tcl_GetIndexFromObjStruct(interp, objv[i], argStrings, + sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum args) index) { - case KEYEVENT_UNMOD: - if (++i >= objc) { - goto wrongArgs; - } - unmod = Tcl_GetString(objv[i]), - unmodChar = [[[NSString alloc] initWithUTF8String:unmod] - autorelease]; + case KEYEVENT_SHIFT: + mods |= NSShiftKeyMask; + unmod = [keyChar lowercaseString]; + break; + case KEYEVENT_CONTROL: + mods |= NSShiftKeyMask; + break; + case KEYEVENT_OPTION: + mods |= NSShiftKeyMask; + break; + case KEYEVENT_COMMAND: + mods |= NSShiftKeyMask; break; - case KEYEVENT_MODIFIERS: - if (++i >= objc) { - goto wrongArgs; - } - if (Tcl_GetIntFromObj(interp,objv[i],&mods) != TCL_OK) { - return TCL_ERROR; - } - break; case KEYEVENT_X: if (++i >= objc) { goto wrongArgs; } - if (Tcl_GetIntFromObj(interp,objv[i],&x) != TCL_OK) { + if (Tcl_GetIntFromObj(interp,objv[i], &x) != TCL_OK) { return TCL_ERROR; } break; @@ -298,9 +297,10 @@ InjectKeyEventObjCmd( if (++i >= objc) { goto wrongArgs; } - if (Tcl_GetIntFromObj(interp,objv[i],&y) != TCL_OK) { + if (Tcl_GetIntFromObj(interp,objv[i], &y) != TCL_OK) { return TCL_ERROR; } + break; } } keyEvent = [NSEvent keyEventWithType:type @@ -309,9 +309,8 @@ InjectKeyEventObjCmd( timestamp:GetCurrentEventTime() windowNumber:0 context:nil - characters:[[[NSString alloc] initWithUTF8String:character] - autorelease] - charactersIgnoringModifiers:unmodChar + characters:keyChar + charactersIgnoringModifiers:unmod isARepeat:NO keyCode:key]; [NSApp postEvent:keyEvent atStart:NO]; |