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