OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / tk8.6.12 / macosx / tkMacOSXKeyboard.c
diff --git a/util/src/TclTk/tk8.6.12/macosx/tkMacOSXKeyboard.c b/util/src/TclTk/tk8.6.12/macosx/tkMacOSXKeyboard.c
new file mode 100644 (file)
index 0000000..9aca9e1
--- /dev/null
@@ -0,0 +1,969 @@
+/*
+ * tkMacOSXKeyboard.c --
+ *
+ *     Routines to support keyboard events on the Macintosh.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ * Copyright 2001-2009, Apple Inc.
+ * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
+ * Copyright (c) 2020 Marc Culler
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkMacOSXPrivate.h"
+#include "tkMacOSXEvent.h"
+#include "tkMacOSXConstants.h"
+#include "tkMacOSXKeysyms.h"
+
+/*
+ * About keyboards
+ * ---------------
+ * Keyboards are complicated.  This long comment is an attempt to provide
+ * enough information about them to make it possible to read and understand
+ * the code in this file.
+ *
+ * Every key on a keyboard is identified by a number between 0 and 127.  In
+ * macOS, pressing or releasing a key on the keyboard generates an NSEvent of
+ * type KeyDown, KeyUp or FlagsChanged.  The 8-bit identifier of the key that
+ * was involved in this event is provided in the attribute [NSEvent keyCode].
+ * Apple also refers to this number as a "Virtual KeyCode".  In this file, to
+ * avoid confusion with other uses of the word keycode, we will refer to this
+ * key identifier as a "virtual keycode", usually the value of a variable named
+ * "virtual".
+ *
+ * Some of the keys on a keyboard, such as the Shift, Option, Command or
+ * Control keys, are "modifier" keys.  The effect of pressing or releasing a
+ * key depends on three quantities:
+ *     - which key is being pressed or released
+ *     - which modifier keys are being held down at the moment
+ *     - the current keyboard layout
+ * If the key is a modifier key then the effect of pressing or releasing it is
+ * only to change the list of which modifier keys are being held down.  Apple
+ * reports this by sending an NSEvent of type FlagsChanged.  X11 reports this
+ * as a KeyPress or KeyRelease event for the modifier key.  Note that there may
+ * be combinations of modifier key states and key presses which have no effect.
+ *
+ * In X11 every meaningful effect from a key action is identified by a 16 bit
+ * value known as a keysym.  Every keysym has an associated string name, also
+ * known as a keysym.  The Tk bind command uses the X11 keysym string to
+ * specify a key event which should invoke a certain action and it provides the
+ * numeric and symbolic keysyms to the bound proc as %N and %K respectively.
+ * An X11 XEvent which reports a KeyPress or KeyRelease does not include the
+ * keysym.  Instead it includes a platform-specific numerical value called a
+ * keycode which is available to the bound procedure as %k.  A platform port of
+ * Tk must provide functions which convert between keycodes and numerical
+ * keysyms.  Conversion between numerical and symbolic keysyms is provided by
+ * the generic Tk code, although platforms are allowed to provide their own by
+ * defining the XKeysymToString and XStringToKeysym functions and undefining
+ * the macro REDO_KEYSYM_LOOKUP.  This macOS port uses the conversion provided
+ * by the generic code.
+ *
+ * When the keyboard focus is on a Tk widget which provides text input, there
+ * are some X11 KeyPress events which cause text to be inserted.  We will call
+ * these "printable" events. The UCS-32 character stored in the keycode field
+ * of an XKeyEvent depends on more than the three items above.  It may also
+ * depend on the sequence of keypresses that preceded the one being reported by
+ * the XKeyEvent.  For example, on macOS an <Alt-e> event does not cause text
+ * to be inserted but a following <a> event causes an accented 'a' to be
+ * inserted.  The events in such a composition sequence, other than the final
+ * one, are known as "dead-key" events.
+ *
+ * MacOS packages the information described above in a different way.  Every
+ * meaningful effect from a key action *other than changing the state of
+ * modifier keys* is identified by a unicode string which is provided as the
+ * [NSEvent characters] attribute of a KeyDown or KeyUp event.  FlagsChanged
+ * events do not have characters.  In principle, the characters attribute could
+ * be an arbitrary unicode string but in practice it is always a single UTF-16
+ * character which we usually store in a variable named keychar.  While the
+ * keychar is a legal unicode code point, it does not necessarily represent a
+ * glyph. MacOS uses unicode code points in the private-use range 0xF700-0xF8FF
+ * for non-printable events which have no associated ASCII code point.  For
+ * example, pressing the F2 key generates an NSEvent with the character 0xF705,
+ * the Backspace key produces 0x7F (ASCII del) and the Delete key produces
+ * 0xF728.
+ *
+ * With the exception of modifier keys, it is possible to translate between
+ * 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 MOD_KEYCHAR.  Keys which do not appear on any Macintosh keyboard, such
+ * as the Menu key on PC keyboards, are assigned UNKNOWN_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
+ * or KeyRelease is as follows:
+ *     keycode = (virtual << 24) | index << 22 | keychar
+ * where index is a 2-bit quantity whose bits indicate the state of the Option
+ * and Shift keys.
+ *
+ * A few remarks are in order.  First, we are using 32 bits for the keycode and
+ * we are allowing room for up to 22 bits for the keychar.  This means that
+ * there is enough room in the keycode to hold a UTF-32 character, which only
+ * requires 21 bits.  Second, the KeyCode type for the keycode field in an
+ * XEvent is currently defined as unsigned 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 macros and structures related to key event processing.
+ */
+
+/*
+ * Hash tables and array used to translate between various key attributes.
+ */
+
+static Tcl_HashTable special2keysym;   /* 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 unichar2xvirtual; /* unichar to virtual with index */
+static UniChar xvirtual2unichar[512];  /* virtual with index to unichar */
+
+/*
+ * Flags.
+ */
+
+static BOOL initialized = NO;
+static BOOL keyboardChanged = YES;
+
+/*
+ * Prototypes for static functions used in this file.
+ */
+
+static void    InitHashTables(void);
+static void     UpdateKeymaps(void);
+static int     KeyDataToUnicode(UniChar *uniChars, int maxChars,
+                       UInt16 keyaction, UInt32 virt, UInt32 modifiers,
+                       UInt32 * deadKeyStatePtr);
+
+#pragma mark TKApplication(TKKeyboard)
+
+@implementation TKApplication(TKKeyboard)
+- (void) keyboardChanged: (NSNotification *) notification
+{
+    (void)notification;
+#ifdef TK_MAC_DEBUG_NOTIFICATIONS
+    TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
+#else
+    (void)notification;
+#endif
+    keyboardChanged = YES;
+    UpdateKeymaps();
+}
+@end
+
+#pragma mark -
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitHashTables --
+ *
+ *     Creates hash tables used by some of the functions in this file.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Allocates memory & creates some hash tables.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InitHashTables(void)
+{
+    Tcl_HashEntry *hPtr;
+    const KeyInfo *kPtr;
+    const KeysymInfo *ksPtr;
+    int dummy, index;
+
+    Tcl_InitHashTable(&special2keysym, TCL_ONE_WORD_KEYS);
+    Tcl_InitHashTable(&keysym2keycode, TCL_ONE_WORD_KEYS);
+    for (kPtr = keyArray; kPtr->virt != 0; kPtr++) {
+       MacKeycode macKC;
+       macKC.v.o_s = 0;
+       hPtr = Tcl_CreateHashEntry(&special2keysym, INT2PTR(kPtr->virt),
+                                  &dummy);
+       Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keysym));
+       hPtr = Tcl_CreateHashEntry(&keysym2keycode, INT2PTR(kPtr->keysym),
+                                  &dummy);
+       macKC.v.virt = kPtr->virt;
+       macKC.v.keychar = kPtr->keychar;
+       Tcl_SetHashValue(hPtr, INT2PTR(macKC.uint));
+
+       /*
+        * The Carbon framework does not work for finding the unicode character
+        * of a special key.  But that does not depend on the keyboard layout,
+        * so we can record the information here.
+        */
+
+       for (index = 3; index >= 0; index--) {
+           macKC.v.o_s = index;
+           xvirtual2unichar[macKC.x.xvirtual] = macKC.x.keychar;
+       }
+    }
+    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));
+    }
+    UpdateKeymaps();
+    initialized = YES;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateKeymaps --
+ *
+ *     Called when the keyboard changes to update the hash tables that provide
+ *      maps between unicode characters and virtual keycodes with indexes.  In
+ *      order for the map from characters to virtual keycodes to be
+ *      well-defined we have to ignore virtual keycodes for keypad keys, since
+ *      each keypad key has the same character as the corresponding key on the
+ *      main keyboard.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Initializes, if necessary, and updates the unichar2xvirtual hash table
+ *      and the xvirtual2unichar array.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateKeymaps()
+{
+    static Bool keymapInitialized = false;
+    Tcl_HashEntry *hPtr;
+    int virt, index, dummy;
+
+    if (!keymapInitialized) {
+       Tcl_InitHashTable(&unichar2xvirtual, TCL_ONE_WORD_KEYS);
+       keymapInitialized = true;
+    } else {
+       Tcl_DeleteHashTable(&unichar2xvirtual);
+       Tcl_InitHashTable(&unichar2xvirtual, TCL_ONE_WORD_KEYS);
+    }
+    /*
+     * This loop goes backwards so that a lookup by keychar will provide the
+     * minimal modifier mask.  Simpler combinations will overwrite more complex
+     * ones when constructing the table.
+     */
+
+    for (index = 3; index >= 0; index--) {
+        for (virt = 0; virt < 128; virt++) {
+           MacKeycode macKC;
+           macKC.v = (keycode_v) {.virt = virt, .o_s = index, .keychar = 0};
+           int modifiers = INDEX2CARBON(index), result;
+           UniChar keychar = 0;
+           result = KeyDataToUnicode(&keychar, 1, kUCKeyActionDown, virt,
+                                     modifiers, NULL);
+           if (keychar == 0x10) {
+
+               /*
+                * This is a special key, handled in InitHashTables.
+                */
+
+               continue;
+           }
+           macKC.v.keychar = keychar;
+           if (! ON_KEYPAD(virt)) {
+               hPtr = Tcl_CreateHashEntry(&unichar2xvirtual,
+                                          INT2PTR(macKC.x.keychar), &dummy);
+               Tcl_SetHashValue(hPtr, INT2PTR(macKC.x.xvirtual));
+            }
+           xvirtual2unichar[macKC.x.xvirtual] = macKC.x.keychar;
+       }
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * KeyDataToUnicode --
+ *
+ *     Given MacOS key event data this function generates the keychar.  It
+ *     does this by using OS resources from the Carbon framework.  Note that
+ *      the Carbon functions used here are not aware of the keychars in the
+ *      private-use range which macOS now uses for special keys.  For those
+ *      keys this function returns 0x10 (ASCII dle).
+ *
+ *     The parameter deadKeyStatePtr can be NULL, if no deadkey handling is
+ *     needed (which is always the case here).
+ *
+ *     This function is called in XKeycodeToKeysym and UpdateKeymaps.
+ *
+ * Results:
+ *     The number of characters generated if any, 0 if we are waiting for
+ *     another byte of a dead-key sequence.
+ *
+ * Side Effects:
+ *      Fills in the uniChars array with a Unicode string.
+ *
+ *----------------------------------------------------------------------
+ */
+
+
+static int
+KeyDataToUnicode(
+    UniChar *uniChars,
+    int maxChars,
+    UInt16 keyaction,
+    UInt32 virt,
+    UInt32 modifiers,
+    UInt32 *deadKeyStatePtr)
+{
+    static const void *layoutData = NULL;
+    static UInt32 keyboardType = 0;
+    UniCharCount actuallength = 0;
+
+    if (keyboardChanged) {
+       TISInputSourceRef currentKeyboardLayout =
+               TISCopyCurrentKeyboardLayoutInputSource();
+
+       if (currentKeyboardLayout) {
+           CFDataRef keyLayoutData = (CFDataRef) TISGetInputSourceProperty(
+                   currentKeyboardLayout, kTISPropertyUnicodeKeyLayoutData);
+
+           if (keyLayoutData) {
+               layoutData = CFDataGetBytePtr(keyLayoutData);
+               keyboardType = LMGetKbdType();
+           }
+           CFRelease(currentKeyboardLayout);
+       }
+       keyboardChanged = 0;
+    }
+    if (layoutData) {
+       OptionBits options = 0;
+       UInt32 dummyState;
+       OSStatus err;
+
+       virt &= 0xFF;
+       modifiers = (modifiers >> 8) & 0xFF;
+       if (!deadKeyStatePtr) {
+           options = kUCKeyTranslateNoDeadKeysMask;
+           dummyState = 0;
+           deadKeyStatePtr = &dummyState;
+       }
+       err = ChkErr(UCKeyTranslate, (const UCKeyboardLayout *)layoutData, virt, keyaction, modifiers,
+               keyboardType, options, deadKeyStatePtr, maxChars,
+               &actuallength, uniChars);
+       if (!actuallength && *deadKeyStatePtr) {
+
+           /*
+            * We are waiting for another key.
+            */
+
+           return 0;
+       }
+       *deadKeyStatePtr = 0;
+       if (err != noErr) {
+           actuallength = 0;
+       }
+    }
+    return actuallength;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XKeycodeToKeysym --
+ *
+ *     This is a stub function which translates from the keycode used in an
+ *      XEvent to a numerical keysym.  On macOS, the display parameter is
+ *      ignored and only the the virtual keycode stored in the .virtual bitfield
+ *      of a MacKeycode.v.
+ *
+ * Results:
+ *      Returns the corresponding numerical keysym, or NoSymbol if the keysym
+ *      cannot be found.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeySym
+XkbKeycodeToKeysym(
+    TCL_UNUSED(Display *),
+    unsigned int keycode,
+    TCL_UNUSED(int),
+    int index)
+{
+    Tcl_HashEntry *hPtr;
+    MacKeycode macKC;
+    int modifiers, result;
+    UniChar keychar = 0;
+
+    if (!initialized) {
+       InitHashTables();
+    }
+    macKC.uint = keycode;
+    macKC.v.o_s = index;
+
+    /*
+     * First check if the virtual keycode corresponds to a special key, such as
+     * an Fn function key or Tab, Backspace, Home, End, etc.
+     */
+
+    hPtr = Tcl_FindHashEntry(&special2keysym, INT2PTR(macKC.v.virt));
+    if (hPtr != NULL) {
+       return (KeySym) Tcl_GetHashValue(hPtr);
+    }
+
+    /*
+     * If the virtual value in this keycode does not correspond to an actual
+     * key in the current keyboard layout, try using its keychar to look up a
+     * keysym.
+     */
+
+    if (macKC.v.virt > 127) {
+       hPtr = Tcl_FindHashEntry(&unichar2keysym, INT2PTR(macKC.v.keychar));
+       if (hPtr != NULL) {
+           return (KeySym) Tcl_GetHashValue(hPtr);
+       }
+    }
+
+    /*
+     * If the virtual keycode does belong to a key, use the virtual and the
+     * Option-Shift from index to look up a keychar by using the Carbon
+     * Framework; then translate the keychar to a keysym using the
+     * unicode2keysym hash table.
+     */
+
+    modifiers = INDEX2CARBON(macKC.v.o_s);
+    result = KeyDataToUnicode(&keychar, 1, kUCKeyActionDown, macKC.v.virt,
+                             modifiers, NULL);
+    if (result) {
+       hPtr = Tcl_FindHashEntry(&unichar2keysym, INT2PTR(keychar));
+       if (hPtr != NULL) {
+           return (KeySym) Tcl_GetHashValue(hPtr);
+       }
+    }
+    return NoSymbol;
+}
+
+KeySym
+XKeycodeToKeysym(
+    TCL_UNUSED(Display *),
+    KeyCode keycode,
+    int index)
+{
+    return XkbKeycodeToKeysym(NULL, keycode, 0, index);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetString --
+ *
+ *     This is a stub function which retrieves the string stored in the
+ *      transchars field of an XEvent and converts it to a Tcl_DString.
+ *
+ * Results:
+ *     Returns a pointer to the string value of the DString.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+const char *
+TkpGetString(
+    TCL_UNUSED(TkWindow *),    /* Window where event occurred: Needed to get
+                                * input context. */
+    XEvent *eventPtr,          /* X keyboard event. */
+    Tcl_DString *dsPtr)                /* Uninitialized or empty string to hold
+                                * result. */
+{
+    MacKeycode macKC;
+    char utfChars[8];
+    int length = 0;
+
+    macKC.uint = eventPtr->xkey.keycode;
+    if (IS_PRINTABLE(macKC.v.keychar)) {
+       length = TkUniCharToUtf(macKC.v.keychar, utfChars);
+    }
+    utfChars[length] = 0;
+
+    Tcl_DStringInit(dsPtr);
+    return Tcl_DStringAppend(dsPtr, utfChars, length);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGetModifierMapping --
+ *
+ *     X11 stub function to get the keycodes used as modifiers.  This
+ *      is never called by the macOS port.
+ *
+ * Results:
+ *     Returns a newly allocated modifier map.
+ *
+ * Side effects:
+ *     Allocates a new modifier map data structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+XModifierKeymap *
+XGetModifierMapping(
+    TCL_UNUSED(Display *))
+{
+    XModifierKeymap *modmap;
+
+    modmap = (XModifierKeymap *)ckalloc(sizeof(XModifierKeymap));
+    modmap->max_keypermod = 0;
+    modmap->modifiermap = NULL;
+    return modmap;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFreeModifiermap --
+ *
+ *     Deallocates a modifier map that was created by XGetModifierMapping.
+ *      This is also never called by the macOS port.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Frees the datastructure referenced by modmap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XFreeModifiermap(
+    XModifierKeymap *modmap)
+{
+    if (modmap->modifiermap != NULL) {
+       ckfree(modmap->modifiermap);
+    }
+    ckfree(modmap);
+    return Success;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XKeysymToString, XStringToKeysym --
+ *
+ *     These X11 stub functions map keysyms to strings & strings to keysyms.
+ *      A platform can do its own conversion by defining these and undefining
+ *      REDO_KEYSYM_LOOKUP.  The macOS port defines REDO_KEYSYM_LOOKUP so these
+ *      are never called and Tk does the conversion for us.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+XKeysymToString(
+    TCL_UNUSED(KeySym))
+{
+    return NULL;
+}
+
+KeySym
+XStringToKeysym(
+    TCL_UNUSED(const char *))
+{
+    return NoSymbol;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * XKeysymToKeycode --
+ *
+ *     This is a stub function which converts a numerical keysym to the
+ *      platform-specific keycode used in a KeyPress or KeyRelease XEvent.
+ *      For macOS the keycode is an unsigned int with bitfields described
+ *      in the definition of the MacKeycode type.
+ *
+ * Results:
+ *
+ *      A macOS KeyCode. See the description of keycodes at the top of this
+ *     file and the definition of the MacKeycode type in tkMacOSXPrivate.h.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeyCode
+XKeysymToKeycode(
+    TCL_UNUSED(Display *),
+    KeySym keysym)
+{
+    Tcl_HashEntry *hPtr;
+    MacKeycode macKC;
+    if (!initialized) {
+       InitHashTables();
+    }
+
+    /*
+     * First check for a special key.
+     */
+
+    hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym));
+    if (hPtr != NULL) {
+       return (KeyCode) PTR2INT(Tcl_GetHashValue(hPtr));
+    }
+
+    /*
+     * Initialize the keycode as if the keysym cannot be converted to anything
+     * else.
+     */
+
+    macKC.v.virt = NO_VIRTUAL;
+    macKC.v.o_s = 0;
+    macKC.v.keychar = 0;
+
+    /*
+     * If the keysym is recognized fill in the keychar.  Also fill in the
+     * xvirtual field if the key exists on the current keyboard.
+     */
+
+    hPtr = (Tcl_HashEntry *) Tcl_FindHashEntry(&keysym2unichar,
+                                              INT2PTR(keysym));
+    if (hPtr != NULL) {
+       unsigned long data = (unsigned long) Tcl_GetHashValue(hPtr);
+       macKC.x.keychar = (unsigned int) data;
+       hPtr = Tcl_FindHashEntry(&unichar2xvirtual, INT2PTR(macKC.x.keychar));
+       if (hPtr != NULL) {
+           data = (unsigned long) Tcl_GetHashValue(hPtr);
+           macKC.x.xvirtual = (unsigned int) data;
+       }
+    }
+    return (KeyCode) macKC.uint;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetKeycodeAndState --
+ *
+ *     This function accepts a keysym and an XEvent and sets some fields of
+ *     the XEvent.  It is used by the event generate command.
+ *
+ * Results:
+ *      None
+ *
+ * Side effects:
+ *
+ *     Modifies the XEvent. Sets the xkey.keycode to a keycode value formatted
+ *     by XKeysymToKeycode and updates the shift and option flags in
+ *     xkey.state if either of those modifiers is required to generate the
+ *     keysym.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TkpSetKeycodeAndState(
+    TCL_UNUSED(Tk_Window),
+    KeySym keysym,
+    XEvent *eventPtr)
+{
+    if (keysym == NoSymbol) {
+       eventPtr->xkey.keycode = 0;
+    } else {
+       int eventIndex = STATE2INDEX(eventPtr->xkey.state);
+       MacKeycode macKC;
+       macKC.uint = XKeysymToKeycode(NULL, keysym);
+
+       /*
+        * We have a virtual keycode and a minimal choice for Shift and Option
+        * modifiers 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 the extra modifiers and
+        * do another lookup for the keychar.  We don't want to do this for
+        * special keys, however.
+        */
+
+       if (macKC.v.o_s != eventIndex) {
+           macKC.v.o_s |= eventIndex;
+       }
+       if (macKC.v.keychar < 0xF700) {
+           UniChar keychar = macKC.v.keychar;
+           NSString *str, *lower, *upper;
+           if (macKC.v.virt != NO_VIRTUAL) {
+               macKC.x.keychar = xvirtual2unichar[macKC.x.xvirtual];
+           } else {
+               str = [[NSString alloc] initWithCharacters:&keychar length:1];
+               lower = [str lowercaseString];
+               upper = [str uppercaseString];
+               if (![str isEqual: lower]) {
+                   macKC.v.o_s |= INDEX_SHIFT;
+               }
+               if (macKC.v.o_s & INDEX_SHIFT) {
+                   macKC.v.keychar = [upper characterAtIndex:0];
+               }
+           }
+       }
+       eventPtr->xkey.keycode = macKC.uint;
+       eventPtr->xkey.state |= INDEX2STATE(macKC.v.o_s);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetKeySym --
+ *
+ *     This is a stub function called in tkBind.c.  Given a KeyPress or
+ *     KeyRelease XEvent, it maps the keycode in the event to a numerical
+ *      keysym.
+ *
+ * Results:
+ *     The return value is the keysym corresponding to eventPtr, or NoSymbol
+ *     if no matching keysym could be found.
+ *
+ * Side effects:
+ *     In the first call for a given display, calls TkpInitKeymapInfo.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeySym
+TkpGetKeySym(
+    TkDisplay *dispPtr,                /* Display in which to map keycode. */
+    XEvent *eventPtr)          /* Description of X event. */
+{
+    KeySym sym;
+    int index;
+    MacKeycode macKC;
+    macKC.uint = eventPtr->xkey.keycode;
+
+    /*
+     * Refresh the mapping information if it's stale.
+     */
+
+    if (dispPtr->bindInfoStale) {
+       TkpInitKeymapInfo(dispPtr);
+    }
+
+    /*
+     * Modifier key events have a special mac keycode (see tkProcessKeyEvent).
+     */
+
+    if (macKC.v.keychar == MOD_KEYCHAR) {
+       switch (macKC.v.virt) {
+       case 54:
+           return XK_Meta_R;
+       case 55:
+           return XK_Meta_L;
+       case 56:
+           return XK_Shift_L;
+       case 57:
+           return XK_Caps_Lock;
+       case 58:
+           return XK_Alt_L;
+       case 59:
+           return XK_Control_L;
+       case 60:
+           return XK_Shift_R;
+       case 61:
+           return XK_Alt_R;
+       case 62:
+           return XK_Control_R;
+       case 63:
+           return XK_Super_L;
+       default:
+           return NoSymbol;
+       }
+    }
+
+    /*
+     * Figure out which of the four slots in the keymap vector to use for this
+     * key. Refer to Xlib documentation for more info on how this computation
+     * works.
+     */
+
+    index = STATE2INDEX(eventPtr->xkey.state);
+    if (eventPtr->xkey.state & LockMask) {
+       index |= INDEX_SHIFT;
+    }
+
+    /*
+     * First do the straightforward lookup.
+     */
+
+    sym = XkbKeycodeToKeysym(dispPtr->display, macKC.uint, 0, index);
+
+    /*
+     * 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 & INDEX_SHIFT) && !(eventPtr->xkey.state & ShiftMask)) {
+       if ((sym == NoSymbol) || !Tcl_UniCharIsUpper(sym)) {
+           sym = XkbKeycodeToKeysym(dispPtr->display, macKC.uint, 0,
+                                  index & ~INDEX_SHIFT);
+       }
+    }
+
+    /*
+     * Another bit of special handling: If this is a shifted key and there is
+     * no keysym defined, then use the keysym for the unshifted key.
+     */
+
+    if ((index & INDEX_SHIFT) && (sym == NoSymbol)) {
+       sym = XkbKeycodeToKeysym(dispPtr->display, macKC.uint, 0,
+                              index & ~INDEX_SHIFT);
+    }
+    return sym;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpInitKeymapInfo --
+ *
+ *     This procedure initializes fields in the display that pertain
+ *      to modifier keys.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Modifier key information in dispPtr is initialized.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkpInitKeymapInfo(
+    TkDisplay *dispPtr)                /* Display for which to recompute keymap
+                                * information. */
+{
+    dispPtr->bindInfoStale = 0;
+
+    /*
+     * On macOS the caps lock key is always interpreted to mean that alphabetic
+     * keys become uppercase but other keys do not get shifted.  (X11 allows
+     * a configuration option which makes the caps lock equivalent to holding
+     * down the shift key.)
+     * There is no offical "Mode_switch" key.
+     */
+
+    dispPtr->lockUsage = LU_CAPS;
+
+    /* This field is no longer used by tkBind.c */
+
+    dispPtr->modeModMask = 0;
+
+    /* The Alt and Meta keys are interchanged on Macintosh keyboards compared
+     * to PC keyboards.  These fields could be set to make the Alt key on a PC
+     * keyboard behave likd an Alt key. That would also require interchanging
+     * Mod1Mask and Mod2Mask in tkMacOSXKeyEvent.c.
+     */
+
+    dispPtr->altModMask = 0;
+    dispPtr->metaModMask = 0;
+
+    /*
+     * The modKeyCodes table lists the keycodes that appear in KeyPress or
+     * KeyRelease XEvents for modifier keys.  In tkBind.c this table is
+     * searched to determine whether an XEvent corresponds to a modifier key.
+     */
+
+    if (dispPtr->modKeyCodes != NULL) {
+       ckfree(dispPtr->modKeyCodes);
+    }
+    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] = XKeysymToKeycode(NULL, modKeyArray[i]);
+    }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * TkMacOSXAddVirtual --
+ *
+ *     This procedure is an internal utility which accepts an unsigned int
+ *      that has been partially filled as a MacKeycode, having the Option and
+ *      Shift state set in the o_s field and the keychar field set but with the
+ *      virtual keycode blank.  It looks up the virtual keycode for the keychar
+ *      (possibly NO_VIRTUAL) and returns an unsigned int which is a complete
+ *      MacKeycode with the looked up virtual keycode added.  This is used when
+ *      creating XEvents for the unicode characters which are generated by the
+ *      NSTextInputClient.
+ *
+ * Results:
+ *      An unsigned int which is a complete MacKeycode, including a virtual
+ *     keycode which matches the Option-Shift state and keychar.
+ *
+ * Side effects:
+ *     None
+ *
+ *--------------------------------------------------------------
+ */
+unsigned
+TkMacOSXAddVirtual(
+    unsigned int keycode)
+{
+    MacKeycode macKC;
+    Tcl_HashEntry *hPtr;
+    macKC.uint = keycode;
+
+    if (!initialized) {
+       InitHashTables();
+    }
+
+    hPtr = (Tcl_HashEntry *) Tcl_FindHashEntry(&unichar2xvirtual,
+                                              INT2PTR(macKC.v.keychar));
+    if (hPtr != NULL) {
+       unsigned long data = (unsigned long) Tcl_GetHashValue(hPtr);
+       macKC.x.xvirtual = (unsigned int) data;
+    } else {
+       macKC.v.virt = NO_VIRTUAL;
+    }
+    return macKC.uint;
+}
+/*
+ * Local Variables:
+ * mode: objc
+ * c-basic-offset: 4
+ * fill-column: 79
+ * coding: utf-8
+ * End:
+ */