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