From 261c92277b500b2f6d7f8c049c54ac3535de8f63 Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 17 Apr 2020 18:26:49 +0000 Subject: Fix [585584ad66]: Aqua: event generate not working for function keys --- macosx/tkMacOSXKeyEvent.c | 121 +++++++++------------------------------------- macosx/tkMacOSXKeyboard.c | 107 +++++++++++++++++----------------------- 2 files changed, 67 insertions(+), 161 deletions(-) diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index f69a531..8d7f258 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -33,8 +33,6 @@ static int caret_x = 0, caret_y = 0, caret_height = 0; static TkWindow *caret_win = NULL; static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); -static unsigned isFunctionKey(unsigned int code); - #pragma mark TKApplication(TKKeyEvent) @@ -52,8 +50,7 @@ static unsigned isFunctionKey(unsigned int code); NSDeviceIndependentModifierFlagsMask); NSString *characters = nil; NSString *charactersIgnoringModifiers = nil; - NSUInteger len = 0; - int code = 0; + UniChar unichar = 0; static NSUInteger savedModifiers = 0; static NSMutableArray *nsEvArray; @@ -96,6 +93,20 @@ static unsigned isFunctionKey(unsigned int code); BOOL has_modifiers = NO; XEvent xEvent; + if (type == NSKeyUp || type == NSKeyDown) { + characters = [theEvent characters]; + unichar = [characters characterAtIndex:0]; + charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers]; + +#if defined(TK_MAC_DEBUG_EVENTS) || NS_KEYLOG == 1 + TKLog(@"-[%@(%p) %s] r=%d mods=%u '%@' '%@' keyCode=%u c=%d %@ %d", + [self class], self, _cmd, (type == NSKeyDown) && [theEvent isARepeat], + modifiers, characters, charactersIgnoringModifiers, keyCode, + ([charactersIgnoringModifiers length] == 0) ? 0 : + [charactersIgnoringModifiers characterAtIndex: 0], w, type); +#endif + } + /* * Check whether this key event belongs to the caret window. */ @@ -128,18 +139,13 @@ static unsigned isFunctionKey(unsigned int code); xEvent.xkey.keycode = (modifiers ^ savedModifiers); break; case NSKeyUp: - characters = [theEvent characters]; xEvent.xany.type = KeyRelease; - xEvent.xkey.keycode = (keyCode << 16) | (UInt16) [characters characterAtIndex:0]; + xEvent.xkey.keycode = (keyCode << 16) | unichar; break; case NSKeyDown: - characters = [theEvent characters]; - charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers]; xEvent.xany.type = KeyPress; - xEvent.xkey.keycode = (keyCode << 16) | (UInt16) [characters characterAtIndex:0]; - len = [charactersIgnoringModifiers length]; - if (len > 0) { - code = [charactersIgnoringModifiers characterAtIndex:0]; + xEvent.xkey.keycode = (keyCode << 16) | unichar; + if ([charactersIgnoringModifiers length] > 0) { has_modifiers = xEvent.xkey.state & (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask); } @@ -148,15 +154,10 @@ static unsigned isFunctionKey(unsigned int code); return theEvent; /* Unrecognized key event. */ } -#if defined(TK_MAC_DEBUG_EVENTS) || NS_KEYLOG == 1 - TKLog(@"-[%@(%p) %s] r=%d mods=%u '%@' '%@' code=%u c=%d %@ %d", - [self class], self, _cmd, (type == NSKeyDown) && [theEvent isARepeat], - modifiers, characters, charactersIgnoringModifiers, keyCode, - ([charactersIgnoringModifiers length] == 0) ? 0 : - [charactersIgnoringModifiers characterAtIndex: 0], w, type); -#endif - - if (type != NSKeyDown || !has_caret || isFunctionKey(code) || has_modifiers) { + if (type != NSKeyDown || + !has_caret || + has_modifiers || + ((keyCode >= 0xF700) && (keyCode <= 0xF8FF))) { if (type == NSKeyDown && [theEvent isARepeat]) { /* @@ -723,84 +724,6 @@ Tk_SetCaretPos( caret_height = height; } - -static unsigned convert_ns_to_X_keysym[] = -{ - NSHomeFunctionKey, 0x50, - NSLeftArrowFunctionKey, 0x51, - NSUpArrowFunctionKey, 0x52, - NSRightArrowFunctionKey, 0x53, - NSDownArrowFunctionKey, 0x54, - NSPageUpFunctionKey, 0x55, - NSPageDownFunctionKey, 0x56, - NSEndFunctionKey, 0x57, - NSBeginFunctionKey, 0x58, - NSSelectFunctionKey, 0x60, - NSPrintFunctionKey, 0x61, - NSExecuteFunctionKey, 0x62, - NSInsertFunctionKey, 0x63, - NSUndoFunctionKey, 0x65, - NSRedoFunctionKey, 0x66, - NSMenuFunctionKey, 0x67, - NSFindFunctionKey, 0x68, - NSHelpFunctionKey, 0x6A, - NSBreakFunctionKey, 0x6B, - - NSF1FunctionKey, 0xBE, - NSF2FunctionKey, 0xBF, - NSF3FunctionKey, 0xC0, - NSF4FunctionKey, 0xC1, - NSF5FunctionKey, 0xC2, - NSF6FunctionKey, 0xC3, - NSF7FunctionKey, 0xC4, - NSF8FunctionKey, 0xC5, - NSF9FunctionKey, 0xC6, - NSF10FunctionKey, 0xC7, - NSF11FunctionKey, 0xC8, - NSF12FunctionKey, 0xC9, - NSF13FunctionKey, 0xCA, - NSF14FunctionKey, 0xCB, - NSF15FunctionKey, 0xCC, - NSF16FunctionKey, 0xCD, - NSF17FunctionKey, 0xCE, - NSF18FunctionKey, 0xCF, - NSF19FunctionKey, 0xD0, - NSF20FunctionKey, 0xD1, - NSF21FunctionKey, 0xD2, - NSF22FunctionKey, 0xD3, - NSF23FunctionKey, 0xD4, - NSF24FunctionKey, 0xD5, - - NSBackspaceCharacter, 0x08, /* 8: Not on some KBs. */ - NSDeleteCharacter, 0xFF, /* 127: Big 'delete' key upper right. */ - NSDeleteFunctionKey, 0x9F, /* 63272: Del forw key off main array. */ - - NSTabCharacter, 0x09, - 0x19, 0x09, /* left tab->regular since pass shift */ - NSCarriageReturnCharacter, 0x0D, - NSNewlineCharacter, 0x0D, - NSEnterCharacter, 0x8D, - - 0x1B, 0x1B /* escape */ -}; - - -static unsigned -isFunctionKey( - unsigned code) -{ - const unsigned last_keysym = (sizeof(convert_ns_to_X_keysym) - / sizeof(convert_ns_to_X_keysym[0])); - unsigned keysym; - - for (keysym = 0; keysym < last_keysym; keysym += 2) { - if (code == convert_ns_to_X_keysym[keysym]) { - return 0xFF00 | convert_ns_to_X_keysym[keysym + 1]; - } - } - return 0; -} - /* * Local Variables: * mode: objc diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index 60e67c8..961808c 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -23,7 +23,7 @@ #define LATIN1_MAX 255 #define MAC_KEYCODE_MAX 0x7F -#define MAC_KEYCODE_MASK 0x7F +#define MAC_KEYCODE_MASK 0xFF #define COMMAND_MASK Mod1Mask #define OPTION_MASK Mod2Mask @@ -35,65 +35,49 @@ */ typedef struct { - int keycode; /* Macintosh keycode. */ + int keycode; /* Macintosh keycode in NSEvent */ KeySym keysym; /* X windows keysym. */ } KeyInfo; -/* - * Notes on keyArray: - * - * 0x34, XK_Return - Powerbooks use this and some keymaps define it. - * - * 0x4C, XK_Return - XFree86 and Apple's X11 call this one XK_KP_Enter. - * - * 0x47, XK_Clear - This key is NumLock when used on PCs, but Mac - * applications don't use it like that, nor does Apple's X11. - * - * All other keycodes are taken from the published ADB keyboard layouts. - */ static KeyInfo keyArray[] = { - {0x24, XK_Return}, - {0x30, XK_Tab}, - {0x33, XK_BackSpace}, - {0x34, XK_Return}, - {0x35, XK_Escape}, - - {0x47, XK_Clear}, - {0x4C, XK_KP_Enter}, - - {0x72, XK_Help}, - {0x73, XK_Home}, - {0x74, XK_Page_Up}, - {0x75, XK_Delete}, - {0x77, XK_End}, - {0x79, XK_Page_Down}, - - {0x7B, XK_Left}, - {0x7C, XK_Right}, - {0x7D, XK_Down}, - {0x7E, XK_Up}, - - {0, 0} -}; - -static KeyInfo virtualkeyArray[] = { - {122, XK_F1}, - {120, XK_F2}, - {99, XK_F3}, - {118, XK_F4}, + {36, XK_Return}, + {48, XK_Tab}, + {51, XK_BackSpace}, + {52, XK_Return}, /* Used on some powerbooks. */ + {53, XK_Escape}, + {64, XK_F17}, + {71, XK_Clear}, /* Numlock on PC */ + {76, XK_KP_Enter}, + {79, XK_F18}, + {80, XK_F19}, {96, XK_F5}, {97, XK_F6}, {98, XK_F7}, + {99, XK_F3}, {100, XK_F8}, {101, XK_F9}, - {109, XK_F10}, {103, XK_F11}, - {111, XK_F12}, {105, XK_F13}, + {106, XK_F16}, {107, XK_F14}, + {109, XK_F10}, + {111, XK_F12}, {113, XK_F15}, - {0, 0} + {114, XK_Help}, + {115, XK_Home}, + {116, XK_Page_Up}, + {117, XK_Delete}, + {118, XK_F4}, + {119, XK_End}, + {120, XK_F2}, + {121, XK_Page_Down}, + {122, XK_F1}, + {123, XK_Left}, + {124, XK_Right}, + {125, XK_Down}, + {126, XK_Up}, + {0, 0}, }; #define NUM_MOD_KEYCODES 14 @@ -116,9 +100,6 @@ static KeyCode modKeyArray[NUM_MOD_KEYCODES] = { static int initialized = 0; static Tcl_HashTable keycodeTable; /* keyArray hashed by keycode value. */ -static Tcl_HashTable vkeyTable; /* virtualkeyArray hashed by virtual - * keycode value. */ - static int latin1Table[LATIN1_MAX+1]; /* Reverse mapping table for * controls, ASCII and Latin-1. */ @@ -182,12 +163,6 @@ InitKeyMaps(void) &dummy); Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keysym)); } - Tcl_InitHashTable(&vkeyTable, TCL_ONE_WORD_KEYS); - for (kPtr = virtualkeyArray; kPtr->keycode != 0; kPtr++) { - hPtr = Tcl_CreateHashEntry(&vkeyTable, INT2PTR(kPtr->keycode), - &dummy); - Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keysym)); - } initialized = 1; } @@ -381,7 +356,7 @@ XKeycodeToKeysym( newKeycode = keycode >> 16; if ((keycode & 0xFFFF) >= 0xF700) { /* NSEvent.h function key unicodes */ - hPtr = Tcl_FindHashEntry(&vkeyTable, INT2PTR(newKeycode)); + hPtr = Tcl_FindHashEntry(&keycodeTable, INT2PTR(newKeycode)); if (hPtr != NULL) { return (KeySym) Tcl_GetHashValue(hPtr); } @@ -592,7 +567,8 @@ XKeysymToMacKeycode( return kPtr->keycode; } } - for (kPtr = virtualkeyArray; kPtr->keycode != 0; kPtr++) { + /* Should do hash lookup. */ + for (kPtr = keyArray; kPtr->keycode != 0; kPtr++) { if (kPtr->keysym == keysym) { return kPtr->keycode; } @@ -646,13 +622,12 @@ XKeysymToKeycode( KeyCode result; /* - * See also TkpSetKeycodeAndState. The 0x0010 magic is used in - * XKeycodeToKeysym. For special keys like XK_Return the lower 8 bits of - * the keysym are usually a related ASCII control code. + * See also TkpSetKeycodeAndState. For special keys like XK_Return the + * lower 8 bits of the keysym are usually a related ASCII control code. */ if ((keysym >= XK_F1) && (keysym <= XK_F35)) { - result = 0x0010; + result = 0xf704 + keysym - XK_F1; } else { result = 0x00FF & keysym; } @@ -705,8 +680,16 @@ TkpSetKeycodeAndState( /* * See also XKeysymToKeycode. */ + if ((keysym >= XK_F1) && (keysym <= XK_F35)) { - eventPtr->xkey.keycode = 0x0010; + + /* + * NSEvent.h defines the unicode values in the "Private Use" range + * 0xF700-0xF8FF to correspond to function keys. The first four + * are arrow keys, followed by the Fn function keys in order. + */ + + eventPtr->xkey.keycode = 0xf704 + keysym - XK_F1; } else { eventPtr->xkey.keycode = 0x00FF & keysym; } -- cgit v0.12 From d0ac480acbd80c95c07b982ca19b175690c4c5ff Mon Sep 17 00:00:00 2001 From: marc_culler Date: Sun, 19 Apr 2020 23:43:24 +0000 Subject: Another major overhaul: more code cleanup; make event generate work for Home, End, ...; distinguish left and right mod keys. --- macosx/tkMacOSXKeyEvent.c | 68 +++++--- macosx/tkMacOSXKeyboard.c | 401 ++++++++++++++++++---------------------------- 2 files changed, 203 insertions(+), 266 deletions(-) diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 8d7f258..100ac02 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -43,14 +43,14 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); #ifdef TK_MAC_DEBUG_EVENTS TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); #endif - unsigned short keyCode = [theEvent keyCode]; + unsigned int keyCode = [theEvent keyCode]; NSWindow *w = [theEvent window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); NSUInteger modifiers = ([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); NSString *characters = nil; NSString *charactersIgnoringModifiers = nil; - UniChar unichar = 0; + UniChar keychar = 0; static NSUInteger savedModifiers = 0; static NSMutableArray *nsEvArray; @@ -95,9 +95,9 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); if (type == NSKeyUp || type == NSKeyDown) { characters = [theEvent characters]; - unichar = [characters characterAtIndex:0]; + keychar = [characters length] > 0 ? [characters characterAtIndex:0] : 0; charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers]; - + #if defined(TK_MAC_DEBUG_EVENTS) || NS_KEYLOG == 1 TKLog(@"-[%@(%p) %s] r=%d mods=%u '%@' '%@' keyCode=%u c=%d %@ %d", [self class], self, _cmd, (type == NSKeyDown) && [theEvent isARepeat], @@ -105,50 +105,63 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); ([charactersIgnoringModifiers length] == 0) ? 0 : [charactersIgnoringModifiers characterAtIndex: 0], w, type); #endif + + } + if (type == NSKeyDown) { + has_modifiers = xEvent.xkey.state & + (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask); } - + /* - * Check whether this key event belongs to the caret window. + * Build the XEvent. */ setupXEvent(&xEvent, tkwin, modifiers); + + /* + * Check whether this key event belongs to the caret window. + */ + Bool has_caret = (TkFocusKeyEvent(winPtr, &xEvent) == caret_win); /* * A KeyDown event received for the caret window which is not a function key - * and has no modifiers other than Shift or Alt will be processed with the + * and has no modifiers other than Shift or Option will be processed with the * TextInputClient protocol below. All other key events are handled here * by queueing the XEvent created above. */ if (!processingCompose || !has_caret) { + xEvent.xkey.keycode = (keyCode << 16) | keychar; switch (type) { case NSFlagsChanged: - if (savedModifiers > modifiers) { - xEvent.xany.type = KeyRelease; - } else { - xEvent.xany.type = KeyPress; - } /* - * The value -1 indicates a special keycode to the platform - * specific code in tkMacOSXKeyboard.c. + * If the highest bit where they differ was set then this is a press. + */ + + xEvent.xany.type = modifiers > savedModifiers ? KeyPress : KeyRelease; + + /* + * The value -1 signals a modifier keycode to the function TkpGetKeySym + * defined in tkMacOSXKeyboard.c. */ xEvent.xany.send_event = -1; - xEvent.xkey.keycode = (modifiers ^ savedModifiers); + + /* + * Update savedModifiers for NSFlagsChanged events and no others, + * since some non-modifier keys (e.g. function keys) have modifier + * flags set + */ + + savedModifiers = modifiers; break; case NSKeyUp: xEvent.xany.type = KeyRelease; - xEvent.xkey.keycode = (keyCode << 16) | unichar; break; case NSKeyDown: xEvent.xany.type = KeyPress; - xEvent.xkey.keycode = (keyCode << 16) | unichar; - if ([charactersIgnoringModifiers length] > 0) { - has_modifiers = xEvent.xkey.state & - (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask); - } break; default: return theEvent; /* Unrecognized key event. */ @@ -157,7 +170,7 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); if (type != NSKeyDown || !has_caret || has_modifiers || - ((keyCode >= 0xF700) && (keyCode <= 0xF8FF))) { + ((keychar >= 0xF700) && (keychar <= 0xF8FF))) { if (type == NSKeyDown && [theEvent isARepeat]) { /* @@ -174,7 +187,6 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); */ Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); - savedModifiers = modifiers; return theEvent; } } @@ -185,7 +197,8 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); * interpretKeyEvents. But we only need to send KeyDown events. */ - if (type == NSKeyDown) { + switch (type) { + case NSKeyDown: if (NS_KEYLOG) { TKLog(@"keyDown: %s compose sequence.\n", processingCompose == YES ? "Continue" : "Begin"); @@ -212,8 +225,13 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); break; } } + break; + case NSFlagsChanged: + savedModifiers = modifiers; + break; + default: + break; } - savedModifiers = modifiers; return theEvent; } @end diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index 961808c..c007cc9 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -29,57 +29,78 @@ /* - * Tables enumerating the special keys defined on Mac keyboards. These are + * Table enumerating the special keys defined on Mac keyboards. These are * necessary for correct keysym mappings for all keys where the keysyms are * not identical with their ASCII or Latin-1 code points. + * + * This table includes every key listed in Apple's documentation of Function-Key + * Unicodes which is not marked as "Not on most Macintosh keyboards". I + * know of no reliable way to find the Apple "Virtual Keycode" for any of the + * keys that are so marked. */ typedef struct { - int keycode; /* Macintosh keycode in NSEvent */ - KeySym keysym; /* X windows keysym. */ + int virtual; /* value of [NSEvent keyCode] */ + KeySym keysym; /* X11 keysym */ + KeyCode keychar; /* XEvent keycode & 0xFFFF */ } KeyInfo; - -static KeyInfo keyArray[] = { - {36, XK_Return}, - {48, XK_Tab}, - {51, XK_BackSpace}, - {52, XK_Return}, /* Used on some powerbooks. */ - {53, XK_Escape}, - {64, XK_F17}, - {71, XK_Clear}, /* Numlock on PC */ - {76, XK_KP_Enter}, - {79, XK_F18}, - {80, XK_F19}, - {96, XK_F5}, - {97, XK_F6}, - {98, XK_F7}, - {99, XK_F3}, - {100, XK_F8}, - {101, XK_F9}, - {103, XK_F11}, - {105, XK_F13}, - {106, XK_F16}, - {107, XK_F14}, - {109, XK_F10}, - {111, XK_F12}, - {113, XK_F15}, - {114, XK_Help}, - {115, XK_Home}, - {116, XK_Page_Up}, - {117, XK_Delete}, - {118, XK_F4}, - {119, XK_End}, - {120, XK_F2}, - {121, XK_Page_Down}, - {122, XK_F1}, - {123, XK_Left}, - {124, XK_Right}, - {125, XK_Down}, - {126, XK_Up}, - {0, 0}, +static const KeyInfo keyArray[] = { + {36, XK_Return, NSNewlineCharacter}, + {48, XK_Tab, NSTabCharacter}, + {51, XK_BackSpace, NSBackspaceCharacter}, + {52, XK_Return, NSNewlineCharacter}, /* Used on some Powerbooks */ + {53, XK_Escape, 0x1B}, + {54, XK_Meta_R, 0}, + {55, XK_Meta_L, 0}, + {56, XK_Shift_L, 0}, + {57, XK_Caps_Lock, 0}, + {58, XK_Alt_L, 0}, + {59, XK_Control_L, 0}, + {60, XK_Shift_R, 0}, + {61, XK_Alt_R, 0}, + {62, XK_Control_R, 0}, + {63, XK_Super_L, 0}, + {64, XK_F17, NSF17FunctionKey}, + {71, XK_Clear, NSClearLineFunctionKey}, /* Numlock on PC */ + {76, XK_KP_Enter, NSEnterCharacter}, /* Fn Return */ + {79, XK_F18, NSF18FunctionKey}, + {80, XK_F19, NSF19FunctionKey}, + {90, XK_F20, NSF20FunctionKey}, /* For scripting only */ + {96, XK_F5, NSF5FunctionKey}, + {97, XK_F6, NSF6FunctionKey}, + {98, XK_F7, NSF7FunctionKey}, + {99, XK_F3, NSF3FunctionKey}, + {100, XK_F8, NSF8FunctionKey}, + {101, XK_F9, NSF9FunctionKey}, + {103, XK_F11, NSF11FunctionKey}, + {105, XK_F13, NSF13FunctionKey}, + {107, XK_F14, NSF14FunctionKey}, + {109, XK_F10, NSF10FunctionKey}, + {105, XK_F13, NSF13FunctionKey}, + {106, XK_F16, NSF16FunctionKey}, + {111, XK_F12, NSF12FunctionKey}, + {113, XK_F15, NSF15FunctionKey}, + {114, XK_Help, NSHelpFunctionKey}, + {115, XK_Home, NSHomeFunctionKey}, /* Fn Left */ + {116, XK_Page_Up, NSPageUpFunctionKey}, /* Fn Up */ + {117, XK_Delete, NSDeleteFunctionKey}, /* Fn Deleete */ + {118, XK_F4, NSF4FunctionKey}, + {119, XK_End, NSEndFunctionKey}, /* Fn Right */ + {120, XK_F2, NSF2FunctionKey}, + {121, XK_Page_Down, NSPageDownFunctionKey}, /* Fn Down */ + {122, XK_F1, NSF1FunctionKey}, + {123, XK_Left, NSLeftArrowFunctionKey}, + {124, XK_Right, NSRightArrowFunctionKey}, + {125, XK_Down, NSDownArrowFunctionKey}, + {126, XK_Up, NSUpArrowFunctionKey}, + {0, 0, 0} }; +/* + * X11 keysyms for modifier keys, in order. + */ + #define NUM_MOD_KEYCODES 14 static KeyCode modKeyArray[NUM_MOD_KEYCODES] = { XK_Shift_L, @@ -99,11 +120,10 @@ static KeyCode modKeyArray[NUM_MOD_KEYCODES] = { }; static int initialized = 0; -static Tcl_HashTable keycodeTable; /* keyArray hashed by keycode value. */ -static int latin1Table[LATIN1_MAX+1]; /* Reverse mapping table for - * controls, ASCII and Latin-1. */ - static int keyboardChanged = 1; +static Tcl_HashTable virtual2keysym; /* Maps Mac keyCode to X11 keysym. */ +static Tcl_HashTable keysym2keycode; /* Maps X11 keysym to Mac keycode. */ +static int latin1Table[LATIN1_MAX+1]; /* Reverse mapping table for Latin-1. */ /* * Prototypes for static functions used in this file. @@ -111,7 +131,6 @@ static int keyboardChanged = 1; static void InitKeyMaps (void); static void InitLatin1Table(Display *display); -static int XKeysymToMacKeycode(Display *display, KeySym keysym); static int KeycodeToUnicode(UniChar * uniChars, int maxChars, UInt16 keyaction, UInt32 keycode, UInt32 modifiers, UInt32 * deadKeyStatePtr); @@ -137,10 +156,6 @@ static int KeycodeToUnicode(UniChar * uniChars, int maxChars, * * Creates hash tables used by some of the functions in this file. * - * FIXME: As keycodes are defined to be in the limited range 0-127, it - * would be easier and more efficient to use directly initialized plain - * arrays and drop this function. - * * Results: * None. * @@ -154,14 +169,18 @@ static void InitKeyMaps(void) { Tcl_HashEntry *hPtr; - KeyInfo *kPtr; + const KeyInfo *kPtr; int dummy; - Tcl_InitHashTable(&keycodeTable, TCL_ONE_WORD_KEYS); - for (kPtr = keyArray; kPtr->keycode != 0; kPtr++) { - hPtr = Tcl_CreateHashEntry(&keycodeTable, INT2PTR(kPtr->keycode), - &dummy); + Tcl_InitHashTable(&virtual2keysym, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&keysym2keycode, TCL_ONE_WORD_KEYS); + for (kPtr = keyArray; kPtr->virtual != 0; kPtr++) { + hPtr = Tcl_CreateHashEntry(&virtual2keysym, INT2PTR(kPtr->virtual), + &dummy); Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keysym)); + hPtr = Tcl_CreateHashEntry(&keysym2keycode, INT2PTR(kPtr->keysym), + &dummy); + Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keychar | (kPtr->virtual << 16))); } initialized = 1; } @@ -171,10 +190,12 @@ InitKeyMaps(void) * * InitLatin1Table -- * - * Creates a simple table to be used for mapping from keysyms to keycodes. - * Always needs to be called before using latin1Table, because the - * keyboard layout may have changed, and than the table must be - * re-computed. + * Creates a simple table to be used for mapping from Latin-1 keysyms to + * keycodes as used in XEvents. Always needs to be called before using + * latin1Table, because the keyboard layout may have changed, and then the + * table must be re-computed. The high order byte of these keycodes is + * overwritten with the modifier flags, which can be used to set the state + * of an XEvent that uses the keycode. * * Results: * None. @@ -219,9 +240,9 @@ InitLatin1Table( } for (keycode = 0; keycode <= MAC_KEYCODE_MAX; keycode++) { - keysym = XKeycodeToKeysym(display,keycode<<16,state); - if (keysym <= LATIN1_MAX) { - latin1Table[keysym] = keycode | modifiers; + keysym = XKeycodeToKeysym(display, keycode<<16, state); + if (keysym != NoSymbol && keysym <= LATIN1_MAX) { + latin1Table[keysym] = modifiers << 16 | keycode << 16 | keysym; } } } @@ -287,20 +308,18 @@ KeycodeToUnicode( keycode &= 0xFF; modifiers = (modifiers >> 8) & 0xFF; - if (!deadKeyStatePtr) { options = kUCKeyTranslateNoDeadKeysMask; dummyState = 0; deadKeyStatePtr = &dummyState; } - err = ChkErr(UCKeyTranslate, uchr, keycode, keyaction, modifiers, keyboardType, options, deadKeyStatePtr, maxChars, &actuallength, uniChars); - if (!actuallength && *deadKeyStatePtr) { + /* - * More data later + * We are waiting for another key. */ return 0; @@ -318,8 +337,8 @@ KeycodeToUnicode( * * XKeycodeToKeysym -- * - * Translate from a system-dependent keycode to a system-independent - * keysym. + * Translate from the system-dependent keycode used in an XEvent + * to a system-independent X11 keysym. * * Results: * Returns the translated keysym, or NoSymbol on failure. @@ -334,11 +353,11 @@ KeySym XKeycodeToKeysym( Display* display, KeyCode keycode, - int index) + int state) { Tcl_HashEntry *hPtr; - int newKeycode; - UniChar newChar; + int virtual, modifiers = 0; + UniChar keyChar = 0; (void) display; /*unused*/ @@ -347,49 +366,32 @@ XKeycodeToKeysym( } /* - * When determining what keysym to produce we first check to see if the key - * is a function key. We then check to see if the character is another - * non-printing key. Finally, we return the key syms for all ASCII and - * Latin-1 chars. + * First check if the virtual keycode corresponds to a special key such as + * an Fn function key or Tab, Backspace, Home, End, etc. */ - newKeycode = keycode >> 16; - - if ((keycode & 0xFFFF) >= 0xF700) { /* NSEvent.h function key unicodes */ - hPtr = Tcl_FindHashEntry(&keycodeTable, INT2PTR(newKeycode)); + virtual = (keycode >> 16) & 0xFF; + if (virtual) { + hPtr = Tcl_FindHashEntry(&virtual2keysym, INT2PTR(virtual)); if (hPtr != NULL) { return (KeySym) Tcl_GetHashValue(hPtr); } } - hPtr = Tcl_FindHashEntry(&keycodeTable, INT2PTR(newKeycode)); - if (hPtr != NULL) { - return (KeySym) Tcl_GetHashValue(hPtr); - } /* - * Add in the Mac modifier flags for shift and option. + * No? Check if the key represents a Latin-1 letter. */ - if (index & 1) { - newKeycode |= shiftKey; - } - if (index & 2) { - newKeycode |= optionKey; + modifiers = (state & 1 ? shiftKey : 0) | (state & 2 ? optionKey : 0); + KeycodeToUnicode(&keyChar, 1, kUCKeyActionDown, virtual, modifiers, NULL); + if ((keyChar >= XK_space) && (keyChar <= LATIN1_MAX)) { + return keyChar; } - newChar = 0; - KeycodeToUnicode(&newChar, 1, kUCKeyActionDown, newKeycode & 0x00FF, - newKeycode & 0xFF00, NULL); - /* - * X11 keysyms are identical to Unicode for ASCII and Latin-1. Give up for - * other characters for now. + * This keycode does not belong to a key on any known Macintosh keyboard. */ - if ((newChar >= XK_space) && (newChar <= LATIN1_MAX)) { - return newChar; - } - return NoSymbol; } @@ -522,14 +524,23 @@ XStringToKeysym( /* *---------------------------------------------------------------------- * - * XKeysymToMacKeycode -- + * XKeysymToKeycode -- * - * An internal function like XKeysymToKeycode but only generating the Mac - * specific keycode plus the modifiers Shift and Option. + * Converts an XKeysym to the value which would be used as the keycode + * field of a KeyPress or KeyRelease XEvent for the corresponding key. + * This is an opaque stub function which knows how the keycode field is + * generated on a Mac. * * Results: - * A Mac keycode with the actual keycode in the low byte and Mac-style - * modifier bits in the high byte. + * + * An X11 KeyCode with a unicode character in the low 16 bits and the + * 8-bit "virtual keycode" in the third byte. (The virtual keycode is the + * 8-bit value that appears as [NSEvent keycode] in an NSKeyUp or + * NSKeyDown event.) On the Mac pressing a modifier key generates an + * NSFlagsChanged event but not an NSKeyDown event. If a keysym + * represents a modifier key, the unicode character is 0, but the modifier + * flags are set in the high order byte. For keysyms that represent other + * keys, the resulting keycode has no modifiers. * * Side effects: * None. @@ -537,13 +548,15 @@ XStringToKeysym( *---------------------------------------------------------------------- */ -static int -XKeysymToMacKeycode( +KeyCode +XKeysymToKeycode( Display *display, KeySym keysym) { - KeyInfo *kPtr; + Tcl_HashEntry *hPtr; + if (keysym <= LATIN1_MAX) { + /* * Handle keysyms in the Latin-1 range where keysym and Unicode * character code point are the same. @@ -557,37 +570,22 @@ XKeysymToMacKeycode( } /* - * Handle special keys from our exception tables. Don't mind if this is - * slow, neither the test suite nor [event generate] need to be optimized - * (we hope). + * This is not a Latin-1 key. Try doing a hash table lookup to find the + * keycode. */ - for (kPtr = keyArray; kPtr->keycode != 0; kPtr++) { - if (kPtr->keysym == keysym) { - return kPtr->keycode; - } - } - /* Should do hash lookup. */ - for (kPtr = keyArray; kPtr->keycode != 0; kPtr++) { - if (kPtr->keysym == keysym) { - return kPtr->keycode; - } + if (!initialized) { + InitKeyMaps(); } - /* - * Modifier keycodes only come from generated events. No translation - * is needed. - */ - - for (int i=0; i < NUM_MOD_KEYCODES; i++) { - if (keysym == modKeyArray[i]) { - return keysym; - } + hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym)); + if (hPtr != NULL) { + return (KeyCode) Tcl_GetHashValue(hPtr); } /* - * For other keysyms (not Latin-1 and not special keys), we'd need a - * generic keysym-to-unicode table. We don't have that, so we give up here. + * The keysym is not Latin-1 and not in our hash table, so it does not + * appear on any known Macintosh keyboard; just return 0. */ return 0; @@ -596,68 +594,23 @@ XKeysymToMacKeycode( /* *---------------------------------------------------------------------- * - * XKeysymToKeycode -- - * - * The function XKeysymToKeycode takes an X11 keysym and converts it into - * a Mac keycode. It is in the stubs table for compatibility but not used - * anywhere in the core. - * - * Results: - * A 32 bit keycode with the the mac keycode (without modifiers) in the - * higher 16 bits of the keycode and the ASCII or Latin-1 code in the - * lower 8 bits of the keycode. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -KeyCode -XKeysymToKeycode( - Display* display, - KeySym keysym) -{ - int macKeycode = XKeysymToMacKeycode(display, keysym); - KeyCode result; - - /* - * See also TkpSetKeycodeAndState. For special keys like XK_Return the - * lower 8 bits of the keysym are usually a related ASCII control code. - */ - - if ((keysym >= XK_F1) && (keysym <= XK_F35)) { - result = 0xf704 + keysym - XK_F1; - } else { - result = 0x00FF & keysym; - } - result |= (macKeycode & MAC_KEYCODE_MASK) << 16; - - return result; -} - -/* - *---------------------------------------------------------------------- - * * TkpSetKeycodeAndState -- * - * The function TkpSetKeycodeAndState takes a keysym and fills in the - * appropriate members of an XEvent. It is similar to XKeysymToKeycode, - * but it also sets the modifier mask in the XEvent. It is used by [event - * generate] and it is in the stubs table. + * This function accepts a keysym and an XEvent and sets some fields of + * the XEvent. It is used by the event generate command. * * Results: - * Fills an XEvent, sets the member xkey.keycode with a keycode - * formatted the same as XKeysymToKeycode and the member xkey.state with - * the modifiers implied by the keysym. Also fills in xkey.trans_chars, - * so that the actual characters can be retrieved later. + * None * * Side effects: - * None. + * + * Modifies the XEvent. Sets the xkey.keycode to a keycode value formatted + * by XKeysymToKeycode and sets the shift and option flags in xkey.state + * to the values implied by the keysym. Also fills in xkey.trans_chars, so + * that the actual characters can be retrieved later. * *---------------------------------------------------------------------- */ - void TkpSetKeycodeAndState( Tk_Window tkwin, @@ -666,46 +619,19 @@ TkpSetKeycodeAndState( { if (keysym == NoSymbol) { eventPtr->xkey.keycode = 0; - } else if ( modKeyArray[0] <= keysym && - keysym <= modKeyArray[NUM_MOD_KEYCODES - 1]) { - /* - * Keysyms for pure modifiers only arise in generated events. - * We should just copy them to the keycode. - */ - eventPtr->xkey.keycode = keysym; } else { Display *display = Tk_Display(tkwin); - int macKeycode = XKeysymToMacKeycode(display, keysym); - - /* - * See also XKeysymToKeycode. - */ - - if ((keysym >= XK_F1) && (keysym <= XK_F35)) { - - /* - * NSEvent.h defines the unicode values in the "Private Use" range - * 0xF700-0xF8FF to correspond to function keys. The first four - * are arrow keys, followed by the Fn function keys in order. - */ - - eventPtr->xkey.keycode = 0xf704 + keysym - XK_F1; - } else { - eventPtr->xkey.keycode = 0x00FF & keysym; - } - eventPtr->xkey.keycode |= (macKeycode & MAC_KEYCODE_MASK) << 16; - - if (shiftKey & macKeycode) { + eventPtr->xkey.keycode = XKeysymToKeycode(display, keysym); + if ((shiftKey << 16) & eventPtr->xkey.keycode) { eventPtr->xkey.state |= ShiftMask; } - if (optionKey & macKeycode) { + if ((optionKey << 16) & eventPtr->xkey.keycode) { eventPtr->xkey.state |= OPTION_MASK; } - + eventPtr->xkey.keycode &= 0xFFFFFF; if (keysym <= LATIN1_MAX) { - int done = TkUniCharToUtf(keysym, eventPtr->xkey.trans_chars); - - eventPtr->xkey.trans_chars[done] = 0; + int length = TkUniCharToUtf(keysym, eventPtr->xkey.trans_chars); + eventPtr->xkey.trans_chars[length] = 0; } else { eventPtr->xkey.trans_chars[0] = 0; } @@ -718,7 +644,7 @@ TkpSetKeycodeAndState( * TkpGetKeySym -- * * Given an X KeyPress or KeyRelease event, map the keycode in the event - * into a keysym. + * to a keysym. * * Results: * The return value is the keysym corresponding to eventPtr, or NoSymbol @@ -753,35 +679,28 @@ TkpGetKeySym( */ if (eventPtr->xany.send_event == -1) { - int modifier = eventPtr->xkey.keycode & NSDeviceIndependentModifierFlagsMask; - - if (modifier == NSCommandKeyMask) { + switch (eventPtr->xkey.keycode >> 16) { + case 54: + return XK_Meta_R; + case 55: return XK_Meta_L; - } else if (modifier == NSShiftKeyMask) { + case 56: return XK_Shift_L; - } else if (modifier == NSAlphaShiftKeyMask) { + case 57: return XK_Caps_Lock; - } else if (modifier == NSAlternateKeyMask) { + case 58: return XK_Alt_L; - } else if (modifier == NSControlKeyMask) { + case 59: return XK_Control_L; - } else if (modifier == NSNumericPadKeyMask) { - return XK_Num_Lock; - } else if (modifier == NSFunctionKeyMask) { - return XK_Super_L; -/* - } else if (modifier == rightShiftKey) { + case 60: return XK_Shift_R; - } else if (modifier == rightOptionKey) { + case 61: return XK_Alt_R; - } else if (modifier == rightControlKey) { + case 62: return XK_Control_R; -*/ - } else { - /* - * If we get here, we probably need to implement something new. - */ - + case 63: + return XK_Super_L; + default: return NoSymbol; } } @@ -921,7 +840,7 @@ TkpInitKeymapInfo( dispPtr->numModKeyCodes = NUM_MOD_KEYCODES; dispPtr->modKeyCodes = (KeyCode *)ckalloc(NUM_MOD_KEYCODES * sizeof(KeyCode)); for (int i = 0; i < NUM_MOD_KEYCODES; i++) { - dispPtr->modKeyCodes[i] = modKeyArray[i]; + dispPtr->modKeyCodes[i] = XKeysymToKeycode(NULL, modKeyArray[i]); } } -- cgit v0.12 From 3fcc0bae6630b843931fdcf8636a4306a581739c Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Apr 2020 17:14:40 +0000 Subject: Another rewrite of tkProcessKeyEvent to repair and clarify the logic of when to use the TextInputClient and when to send an XEvent. --- macosx/tkMacOSXKeyEvent.c | 214 +++++++++++++++++++++------------------------- macosx/tkMacOSXKeyboard.c | 13 ++- 2 files changed, 102 insertions(+), 125 deletions(-) diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 100ac02..ca6fe32 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -23,7 +23,7 @@ #endif */ #define NS_KEYLOG 0 - +#define XEVENT_MOD_MASK (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask) static Tk_Window keyboardGrabWinPtr = NULL; /* Current keyboard grab window. */ static NSWindow *keyboardGrabNSWindow = nil; /* Its underlying NSWindow.*/ static NSModalSession modalSession = nil; @@ -43,21 +43,29 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); #ifdef TK_MAC_DEBUG_EVENTS TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); #endif - unsigned int keyCode = [theEvent keyCode]; NSWindow *w = [theEvent window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); + Tk_Window tkwin = (Tk_Window) winPtr; + TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr; + NSEventType type = [theEvent type]; + NSUInteger keyCode = [theEvent keyCode]; NSUInteger modifiers = ([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); - NSString *characters = nil; - NSString *charactersIgnoringModifiers = nil; + XEvent xEvent; + Bool has_modifiers = NO; + Bool has_caret = NO; + Bool use_text_input = NO; UniChar keychar = 0; static NSUInteger savedModifiers = 0; - static NSMutableArray *nsEvArray; + static NSMutableArray *nsEvArray = nil; if (nsEvArray == nil) { nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1]; processingCompose = NO; } + if (!winPtr) { + return theEvent; + } /* * Control-Tab and Control-Shift-Tab are used to switch tabs in a tabbed @@ -69,19 +77,13 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); return theEvent; } - if (!winPtr) { - return theEvent; - } - /* * If a local grab is in effect, key events for windows in the * grabber's application are redirected to the grabber. Key events - * for other applications are delivered normally. If a global + * for dispPtr->grabWinPtr; if (grabWinPtr) { if (winPtr->dispPtr->grabFlags || /* global grab */ grabWinPtr->mainPtr == winPtr->mainPtr){ /* same application */ @@ -89,131 +91,65 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); } } - NSEventType type = [theEvent type]; - BOOL has_modifiers = NO; - XEvent xEvent; + /* + * Extract the unicode character from KeyUp and KeyDown events. + */ if (type == NSKeyUp || type == NSKeyDown) { - characters = [theEvent characters]; - keychar = [characters length] > 0 ? [characters characterAtIndex:0] : 0; - charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers]; - + if ([[theEvent characters] length] > 0) { + keychar = [[theEvent characters] characterAtIndex:0]; + } else { + + /* + * This is a dead key, so it should go to the TextInputClient. + */ + + use_text_input = YES; + } #if defined(TK_MAC_DEBUG_EVENTS) || NS_KEYLOG == 1 - TKLog(@"-[%@(%p) %s] r=%d mods=%u '%@' '%@' keyCode=%u c=%d %@ %d", - [self class], self, _cmd, (type == NSKeyDown) && [theEvent isARepeat], - modifiers, characters, charactersIgnoringModifiers, keyCode, - ([charactersIgnoringModifiers length] == 0) ? 0 : - [charactersIgnoringModifiers characterAtIndex: 0], w, type); + TKLog(@"-[%@(%p) %s] repeat=%d mods=%x char=%x code=%lu c=%d type=%d", + [self class], self, _cmd, + (type == NSKeyDown) && [theEvent isARepeat], modifiers, keychar, + keyCode, w, type); #endif - - } - if (type == NSKeyDown) { - has_modifiers = xEvent.xkey.state & - (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask); } /* - * Build the XEvent. + * Build a skeleton XEvent. We need to build it here, even if we will not + * send it, so we can pass it to TkFocusKeyEvent to determine whether the + * target windows has the caret. */ setupXEvent(&xEvent, tkwin, modifiers); + has_modifiers = xEvent.xkey.state & XEVENT_MOD_MASK; + has_caret = (TkFocusKeyEvent(winPtr, &xEvent) == caret_win); /* - * Check whether this key event belongs to the caret window. + * A KeyDown event targetting the caret window and having alphanumeric + * keychar should be processed by our TextInputClient. The XEvent will not + * be sent in this case. */ - Bool has_caret = (TkFocusKeyEvent(winPtr, &xEvent) == caret_win); - - /* - * A KeyDown event received for the caret window which is not a function key - * and has no modifiers other than Shift or Option will be processed with the - * TextInputClient protocol below. All other key events are handled here - * by queueing the XEvent created above. - */ - - if (!processingCompose || !has_caret) { - xEvent.xkey.keycode = (keyCode << 16) | keychar; - switch (type) { - case NSFlagsChanged: - - /* - * If the highest bit where they differ was set then this is a press. - */ - - xEvent.xany.type = modifiers > savedModifiers ? KeyPress : KeyRelease; - - /* - * The value -1 signals a modifier keycode to the function TkpGetKeySym - * defined in tkMacOSXKeyboard.c. - */ - - xEvent.xany.send_event = -1; - - /* - * Update savedModifiers for NSFlagsChanged events and no others, - * since some non-modifier keys (e.g. function keys) have modifier - * flags set - */ - - savedModifiers = modifiers; - break; - case NSKeyUp: - xEvent.xany.type = KeyRelease; - break; - case NSKeyDown: - xEvent.xany.type = KeyPress; - break; - default: - return theEvent; /* Unrecognized key event. */ - } - - if (type != NSKeyDown || - !has_caret || - has_modifiers || - ((keychar >= 0xF700) && (keychar <= 0xF8FF))) { - if (type == NSKeyDown && [theEvent isARepeat]) { - - /* - * Insert a KeyRelease XEvent before a repeated KeyPress. - */ - - xEvent.xany.type = KeyRelease; - Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); - xEvent.xany.type = KeyPress; - } - - /* - * Queue the XEvent and return. - */ - - Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); - return theEvent; - } + if (type == NSKeyDown && has_caret && !has_modifiers && + (keychar >= 0x0020) && (keychar <= 0x00FF)) { + use_text_input = YES; } - - /* - * Either we are processing a composition or this is a normal KeyDown event - * for the caret window. Either way the event should be passed to - * interpretKeyEvents. But we only need to send KeyDown events. - */ - - switch (type) { - case NSKeyDown: - if (NS_KEYLOG) { - TKLog(@"keyDown: %s compose sequence.\n", - processingCompose == YES ? "Continue" : "Begin"); - } + if (use_text_input) { +#if (NS_KEYLOG) + TKLog(@"keyDown: %s compose sequence.\n", + processingCompose == YES ? "Continue" : "Begin"); +#endif /* - * Call the interpretKeyEvents method to handle the event as a + * Call our interpretKeyEvents method to handle the event as a * TextInputClient. When the composition sequence is complete, our - * implementation of insertText: replacementRange will be called. that - * method generates a key down XEvent with the selected character. In + * implementation of insertText: replacementRange will be called. That + * method generates a keyPress XEvent with the selected character. In * IME when multiple characters have the same composition sequence and * the selected character is not the default it may be necessary to hit * the Enter key multiple times before the character is accepted and - * rendered. So we repeatedly send Enter key events until inputText has - * cleared the processingCompose flag. + * rendered. So we repeatedly send Enter key events until the inputText + * method has cleared the processingCompose flag. */ processingCompose = YES; @@ -225,13 +161,55 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); break; } } - break; + return theEvent; + } + + /* + * We need to send an XEvent. Finish constructing it. + */ + + xEvent.xkey.keycode = (keyCode << 16) | keychar; + switch (type) { case NSFlagsChanged: + + /* + * We simulate KeyPress and KeyRelease events whenever a modifier flag + * change is reported. To determine the type, note that the highest + * bit where the flags differ is 1 if and only if it is a KeyPress. + * Remember the modifiers for the next flag change. + */ + + xEvent.xany.type = modifiers > savedModifiers ? KeyPress : KeyRelease; savedModifiers = modifiers; + + /* + * The value -1 signals a modifier keycode to the function TkpGetKeySym + * (see tkMacOSXKeyboard.c). + */ + + xEvent.xany.send_event = -1; break; - default: + case NSKeyUp: + xEvent.xany.type = KeyRelease; break; + case NSKeyDown: + xEvent.xany.type = KeyPress; + break; + default: + return theEvent; /* Unrecognized key event. */ + } + + /* + * Finally we can queue an XEvent, inserting a KeyRelease before a + * repeated KeyPress. + */ + + if (type == NSKeyDown && [theEvent isARepeat]) { + xEvent.xany.type = KeyRelease; + Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); + xEvent.xany.type = KeyPress; } + Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); return theEvent; } @end diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index c007cc9..c4299cf 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -75,10 +75,9 @@ static const KeyInfo keyArray[] = { {101, XK_F9, NSF9FunctionKey}, {103, XK_F11, NSF11FunctionKey}, {105, XK_F13, NSF13FunctionKey}, + {106, XK_F16, NSF16FunctionKey}, {107, XK_F14, NSF14FunctionKey}, {109, XK_F10, NSF10FunctionKey}, - {105, XK_F13, NSF13FunctionKey}, - {106, XK_F16, NSF16FunctionKey}, {111, XK_F12, NSF12FunctionKey}, {113, XK_F15, NSF15FunctionKey}, {114, XK_Help, NSHelpFunctionKey}, @@ -102,7 +101,7 @@ static const KeyInfo keyArray[] = { */ #define NUM_MOD_KEYCODES 14 -static KeyCode modKeyArray[NUM_MOD_KEYCODES] = { +static const KeyCode modKeyArray[NUM_MOD_KEYCODES] = { XK_Shift_L, XK_Shift_R, XK_Control_L, @@ -119,8 +118,8 @@ static KeyCode modKeyArray[NUM_MOD_KEYCODES] = { XK_Hyper_R, }; -static int initialized = 0; -static int keyboardChanged = 1; +static BOOL initialized = NO; +static BOOL keyboardChanged = YES; static Tcl_HashTable virtual2keysym; /* Maps Mac keyCode to X11 keysym. */ static Tcl_HashTable keysym2keycode; /* Maps X11 keysym to Mac keycode. */ static int latin1Table[LATIN1_MAX+1]; /* Reverse mapping table for Latin-1. */ @@ -143,7 +142,7 @@ static int KeycodeToUnicode(UniChar * uniChars, int maxChars, #ifdef TK_MAC_DEBUG_NOTIFICATIONS TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); #endif - keyboardChanged = 1; + keyboardChanged = YES; } @end @@ -182,7 +181,7 @@ InitKeyMaps(void) &dummy); Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keychar | (kPtr->virtual << 16))); } - initialized = 1; + initialized = YES; } /* -- cgit v0.12 From 3964e6aefdbae03b25d536bac0ae2ea275b6adbb Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 20 Apr 2020 19:18:01 +0000 Subject: Fix two typos and a bug -- thanks to Christopher Chavez. --- macosx/tkMacOSXKeyEvent.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index ca6fe32..6d468f7 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -44,9 +44,8 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); #endif NSWindow *w = [theEvent window]; - TkWindow *winPtr = TkMacOSXGetTkWindow(w); + TkWindow *winPtr = TkMacOSXGetTkWindow(w), *grabWinPtr; Tk_Window tkwin = (Tk_Window) winPtr; - TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr; NSEventType type = [theEvent type]; NSUInteger keyCode = [theEvent keyCode]; NSUInteger modifiers = ([theEvent modifierFlags] & @@ -80,10 +79,11 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); /* * If a local grab is in effect, key events for windows in the * grabber's application are redirected to the grabber. Key events - * for dispPtr->grabWinPtr; if (grabWinPtr) { if (winPtr->dispPtr->grabFlags || /* global grab */ grabWinPtr->mainPtr == winPtr->mainPtr){ /* same application */ @@ -125,7 +125,7 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); has_caret = (TkFocusKeyEvent(winPtr, &xEvent) == caret_win); /* - * A KeyDown event targetting the caret window and having alphanumeric + * A KeyDown event targeting the caret window and having alphanumeric * keychar should be processed by our TextInputClient. The XEvent will not * be sent in this case. */ -- cgit v0.12 From e925338c5bc25742dbe0a932d8d3d9dcdc0f56a2 Mon Sep 17 00:00:00 2001 From: culler Date: Mon, 20 Apr 2020 21:06:00 +0000 Subject: Process Control-Tab normally; stop using an illegal value for send_event as a flag. --- macosx/tkMacOSXKeyEvent.c | 38 +++++++++++++++----------------------- macosx/tkMacOSXKeyboard.c | 10 ++++------ 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 6d468f7..2237f34 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -67,16 +67,6 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); } /* - * Control-Tab and Control-Shift-Tab are used to switch tabs in a tabbed - * window. We do not want to generate an Xevent for these since that might - * cause the deselected tab to be reactivated. - */ - - if (keyCode == 48 && (modifiers & NSControlKeyMask) == NSControlKeyMask) { - return theEvent; - } - - /* * If a local grab is in effect, key events for windows in the * grabber's application are redirected to the grabber. Key events * for other applications are delivered normally. If a global @@ -101,7 +91,8 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); } else { /* - * This is a dead key, so it should go to the TextInputClient. + * This is a dead key, such as Option-e, so it should go to the + * TextInputClient. */ use_text_input = YES; @@ -117,7 +108,7 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); /* * Build a skeleton XEvent. We need to build it here, even if we will not * send it, so we can pass it to TkFocusKeyEvent to determine whether the - * target windows has the caret. + * target window has the caret. */ setupXEvent(&xEvent, tkwin, modifiers); @@ -125,7 +116,7 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); has_caret = (TkFocusKeyEvent(winPtr, &xEvent) == caret_win); /* - * A KeyDown event targeting the caret window and having alphanumeric + * A KeyDown event targeting the caret window and having an alphanumeric * keychar should be processed by our TextInputClient. The XEvent will not * be sent in this case. */ @@ -148,8 +139,9 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); * IME when multiple characters have the same composition sequence and * the selected character is not the default it may be necessary to hit * the Enter key multiple times before the character is accepted and - * rendered. So we repeatedly send Enter key events until the inputText - * method has cleared the processingCompose flag. + * rendered (See ticket 39de9677aa]). So we repeatedly send Enter key + * events until the inputText method has cleared the processingCompose + * flag. */ processingCompose = YES; @@ -173,21 +165,21 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); case NSFlagsChanged: /* - * We simulate KeyPress and KeyRelease events whenever a modifier flag - * change is reported. To determine the type, note that the highest - * bit where the flags differ is 1 if and only if it is a KeyPress. - * Remember the modifiers for the next flag change. + * This XEvent is a simulated KeyPress or KeyRelease event for a + * modifier key. To determine the type, note that the highest bit + * where the flags differ is 1 if and only if it is a KeyPress. The + * modifiers are saved so we can detect the next flag change. */ xEvent.xany.type = modifiers > savedModifiers ? KeyPress : KeyRelease; savedModifiers = modifiers; /* - * The value -1 signals a modifier keycode to the function TkpGetKeySym - * (see tkMacOSXKeyboard.c). + * Set the keychar to 0xF8FF, the largest value reserved for a function + * key, as a signal to TkpGetKeySym (see tkMacOSXKeyboard.c) that this + * is a modifier key event. */ - - xEvent.xany.send_event = -1; + xEvent.xkey.keycode |= 0xF8FF; break; case NSKeyUp: xEvent.xany.type = KeyRelease; diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index c4299cf..76dceb7 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -673,12 +673,10 @@ TkpGetKeySym( } /* - * Handle pure modifier keys specially. We use -1 as a signal for - * this. + * Modifier key events have a special mac keycode (see tkProcessKeyEvent). */ - - if (eventPtr->xany.send_event == -1) { - switch (eventPtr->xkey.keycode >> 16) { + if ((eventPtr->xkey.keycode & MAC_KEYCODE_MASK) == 0xF8FF) { + switch (eventPtr->xkey.keycode >> 16) { /* the virtual keyCode */ case 54: return XK_Meta_R; case 55: @@ -802,7 +800,7 @@ TkpInitKeymapInfo( dispPtr->bindInfoStale = 0; /* - * Behaviours that are variable on X11 are defined constant on MacOSX. + * Behaviors that are variable on X11 are defined constant on MacOSX. * lockUsage is only used above in TkpGetKeySym(), nowhere else currently. * There is no offical "Mode_switch" key. */ -- cgit v0.12 From bf2a61422685c1304d7ac9373cfe5ffeea1a4e64 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Tue, 21 Apr 2020 17:29:25 +0000 Subject: Send all chars up to 0x7F to the TextInputClient so non-Latin1 keyboards work again. --- macosx/tkMacOSXKeyEvent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 2237f34..3b213c5 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -122,7 +122,7 @@ static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers); */ if (type == NSKeyDown && has_caret && !has_modifiers && - (keychar >= 0x0020) && (keychar <= 0x00FF)) { + (keychar >= 0x0020) && (keychar <= 0xF700)) { use_text_input = YES; } if (use_text_input) { -- cgit v0.12 From 6435fba8f65521e30dc051e5eaaa59c84462db4a Mon Sep 17 00:00:00 2001 From: marc_culler Date: Tue, 21 Apr 2020 19:33:53 +0000 Subject: Make the structure of the latin1Table explicit --- macosx/tkMacOSXKeyboard.c | 83 ++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index 76dceb7..c495d49 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -22,21 +22,22 @@ */ #define LATIN1_MAX 255 -#define MAC_KEYCODE_MAX 0x7F +#define VIRTUAL_MAX 0x7F #define MAC_KEYCODE_MASK 0xFF #define COMMAND_MASK Mod1Mask #define OPTION_MASK Mod2Mask - /* - * Table enumerating the special keys defined on Mac keyboards. These are - * necessary for correct keysym mappings for all keys where the keysyms are - * not identical with their ASCII or Latin-1 code points. - * - * This table includes every key listed in Apple's documentation of Function-Key - * Unicodes which is not marked as "Not on most Macintosh keyboards". I - * know of no reliable way to find the Apple "Virtual Keycode" for any of the - * keys that are so marked. + * This table enumerates the keys on Mac keyboards which do not represent + * letters. This is static data -- these keys do not change when the keyboard + * layout changes. The unicode representation of a special key which is not a + * modifier and does not have an ASCII code point lies in the reserved range + * 0xF700 - 0xF8FF. + * + * The table includes every key listed in Apple's documentation of Function-Key + * Unicodes which is not marked as "Not on most Macintosh keyboards", as well + * as F20, which is reported to be usable in scripts even though it does not + * appear on any Macintosh keyboard. */ typedef struct { @@ -118,18 +119,23 @@ static const KeyCode modKeyArray[NUM_MOD_KEYCODES] = { XK_Hyper_R, }; -static BOOL initialized = NO; -static BOOL keyboardChanged = YES; +typedef struct { + KeyCode keycode; + int state; +} Latin1KeyInfo; + +static Latin1KeyInfo latin1Table[LATIN1_MAX+1]; /* keysym to Latin1KeyInfo. */ static Tcl_HashTable virtual2keysym; /* Maps Mac keyCode to X11 keysym. */ static Tcl_HashTable keysym2keycode; /* Maps X11 keysym to Mac keycode. */ -static int latin1Table[LATIN1_MAX+1]; /* Reverse mapping table for Latin-1. */ +static BOOL initialized = NO; +static BOOL keyboardChanged = YES; /* * Prototypes for static functions used in this file. */ static void InitKeyMaps (void); -static void InitLatin1Table(Display *display); +static void InitLatin1Table(void); static int KeycodeToUnicode(UniChar * uniChars, int maxChars, UInt16 keyaction, UInt32 keycode, UInt32 modifiers, UInt32 * deadKeyStatePtr); @@ -206,13 +212,11 @@ InitKeyMaps(void) */ static void -InitLatin1Table( - Display *display) +InitLatin1Table(void) { - int keycode; + int virtual; KeySym keysym; int state; - int modifiers; memset(latin1Table, 0, sizeof(latin1Table)); @@ -230,18 +234,11 @@ InitLatin1Table( */ for (state = 3; state >= 0; state--) { - modifiers = 0; - if (state & 1) { - modifiers |= shiftKey; - } - if (state & 2) { - modifiers |= optionKey; - } - - for (keycode = 0; keycode <= MAC_KEYCODE_MAX; keycode++) { - keysym = XKeycodeToKeysym(display, keycode<<16, state); + for (virtual = 0; virtual <= VIRTUAL_MAX; virtual++) { + keysym = XKeycodeToKeysym(NULL, virtual<<16, state); if (keysym != NoSymbol && keysym <= LATIN1_MAX) { - latin1Table[keysym] = modifiers << 16 | keycode << 16 | keysym; + latin1Table[keysym].state = state; + latin1Table[keysym].keycode = virtual << 16 | keysym; } } } @@ -388,7 +385,11 @@ XKeycodeToKeysym( } /* - * This keycode does not belong to a key on any known Macintosh keyboard. + * Unfortunately we currently cannot generate keysyms for alphabetical keys + * whose unicode value is outside of Latin-1. This means that when such a + * letter key is pressed or released on, say, a Greek keyboard the XEvent + * that is sent will have its keysym field set to NoSymbol. Also, it is not + * possible to generate events for these keys. */ return NoSymbol; @@ -562,10 +563,10 @@ XKeysymToKeycode( */ if (keyboardChanged) { - InitLatin1Table(display); + InitLatin1Table(); keyboardChanged = 0; } - return latin1Table[keysym]; + return latin1Table[keysym].keycode; } /* @@ -619,19 +620,19 @@ TkpSetKeycodeAndState( if (keysym == NoSymbol) { eventPtr->xkey.keycode = 0; } else { - Display *display = Tk_Display(tkwin); - eventPtr->xkey.keycode = XKeysymToKeycode(display, keysym); - if ((shiftKey << 16) & eventPtr->xkey.keycode) { - eventPtr->xkey.state |= ShiftMask; - } - if ((optionKey << 16) & eventPtr->xkey.keycode) { - eventPtr->xkey.state |= OPTION_MASK; - } - eventPtr->xkey.keycode &= 0xFFFFFF; if (keysym <= LATIN1_MAX) { + if (keyboardChanged) { + InitLatin1Table(); + keyboardChanged = 0; + } + Latin1KeyInfo info = latin1Table[keysym]; int length = TkUniCharToUtf(keysym, eventPtr->xkey.trans_chars); eventPtr->xkey.trans_chars[length] = 0; + eventPtr->xkey.keycode = info.keycode; + eventPtr->xkey.state |= info.state; } else { + Display *display = Tk_Display(tkwin); + eventPtr->xkey.keycode = XKeysymToKeycode(display, keysym); eventPtr->xkey.trans_chars[0] = 0; } } -- cgit v0.12 From f9ca46b9cf499b4a960aad013afc784d19a46ebf Mon Sep 17 00:00:00 2001 From: marc_culler Date: Thu, 23 Apr 2020 15:39:07 +0000 Subject: Rework the keysym handling so we can generate keysyms for non-Latin-1 keys. Also, edit comments and try to make names more accurate. --- macosx/tkMacOSXKeyboard.c | 383 ++++++-------- macosx/tkMacOSXKeysyms.h | 1286 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1434 insertions(+), 235 deletions(-) create mode 100644 macosx/tkMacOSXKeysyms.h diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c index c495d49..a2fdfd6 100644 --- a/macosx/tkMacOSXKeyboard.c +++ b/macosx/tkMacOSXKeyboard.c @@ -14,119 +14,53 @@ #include "tkMacOSXPrivate.h" #include "tkMacOSXEvent.h" #include "tkMacOSXConstants.h" +#include "tkMacOSXKeysyms.h" + /* - * A couple of simple definitions to make code a bit more self-explaining. - * - * For the assignments of Mod1==meta==command and Mod2==alt==option, see also - * tkMacOSXMouseEvent.c. + * When converting native key events to X11 key events, each platform is + * allowed to choose what information to encode in the XEvent.xkey.keycode + * field. On the Macintosh, every non-modifier key has a unicode character + * associated to it. For non-text keys this unicode character is in the + * private use range 0xF700 - 0xF8FF. Modifier keys, however, do not produce + * KeyDown or KeyUp events, rather FlagsChanged events, so they do not have an + * associated unicode character. + * + * When constructing an XEvent from a KeyDown or KeyUo NSEvent, the + * XEvent.xkey.keycode field is constructed by using bits 0-15 to hold the + * first unicode character of the [NSEvent characters] attribute of the + * NSEvent, and bits 16-23 to hold the value of the [NSEvent keyCode] + * attribute. The keyCode attribute identifies a location on the keyboard, + * and Apple calls it a "virtual keycode". It is allowed for the characters + * attribute to have length greater than 1, but that does not appear to happen + * for any known keyboards. + * + * When generating an XEvent with the event generate command, the unicode + * character is determined from the X11 keysym and, if that unicode character + * belongs to a key on the current keyboard layout bits 16-23 are set to the + * virtual keycode of that key. Otherwise they are cleared. + * + * KeyPress or KeyRelease XEvents are also constructed when a FlagsChanged + * NSEvent is processed. For these, the unicode character is set to 0xF8FF, + * the last value of the private use range. */ -#define LATIN1_MAX 255 #define VIRTUAL_MAX 0x7F #define MAC_KEYCODE_MASK 0xFF -#define COMMAND_MASK Mod1Mask -#define OPTION_MASK Mod2Mask /* - * This table enumerates the keys on Mac keyboards which do not represent - * letters. This is static data -- these keys do not change when the keyboard - * layout changes. The unicode representation of a special key which is not a - * modifier and does not have an ASCII code point lies in the reserved range - * 0xF700 - 0xF8FF. - * - * The table includes every key listed in Apple's documentation of Function-Key - * Unicodes which is not marked as "Not on most Macintosh keyboards", as well - * as F20, which is reported to be usable in scripts even though it does not - * appear on any Macintosh keyboard. + * Hash tables used to translate between various key attributes. */ -typedef struct { - int virtual; /* value of [NSEvent keyCode] */ - KeySym keysym; /* X11 keysym */ - KeyCode keychar; /* XEvent keycode & 0xFFFF */ -} KeyInfo; - -static const KeyInfo keyArray[] = { - {36, XK_Return, NSNewlineCharacter}, - {48, XK_Tab, NSTabCharacter}, - {51, XK_BackSpace, NSBackspaceCharacter}, - {52, XK_Return, NSNewlineCharacter}, /* Used on some Powerbooks */ - {53, XK_Escape, 0x1B}, - {54, XK_Meta_R, 0}, - {55, XK_Meta_L, 0}, - {56, XK_Shift_L, 0}, - {57, XK_Caps_Lock, 0}, - {58, XK_Alt_L, 0}, - {59, XK_Control_L, 0}, - {60, XK_Shift_R, 0}, - {61, XK_Alt_R, 0}, - {62, XK_Control_R, 0}, - {63, XK_Super_L, 0}, - {64, XK_F17, NSF17FunctionKey}, - {71, XK_Clear, NSClearLineFunctionKey}, /* Numlock on PC */ - {76, XK_KP_Enter, NSEnterCharacter}, /* Fn Return */ - {79, XK_F18, NSF18FunctionKey}, - {80, XK_F19, NSF19FunctionKey}, - {90, XK_F20, NSF20FunctionKey}, /* For scripting only */ - {96, XK_F5, NSF5FunctionKey}, - {97, XK_F6, NSF6FunctionKey}, - {98, XK_F7, NSF7FunctionKey}, - {99, XK_F3, NSF3FunctionKey}, - {100, XK_F8, NSF8FunctionKey}, - {101, XK_F9, NSF9FunctionKey}, - {103, XK_F11, NSF11FunctionKey}, - {105, XK_F13, NSF13FunctionKey}, - {106, XK_F16, NSF16FunctionKey}, - {107, XK_F14, NSF14FunctionKey}, - {109, XK_F10, NSF10FunctionKey}, - {111, XK_F12, NSF12FunctionKey}, - {113, XK_F15, NSF15FunctionKey}, - {114, XK_Help, NSHelpFunctionKey}, - {115, XK_Home, NSHomeFunctionKey}, /* Fn Left */ - {116, XK_Page_Up, NSPageUpFunctionKey}, /* Fn Up */ - {117, XK_Delete, NSDeleteFunctionKey}, /* Fn Deleete */ - {118, XK_F4, NSF4FunctionKey}, - {119, XK_End, NSEndFunctionKey}, /* Fn Right */ - {120, XK_F2, NSF2FunctionKey}, - {121, XK_Page_Down, NSPageDownFunctionKey}, /* Fn Down */ - {122, XK_F1, NSF1FunctionKey}, - {123, XK_Left, NSLeftArrowFunctionKey}, - {124, XK_Right, NSRightArrowFunctionKey}, - {125, XK_Down, NSDownArrowFunctionKey}, - {126, XK_Up, NSUpArrowFunctionKey}, - {0, 0, 0} -}; +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 */ /* - * X11 keysyms for modifier keys, in order. + * Flags. */ -#define NUM_MOD_KEYCODES 14 -static const KeyCode modKeyArray[NUM_MOD_KEYCODES] = { - XK_Shift_L, - XK_Shift_R, - XK_Control_L, - XK_Control_R, - XK_Caps_Lock, - XK_Shift_Lock, - XK_Meta_L, - XK_Meta_R, - XK_Alt_L, - XK_Alt_R, - XK_Super_L, - XK_Super_R, - XK_Hyper_L, - XK_Hyper_R, -}; - -typedef struct { - KeyCode keycode; - int state; -} Latin1KeyInfo; - -static Latin1KeyInfo latin1Table[LATIN1_MAX+1]; /* keysym to Latin1KeyInfo. */ -static Tcl_HashTable virtual2keysym; /* Maps Mac keyCode to X11 keysym. */ -static Tcl_HashTable keysym2keycode; /* Maps X11 keysym to Mac keycode. */ static BOOL initialized = NO; static BOOL keyboardChanged = YES; @@ -134,9 +68,9 @@ static BOOL keyboardChanged = YES; * Prototypes for static functions used in this file. */ -static void InitKeyMaps (void); -static void InitLatin1Table(void); -static int KeycodeToUnicode(UniChar * uniChars, int maxChars, +static void InitHashTables(void); +static void UpdateKeymap(void); +static int KeyDataToUnicode(UniChar * uniChars, int maxChars, UInt16 keyaction, UInt32 keycode, UInt32 modifiers, UInt32 * deadKeyStatePtr); @@ -149,6 +83,7 @@ static int KeycodeToUnicode(UniChar * uniChars, int maxChars, TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); #endif keyboardChanged = YES; + UpdateKeymap(); } @end @@ -157,7 +92,7 @@ static int KeycodeToUnicode(UniChar * uniChars, int maxChars, /* *---------------------------------------------------------------------- * - * InitKeyMaps -- + * InitHashTables -- * * Creates hash tables used by some of the functions in this file. * @@ -171,10 +106,11 @@ static int KeycodeToUnicode(UniChar * uniChars, int maxChars, */ static void -InitKeyMaps(void) +InitHashTables(void) { Tcl_HashEntry *hPtr; const KeyInfo *kPtr; + const KeysymInfo *ksPtr; int dummy; Tcl_InitHashTable(&virtual2keysym, TCL_ONE_WORD_KEYS); @@ -187,70 +123,70 @@ InitKeyMaps(void) &dummy); Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keychar | (kPtr->virtual << 16))); } + Tcl_InitHashTable(&keysym2unichar, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&unichar2keysym, TCL_ONE_WORD_KEYS); + for (ksPtr = keysymTable; ksPtr->keysym != 0; ksPtr++) { + hPtr = Tcl_CreateHashEntry(&keysym2unichar, INT2PTR(ksPtr->keysym), + &dummy); + Tcl_SetHashValue(hPtr, INT2PTR(ksPtr->keycode)); + hPtr = Tcl_CreateHashEntry(&unichar2keysym, INT2PTR(ksPtr->keycode), + &dummy); + Tcl_SetHashValue(hPtr, INT2PTR(ksPtr->keysym)); + } + UpdateKeymap(); initialized = YES; } /* *---------------------------------------------------------------------- * - * InitLatin1Table -- + * UpdateKeymap -- * - * Creates a simple table to be used for mapping from Latin-1 keysyms to - * keycodes as used in XEvents. Always needs to be called before using - * latin1Table, because the keyboard layout may have changed, and then the - * table must be re-computed. The high order byte of these keycodes is - * overwritten with the modifier flags, which can be used to set the state - * of an XEvent that uses the keycode. + * Called when the keyboard changes to update the hash table that + * maps unicode characters to virtual keycodes. * * Results: * None. * * Side effects: - * Sets the global latin1Table. + * Initializes, if necessary, and updates the unichar2virtual table. * *---------------------------------------------------------------------- */ static void -InitLatin1Table(void) +UpdateKeymap() { - int virtual; - KeySym keysym; - int state; - - memset(latin1Table, 0, sizeof(latin1Table)); - - /* - * In the common X11 implementations, a keymap has four columns - * "plain", "Shift", "Mode_switch" and "Mode_switch + Shift". We don't - * use "Mode_switch", but we use "Option" instead. (This is similar to - * Apple's X11 implementation, where "Mode_switch" is used as an alias - * for "Option".) - * - * So here we go through all 4 columns of the keymap and find all - * Latin-1 compatible keycodes. We go through the columns back-to-front - * from the more exotic columns to the more simple, so that simple - * keycode-modifier combinations are preferred in the resulting table. - */ + static int keymapInitialized = 0; + UniChar keyChar = 0; + Tcl_HashEntry *hPtr; + int virtual, state, modifiers, dummy; + if (!keymapInitialized) { + Tcl_InitHashTable(&unichar2virtual, TCL_ONE_WORD_KEYS); + } else { + Tcl_DeleteHashTable(&unichar2virtual); + } for (state = 3; state >= 0; state--) { for (virtual = 0; virtual <= VIRTUAL_MAX; virtual++) { - keysym = XKeycodeToKeysym(NULL, virtual<<16, state); - if (keysym != NoSymbol && keysym <= LATIN1_MAX) { - latin1Table[keysym].state = state; - latin1Table[keysym].keycode = virtual << 16 | keysym; - } + modifiers = (state & 1 ? shiftKey : 0) | (state & 2 ? optionKey : 0); + KeyDataToUnicode(&keyChar, 1, kUCKeyActionDown, virtual, modifiers, NULL); + hPtr = Tcl_CreateHashEntry(&unichar2virtual, INT2PTR(keyChar), + &dummy); + Tcl_SetHashValue(hPtr, INT2PTR(state << 8 | virtual)); } } } + /* *---------------------------------------------------------------------- * - * KeycodeToUnicode -- + * KeyDataToUnicode -- * - * Given MacOS key event data this function generates the Unicode - * characters. It does this using OS resources and APIs. + * Given MacOS key event data this function generates the unicode + * characters. It does this using OS resources from the Carbon + * framework. * * The parameter deadKeyStatePtr can be NULL, if no deadkey handling is * needed. @@ -269,7 +205,7 @@ InitLatin1Table(void) */ static int -KeycodeToUnicode( +KeyDataToUnicode( UniChar *uniChars, int maxChars, UInt16 keyaction, @@ -302,7 +238,7 @@ KeycodeToUnicode( UInt32 dummyState; OSStatus err; - keycode &= 0xFF; + keycode &= 0xFFFF; modifiers = (modifiers >> 8) & 0xFF; if (!deadKeyStatePtr) { options = kUCKeyTranslateNoDeadKeysMask; @@ -333,11 +269,13 @@ KeycodeToUnicode( * * XKeycodeToKeysym -- * - * Translate from the system-dependent keycode used in an XEvent - * to a system-independent X11 keysym. + * Stub function which translate from the platform-specific keycode used + * in an XEvent to an X11 keysym. On the Macintosh, the display input + * is ignored and only the virtual keycode in bits 16-23 is used. * * Results: - * Returns the translated keysym, or NoSymbol on failure. + * Returns the corresponding keysym, or NoSymbol if the keysym cannot + * be found. * * Side effects: * None. @@ -358,11 +296,11 @@ XKeycodeToKeysym( (void) display; /*unused*/ if (!initialized) { - InitKeyMaps(); + InitHashTables(); } /* - * First check if the virtual keycode corresponds to a special key such as + * First check if the virtual keycode corresponds to a special key, such as * an Fn function key or Tab, Backspace, Home, End, etc. */ @@ -375,23 +313,16 @@ XKeycodeToKeysym( } /* - * No? Check if the key represents a Latin-1 letter. + * If not, use Carbon to find the unicode character and translate it + * to a keysym using a hash table. */ modifiers = (state & 1 ? shiftKey : 0) | (state & 2 ? optionKey : 0); - KeycodeToUnicode(&keyChar, 1, kUCKeyActionDown, virtual, modifiers, NULL); - if ((keyChar >= XK_space) && (keyChar <= LATIN1_MAX)) { - return keyChar; + KeyDataToUnicode(&keyChar, 1, kUCKeyActionDown, virtual, modifiers, NULL); + hPtr = Tcl_FindHashEntry(&unichar2keysym, INT2PTR(keyChar)); + if (hPtr != NULL) { + return (KeySym) Tcl_GetHashValue(hPtr); } - - /* - * Unfortunately we currently cannot generate keysyms for alphabetical keys - * whose unicode value is outside of Latin-1. This means that when such a - * letter key is pressed or released on, say, a Greek keyboard the XEvent - * that is sent will have its keysym field set to NoSymbol. Also, it is not - * possible to generate events for these keys. - */ - return NoSymbol; } @@ -400,10 +331,11 @@ XKeycodeToKeysym( * * TkpGetString -- * - * Retrieve the string equivalent for the given keyboard event. + * Retrieve the string stored in the transchars field of an XEvent + * and convert it to a DString. * * Results: - * Returns the UTF string. + * Returns the DString. * * Side effects: * None. @@ -452,9 +384,10 @@ XGetModifierMapping( (void) display; /*unused*/ /* - * MacOSX doesn't use the key codes for the modifiers for anything, and we + * MacOS doesn't use the key codes for the modifiers for anything, and we * don't generate them either. So there is no modifier map. */ + modmap = ckalloc(sizeof(XModifierKeymap)); modmap->max_keypermod = 0; modmap->modifiermap = NULL; @@ -494,9 +427,8 @@ XFreeModifiermap( * XKeysymToString, XStringToKeysym -- * * These X window functions map keysyms to strings & strings to keysyms. - * However, Tk already does this for the most common keysyms. Therefore, - * these functions only need to support keysyms that will be specific to - * the Macintosh. Currently, there are none. + * These are never called because we define REDO_KEYSYM_LOOKUP, which + * instructs tkBind to do the conversion for us. * * Results: * None. @@ -534,62 +466,64 @@ XStringToKeysym( * Results: * * An X11 KeyCode with a unicode character in the low 16 bits and the - * 8-bit "virtual keycode" in the third byte. (The virtual keycode is the - * 8-bit value that appears as [NSEvent keycode] in an NSKeyUp or - * NSKeyDown event.) On the Mac pressing a modifier key generates an - * NSFlagsChanged event but not an NSKeyDown event. If a keysym - * represents a modifier key, the unicode character is 0, but the modifier - * flags are set in the high order byte. For keysyms that represent other - * keys, the resulting keycode has no modifiers. + * 8-bit "virtual keycode" in the third byte. See the description of + * keycodes on the Macintosh at the top of this file. * * Side effects: * None. * *---------------------------------------------------------------------- */ - KeyCode -XKeysymToKeycode( +XKeysymToKeycodeWithState( Display *display, - KeySym keysym) + KeySym keysym, + int *state) { Tcl_HashEntry *hPtr; - if (keysym <= LATIN1_MAX) { - - /* - * Handle keysyms in the Latin-1 range where keysym and Unicode - * character code point are the same. - */ - - if (keyboardChanged) { - InitLatin1Table(); - keyboardChanged = 0; + if (!initialized) { + InitHashTables(); + } + hPtr = Tcl_FindHashEntry(&keysym2unichar, INT2PTR(keysym)); + if (hPtr != NULL) { + KeyCode character = (KeyCode) Tcl_GetHashValue(hPtr); + hPtr = Tcl_FindHashEntry(&unichar2virtual, INT2PTR(character)); + if (hPtr != NULL) { + KeyCode lookup = ((KeyCode) Tcl_GetHashValue(hPtr)); + KeyCode virtual = lookup & 0xFF; + *state = lookup >> 8; + return virtual << 16 | character; + } else { + return character; } - return latin1Table[keysym].keycode; } /* - * This is not a Latin-1 key. Try doing a hash table lookup to find the - * keycode. + * This is not a text key. Try doing a hash table lookup to find the + * keycode for a special key. */ - if (!initialized) { - InitKeyMaps(); - } - hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym)); if (hPtr != NULL) { return (KeyCode) Tcl_GetHashValue(hPtr); } /* - * The keysym is not Latin-1 and not in our hash table, so it does not - * appear on any known Macintosh keyboard; just return 0. + * Could not construct a keycode. */ return 0; } + +KeyCode +XKeysymToKeycode( + Display *display, + KeySym keysym) +{ + int state; + return XKeysymToKeycodeWithState(display, keysym, &state); +} /* *---------------------------------------------------------------------- @@ -620,21 +554,12 @@ TkpSetKeycodeAndState( if (keysym == NoSymbol) { eventPtr->xkey.keycode = 0; } else { - if (keysym <= LATIN1_MAX) { - if (keyboardChanged) { - InitLatin1Table(); - keyboardChanged = 0; - } - Latin1KeyInfo info = latin1Table[keysym]; - int length = TkUniCharToUtf(keysym, eventPtr->xkey.trans_chars); - eventPtr->xkey.trans_chars[length] = 0; - eventPtr->xkey.keycode = info.keycode; - eventPtr->xkey.state |= info.state; - } else { - Display *display = Tk_Display(tkwin); - eventPtr->xkey.keycode = XKeysymToKeycode(display, keysym); - eventPtr->xkey.trans_chars[0] = 0; - } + int state; + Display *display = Tk_Display(tkwin); + eventPtr->xkey.keycode = XKeysymToKeycodeWithState(display, keysym, &state); + eventPtr->xkey.state |= state; + int length = TkUniCharToUtf(keysym, eventPtr->xkey.trans_chars); + eventPtr->xkey.trans_chars[length] = 0; } } @@ -676,6 +601,7 @@ TkpGetKeySym( /* * Modifier key events have a special mac keycode (see tkProcessKeyEvent). */ + if ((eventPtr->xkey.keycode & MAC_KEYCODE_MASK) == 0xF8FF) { switch (eventPtr->xkey.keycode >> 16) { /* the virtual keyCode */ case 54: @@ -703,10 +629,13 @@ TkpGetKeySym( } } - /* If nbytes has been set, it's not a function key, but a regular key that - has been translated in tkMacOSXKeyEvent.c; just use that. */ + /* + * If nbytes has been set, it's not a function key, but a regular key that + * has been translated in tkMacOSXKeyEvent.c; just use that. + */ + if (eventPtr->xkey.nbytes) { - return eventPtr->xkey.keycode; + return eventPtr->xkey.keycode; } /* @@ -717,18 +646,9 @@ TkpGetKeySym( */ index = 0; - - /* - * We want Option key combinations to use their base chars as keysyms, so - * we ignore the option modifier here. - */ - -#if 0 - if (eventPtr->xkey.state & OPTION_MASK) { + if (eventPtr->xkey.state & Mod2Mask) { /* Option */ index |= 2; } -#endif - if ((eventPtr->xkey.state & ShiftMask) || (/* (dispPtr->lockUsage != LU_IGNORE) && */ (eventPtr->xkey.state & LockMask))) { @@ -736,7 +656,7 @@ TkpGetKeySym( } /* - * First try of the actual translation. + * First do the straightforward lookup. */ sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index); @@ -749,14 +669,8 @@ TkpGetKeySym( if ((index & 1) && !(eventPtr->xkey.state & ShiftMask) /*&& (dispPtr->lockUsage == LU_CAPS)*/ ) { - /* - * FIXME: Keysyms are only identical to Unicode for ASCII and Latin-1, - * so we can't use Tcl_UniCharIsUpper() for keysyms outside that range. - * This may be a serious problem here. - */ - - if ((sym == NoSymbol) || (sym > LATIN1_MAX) - || !Tcl_UniCharIsUpper(sym)) { + + if ((sym == NoSymbol) || !Tcl_UniCharIsUpper(sym)) { index &= ~1; sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index); @@ -808,7 +722,6 @@ TkpInitKeymapInfo( dispPtr->lockUsage = LU_CAPS; dispPtr->modeModMask = 0; - #if 0 /* * With this, and become synonyms for and