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