diff options
Diffstat (limited to 'macosx/tkMacOSXKeyEvent.c')
-rw-r--r-- | macosx/tkMacOSXKeyEvent.c | 853 |
1 files changed, 572 insertions, 281 deletions
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 78a37eb..eee1844 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -1,7 +1,7 @@ /* * tkMacOSXKeyEvent.c -- * - * This file implements functions that decode & handle keyboard events + * This file implements functions that decode & handle keyboard events * on MacOS X. * * Copyright 2001, Apple Computer, Inc. @@ -66,35 +66,51 @@ typedef struct { UInt32 message; } KeyEventData; -static Tk_Window gGrabWinPtr = NULL; /* Current grab window, NULL if no grab. */ +static Tk_Window gGrabWinPtr = NULL; /* Current grab window, + * NULL if no grab. */ static Tk_Window gKeyboardWinPtr = NULL; /* Current keyboard grab window. */ -static UInt32 deadKeyState = 0; +static UInt32 deadKeyStateUp = 0; /* The deadkey state for the current + * sequence of keyup events or 0 if + * not in a deadkey sequence */ +static UInt32 deadKeyStateDown = 0; /* Ditto for keydown */ /* * Declarations for functions used only in this file. */ -static int GenerateKeyEvent _ANSI_ARGS_(( EventKind eKind, +static int InitKeyData( + KeyEventData * keyEventDataPtr); + +static int InitKeyEvent( + XEvent * eventPtr, KeyEventData * e, - Window window, UInt32 savedKeyCode, - UInt32 savedModifiers)); + UInt32 savedModifiers); +static int GenerateKeyEvent ( + UInt32 eKind, + KeyEventData * e, + UInt32 savedKeyCode, + UInt32 savedModifiers, + const UniChar * chars, int numChars); static int GetKeyboardLayout ( - Ptr * resource ); - -static int DecodeViaUnicodeResource( - Ptr uchr, - EventKind eKind, - const KeyEventData * e, - XEvent * event ); -static int DecodeViaKCHRResource( - Ptr kchr, - const KeyEventData * e, - XEvent * event ); - + Ptr * resource); + +static int KeycodeToUnicodeViaUnicodeResource( + UniChar * uniChars, int maxChars, + Ptr uchr, + EventKind eKind, + UInt32 keycode, UInt32 modifiers, + UInt32 * deadKeyStatePtr); + +static int KeycodeToUnicodeViaKCHRResource( + UniChar * uniChars, int maxChars, + Ptr kchr, + EventKind eKind, + UInt32 keycode, UInt32 modifiers, + UInt32 * deadKeyStatePtr); /* *---------------------------------------------------------------------- @@ -119,22 +135,22 @@ int TkMacOSXProcessKeyboardEvent( { static UInt32 savedKeyCode = 0; static UInt32 savedModifiers = 0; + static UniChar savedChar = 0; OSStatus status; KeyEventData keyEventData; - Window window; +#if 0 MenuRef menuRef; MenuItemIndex menuItemIndex; +#endif int eventGenerated; - - statusPtr->handledByTk = 1; - keyEventData.whichWindow = FrontNonFloatingWindow(); - if (keyEventData.whichWindow == NULL) { - return 0; + UniChar uniChars[5]; /* make this larger, if needed */ + UInt32 uniCharsLen; + + if (!InitKeyData(&keyEventData)) { + statusPtr->err = 1; + return false; } - GetMouse(&keyEventData.local); - keyEventData.global = keyEventData.local; - LocalToGlobal(&keyEventData.global); - keyEventData.state = TkMacOSXButtonKeyState(); + #if 0 /* * This block of code seems like a good idea, to trap @@ -163,7 +179,7 @@ int TkMacOSXProcessKeyboardEvent( int selection; menuID = GetMenuID(menuRef); - selection = (menuID << 16 ) | menuItemIndex; + selection = (menuID << 16) | menuItemIndex; GetKeys(theKeys); oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); @@ -173,10 +189,10 @@ int TkMacOSXProcessKeyboardEvent( * Handle -postcommand */ - TkMacOSXPreprocessMenu(); - TkMacOSXHandleMenuSelect(selection, theKeys[1] & 4); - Tcl_SetServiceMode(oldMode); - return 0; /* TODO: may not be on event on queue. */ + TkMacOSXPreprocessMenu(); + TkMacOSXHandleMenuSelect(selection, theKeys[1] & 4); + Tcl_SetServiceMode(oldMode); + return 0; /* TODO: may not be on event on queue. */ } #endif @@ -186,9 +202,9 @@ int TkMacOSXProcessKeyboardEvent( sizeof(keyEventData.ch), NULL, &keyEventData.ch); if (status != noErr) { - fprintf(stderr, "Failed to retrieve KeyMacCharCodes\n" ); + fprintf (stderr, "Failed to retrieve KeyMacCharCodes\n"); statusPtr->err = 1; - return 1; + return false; } status = GetEventParameter(eventPtr->eventRef, kEventParamKeyCode, @@ -196,9 +212,9 @@ int TkMacOSXProcessKeyboardEvent( sizeof(keyEventData.keyCode), NULL, &keyEventData.keyCode); if (status != noErr) { - fprintf(stderr, "Failed to retrieve KeyCode\n" ); + fprintf (stderr, "Failed to retrieve KeyCode\n"); statusPtr->err = 1; - return 1; + return false; } status = GetEventParameter(eventPtr->eventRef, kEventParamKeyModifiers, @@ -206,43 +222,102 @@ int TkMacOSXProcessKeyboardEvent( sizeof(keyEventData.keyModifiers), NULL, &keyEventData.keyModifiers); if (status != noErr) { - fprintf(stderr, "Failed to retrieve KeyModifiers\n" ); + fprintf (stderr, "Failed to retrieve KeyModifiers\n"); statusPtr->err = 1; - return 1; + return false; + } + + switch (eventPtr->eKind) { + case kEventRawKeyUp: + case kEventRawKeyDown: + case kEventRawKeyRepeat: + { + UInt32 *deadKeyStatePtr; + + if (kEventRawKeyDown == eventPtr->eKind) { + deadKeyStatePtr = &deadKeyStateDown; + } else { + deadKeyStatePtr = &deadKeyStateUp; + } + + uniCharsLen = TkMacOSXKeycodeToUnicode( + uniChars, sizeof(uniChars)/sizeof(*uniChars), + eventPtr->eKind, + keyEventData.keyCode, keyEventData.keyModifiers, + deadKeyStatePtr); + } + } + + if (kEventRawKeyUp == eventPtr->eKind) { + /* + * For some reason the deadkey processing for KeyUp doesn't work + * sometimes, so we fudge and use the last detected KeyDown. + */ + + if((0 == uniCharsLen) && (0 != savedChar)) { + uniChars[0] = savedChar; + uniCharsLen = 1; + } + + /* + * Suppress keyup events while we have a deadkey sequence on keydown. + * We still *do* want to collect deadkey state in this situation if + * the system provides it, that's why we do this only after + * TkMacOSXKeycodeToUnicode(). + */ + + if (0 != deadKeyStateDown) { + uniCharsLen = 0; + } } + keyEventData.message = keyEventData.ch|(keyEventData.keyCode << 8); - window = TkMacOSXGetXWindow(keyEventData.whichWindow); - - eventGenerated = GenerateKeyEvent(eventPtr->eKind, &keyEventData, - window, savedKeyCode, savedModifiers); + eventGenerated = GenerateKeyEvent( + eventPtr->eKind, &keyEventData, + savedKeyCode, savedModifiers, + uniChars, uniCharsLen); + savedModifiers = keyEventData.keyModifiers; + + if ((kEventRawKeyDown == eventPtr->eKind) && (uniCharsLen > 0)) { + savedChar = uniChars[0]; + } else { + savedChar = 0; + } + statusPtr->stopProcessing = 1; + if (eventGenerated == 0) { - savedKeyCode = keyEventData.message; - return false; + savedKeyCode = keyEventData.message; + return false; } else if (eventGenerated == -1) { - savedKeyCode = 0; - return false; + savedKeyCode = 0; + statusPtr->stopProcessing = 0; + return false; } else { - savedKeyCode = 0; - return true; + savedKeyCode = 0; + return true; } } - + /* *---------------------------------------------------------------------- * * GenerateKeyEvent -- * - * Given Macintosh keyUp, keyDown & autoKey events this function - * generates the appropiate X key events. The window that is passed - * should represent the frontmost window - which will recieve the - * event. + * Given Macintosh keyUp, keyDown & autoKey events (in their "raw" + * form) and a list of unicode characters this function generates the + * appropriate X key events. + * + * Parameter eKind is a raw keyboard event. e contains the data sent + * with the event. savedKeyCode and savedModifiers contain the values + * from the last event that came before (see + * TkMacOSXProcessKeyboardEvent()). chars/numChars has the Unicode + * characters for which we want to create events. * * Results: - * 1 if an event was generated, 0 if we are waiting for another - * byte of a multi-byte sequence, and -1 for any other error. + * 1 if an event was generated, -1 for any error. * * Side effects: * Additional events may be place on the Tk event queue. @@ -251,14 +326,173 @@ int TkMacOSXProcessKeyboardEvent( */ static int -GenerateKeyEvent( EventKind eKind, +GenerateKeyEvent( + UInt32 eKind, KeyEventData * e, - Window window, UInt32 savedKeyCode, - UInt32 savedModifiers ) + UInt32 savedModifiers, + const UniChar * chars, int numChars) { - Tk_Window tkwin; XEvent event; + int i; + + if (-1 == InitKeyEvent(&event, e, savedKeyCode, savedModifiers)) { + return -1; + } + + if (kEventRawKeyModifiersChanged == eKind) { + + if (savedModifiers > e->keyModifiers) { + event.xany.type = KeyRelease; + } else { + event.xany.type = KeyPress; + } + + /* + * Use special '-1' to signify a special keycode to our + * platform specific code in tkMacOSXKeyboard.c. This is + * rather like what happens on Windows. + */ + + event.xany.send_event = -1; + + /* + * Set keycode (which was zero) to the changed modifier + */ + + event.xkey.keycode = (e->keyModifiers ^ savedModifiers); + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + + } else { + + for (i=0; i<numChars; ++i) { + + /* + * Encode one char in the trans_chars array that was already + * introduced for MS Windows. Don't encode the string, if it is + * a control character but was not generated with a real control + * modifier. Such control characters get generated by KeyTrans() + * for special keys, but we rather want to identify those by + * their KeySyms. + */ + + event.xkey.trans_chars[0] = 0; + if ((controlKey & e->keyModifiers) || (chars[i] >= ' ')) { + int done; + done = Tcl_UniCharToUtf(chars[i],event.xkey.trans_chars); + event.xkey.trans_chars[done] = 0; + } + + switch(eKind) { + case kEventRawKeyDown: + event.xany.type = KeyPress; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + break; + case kEventRawKeyUp: + event.xany.type = KeyRelease; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + break; + case kEventRawKeyRepeat: + event.xany.type = KeyRelease; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + event.xany.type = KeyPress; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + break; + default: + fprintf (stderr, + "GenerateKeyEvent(): Invalid parameter eKind %d\n", + (int) eKind); + return -1; + } + } + } + + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * InitKeyData -- + * + * This routine initializes a KeyEventData structure by asking the OS + * and Tk for all the global information needed here. + * + * Results: + * True if the current front window can be found in Tk data structures + * - false otherwise. + * + * Side Effects: + * None + * + *---------------------------------------------------------------------- + */ +static int +InitKeyData(KeyEventData * keyEventDataPtr) +{ + memset (keyEventDataPtr, 0, sizeof(*keyEventDataPtr)); + + keyEventDataPtr->whichWindow = FrontNonFloatingWindow(); + if (keyEventDataPtr->whichWindow == NULL) { + return false; + } + GetMouse(&keyEventDataPtr->local); + keyEventDataPtr->global = keyEventDataPtr->local; + LocalToGlobal(&keyEventDataPtr->global); + keyEventDataPtr->state = TkMacOSXButtonKeyState(); + + return true; +} + +/* + *---------------------------------------------------------------------- + * + * InitKeyEvent -- + * + * Initialize an XEvent structure by asking Tk for global information. + * Also uses a KeyEventData structure and other current state. + * + * Results: + * 1 on success, -1 for any error. + * + * Side effects: + * Additional events may be place on the Tk event queue. + * + *---------------------------------------------------------------------- + */ + +/* + * We have a general problem here. How do we handle 'Option-char' + * keypresses? The problem is that we might want to bind to some of these + * (e.g. Cmd-Opt-d is 'uncomment' in Alpha). OTOH Option-d actually produces + * a real character on MacOS, namely a mathematical delta. + * + * The current behaviour is that a binding goes by the combinations of + * modifiers and base keysym, that is Option-d. The string value of the + * event is the mathematical delta character, so if no binding calls + * [break], the text widget will insert that character. + * + * Note that this is similar to control combinations on all platforms. They + * also generate events that have the base character as keysym and a real + * control character as character value. So Ctrl+C gets us the keysym XK_C, + * the modifier Control (so you can bind <Control-C>) and a string value as + * "\u0003". + * + * For a different solutions we may want for the event to contain keysyms for + * *both* the 'Opt-d' side of things and the mathematical delta. Then a + * binding on Opt-d will trigger, but a binding on mathematical delta would + * also trigger. This would require changes in the core, though. + */ + +static int +InitKeyEvent( + XEvent * eventPtr, + KeyEventData * e, + UInt32 savedKeyCode, + UInt32 savedModifiers) +{ + Window window; + Tk_Window tkwin; TkDisplay *dispPtr; /* @@ -267,6 +501,7 @@ GenerateKeyEvent( EventKind eKind, * that owns the focus. */ + window = TkMacOSXGetXWindow(e->whichWindow); dispPtr = TkGetDisplayList(); tkwin = Tk_IdToWindow(dispPtr->display, window); @@ -281,127 +516,58 @@ GenerateKeyEvent( EventKind eKind, return -1; } - event.xkey.trans_chars[0] = 0; - - if (0 != e->ch) { - Ptr resource = NULL; - if (GetKeyboardLayout(&resource)) { - if (0 == DecodeViaUnicodeResource(resource,eKind,e,&event)) - return 0; - } else { - if (0 == DecodeViaKCHRResource(resource,e,&event)) - return 0; - } - } - - event.xany.send_event = False; - event.xkey.same_screen = true; - event.xkey.subwindow = None; - event.xkey.time = TkpGetMS(); - - event.xkey.x_root = e->global.h; - event.xkey.y_root = e->global.v; - Tk_TopCoordsToWindow(tkwin, e->local.h, e->local.v, - &event.xkey.x, &event.xkey.y); - - /* - * Now, we may have a problem here. How do we handle 'Option-char' - * keypresses? The problem is that we might want to bind to some of - * these (e.g. Cmd-Opt-d is 'uncomment' in Alpha), but Option-d - * generates a 'delta' symbol with some keycode unrelated to 'd', and so - * the binding never triggers. In any case, the delta that is produced - * is never mapped to an 'XK_Greek_DELTA' keysym so bindings on that - * won't work either (a general KeyPress binding will of course trigger, - * but a specific binding on XK_Greek_DELTA will not). - * - * I think what we want is for the event to contain information on - * both the 'Opt-d' side of things and the 'delta'. Then a binding - * on Opt-d will trigger, but the ascii/string representation of the - * event will be a delta. - * - * A different way to look at this is that 'Opt-d' is delta, but that - * Command-Opt-d is nothing to do with delta, but I'm not sure that is - * helpful. - * - * Also some keypresses (Opt-e) are dead-keys to add accents to - * letters. We don't handle them yet. - * - * Help needed! - */ - event.xkey.keycode = e->ch | + eventPtr->xany.send_event = false; + eventPtr->xany.serial = Tk_Display(tkwin)->request; + + eventPtr->xkey.same_screen = true; + eventPtr->xkey.subwindow = None; + eventPtr->xkey.time = TkpGetMS(); + eventPtr->xkey.x_root = e->global.h; + eventPtr->xkey.y_root = e->global.v; + eventPtr->xkey.window = Tk_WindowId(tkwin); + eventPtr->xkey.display = Tk_Display(tkwin); + eventPtr->xkey.root = XRootWindow(Tk_Display(tkwin), 0); + eventPtr->xkey.state = e->state; + eventPtr->xkey.trans_chars[0] = 0; + + Tk_TopCoordsToWindow( + tkwin, e->local.h, e->local.v, + &eventPtr->xkey.x, &eventPtr->xkey.y); + + eventPtr->xkey.keycode = e->ch | ((savedKeyCode & charCodeMask) << 8) | ((e->message&keyCodeMask) << 8); - - event.xany.serial = Tk_Display(tkwin)->request; - event.xkey.window = Tk_WindowId(tkwin); - event.xkey.display = Tk_Display(tkwin); - event.xkey.root = XRootWindow(Tk_Display(tkwin), 0); - event.xkey.state = e->state; - - switch(eKind) { - case kEventRawKeyDown: - event.xany.type = KeyPress; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - break; - case kEventRawKeyUp: - event.xany.type = KeyRelease; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - break; - case kEventRawKeyRepeat: - event.xany.type = KeyRelease; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - event.xany.type = KeyPress; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - break; - case kEventRawKeyModifiersChanged: - if (savedModifiers > e->keyModifiers) { - event.xany.type = KeyRelease; - } else { - event.xany.type = KeyPress; - } - /* - * Use special '-1' to signify a special keycode to - * our platform specific code in tkMacOSXKeyboard.c. - * This is rather like what happens on Windows. - */ - event.xany.send_event = -1; - /* Set keycode (which was zero) to the changed modifier */ - event.xkey.keycode = (e->keyModifiers ^ savedModifiers); - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - break; - default: - break; - } + return 1; } + /* *---------------------------------------------------------------------- * * GetKeyboardLayout -- * - * Queries the OS for a pointer to a keyboard resource. + * Queries the OS for a pointer to a keyboard resource. * - * NB (benny): This function is supposed to work with the - * keyboard layout switch menu that we have in 10.2. Currently - * the menu is not enabled at all for wish, so I can not really - * test it. We will probably have to use real TSM-style event - * handling to get all those goodies, but I haven't figured out - * those bits yet. + * This function works with the keyboard layout switch menu that + * we have in 10.2. * * Results: - * 1 if there is returned a Unicode 'uchr' resource in - * "*resource", 0 if it is a classic 'KCHR' resource. + * 1 if there is returned a Unicode 'uchr' resource in + * "*resource", 0 if it is a classic 'KCHR' resource. A pointer + * to the actual resource data goes into *resource. * * Side effects: - * Sets some internal static variables. + * Sets some internal static variables. * *---------------------------------------------------------------------- */ + static int -GetKeyboardLayout ( Ptr * resource ) +GetKeyboardLayout (Ptr * resource) { - static SInt16 lastKeyLayoutID = -1; /* should be safe */ + static Boolean initialized = false; + static SInt16 lastKeyLayoutID = -1; static Handle uchrHnd = NULL; static Handle KCHRHnd = NULL; @@ -411,182 +577,307 @@ GetKeyboardLayout ( Ptr * resource ) keyScript = GetScriptManagerVariable(smKeyScript); keyLayoutID = GetScriptVariable(keyScript,smScriptKeys); - if (lastKeyLayoutID != keyLayoutID) { - deadKeyState = 0; - lastKeyLayoutID = keyLayoutID; - uchrHnd = GetResource('uchr',keyLayoutID); - if (NULL == uchrHnd) { - KCHRHnd = GetResource('KCHR',keyLayoutID); - } + if (!initialized || (lastKeyLayoutID != keyLayoutID)) { + initialized = true; + deadKeyStateUp = deadKeyStateDown = 0; + lastKeyLayoutID = keyLayoutID; + uchrHnd = GetResource('uchr',keyLayoutID); + if (NULL == uchrHnd) { + KCHRHnd = GetResource('KCHR',keyLayoutID); + } + if ((NULL == uchrHnd) && (NULL == KCHRHnd)) { + initialized = false; + fprintf (stderr, + "GetKeyboardLayout(): " + "Can't get a keyboard layout for layout %d " + "(error code %d)?\n", + (int) keyLayoutID, (int) ResError()); + *resource = (Ptr) GetScriptManagerVariable(smKCHRCache); + fprintf (stderr, + "GetKeyboardLayout(): Trying the cache: %p\n", + *resource); + return 0; + } } if (NULL != uchrHnd) { - *resource = *uchrHnd; - return 1; + *resource = *uchrHnd; + return 1; } else { - *resource = *KCHRHnd; - return 0; + *resource = *KCHRHnd; + return 0; } } + /* *---------------------------------------------------------------------- * - * DecodeViaUnicodeResource -- + * KeycodeToUnicodeViaUnicodeResource -- * - * Given MacOS key event data this function generates the UTF-8 - * characters. It does this using a 'uchr' and the - * UCKeyTranslate API. + * Given MacOS key event data this function generates the Unicode + * characters. It does this using a 'uchr' and the UCKeyTranslate + * API. * - * NB (benny): This function is not tested at all, because my - * system does not actually return a 'uchr' resource in - * GetKeyboardLayout currently. We probably need to do - * TSM-style event handling to get keyboard layout switching - * first. + * The parameter deadKeyStatePtr can be NULL, if no deadkey handling + * is needed. + * + * Tested and known to work with US, Hebrew, Greek and Russian layouts + * as well as "Unicode Hex Input". * * Results: - * 1 if the data was generated, 0 if we are waiting for another - * byte of a dead-key sequence. + * The number of characters generated if any, 0 if we are waiting for + * another byte of a dead-key sequence. Fills in the uniChars array + * with a Unicode string. * - * Side effects: - * Sets the trans_chars array in the XEvent->xkey structure. + * Side Effects: + * None * *---------------------------------------------------------------------- */ static int -DecodeViaUnicodeResource( - Ptr uchr, - EventKind eKind, - const KeyEventData * e, - XEvent * event ) +KeycodeToUnicodeViaUnicodeResource( + UniChar * uniChars, int maxChars, + Ptr uchr, + EventKind eKind, + UInt32 keycode, UInt32 modifiers, + UInt32 * deadKeyStatePtr) { - /* input of UCKeyTranslate */ - unsigned vkey; int action; - unsigned modifiers; unsigned long keyboardType; - - /* output of UCKeyTranslate */ - enum { BUFFER_SIZE = 16 }; - UniChar unistring[BUFFER_SIZE]; + OptionBits options = 0; + UInt32 dummy_state; UniCharCount actuallength; OSStatus status; - /* for converting the result */ - char utf8buffer[sizeof(event->xkey.trans_chars)+4]; - int s, d; - - vkey = ((e->message) >> 8) & 0xFF; - modifiers = ((e->keyModifiers) >> 8) & 0xFF; + keycode &= 0xFF; + modifiers = (modifiers >> 8) & 0xFF; keyboardType = LMGetKbdType(); - switch(eKind) { - default: /* keep compilers happy */ - case kEventRawKeyDown: action = kUCKeyActionDown; break; - case kEventRawKeyUp: action = kUCKeyActionUp; break; - case kEventRawKeyRepeat: action = kUCKeyActionAutoKey; break; + if (NULL==deadKeyStatePtr) { + options = kUCKeyTranslateNoDeadKeysMask; + dummy_state = 0; + deadKeyStatePtr = &dummy_state; + } + + switch(eKind) { + case kEventRawKeyDown: + action = kUCKeyActionDown; + break; + case kEventRawKeyUp: + action = kUCKeyActionUp; + break; + case kEventRawKeyRepeat: + action = kUCKeyActionAutoKey; + break; + default: + fprintf (stderr, + "KeycodeToUnicodeViaUnicodeResource(): " + "Invalid parameter eKind %d\n", + (int) eKind); + return 0; } status = UCKeyTranslate( - (const UCKeyboardLayout *)uchr, - vkey, action, modifiers, keyboardType, - 0, &deadKeyState, BUFFER_SIZE, &actuallength, unistring); + (const UCKeyboardLayout *) uchr, + keycode, action, modifiers, keyboardType, + options, deadKeyStatePtr, + maxChars, &actuallength, uniChars); - if (0 != deadKeyState) - return 0; /* more data later */ + if ((0 == actuallength) && (0 != *deadKeyStatePtr)) { + /* + * More data later + */ + + return 0; + } + + /* + * some IMEs leave residue :-( + */ + + *deadKeyStatePtr = 0; if (noErr != status) { - fprintf(stderr,"UCKeyTranslate failed: %d", (int) status); - actuallength = 0; - } - s = 0; - d = 0; - while (s<actuallength) { - int newd = d + Tcl_UniCharToUtf(unistring[s],utf8buffer+d); - if (newd > (sizeof(event->xkey.trans_chars)-1)) { - break; - } - d = newd; - ++s; + fprintf(stderr,"UCKeyTranslate failed: %d", (int) status); + actuallength = 0; } - utf8buffer[d] = 0; - strcpy(event->xkey.trans_chars, utf8buffer); - return 1; + return actuallength; } - + /* *---------------------------------------------------------------------- * - * DecodeViaKCHRResource -- + * KeycodeToUnicodeViaKCHRResource -- * - * Given MacOS key event data this function generates the UTF-8 - * characters. It does this using a 'KCHR' and the - * KeyTranslate API. + * Given MacOS key event data this function generates the Unicode + * characters. It does this using a 'KCHR' and the KeyTranslate API. * - * NB (benny): The function is not actually tested with double - * byte encodings yet. + * The parameter deadKeyStatePtr can be NULL, if no deadkey handling + * is needed. * * Results: - * 1 if the data was generated, 0 if we are waiting for another - * byte of a dead-key sequence. + * The number of characters generated if any, 0 if we are waiting for + * another byte of a dead-key sequence. Fills in the uniChars array + * with a Unicode string. * - * Side effects: - * Sets the trans_chars array in the XEvent->xkey structure. + * Side Effects: + * None * *---------------------------------------------------------------------- */ + static int -DecodeViaKCHRResource( - Ptr kchr, - const KeyEventData * e, - XEvent * event ) +KeycodeToUnicodeViaKCHRResource( + UniChar * uniChars, int maxChars, + Ptr kchr, + EventKind eKind, + UInt32 keycode, UInt32 modifiers, + UInt32 * deadKeyStatePtr) { - /* input and output of KeyTranslate */ - UInt16 keycode; UInt32 result; + char macBuff[3]; + char * macStr; + int macStrLen; + UInt32 dummy_state = 0; - /* for converting the result */ - char macbuff[2]; - char * macstr; - int maclen; - keycode = e->keyCode | e->keyModifiers; - result = KeyTranslate(kchr, keycode, &deadKeyState); + if (NULL == deadKeyStatePtr) { + deadKeyStatePtr = &dummy_state; + } + + keycode |= modifiers; + result = KeyTranslate(kchr, keycode, deadKeyStatePtr); + + if ((0 == result) && (0 != dummy_state)) { + /* + * 'dummy_state' gets only filled if the caller did not want deadkey + * processing (deadKeyStatePtr was NULL originally), but we still + * have a deadkey. We just push the keycode for the space bar to get + * the real key value. + */ + + result = KeyTranslate(kchr, 0x31, deadKeyStatePtr); + *deadKeyStatePtr = 0; + } - if (0 != deadKeyState) - return 0; /* more data later */ + if ((0 == result) && (0 != *deadKeyStatePtr)) { + /* + * More data later + */ + + return 0; + } - macbuff[0] = (char) (result >> 16); - macbuff[1] = (char) result; + macBuff[0] = (char) (result >> 16); + macBuff[1] = (char) result; + macBuff[2] = 0; - if (0 != macbuff[0]) { - /* if the first byte is valid, the second is too */ - macstr = macbuff; - maclen = 2; - } else if (0 != macbuff[1]) { - /* only the second is valid */ - macstr = macbuff+1; - maclen = 1; + if (0 != macBuff[0]) { + /* + * If the first byte is valid, the second is too + */ + + macStr = macBuff; + macStrLen = 2; + } else if (0 != macBuff[1]) { + /* + * Only the second is valid + */ + + macStr = macBuff+1; + macStrLen = 1; } else { - /* no valid bytes at all */ - macstr = NULL; - maclen = 0; + /* + * No valid bytes at all -- shouldn't happen + */ + + macStr = NULL; + macStrLen = 0; } - if (maclen > 0) { - int result = Tcl_ExternalToUtf( - NULL, TkMacOSXCarbonEncoding, - macstr, maclen, 0, NULL, - event->xkey.trans_chars, sizeof(event->xkey.trans_chars), - NULL, NULL, NULL); + if (macStrLen <= 0) { + return 0; + } else { + /* + * Use the CFString conversion routines. This is the easiest and + * most compatible way to get from an 8-bit string and a MacOS script + * code to a Unicode string. + */ + + CFStringRef cfString; + int uniStrLen; + + cfString = CFStringCreateWithCStringNoCopy( + NULL, macStr, + GetScriptManagerVariable(smKeyScript), + kCFAllocatorNull); + uniStrLen = CFStringGetLength(cfString); + if (uniStrLen > maxChars) { + uniStrLen = maxChars; + } + CFStringGetCharacters(cfString, CFRangeMake(0,uniStrLen), uniChars); + CFRelease(cfString); + + return uniStrLen; } +} - return 1; + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXKeycodeToUnicode -- + * + * Given MacOS key event data this function generates the Unicode + * characters. It does this using OS resources and APIs. + * + * The parameter deadKeyStatePtr can be NULL, if no deadkey handling + * is needed. + * + * This function is called from XKeycodeToKeysym() in + * tkMacOSKeyboard.c. + * + * Results: + * The number of characters generated if any, 0 if we are waiting for + * another byte of a dead-key sequence. Fills in the uniChars array + * with a Unicode string. + * + * Side Effects: + * None + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXKeycodeToUnicode( + UniChar * uniChars, int maxChars, + EventKind eKind, + UInt32 keycode, UInt32 modifiers, + UInt32 * deadKeyStatePtr) +{ + Ptr resource = NULL; + int len; + + + if (GetKeyboardLayout(&resource)) { + len = KeycodeToUnicodeViaUnicodeResource( + uniChars, maxChars, resource, eKind, + keycode, modifiers, deadKeyStatePtr); + } else { + len = KeycodeToUnicodeViaKCHRResource( + uniChars, maxChars, resource, eKind, + keycode, modifiers, deadKeyStatePtr); + } + + return len; } + + /* *---------------------------------------------------------------------- * @@ -691,15 +982,15 @@ TkpSetCapture( * * Tk_SetCaretPos -- * - * This enables correct placement of the XIM caret. This is called - * by widgets to indicate their cursor placement, and the caret - * location is used by TkpGetString to place the XIM caret. + * This enables correct placement of the XIM caret. This is called + * by widgets to indicate their cursor placement, and the caret + * location is used by TkpGetString to place the XIM caret. * * Results: - * None + * None * * Side effects: - * None + * None * *---------------------------------------------------------------------- */ @@ -707,8 +998,8 @@ TkpSetCapture( void Tk_SetCaretPos(tkwin, x, y, height) Tk_Window tkwin; - int x; - int y; - int height; + int x; + int y; + int height; { } |