1 /*
2  * tkMacOSXKeyboard.c --
3  *
4  *	Routines to support keyboard events on the Macintosh.
5  *
6  * Copyright © 1995-1997 Sun Microsystems, Inc.
7  * Copyright © 2001-2009, Apple Inc.
8  * Copyright © 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
9  * Copyright © 2020 Marc Culler
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14 
15 #include "tkMacOSXPrivate.h"
16 #include "tkMacOSXInt.h"
17 #include "tkMacOSXConstants.h"
18 #include "tkMacOSXKeysyms.h"
19 
20 /*
21  * About keyboards
22  * ---------------
23  * Keyboards are complicated.  This long comment is an attempt to provide
24  * enough information about them to make it possible to read and understand
25  * the code in this file.
26  *
27  * Every key on a keyboard is identified by a number between 0 and 127.  In
28  * macOS, pressing or releasing a key on the keyboard generates an NSEvent of
29  * type KeyDown, KeyUp or FlagsChanged.  The 8-bit identifier of the key that
30  * was involved in this event is provided in the attribute [NSEvent keyCode].
31  * Apple also refers to this number as a "Virtual KeyCode".  In this file, to
32  * avoid confusion with other uses of the word keycode, we will refer to this
33  * key identifier as a "virtual keycode", usually the value of a variable named
34  * "virtual".
35  *
36  * Some of the keys on a keyboard, such as the Shift, Option, Command or
37  * Control keys, are "modifier" keys.  The effect of pressing or releasing a
38  * key depends on three quantities:
39  *     - which key is being pressed or released
40  *     - which modifier keys are being held down at the moment
41  *     - the current keyboard layout
42  * If the key is a modifier key then the effect of pressing or releasing it is
43  * only to change the list of which modifier keys are being held down.  Apple
44  * reports this by sending an NSEvent of type FlagsChanged.  X11 reports this
45  * as a KeyPress or KeyRelease event for the modifier key.  Note that there may
46  * be combinations of modifier key states and key presses which have no effect.
47  *
48  * In X11 every meaningful effect from a key action is identified by a 16 bit
49  * value known as a keysym.  Every keysym has an associated string name, also
50  * known as a keysym.  The Tk bind command uses the X11 keysym string to
51  * specify a key event which should invoke a certain action and it provides the
52  * numeric and symbolic keysyms to the bound proc as %N and %K respectively.
53  * An X11 XEvent which reports a KeyPress or KeyRelease does not include the
54  * keysym.  Instead it includes a platform-specific numerical value called a
55  * keycode which is available to the bound procedure as %k.  A platform port of
56  * Tk must provide functions which convert between keycodes and numerical
57  * keysyms.  Conversion between numerical and symbolic keysyms is provided by
58  * the generic Tk code, although platforms are allowed to provide their own by
59  * defining the XKeysymToString and XStringToKeysym functions and undefining
60  * the macro REDO_KEYSYM_LOOKUP.  This macOS port uses the conversion provided
61  * by the generic code.
62  *
63  * When the keyboard focus is on a Tk widget which provides text input, there
64  * are some X11 KeyPress events which cause text to be inserted.  We will call
65  * these "printable" events. The UCS-32 character stored in the keycode field
66  * of an XKeyEvent depends on more than the three items above.  It may also
67  * depend on the sequence of keypresses that preceded the one being reported by
68  * the XKeyEvent.  For example, on macOS an <Alt-e> event does not cause text
69  * to be inserted but a following <a> event causes an accented 'a' to be
70  * inserted.  The events in such a composition sequence, other than the final
71  * one, are known as "dead-key" events.
72  *
73  * MacOS packages the information described above in a different way.  Every
74  * meaningful effect from a key action *other than changing the state of
75  * modifier keys* is identified by a unicode string which is provided as the
76  * [NSEvent characters] attribute of a KeyDown or KeyUp event.  FlagsChanged
77  * events do not have characters.  In principle, the characters attribute could
78  * be an arbitrary unicode string but in practice it is always a single UTF-16
79  * character which we usually store in a variable named keychar.  While the
80  * keychar is a legal unicode code point, it does not necessarily represent a
81  * glyph. MacOS uses unicode code points in the private-use range 0xF700-0xF8FF
82  * for non-printable events which have no associated ASCII code point.  For
83  * example, pressing the F2 key generates an NSEvent with the character 0xF705,
84  * the Backspace key produces 0x7F (ASCII del) and the Delete key produces
85  * 0xF728.
86  *
87  * With the exception of modifier keys, it is possible to translate between
88  * numerical X11 keysyms and macOS keychars; this file constructs Tcl hash
89  * tables to do this job, using data defined in the file tkMacOSXKeysyms.h.
90  * The code here adopts the convention that the keychar of any modifier key
91  * is MOD_KEYCHAR.  Keys which do not appear on any Macintosh keyboard, such
92  * as the Menu key on PC keyboards, are assigned UNKNOWN_KEYCHAR.
93  *
94  * The macosx platform-specific scheme for generating a keycode when mapping an
95  * NSEvent of type KeyUp, KeyDown or FlagsChanged to an XEvent of type KeyPress
96  * or KeyRelease is as follows:
97  *     keycode = (virtual << 24) | index << 22 | keychar
98  * where index is a 2-bit quantity whose bits indicate the state of the Option
99  * and Shift keys.
100  *
101  * A few remarks are in order.  First, we are using 32 bits for the keycode and
102  * we are allowing room for up to 22 bits for the keychar.  This means that
103  * there is enough room in the keycode to hold a UTF-32 character, which only
104  * requires 21 bits.  Second, the KeyCode type for the keycode field in an
105  * XEvent is currently defined as unsigned int, which was modified from the
106  * unsigned short used in X11 in order to accomodate macOS. Finally, there is
107  * no obstruction to generating KeyPress events for keys that represent letters
108  * which do not exist on the current keyboard layout.  And different keyboard
109  * layouts can assign a given letter to different keys.  So we need a
110  * convention for what value to assign to "virtual" when computing the keycode
111  * for a generated event.  The convention used here is as follows: If there is
112  * a key on the current keyboard which produces the keychar, use the virtual
113  * keycode of that key.  Otherwise set virtual = NO_VIRTUAL.
114  */
115 
116 
117 /*
118  * See tkMacOSXPrivate.h for macros and structures related to key event processing.
119  */
120 
121 /*
122  * Hash tables and array used to translate between various key attributes.
123  */
124 
125 static Tcl_HashTable special2keysym;	/* Special virtual keycode to keysym */
126 static Tcl_HashTable keysym2keycode;	/* keysym to XEvent keycode */
127 static Tcl_HashTable keysym2unichar;	/* keysym to unichar */
128 static Tcl_HashTable unichar2keysym;	/* unichar to X11 keysym */
129 static Tcl_HashTable unichar2xvirtual;	/* unichar to virtual with index */
130 static UniChar xvirtual2unichar[512];	/* virtual with index to unichar */
131 
132 /*
133  * Flags.
134  */
135 
136 static BOOL initialized = NO;
137 static BOOL keyboardChanged = YES;
138 
139 /*
140  * Prototypes for static functions used in this file.
141  */
142 
143 static void	InitHashTables(void);
144 static void     UpdateKeymaps(void);
145 static int	KeyDataToUnicode(UniChar *uniChars, int maxChars,
146 			UInt16 keyaction, UInt32 virt, UInt32 modifiers,
147 			UInt32 * deadKeyStatePtr);
148 
149 #pragma mark TKApplication(TKKeyboard)
150 
151 @implementation TKApplication(TKKeyboard)
152 - (void) keyboardChanged: (NSNotification *) notification
153 {
154     (void)notification;
155 #ifdef TK_MAC_DEBUG_NOTIFICATIONS
156     TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
157 #endif
158     keyboardChanged = YES;
159     UpdateKeymaps();
160 }
161 @end
162 
163 #pragma mark -
164 
165 /*
166  *----------------------------------------------------------------------
167  *
168  * InitHashTables --
169  *
170  *	Creates hash tables used by some of the functions in this file.
171  *
172  * Results:
173  *	None.
174  *
175  * Side effects:
176  *	Allocates memory & creates some hash tables.
177  *
178  *----------------------------------------------------------------------
179  */
180 
181 static void
InitHashTables(void)182 InitHashTables(void)
183 {
184     Tcl_HashEntry *hPtr;
185     const KeyInfo *kPtr;
186     const KeysymInfo *ksPtr;
187     int dummy, index;
188 
189     Tcl_InitHashTable(&special2keysym, TCL_ONE_WORD_KEYS);
190     Tcl_InitHashTable(&keysym2keycode, TCL_ONE_WORD_KEYS);
191     for (kPtr = keyArray; kPtr->virt != 0; kPtr++) {
192 	MacKeycode macKC;
193 	macKC.v.o_s = 0;
194 	hPtr = Tcl_CreateHashEntry(&special2keysym, INT2PTR(kPtr->virt),
195 				   &dummy);
196 	Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keysym));
197 	hPtr = Tcl_CreateHashEntry(&keysym2keycode, INT2PTR(kPtr->keysym),
198 				   &dummy);
199 	macKC.v.virt = kPtr->virt;
200 	macKC.v.keychar = kPtr->keychar;
201 	Tcl_SetHashValue(hPtr, INT2PTR(macKC.uint));
202 
203 	/*
204 	 * The Carbon framework does not work for finding the unicode character
205 	 * of a special key.  But that does not depend on the keyboard layout,
206 	 * so we can record the information here.
207 	 */
208 
209 	for (index = 3; index >= 0; index--) {
210 	    macKC.v.o_s = index;
211 	    xvirtual2unichar[macKC.x.xvirtual] = macKC.x.keychar;
212 	}
213     }
214     Tcl_InitHashTable(&keysym2unichar, TCL_ONE_WORD_KEYS);
215     Tcl_InitHashTable(&unichar2keysym, TCL_ONE_WORD_KEYS);
216     for (ksPtr = keysymTable; ksPtr->keysym != 0; ksPtr++) {
217 	hPtr = Tcl_CreateHashEntry(&keysym2unichar, INT2PTR(ksPtr->keysym),
218 				   &dummy);
219 	Tcl_SetHashValue(hPtr, INT2PTR(ksPtr->keycode));
220 	hPtr = Tcl_CreateHashEntry(&unichar2keysym, INT2PTR(ksPtr->keycode),
221 				   &dummy);
222 	Tcl_SetHashValue(hPtr, INT2PTR(ksPtr->keysym));
223     }
224     UpdateKeymaps();
225     initialized = YES;
226 }
227 
228 /*
229  *----------------------------------------------------------------------
230  *
231  * UpdateKeymaps --
232  *
233  *	Called when the keyboard changes to update the hash tables that provide
234  *      maps between unicode characters and virtual keycodes with indexes.  In
235  *      order for the map from characters to virtual keycodes to be
236  *      well-defined we have to ignore virtual keycodes for keypad keys, since
237  *      each keypad key has the same character as the corresponding key on the
238  *      main keyboard.
239  *
240  * Results:
241  *	None.
242  *
243  * Side effects:
244  *	Initializes, if necessary, and updates the unichar2xvirtual hash table
245  *      and the xvirtual2unichar array.
246  *
247  *----------------------------------------------------------------------
248  */
249 
250 static void
UpdateKeymaps()251 UpdateKeymaps()
252 {
253     static Bool keymapInitialized = false;
254     Tcl_HashEntry *hPtr;
255     int virt, index, dummy;
256 
257     if (!keymapInitialized) {
258 	Tcl_InitHashTable(&unichar2xvirtual, TCL_ONE_WORD_KEYS);
259 	keymapInitialized = true;
260     } else {
261 	Tcl_DeleteHashTable(&unichar2xvirtual);
262 	Tcl_InitHashTable(&unichar2xvirtual, TCL_ONE_WORD_KEYS);
263     }
264     /*
265      * This loop goes backwards so that a lookup by keychar will provide the
266      * minimal modifier mask.  Simpler combinations will overwrite more complex
267      * ones when constructing the table.
268      */
269 
270     for (index = 3; index >= 0; index--) {
271         for (virt = 0; virt < 128; virt++) {
272 	    MacKeycode macKC;
273 	    macKC.v = (keycode_v) {.virt = virt, .o_s = index, .keychar = 0};
274 	    int modifiers = INDEX2CARBON(index), result;
275 	    UniChar keychar = 0;
276 	    result = KeyDataToUnicode(&keychar, 1, kUCKeyActionDown, virt,
277 				      modifiers, NULL);
278 	    if (keychar == 0x10) {
279 
280 		/*
281 		 * This is a special key, handled in InitHashTables.
282 		 */
283 
284 		continue;
285 	    }
286 	    macKC.v.keychar = keychar;
287 	    if (! ON_KEYPAD(virt)) {
288 		hPtr = Tcl_CreateHashEntry(&unichar2xvirtual,
289 					   INT2PTR(macKC.x.keychar), &dummy);
290 		Tcl_SetHashValue(hPtr, INT2PTR(macKC.x.xvirtual));
291             }
292 	    xvirtual2unichar[macKC.x.xvirtual] = macKC.x.keychar;
293 	}
294     }
295 }
296 
297 /*
298  *----------------------------------------------------------------------
299  *
300  * KeyDataToUnicode --
301  *
302  *	Given MacOS key event data this function generates the keychar.  It
303  *	does this by using OS resources from the Carbon framework.  Note that
304  *      the Carbon functions used here are not aware of the keychars in the
305  *      private-use range which macOS now uses for special keys.  For those
306  *      keys this function returns 0x10 (ASCII dle).
307  *
308  *	The parameter deadKeyStatePtr can be NULL, if no deadkey handling is
309  *	needed (which is always the case here).
310  *
311  *	This function is called in XKeycodeToKeysym and UpdateKeymaps.
312  *
313  * Results:
314  *	The number of characters generated if any, 0 if we are waiting for
315  *	another byte of a dead-key sequence.
316  *
317  * Side Effects:
318  *	 Fills in the uniChars array with a Unicode string.
319  *
320  *----------------------------------------------------------------------
321  */
322 
323 
324 static int
KeyDataToUnicode(UniChar * uniChars,int maxChars,UInt16 keyaction,UInt32 virt,UInt32 modifiers,UInt32 * deadKeyStatePtr)325 KeyDataToUnicode(
326     UniChar *uniChars,
327     int maxChars,
328     UInt16 keyaction,
329     UInt32 virt,
330     UInt32 modifiers,
331     UInt32 *deadKeyStatePtr)
332 {
333     static const void *layoutData = NULL;
334     static UInt32 keyboardType = 0;
335     UniCharCount actuallength = 0;
336 
337     if (keyboardChanged) {
338 	TISInputSourceRef currentKeyboardLayout =
339 		TISCopyCurrentKeyboardLayoutInputSource();
340 
341 	if (currentKeyboardLayout) {
342 	    CFDataRef keyLayoutData = (CFDataRef) TISGetInputSourceProperty(
343 		    currentKeyboardLayout, kTISPropertyUnicodeKeyLayoutData);
344 
345 	    if (keyLayoutData) {
346 		layoutData = CFDataGetBytePtr(keyLayoutData);
347 		keyboardType = LMGetKbdType();
348 	    }
349 	    CFRelease(currentKeyboardLayout);
350 	}
351 	keyboardChanged = 0;
352     }
353     if (layoutData) {
354 	OptionBits options = 0;
355 	UInt32 dummyState;
356 	OSStatus err;
357 
358 	virt &= 0xFF;
359 	modifiers = (modifiers >> 8) & 0xFF;
360 	if (!deadKeyStatePtr) {
361 	    options = kUCKeyTranslateNoDeadKeysMask;
362 	    dummyState = 0;
363 	    deadKeyStatePtr = &dummyState;
364 	}
365 	err = ChkErr(UCKeyTranslate, (const UCKeyboardLayout *)layoutData, virt, keyaction, modifiers,
366 		keyboardType, options, deadKeyStatePtr, maxChars,
367 		&actuallength, uniChars);
368 	if (!actuallength && *deadKeyStatePtr) {
369 
370 	    /*
371 	     * We are waiting for another key.
372 	     */
373 
374 	    return 0;
375 	}
376 	*deadKeyStatePtr = 0;
377 	if (err != noErr) {
378 	    actuallength = 0;
379 	}
380     }
381     return actuallength;
382 }
383 
384 /*
385  *----------------------------------------------------------------------
386  *
387  * XKeycodeToKeysym --
388  *
389  *	This is a stub function which translates from the keycode used in an
390  *      XEvent to a numerical keysym.  On macOS, the display parameter is
391  *      ignored and only the the virtual keycode stored in the .virtual bitfield
392  *      of a MacKeycode.v.
393  *
394  * Results:
395  *      Returns the corresponding numerical keysym, or NoSymbol if the keysym
396  *      cannot be found.
397  *
398  * Side effects:
399  *	None.
400  *
401  *----------------------------------------------------------------------
402  */
403 
404 KeySym
XkbKeycodeToKeysym(TCL_UNUSED (Display *),unsigned int keycode,TCL_UNUSED (int),int index)405 XkbKeycodeToKeysym(
406     TCL_UNUSED(Display *),
407     unsigned int keycode,
408     TCL_UNUSED(int),
409     int index)
410 {
411     Tcl_HashEntry *hPtr;
412     MacKeycode macKC;
413     int modifiers, result;
414     UniChar keychar = 0;
415 
416     if (!initialized) {
417 	InitHashTables();
418     }
419     macKC.uint = keycode;
420     macKC.v.o_s = index;
421 
422     /*
423      * First check if the virtual keycode corresponds to a special key, such as
424      * an Fn function key or Tab, Backspace, Home, End, etc.
425      */
426 
427     hPtr = Tcl_FindHashEntry(&special2keysym, INT2PTR(macKC.v.virt));
428     if (hPtr != NULL) {
429 	return (KeySym) Tcl_GetHashValue(hPtr);
430     }
431 
432     /*
433      * If the virtual value in this keycode does not correspond to an actual
434      * key in the current keyboard layout, try using its keychar to look up a
435      * keysym.
436      */
437 
438     if (macKC.v.virt > 127) {
439 	hPtr = Tcl_FindHashEntry(&unichar2keysym, INT2PTR(macKC.v.keychar));
440 	if (hPtr != NULL) {
441 	    return (KeySym) Tcl_GetHashValue(hPtr);
442 	}
443     }
444 
445     /*
446      * If the virtual keycode does belong to a key, use the virtual and the
447      * Option-Shift from index to look up a keychar by using the Carbon
448      * Framework; then translate the keychar to a keysym using the
449      * unicode2keysym hash table.
450      */
451 
452     modifiers = INDEX2CARBON(macKC.v.o_s);
453     result = KeyDataToUnicode(&keychar, 1, kUCKeyActionDown, macKC.v.virt,
454 			      modifiers, NULL);
455     if (result) {
456 	hPtr = Tcl_FindHashEntry(&unichar2keysym, INT2PTR(keychar));
457 	if (hPtr != NULL) {
458 	    return (KeySym) Tcl_GetHashValue(hPtr);
459 	}
460     }
461     return NoSymbol;
462 }
463 
464 KeySym
XKeycodeToKeysym(TCL_UNUSED (Display *),unsigned int keycode,int index)465 XKeycodeToKeysym(
466     TCL_UNUSED(Display *),
467     unsigned int keycode,
468     int index)
469 {
470     return XkbKeycodeToKeysym(NULL, keycode, 0, index);
471 }
472 
473 /*
474  *----------------------------------------------------------------------
475  *
476  * TkpGetString --
477  *
478  *	This is a stub function which retrieves the string stored in the
479  *      transchars field of an XEvent and converts it to a Tcl_DString.
480  *
481  * Results:
482  *	Returns a pointer to the string value of the DString.
483  *
484  * Side effects:
485  *	None.
486  *
487  *----------------------------------------------------------------------
488  */
489 
490 const char *
TkpGetString(TCL_UNUSED (TkWindow *),XEvent * eventPtr,Tcl_DString * dsPtr)491 TkpGetString(
492     TCL_UNUSED(TkWindow *),	/* Window where event occurred: Needed to get
493 				 * input context. */
494     XEvent *eventPtr,		/* X keyboard event. */
495     Tcl_DString *dsPtr)		/* Uninitialized or empty string to hold
496 				 * result. */
497 {
498     MacKeycode macKC;
499     char utfChars[8];
500     int length = 0;
501 
502     macKC.uint = eventPtr->xkey.keycode;
503     if (IS_PRINTABLE(macKC.v.keychar)) {
504 	length = TkUniCharToUtf(macKC.v.keychar, utfChars);
505     }
506     utfChars[length] = 0;
507 
508     Tcl_DStringInit(dsPtr);
509     return Tcl_DStringAppend(dsPtr, utfChars, length);
510 }
511 
512 /*
513  *----------------------------------------------------------------------
514  *
515  * XGetModifierMapping --
516  *
517  *	X11 stub function to get the keycodes used as modifiers.  This
518  *      is never called by the macOS port.
519  *
520  * Results:
521  *	Returns a newly allocated modifier map.
522  *
523  * Side effects:
524  *	Allocates a new modifier map data structure.
525  *
526  *----------------------------------------------------------------------
527  */
528 
529 XModifierKeymap *
XGetModifierMapping(TCL_UNUSED (Display *))530 XGetModifierMapping(
531     TCL_UNUSED(Display *))
532 {
533     XModifierKeymap *modmap;
534 
535     modmap = (XModifierKeymap *)ckalloc(sizeof(XModifierKeymap));
536     modmap->max_keypermod = 0;
537     modmap->modifiermap = NULL;
538     return modmap;
539 }
540 
541 /*
542  *----------------------------------------------------------------------
543  *
544  * XFreeModifiermap --
545  *
546  *	Deallocates a modifier map that was created by XGetModifierMapping.
547  *      This is also never called by the macOS port.
548  *
549  * Results:
550  *	None.
551  *
552  * Side effects:
553  *	Frees the datastructure referenced by modmap.
554  *
555  *----------------------------------------------------------------------
556  */
557 
558 int
XFreeModifiermap(XModifierKeymap * modmap)559 XFreeModifiermap(
560     XModifierKeymap *modmap)
561 {
562     if (modmap->modifiermap != NULL) {
563 	ckfree(modmap->modifiermap);
564     }
565     ckfree(modmap);
566     return Success;
567 }
568 
569 /*
570  *----------------------------------------------------------------------
571  *
572  * XKeysymToString, XStringToKeysym --
573  *
574  *	These X11 stub functions map keysyms to strings & strings to keysyms.
575  *      A platform can do its own conversion by defining these and undefining
576  *      REDO_KEYSYM_LOOKUP.  The macOS port defines REDO_KEYSYM_LOOKUP so these
577  *      are never called and Tk does the conversion for us.
578  *
579  * Results:
580  *	None.
581  *
582  * Side effects:
583  *	None.
584  *
585  *----------------------------------------------------------------------
586  */
587 
588 char *
XKeysymToString(TCL_UNUSED (KeySym))589 XKeysymToString(
590     TCL_UNUSED(KeySym))
591 {
592     return NULL;
593 }
594 
595 KeySym
XStringToKeysym(TCL_UNUSED (const char *))596 XStringToKeysym(
597     TCL_UNUSED(const char *))
598 {
599     return NoSymbol;
600 }
601 
602 /*
603  *----------------------------------------------------------------------
604  *
605  * XKeysymToKeycode --
606  *
607  *	This is a stub function which converts a numerical keysym to the
608  *      platform-specific keycode used in a KeyPress or KeyRelease XEvent.
609  *      For macOS the keycode is an unsigned int with bitfields described
610  *      in the definition of the MacKeycode type.
611  *
612  * Results:
613  *
614  *      A macOS KeyCode. See the description of keycodes at the top of this
615  *	file and the definition of the MacKeycode type in tkMacOSXPrivate.h.
616  *
617  * Side effects:
618  *	None.
619  *
620  *----------------------------------------------------------------------
621  */
622 
623 KeyCode
XKeysymToKeycode(TCL_UNUSED (Display *),KeySym keysym)624 XKeysymToKeycode(
625     TCL_UNUSED(Display *),
626     KeySym keysym)
627 {
628     Tcl_HashEntry *hPtr;
629     MacKeycode macKC;
630     if (!initialized) {
631 	InitHashTables();
632     }
633 
634     /*
635      * First check for a special key.
636      */
637 
638     hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym));
639     if (hPtr != NULL) {
640 	return (KeyCode) PTR2INT(Tcl_GetHashValue(hPtr));
641     }
642 
643     /*
644      * Initialize the keycode as if the keysym cannot be converted to anything
645      * else.
646      */
647 
648     macKC.v.virt = NO_VIRTUAL;
649     macKC.v.o_s = 0;
650     macKC.v.keychar = 0;
651 
652     /*
653      * If the keysym is recognized fill in the keychar.  Also fill in the
654      * xvirtual field if the key exists on the current keyboard.
655      */
656 
657     hPtr = (Tcl_HashEntry *) Tcl_FindHashEntry(&keysym2unichar,
658 					       INT2PTR(keysym));
659     if (hPtr != NULL) {
660 	unsigned long data = (unsigned long) Tcl_GetHashValue(hPtr);
661 	macKC.x.keychar = (unsigned int) data;
662 	hPtr = Tcl_FindHashEntry(&unichar2xvirtual, INT2PTR(macKC.x.keychar));
663 	if (hPtr != NULL) {
664 	    data = (unsigned long) Tcl_GetHashValue(hPtr);
665 	    macKC.x.xvirtual = (unsigned int) data;
666 	}
667     }
668     return (KeyCode) macKC.uint;
669 }
670 
671 /*
672  *----------------------------------------------------------------------
673  *
674  * TkpSetKeycodeAndState --
675  *
676  *	This function accepts a keysym and an XEvent and sets some fields of
677  *	the XEvent.  It is used by the event generate command.
678  *
679  * Results:
680  *      None
681  *
682  * Side effects:
683  *
684  *	Modifies the XEvent. Sets the xkey.keycode to a keycode value formatted
685  *	by XKeysymToKeycode and updates the shift and option flags in
686  *	xkey.state if either of those modifiers is required to generate the
687  *	keysym.
688  *
689  *----------------------------------------------------------------------
690  */
691 void
TkpSetKeycodeAndState(TCL_UNUSED (Tk_Window),KeySym keysym,XEvent * eventPtr)692 TkpSetKeycodeAndState(
693     TCL_UNUSED(Tk_Window),
694     KeySym keysym,
695     XEvent *eventPtr)
696 {
697     if (keysym == NoSymbol) {
698 	eventPtr->xkey.keycode = 0;
699     } else {
700 	int eventIndex = STATE2INDEX(eventPtr->xkey.state);
701 	MacKeycode macKC;
702 	macKC.uint = XKeysymToKeycode(NULL, keysym);
703 
704 	/*
705 	 * We have a virtual keycode and a minimal choice for Shift and Option
706 	 * modifiers which generates the keychar that corresponds to the
707 	 * specified keysym.  But we might not have the correct keychar yet,
708 	 * because the xEvent may have specified modifiers beyond our minimal
709 	 * set.  For example, the events described by <Oslash>, <Shift-oslash>,
710 	 * <Shift-Option-O> and <Shift-Option-o> should all produce the same
711 	 * uppercase Danish O.  So we may need to add the extra modifiers and
712 	 * do another lookup for the keychar.  We don't want to do this for
713 	 * special keys, however.
714 	 */
715 
716 	if (macKC.v.o_s != eventIndex) {
717 	    macKC.v.o_s |= eventIndex;
718 	}
719 	if (macKC.v.keychar < 0xF700) {
720 	    UniChar keychar = macKC.v.keychar;
721 	    NSString *str, *lower, *upper;
722 	    if (macKC.v.virt != NO_VIRTUAL) {
723 		macKC.x.keychar = xvirtual2unichar[macKC.x.xvirtual];
724 	    } else {
725 		str = [[NSString alloc] initWithCharacters:&keychar length:1];
726 		lower = [str lowercaseString];
727 		upper = [str uppercaseString];
728 		if (![str isEqual: lower]) {
729 		    macKC.v.o_s |= INDEX_SHIFT;
730 		}
731 		if (macKC.v.o_s & INDEX_SHIFT) {
732 		    macKC.v.keychar = [upper characterAtIndex:0];
733 		}
734 	    }
735 	}
736 	eventPtr->xkey.keycode = macKC.uint;
737 	eventPtr->xkey.state |= INDEX2STATE(macKC.v.o_s);
738     }
739 }
740 
741 /*
742  *----------------------------------------------------------------------
743  *
744  * TkpGetKeySym --
745  *
746  *	This is a stub function called in tkBind.c.  Given a KeyPress or
747  *	KeyRelease XEvent, it maps the keycode in the event to a numerical
748  *      keysym.
749  *
750  * Results:
751  *	The return value is the keysym corresponding to eventPtr, or NoSymbol
752  *	if no matching keysym could be found.
753  *
754  * Side effects:
755  *	In the first call for a given display, calls TkpInitKeymapInfo.
756  *
757  *
758  *----------------------------------------------------------------------
759  */
760 
761 KeySym
TkpGetKeySym(TkDisplay * dispPtr,XEvent * eventPtr)762 TkpGetKeySym(
763     TkDisplay *dispPtr,		/* Display in which to map keycode. */
764     XEvent *eventPtr)		/* Description of X event. */
765 {
766     KeySym sym;
767     int index;
768     MacKeycode macKC;
769     macKC.uint = eventPtr->xkey.keycode;
770 
771     /*
772      * Refresh the mapping information if it's stale.
773      */
774 
775     if (dispPtr->bindInfoStale) {
776 	TkpInitKeymapInfo(dispPtr);
777     }
778 
779     /*
780      * Modifier key events have a special mac keycode (see tkProcessKeyEvent).
781      */
782 
783     if (macKC.v.keychar == MOD_KEYCHAR) {
784 	switch (macKC.v.virt) {
785 	case 54:
786 	    return XK_Meta_R;
787 	case 55:
788 	    return XK_Meta_L;
789 	case 56:
790 	    return XK_Shift_L;
791 	case 57:
792 	    return XK_Caps_Lock;
793 	case 58:
794 	    return XK_Alt_L;
795 	case 59:
796 	    return XK_Control_L;
797 	case 60:
798 	    return XK_Shift_R;
799 	case 61:
800 	    return XK_Alt_R;
801 	case 62:
802 	    return XK_Control_R;
803 	case 63:
804 	    return XK_Super_L;
805 	default:
806 	    return NoSymbol;
807 	}
808     }
809 
810     /*
811      * Figure out which of the four slots in the keymap vector to use for this
812      * key. Refer to Xlib documentation for more info on how this computation
813      * works.
814      */
815 
816     index = STATE2INDEX(eventPtr->xkey.state);
817     if (eventPtr->xkey.state & LockMask) {
818 	index |= INDEX_SHIFT;
819     }
820 
821     /*
822      * First do the straightforward lookup.
823      */
824 
825     sym = XkbKeycodeToKeysym(dispPtr->display, macKC.uint, 0, index);
826 
827     /*
828      * Special handling: If the key was shifted because of Lock, which is only
829      * caps lock on macOS, not shift lock, and if the shifted keysym isn't
830      * upper-case alphabetic, then switch back to the unshifted keysym.
831      */
832 
833     if ((index & INDEX_SHIFT) && !(eventPtr->xkey.state & ShiftMask)) {
834 	if ((sym == NoSymbol) || !Tcl_UniCharIsUpper(sym)) {
835 	    sym = XkbKeycodeToKeysym(dispPtr->display, macKC.uint, 0,
836 				   index & ~INDEX_SHIFT);
837 	}
838     }
839 
840     /*
841      * Another bit of special handling: If this is a shifted key and there is
842      * no keysym defined, then use the keysym for the unshifted key.
843      */
844 
845     if ((index & INDEX_SHIFT) && (sym == NoSymbol)) {
846 	sym = XkbKeycodeToKeysym(dispPtr->display, macKC.uint, 0,
847 			       index & ~INDEX_SHIFT);
848     }
849     return sym;
850 }
851 
852 /*
853  *--------------------------------------------------------------
854  *
855  * TkpInitKeymapInfo --
856  *
857  *	This procedure initializes fields in the display that pertain
858  *      to modifier keys.
859  *
860  * Results:
861  *	None.
862  *
863  * Side effects:
864  *	Modifier key information in dispPtr is initialized.
865  *
866  *--------------------------------------------------------------
867  */
868 
869 void
TkpInitKeymapInfo(TkDisplay * dispPtr)870 TkpInitKeymapInfo(
871     TkDisplay *dispPtr)		/* Display for which to recompute keymap
872 				 * information. */
873 {
874     dispPtr->bindInfoStale = 0;
875 
876     /*
877      * On macOS the caps lock key is always interpreted to mean that alphabetic
878      * keys become uppercase but other keys do not get shifted.  (X11 allows
879      * a configuration option which makes the caps lock equivalent to holding
880      * down the shift key.)
881      * There is no offical "Mode_switch" key.
882      */
883 
884     dispPtr->lockUsage = LU_CAPS;
885 
886     /* This field is no longer used by tkBind.c */
887 
888     dispPtr->modeModMask = 0;
889 
890     /* The Alt and Meta keys are interchanged on Macintosh keyboards compared
891      * to PC keyboards.  These fields could be set to make the Alt key on a PC
892      * keyboard behave likd an Alt key. That would also require interchanging
893      * Mod1Mask and Mod2Mask in tkMacOSXKeyEvent.c.
894      */
895 
896     dispPtr->altModMask = 0;
897     dispPtr->metaModMask = 0;
898 
899     /*
900      * The modKeyCodes table lists the keycodes that appear in KeyPress or
901      * KeyRelease XEvents for modifier keys.  In tkBind.c this table is
902      * searched to determine whether an XEvent corresponds to a modifier key.
903      */
904 
905     if (dispPtr->modKeyCodes != NULL) {
906 	ckfree(dispPtr->modKeyCodes);
907     }
908     dispPtr->numModKeyCodes = NUM_MOD_KEYCODES;
909     dispPtr->modKeyCodes = (KeyCode *)ckalloc(NUM_MOD_KEYCODES * sizeof(KeyCode));
910     for (int i = 0; i < NUM_MOD_KEYCODES; i++) {
911 	dispPtr->modKeyCodes[i] = XKeysymToKeycode(NULL, modKeyArray[i]);
912     }
913 }
914 
915 /*
916  *--------------------------------------------------------------
917  *
918  * TkMacOSXAddVirtual --
919  *
920  *	This procedure is an internal utility which accepts an unsigned int
921  *      that has been partially filled as a MacKeycode, having the Option and
922  *      Shift state set in the o_s field and the keychar field set but with the
923  *      virtual keycode blank.  It looks up the virtual keycode for the keychar
924  *      (possibly NO_VIRTUAL) and returns an unsigned int which is a complete
925  *      MacKeycode with the looked up virtual keycode added.  This is used when
926  *      creating XEvents for the unicode characters which are generated by the
927  *      NSTextInputClient.
928  *
929  * Results:
930  *      An unsigned int which is a complete MacKeycode, including a virtual
931  *	keycode which matches the Option-Shift state and keychar.
932  *
933  * Side effects:
934  *	None
935  *
936  *--------------------------------------------------------------
937  */
938 unsigned
TkMacOSXAddVirtual(unsigned int keycode)939 TkMacOSXAddVirtual(
940     unsigned int keycode)
941 {
942     MacKeycode macKC;
943     Tcl_HashEntry *hPtr;
944     macKC.uint = keycode;
945 
946     if (!initialized) {
947 	InitHashTables();
948     }
949 
950     hPtr = (Tcl_HashEntry *) Tcl_FindHashEntry(&unichar2xvirtual,
951 					       INT2PTR(macKC.v.keychar));
952     if (hPtr != NULL) {
953 	unsigned long data = (unsigned long) Tcl_GetHashValue(hPtr);
954 	macKC.x.xvirtual = (unsigned int) data;
955     } else {
956 	macKC.v.virt = NO_VIRTUAL;
957     }
958     return macKC.uint;
959 }
960 /*
961  * Local Variables:
962  * mode: objc
963  * c-basic-offset: 4
964  * fill-column: 79
965  * coding: utf-8
966  * End:
967  */
968