summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
authorculler <culler>2020-04-26 22:12:52 (GMT)
committerculler <culler>2020-04-26 22:12:52 (GMT)
commit022c2fd5dc1cc83cc6606d7e83495af76e8bdbd8 (patch)
tree3afc10a28432b557577fc78dd3fb4e97d2eeb302 /macosx
parent551859ab46ad0d92f0097687308150befab95217 (diff)
downloadtk-022c2fd5dc1cc83cc6606d7e83495af76e8bdbd8.zip
tk-022c2fd5dc1cc83cc6606d7e83495af76e8bdbd8.tar.gz
tk-022c2fd5dc1cc83cc6606d7e83495af76e8bdbd8.tar.bz2
Tidy up; expand comments; clarify NSTextInputClient logic; deal with keyPad buttons.
Diffstat (limited to 'macosx')
-rw-r--r--macosx/tkMacOSXKeyEvent.c76
-rw-r--r--macosx/tkMacOSXKeyboard.c221
2 files changed, 182 insertions, 115 deletions
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c
index 109c774..fe71ea9 100644
--- a/macosx/tkMacOSXKeyEvent.c
+++ b/macosx/tkMacOSXKeyEvent.c
@@ -17,6 +17,7 @@
#include "tkMacOSXInt.h"
#include "tkMacOSXConstants.h"
#include "tkMacOSXWm.h"
+#define IS_PRINTABLE(keychar) ((keychar >= 0x20) && (keychar < 0xF700))
/*
#ifdef TK_MAC_DEBUG
@@ -54,7 +55,6 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w);
XEvent xEvent;
UniChar keychar = 0;
Bool can_input_text, has_modifiers = NO, use_text_input = NO;
- int length = 0;
static NSUInteger savedModifiers = 0;
static NSMutableArray *nsEvArray = nil;
@@ -117,64 +117,63 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w);
has_modifiers = xEvent.xkey.state & XEVENT_MOD_MASK;
can_input_text = ((focusWinPtr->flags & TK_CAN_INPUT_TEXT) != 0);
- /*
- * A KeyDown event targeting the caret window and having an alphanumeric
- * keychar should be processed by our TextInputClient. The XEvent will not
- * be sent in this case.
- */
-
#if (NS_KEYLOG)
TKLog(@"keyDown: %s compose sequence.\n",
processingCompose == YES ? "Continue" : "Begin");
#endif
+ /*
+ * Decide whether this event should be processed with the NSTextInputClient
+ * protocol.
+ */
+
if (processingCompose ||
(type == NSKeyDown && can_input_text && !has_modifiers &&
- (keychar >= 0x0020) && (keychar < 0xF700))
+ IS_PRINTABLE(keychar))
) {
use_text_input = YES;
}
+
+ /*
+ * If we are processing this KeyDown event as an NSTextInputClient we do
+ * not queue an XEvent. We pass the NSEvent to our interpretKeyEvents
+ * method. When the composition sequence is complete, the callback method
+ * insertText: replacementRange will be called. That method generates a
+ * keyPress XEvent with the selected character.
+ */
+
if (use_text_input) {
/*
- * Call our interpretKeyEvents method to handle the event as an
- * NSTextInputClient. When the composition sequence is complete, our
- * implementation of insertText: replacementRange will be called. That
- * method generates a keyPress XEvent with the selected character.
+ * In IME the Enter key is used to terminate a composition sequence.
+ * when there are multiple choices of input text available, and the
+ * user's selected choice is not the default it may be necessary to hit
+ * the Enter key multiple times before the text is accepted and
+ * rendered (See ticket 39de9677aa]). So when sending an Enter key
+ * during composition, we continue sending Enter keys until the
+ * inputText method has cleared the processingCompose flag.
*/
- if (!processingCompose) {
+ if (processingCompose && [theEvent keyCode] == 36) {
[nsEvArray addObject: theEvent];
- [[w contentView] interpretKeyEvents: nsEvArray];
- [nsEvArray removeObject: theEvent];
- } else {
-
- /*
- * In IME when there are multiple choices with the same composition
- * sequence and the selected choice is not the default it may be
- * necessary to hit the Enter key twice before the character is
- * accepted and rendered (See ticket 39de9677aa]). So when sending
- * an Enter key, we continue sending Enter keys until the inputText
- * method has cleared the processingCompose flag.
- */
-
while(processingCompose) {
- [nsEvArray addObject: theEvent];
[[w contentView] interpretKeyEvents: nsEvArray];
- [nsEvArray removeObject: theEvent];
- if ([theEvent keyCode] != 36) {
- break;
- }
}
+ [nsEvArray removeObject: theEvent];
+ } else {
+ [nsEvArray addObject: theEvent];
+ [[w contentView] interpretKeyEvents: nsEvArray];
+ [nsEvArray removeObject: theEvent];
}
return theEvent;
}
/*
- * We need to send an XEvent. Finish constructing it.
+ * We are not handling this event as an NSTextInputClient, so we need to
+ * finish constructing the XEvent and queue it.
*/
- xEvent.xkey.keycode = (virtual << 16) | keychar;
+ xEvent.xkey.keycode = (virtual << 24) | keychar;
switch (type) {
case NSFlagsChanged:
@@ -209,15 +208,14 @@ static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w);
/*
* Set the trans_chars for keychars outside of the private-use range.
*/
-
+
setXEventPoint(&xEvent, tkwin, w);
- if ((keychar >= 0x20) && (keychar < 0xF700)) {
- length = TkUniCharToUtf(keychar, xEvent.xkey.trans_chars);
+ if (IS_PRINTABLE(keychar)) {
+ xEvent.xkey.nbytes = 0; /* This string is null-terminated. */
+ int length = TkUniCharToUtf(keychar, xEvent.xkey.trans_chars);
xEvent.xkey.trans_chars[length] = 0;
- } else {
- xEvent.xkey.nbytes = 0;
}
-
+
/*
* Finally we can queue the XEvent, inserting a KeyRelease before a
* repeated KeyPress.
diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c
index 00b8715..0d023ff 100644
--- a/macosx/tkMacOSXKeyboard.c
+++ b/macosx/tkMacOSXKeyboard.c
@@ -16,37 +16,107 @@
#include "tkMacOSXConstants.h"
#include "tkMacOSXKeysyms.h"
+#define IS_PRINTABLE(keychar) ((keychar >= 0x20) && (keychar < 0xF700))
+#define ON_KEYPAD(virtual) ((virtual >= 0x41) && (virtual <= 0x5C))
+#define VIRTUAL_MAX 0x7F
+#define MAC_KEYCHAR_MASK 0xFFFF
+
/*
- * When converting native key events to X11 key events, each platform is
- * allowed to choose what information to encode in the XEvent.xkey.keycode
- * field. On the Macintosh, every non-modifier key has a unicode character
- * associated to it. For non-text keys this unicode character is in the
- * private use range 0xF700 - 0xF8FF. Modifier keys, however, do not produce
- * KeyDown or KeyUp events, rather FlagsChanged events, so they do not have an
- * associated unicode character.
- *
- * When constructing an XEvent from a KeyDown or KeyUo NSEvent, the
- * XEvent.xkey.keycode field is constructed by using bits 0-15 to hold the
- * first unicode character of the [NSEvent characters] attribute of the
- * NSEvent, and bits 16-23 to hold the value of the [NSEvent keyCode]
- * attribute. The keyCode attribute identifies a location on the keyboard,
- * and Apple calls it a "virtual keycode". It is allowed for the characters
- * attribute to have length greater than 1, but that does not appear to happen
- * for any known keyboards.
- *
- * When generating an XEvent with the event generate command, the unicode
- * character is determined from the X11 keysym and, if that unicode character
- * belongs to a key on the current keyboard layout bits 16-23 are set to the
- * virtual keycode of that key. Otherwise they are cleared.
- *
- * KeyPress or KeyRelease XEvents are also constructed when a FlagsChanged
- * NSEvent is processed. For these, the unicode character is set to 0xF8FF,
- * the last value of the private use range.
+ * About keyboards
+
+ * Keyboards are complicated. This long comment is an attempt to provide
+ * enough information about them to make it possible to read and understand
+ * the code in this file.
+ *
+ * Every key on a keyboard is identified by a number between 0 and 127. In
+ * macOS, pressing or releasing a key on the keyboard generates an NSEvent of
+ * type KeyDown, KeyUp or FlagsChanged. The 8-bit identifier of the key that
+ * was involved in this event is provided in the attribute [NSEvent keyCode].
+ * Apple also refers to this number as a "Virtual KeyCode". In this file, to
+ * avoid confusion with other uses of the word keycode, we will refer to this
+ * key identifier as a "virtual keycode", usually the value of a variable named
+ * "virtual".
+ *
+ * Some of the keys on a keyboard are "modifier" keys. The effect of
+ * pressing or releasing a key depends on three quantities:
+ * - which key is being pressed or released
+ * - which modifier keys are being held down at the moment
+ * - the current keyboard layout
+ * If the key is a modifier key then the effect of pressing or releasing it is
+ * only to change the list of which modifier keys are being held down. Apple
+ * reports this by sending an NSEvent of type FlagsChanged. X11 reports this
+ * as a KeyPress or KeyRelease event for the modifier key. Note that there may
+ * be combinations of modifier key states and key presses which have no effect.
+ *
+ * In X11 every meaningful effect from a key action is identified by a 16 bit
+ * value known as a keysym. Every keysym has an associated string name, also
+ * known as a keysym. The Tk bind command uses the X11 keysym string to
+ * specify a key event which should invoke a certain action and it provides the
+ * numeric and symbolic keysyms to the bound proc as %K and %N respectively.
+ * An X11 XEvent which reports a KeyPress or KeyRelease does not include the
+ * keysym. Instead it includes a platform-specific numerical value called a
+ * keycode which is available to the bound procedure as %k. A platform port of
+ * Tk must provide functions which convert between keycodes and numerical
+ * keysyms. Conversion between numerical and symbolic keysyms is provided by
+ * the generic Tk code, although platforms are allowed to provide their own by
+ * defining the XKeysymToString and XStringToKeysym functions and undefining
+ * the macro REDO_KEYSYM_LOOKUP. This macOS port uses the conversion provided
+ * by the generic code.
+ *
+ * When the keyboard focus is on a Tk widget which provides text input, there
+ * are some X11 KeyPress events which cause text to be inserted. We will call
+ * these "printable" events. The text which should be inserted is contained in
+ * the xkeys.trans_chars field of a key XEvent as a unicode string encoded with
+ * a special Tcl encoding. The string is assumed to be null-terminated if the
+ * nbytes field of the XEvent is non-zero. Otherwise the length is given by
+ * the value of nbytes. The value of the trans_chars string in an Xevent
+ * depends on more than the three items above. It may also depend on the
+ * sequence of keypresses that preceded the one being reported by the XEvent.
+ * For example, on macOS an <Alt-e> event does not cause text to be inserted
+ * but a following <a> event causes an accented a to be inserted. The events
+ * in such a composition sequence, other than the final one, are known as
+ * "dead-key" events.
+ *
+ * MacOS packages the information described above in a different way. Every
+ * meaningful effect from a key action *other than changing the state of
+ * modifier keys* is identified by a unicode string which is provided as the
+ * [NSEvent characters] attribute of a KeyDown or KeyUp event. FlagsChanged
+ * events do not have characters. In principle, the characters attribute could
+ * be an arbitrary unicode string but in practice it is always a single UTF-16
+ * character which we usually store in a variable named keychar. While the
+ * keychar is a legal unicode code point, it does not necessarily represent a
+ * glyph. MacOS uses unicode code points in the private-use range 0xF700-0xF8FF
+ * for non-printable events which have no associated ASCII code point. For
+ * example, pressing the F2 key generates an NSEvent with the character 0xF705,
+ * the Backspace key produces 0x7F (ASCII del) and the Delete key produces
+ * 0xF728.
+ *
+ * With the exception of modifier keys, it is possible to translate between
+ * numerical X11 keysyms and macOS keychars; this file constructs Tcl hash
+ * tables to do this job, using data defined in the file tkMacOSXKeysyms.h.
+ * The code here adopts the convention that the keychar of any modifier key
+ * is 0xF8FF, the last value in the private-use range.
+ *
+ * The macosx platform-specific scheme for generating a keycode when mapping an
+ * NSEvent of type KeyUp, KeyDown or FlagsChanged to an XEvent of type KeyPress
+ * or KeyRelease is as follows:
+ * keycode = (virtual << 24) | keychar
+ * A few remarks are in order. First, we are using 32 bits for the keycode and
+ * we are allowing room for up to 24 bits for the keychar. This means that
+ * there is enough room in the keycode to hold a UTF-32 character, which only
+ * requires 21 bits. So, in the future, when macs have emoji keyboards, no
+ * change will be needed in how keycodes are generated. Second, the KeyCode
+ * type for the keycode field in an XEvent is currently defined as unsigned
+ * long, which means that it is 64 bits on modern macOS systems. Finally, there
+ * is no obstruction to generating KeyPress events for keys that represent
+ * letters which do not exist on the current keyboard layout. And different
+ * keyboard layouts can assign a given letter to different keys. So we need a
+ * convention for what value to assign to "virtual" when computing the keycode
+ * for a generated event. The convention used here is as follows:
+ * If there is a key on the current keyboard which produces the keychar,
+ * use the virtual keycode of that key. Otherwise set virtual = 0.
*/
-#define VIRTUAL_MAX 0x7F
-#define MAC_KEYCODE_MASK 0xFF
-
/*
* Hash tables used to translate between various key attributes.
*/
@@ -70,8 +140,8 @@ static BOOL keyboardChanged = YES;
static void InitHashTables(void);
static void UpdateKeymap(void);
-static int KeyDataToUnicode(UniChar * uniChars, int maxChars,
- UInt16 keyaction, UInt32 keycode, UInt32 modifiers,
+static int KeyDataToUnicode(UniChar *uniChars, int maxChars,
+ UInt16 keyaction, UInt32 virtual, UInt32 modifiers,
UInt32 * deadKeyStatePtr);
#pragma mark TKApplication(TKKeyboard)
@@ -121,7 +191,7 @@ InitHashTables(void)
Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keysym));
hPtr = Tcl_CreateHashEntry(&keysym2keycode, INT2PTR(kPtr->keysym),
&dummy);
- Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keychar | (kPtr->virtual << 16)));
+ Tcl_SetHashValue(hPtr, INT2PTR(kPtr->keychar | (kPtr->virtual << 24)));
}
Tcl_InitHashTable(&keysym2unichar, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(&unichar2keysym, TCL_ONE_WORD_KEYS);
@@ -143,7 +213,9 @@ InitHashTables(void)
* UpdateKeymap --
*
* Called when the keyboard changes to update the hash table that
- * maps unicode characters to virtual keycodes.
+ * maps unicode characters to virtual keycodes with states. In order
+ * for this to be well-defined we have to ignore virtual keycodes for
+ * keypad keys.
*
* Results:
* None.
@@ -158,7 +230,7 @@ static void
UpdateKeymap()
{
static int keymapInitialized = 0;
- UniChar keyChar = 0;
+ UniChar keychar;
Tcl_HashEntry *hPtr;
int virtual, state, modifiers, dummy;
@@ -167,31 +239,34 @@ UpdateKeymap()
} else {
Tcl_DeleteHashTable(&unichar2virtual);
}
- for (state = 3; state >= 0; state--) {
+ for (state = 4; state >= 0; state--) {
for (virtual = 0; virtual <= VIRTUAL_MAX; virtual++) {
- modifiers = (state & 1 ? shiftKey : 0) | (state & 2 ? optionKey : 0);
- KeyDataToUnicode(&keyChar, 1, kUCKeyActionDown, virtual, modifiers, NULL);
- hPtr = Tcl_CreateHashEntry(&unichar2virtual, INT2PTR(keyChar),
+ if (ON_KEYPAD(virtual)) {
+ continue;
+ }
+ modifiers = (state & 1 ? shiftKey : 0) |
+ (state & 2 ? optionKey : 0);
+ KeyDataToUnicode(&keychar, 1, kUCKeyActionDown, virtual, modifiers,
+ NULL);
+ hPtr = Tcl_CreateHashEntry(&unichar2virtual, INT2PTR(keychar),
&dummy);
Tcl_SetHashValue(hPtr, INT2PTR(state << 8 | virtual));
}
}
}
-
/*
*----------------------------------------------------------------------
*
* KeyDataToUnicode --
*
- * Given MacOS key event data this function generates the unicode
- * characters. It does this using OS resources from the Carbon
- * framework.
+ * Given MacOS key event data this function generates the keychar. It
+ * does this by using OS resources from the Carbon framework.
*
* The parameter deadKeyStatePtr can be NULL, if no deadkey handling is
- * needed.
+ * needed (which is always the case here).
*
- * This function is called from XKeycodeToKeysym() in tkMacOSKeyboard.c.
+ * This function is called from XKeycodeToKeysym().
*
* Results:
* The number of characters generated if any, 0 if we are waiting for
@@ -209,11 +284,11 @@ KeyDataToUnicode(
UniChar *uniChars,
int maxChars,
UInt16 keyaction,
- UInt32 keycode,
+ UInt32 virtual,
UInt32 modifiers,
UInt32 *deadKeyStatePtr)
{
- static const void *uchr = NULL;
+ static const void *layoutData = NULL;
static UInt32 keyboardType = 0;
UniCharCount actuallength = 0;
@@ -226,26 +301,26 @@ KeyDataToUnicode(
currentKeyboardLayout, kTISPropertyUnicodeKeyLayoutData);
if (keyLayoutData) {
- uchr = CFDataGetBytePtr(keyLayoutData);
+ layoutData = CFDataGetBytePtr(keyLayoutData);
keyboardType = LMGetKbdType();
}
CFRelease(currentKeyboardLayout);
}
keyboardChanged = 0;
}
- if (uchr) {
+ if (layoutData) {
OptionBits options = 0;
UInt32 dummyState;
OSStatus err;
- keycode &= 0xFFFF;
+ virtual &= MAC_KEYCHAR_MASK;
modifiers = (modifiers >> 8) & 0xFF;
if (!deadKeyStatePtr) {
options = kUCKeyTranslateNoDeadKeysMask;
dummyState = 0;
deadKeyStatePtr = &dummyState;
}
- err = ChkErr(UCKeyTranslate, uchr, keycode, keyaction, modifiers,
+ err = ChkErr(UCKeyTranslate, layoutData, virtual, keyaction, modifiers,
keyboardType, options, deadKeyStatePtr, maxChars,
&actuallength, uniChars);
if (!actuallength && *deadKeyStatePtr) {
@@ -269,9 +344,9 @@ KeyDataToUnicode(
*
* XKeycodeToKeysym --
*
- * Stub function which translate from the platform-specific keycode used
+ * This is a stub function which translates from the keycode used
* in an XEvent to an X11 keysym. On the Macintosh, the display input
- * is ignored and only the virtual keycode in bits 16-23 is used.
+ * is ignored and only the virtual keycode in bits 24-31 is used.
*
* Results:
* Returns the corresponding keysym, or NoSymbol if the keysym cannot
@@ -304,7 +379,7 @@ XKeycodeToKeysym(
* an Fn function key or Tab, Backspace, Home, End, etc.
*/
- virtual = (keycode >> 16) & 0xFF;
+ virtual = (keycode >> 24) & MAC_KEYCHAR_MASK;
if (virtual) {
hPtr = Tcl_FindHashEntry(&virtual2keysym, INT2PTR(virtual));
if (hPtr != NULL) {
@@ -313,8 +388,8 @@ XKeycodeToKeysym(
}
/*
- * If not, use Carbon to find the unicode character and translate it
- * to a keysym using a hash table.
+ * If not, use the Carbon Framework to find the unicode character and
+ * translate it to a keysym using the unicode2keysym hash table.
*/
modifiers = (state & 1 ? shiftKey : 0) | (state & 2 ? optionKey : 0);
@@ -427,7 +502,7 @@ XFreeModifiermap(
* XKeysymToString, XStringToKeysym --
*
* These X window functions map keysyms to strings & strings to keysyms.
- * These are never called because we define REDO_KEYSYM_LOOKUP, which
+ * They are never called because we define REDO_KEYSYM_LOOKUP, which
* instructs tkBind to do the conversion for us.
*
* Results:
@@ -458,15 +533,14 @@ XStringToKeysym(
*
* XKeysymToKeycode --
*
- * Converts an XKeysym to the value which would be used as the keycode
- * field of a KeyPress or KeyRelease XEvent for the corresponding key.
- * This is an opaque stub function which knows how the keycode field is
- * generated on a Mac.
+ * This is a stub function which onverts a numerical keysym to the keycode
+ * which should be used as the in a KeyPress or KeyRelease XEvent for the
+ * corresponding key.
*
* Results:
*
* An X11 KeyCode with a unicode character in the low 16 bits and the
- * 8-bit "virtual keycode" in the third byte. See the description of
+ * 8-bit "virtual keycode" in the highest byte. See the description of
* keycodes on the Macintosh at the top of this file.
*
* Side effects:
@@ -474,7 +548,7 @@ XStringToKeysym(
*
*----------------------------------------------------------------------
*/
-KeyCode
+static KeyCode
XKeysymToKeycodeWithState(
Display *display,
KeySym keysym,
@@ -485,6 +559,7 @@ XKeysymToKeycodeWithState(
if (!initialized) {
InitHashTables();
}
+
hPtr = Tcl_FindHashEntry(&keysym2unichar, INT2PTR(keysym));
if (hPtr != NULL) {
KeyCode character = (KeyCode) Tcl_GetHashValue(hPtr);
@@ -493,17 +568,12 @@ XKeysymToKeycodeWithState(
KeyCode lookup = ((KeyCode) Tcl_GetHashValue(hPtr));
KeyCode virtual = lookup & 0xFF;
*state = lookup >> 8;
- return virtual << 16 | character;
+ return virtual << 24 | character;
} else {
return character;
}
}
- /*
- * This is not a text key. Try doing a hash table lookup to find the
- * keycode for a special key.
- */
-
hPtr = Tcl_FindHashEntry(&keysym2keycode, INT2PTR(keysym));
if (hPtr != NULL) {
return (KeyCode) Tcl_GetHashValue(hPtr);
@@ -554,23 +624,22 @@ TkpSetKeycodeAndState(
if (keysym == NoSymbol) {
eventPtr->xkey.keycode = 0;
} else {
- int state, length = 0;
+ int state;
UniChar keychar;
Display *display = Tk_Display(tkwin);
eventPtr->xkey.keycode = XKeysymToKeycodeWithState(display, keysym,
&state);
eventPtr->xkey.state |= state;
- keychar = eventPtr->xkey.keycode & 0xFFFF;
-
+ keychar = eventPtr->xkey.keycode & MAC_KEYCHAR_MASK;
+
/*
* Set trans_chars for keychars outside of the private-use range.
*/
- if ((keychar >= 0x20) && (keychar < 0xF700)) {
- length = TkUniCharToUtf(keychar, eventPtr->xkey.trans_chars);
+ if (IS_PRINTABLE(keychar)) {
+ eventPtr->xkey.nbytes = 0; /* This string is null-terminated. */
+ int length = TkUniCharToUtf(keychar, eventPtr->xkey.trans_chars);
eventPtr->xkey.trans_chars[length] = 0;
- } else {
- eventPtr->xkey.nbytes = 0;
}
}
}
@@ -614,8 +683,8 @@ TkpGetKeySym(
* Modifier key events have a special mac keycode (see tkProcessKeyEvent).
*/
- if ((eventPtr->xkey.keycode & MAC_KEYCODE_MASK) == 0xF8FF) {
- switch (eventPtr->xkey.keycode >> 16) { /* the virtual keyCode */
+ if ((eventPtr->xkey.keycode & MAC_KEYCHAR_MASK) == 0xF8FF) {
+ switch (eventPtr->xkey.keycode >> 24) { /* the virtual keyCode */
case 54:
return XK_Meta_R;
case 55: