diff options
Diffstat (limited to 'macosx/tkMacOSXKeyEvent.c')
-rw-r--r-- | macosx/tkMacOSXKeyEvent.c | 1114 |
1 files changed, 148 insertions, 966 deletions
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 73165f9..6e16583 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -4,51 +4,13 @@ * This file implements functions that decode & handle keyboard events on * MacOS X. * - * Copyright 2001, Apple Computer, Inc. - * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * The following terms apply to all files originating from Apple Computer, - * Inc. ("Apple") and associated with the software unless explicitly - * disclaimed in individual files. - * - * Apple hereby grants permission to use, copy, modify, distribute, and - * license this software and its documentation for any purpose, provided - * that existing copyright notices are retained in all copies and that - * this notice is included verbatim in any distributions. No written - * agreement, license, or royalty fee is required for any of the - * authorized uses. Modifications to this software may be copyrighted by - * their authors and need not follow the licensing terms described here, - * provided that the new terms are clearly indicated on the first page of - * each file where they apply. - * - * IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE SOFTWARE BE - * LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS - * DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF APPLE OR THE AUTHORS - * HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. APPLE, THE AUTHORS - * AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR - * A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED - * ON AN "AS IS" BASIS, AND APPLE,THE AUTHORS AND DISTRIBUTORS HAVE NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR - * MODIFICATIONS. - * - * GOVERNMENT USE: If you are acquiring this software on behalf of the - * U.S. government, the Government shall have only "Restricted Rights" in - * the software and related documentation as defined in the Federal - * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are - * acquiring the software on behalf of the Department of Defense, the - * software shall be classified as "Commercial Computer Software" and the - * Government shall have only "Restricted Rights" as defined in Clause - * 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the - * authors grant the U.S. Government and others acting in its behalf - * permission to use and distribute the software in accordance with the - * terms specified in this license. - * - * RCS: @(#) $Id: tkMacOSXKeyEvent.c,v 1.26 2008/10/27 11:55:44 dkf Exp $ + * RCS: @(#) $Id: tkMacOSXKeyEvent.c,v 1.27 2009/06/29 14:35:01 das Exp $ */ #include "tkMacOSXPrivate.h" @@ -60,931 +22,158 @@ #endif */ -typedef struct { - WindowRef whichWindow; - int global_x, global_y; - int local_x, local_y; - unsigned int state; - UInt32 keyCode; - UInt32 keyModifiers; - UInt32 message; - unsigned char ch; -} KeyEventData; - static Tk_Window grabWinPtr = NULL; /* Current grab window, NULL if no grab. */ static Tk_Window keyboardGrabWinPtr = NULL; /* Current keyboard grab window. */ -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(UInt32 eKind, KeyEventData *e, - UInt32 savedKeyCode, UInt32 savedModifiers, - const UniChar *chars, int numChars); -static TextEncoding GetKCHREncoding(ScriptCode script, SInt32 layoutid); -static int GetKeyboardLayout(Ptr *resourcePtr, - TextEncoding *encodingPtr); -static int InitKeyData(KeyEventData *keyEventDataPtr); -static int InitKeyEvent(XEvent *eventPtr, KeyEventData *e, - UInt32 savedKeyCode, UInt32 savedModifiers); -static int KeycodeToUnicodeViaKCHRResource(UniChar *uniChars, - int maxChars, Ptr kchr, TextEncoding encoding, - EventKind eKind, UInt32 keycode, UInt32 modifiers, - UInt32 *deadKeyStatePtr); -static int KeycodeToUnicodeViaUnicodeResource(UniChar *uniChars, - int maxChars, Ptr uchr, EventKind eKind, - UInt32 keycode, UInt32 modifiers, - UInt32 *deadKeyStatePtr); - -/* - *---------------------------------------------------------------------- - * - * TkMacOSXProcessKeyboardEvent -- - * - * This routine processes the event in eventPtr, and generates the - * appropriate Tk events from it. - * - * Results: - * True if event(s) are generated - false otherwise. - * - * Side effects: - * Additional events may be place on the Tk event queue. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE int -TkMacOSXProcessKeyboardEvent( - TkMacOSXEvent *eventPtr, - MacEventStatus *statusPtr) -{ - static UInt32 savedKeyCode = 0; - static UInt32 savedModifiers = 0; - static UniChar savedChar = 0; - OSStatus err; - KeyEventData keyEventData; - MenuRef menuRef; - MenuItemIndex menuItemIndex; - int eventGenerated; - UniChar uniChars[5]; /* make this larger, if needed */ - UInt32 uniCharsLen = 0; - - if (!InitKeyData(&keyEventData)) { - statusPtr->err = 1; - return false; - } - - /* - * Because of the way that Tk operates, we can't in general funnel menu - * accelerators through IsMenuKeyEvent. Tk treats accelerators as mere - * decoration, and the user has to install bindings to get them to fire. - * - * However, the only way to trigger the Hide & Hide Others functions is by - * invoking the Menu command for Hide. So there is no nice way to provide a - * Tk command to hide the app which would be available for a binding. So I - * am going to hijack Command-H and Command-Shift-H here, and run the menu - * commands. Since the HI Guidelines explicitly reserve these for Hide, - * this isn't such a bad thing. Also, if you do rebind Command-H to another - * menu item, Hide will lose its binding. - * - * Note that I don't really do anything at this point, I just mark - * stopProcessing as 0 and return, and then the RecieveAndProcessEvent code - * will dispatch the event to the default handler. - */ - - if ((eventPtr->eKind == kEventRawKeyDown - || eventPtr->eKind == kEventRawKeyRepeat) - && IsMenuKeyEvent(tkCurrentAppleMenu, eventPtr->eventRef, - kMenuEventQueryOnly, &menuRef, &menuItemIndex)) { - MenuCommand menuCmd; - - GetMenuItemCommandID (menuRef, menuItemIndex, &menuCmd); - switch (menuCmd) { - case kHICommandHide: - case kHICommandHideOthers: - case kHICommandShowAll: - case kHICommandPreferences: - case kHICommandQuit: - statusPtr->stopProcessing = 0; - - /* - * TODO: may not be on event on queue. - */ - - return 0; - default: - break; - } - } - - err = ChkErr(GetEventParameter, eventPtr->eventRef, - kEventParamKeyMacCharCodes, typeChar, NULL, - sizeof(keyEventData.ch), NULL, &keyEventData.ch); - if (err != noErr) { - statusPtr->err = 1; - return false; - } - err = ChkErr(GetEventParameter, eventPtr->eventRef, kEventParamKeyCode, - typeUInt32, NULL, sizeof(keyEventData.keyCode), NULL, - &keyEventData.keyCode); - if (err != noErr) { - statusPtr->err = 1; - return false; - } - err = ChkErr(GetEventParameter, eventPtr->eventRef, - kEventParamKeyModifiers, typeUInt32, NULL, - sizeof(keyEventData.keyModifiers), NULL, - &keyEventData.keyModifiers); - if (err != noErr) { - statusPtr->err = 1; - return false; - } +static NSModalSession modalSession = NULL; - switch (eventPtr->eKind) { - case kEventRawKeyUp: - case kEventRawKeyDown: - case kEventRawKeyRepeat: { - UInt32 *deadKeyStatePtr; +#pragma mark TKApplication(TKKeyEvent) - if (kEventRawKeyDown == eventPtr->eKind) { - deadKeyStatePtr = &deadKeyStateDown; - } else { - deadKeyStatePtr = &deadKeyStateUp; - } +@implementation TKApplication(TKKeyEvent) +- (NSEvent *)tkProcessKeyEvent:(NSEvent *)theEvent { +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); +#endif + NSWindow* w; + NSEventType type = [theEvent type]; + NSUInteger modifiers, len; + BOOL repeat = NO; + unsigned short keyCode; + NSString *characters = nil, *charactersIgnoringModifiers = nil; + static NSUInteger savedModifiers = 0; + + + switch (type) { + case NSKeyUp: + case NSKeyDown: + repeat = [theEvent isARepeat]; + characters = [theEvent characters]; + charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers]; + case NSFlagsChanged: + modifiers = [theEvent modifierFlags]; + keyCode = [theEvent keyCode]; + w = [self windowWithWindowNumber:[theEvent windowNumber]]; +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@"-[%@(%p) %s] %d %u %@ %@ %u %@", [self class], self, _cmd, repeat, modifiers, characters, charactersIgnoringModifiers, keyCode, w); +#endif + break; - uniCharsLen = TkMacOSXKeycodeToUnicode(uniChars, - sizeof(uniChars)/sizeof(*uniChars), eventPtr->eKind, - keyEventData.keyCode, keyEventData.keyModifiers, - deadKeyStatePtr); + default: + return theEvent; break; } - } - 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. - */ + unsigned int state = 0; - 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; - } + if (modifiers & NSAlphaShiftKeyMask) { + state |= LockMask; } - - keyEventData.message = keyEventData.ch|(keyEventData.keyCode << 8); - - eventGenerated = GenerateKeyEvent(eventPtr->eKind, &keyEventData, - savedKeyCode, savedModifiers, uniChars, uniCharsLen); - - savedModifiers = keyEventData.keyModifiers; - - if ((kEventRawKeyDown == eventPtr->eKind) && (uniCharsLen > 0)) { - savedChar = uniChars[0]; - } else { - savedChar = 0; + if (modifiers & NSShiftKeyMask) { + state |= ShiftMask; } - - statusPtr->stopProcessing = 1; - - if (eventGenerated == 0) { - savedKeyCode = keyEventData.message; - return false; + if (modifiers & NSControlKeyMask) { + state |= ControlMask; } - if (eventGenerated == -1) { - savedKeyCode = 0; - statusPtr->stopProcessing = 0; - return false; + if (modifiers & NSCommandKeyMask) { + state |= Mod1Mask; /* command key */ } - savedKeyCode = 0; - return true; -} - -/* - *---------------------------------------------------------------------- - * - * GenerateKeyEvent -- - * - * 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, -1 for any error. - * - * Side effects: - * Additional events may be place on the Tk event queue. - * - *---------------------------------------------------------------------- - */ - -static int -GenerateKeyEvent( - UInt32 eKind, - KeyEventData * e, - UInt32 savedKeyCode, - UInt32 savedModifiers, - const UniChar * chars, - int numChars) -{ - XEvent event; - int i; - - if (-1 == InitKeyEvent(&event, e, savedKeyCode, savedModifiers)) { - return -1; + if (modifiers & NSAlternateKeyMask) { + state |= Mod2Mask; /* option key */ } - - 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 = 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: - TkMacOSXDbgMsg("Invalid parameter eKind %ld", eKind); - return -1; - } - } + if (modifiers & NSNumericPadKeyMask) { + state |= Mod3Mask; } - - 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 = ActiveNonFloatingWindow(); - if (keyEventDataPtr->whichWindow == NULL) { - return false; + if (modifiers & NSFunctionKeyMask) { + state |= Mod4Mask; } - XQueryPointer(NULL, None, NULL, NULL, &keyEventDataPtr->global_x, - &keyEventDataPtr->global_y, &keyEventDataPtr->local_x, - &keyEventDataPtr->local_y, &keyEventDataPtr->state); - 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; - /* * The focus must be in the FrontWindow on the Macintosh. We then query Tk * to determine the exact Tk window that owns the focus. */ - window = TkMacOSXGetXWindow(e->whichWindow); - dispPtr = TkGetDisplayList(); - tkwin = Tk_IdToWindow(dispPtr->display, window); + TkWindow *winPtr = TkMacOSXGetTkWindow(w); + Tk_Window tkwin = (Tk_Window) winPtr; if (!tkwin) { TkMacOSXDbgMsg("tkwin == NULL"); - return -1; + return theEvent; } - - tkwin = (Tk_Window) ((TkWindow *) tkwin)->dispPtr->focusPtr; + tkwin = (Tk_Window) winPtr->dispPtr->focusPtr; if (!tkwin) { TkMacOSXDbgMsg("tkwin == NULL"); - return -1; - } - - 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_x; - eventPtr->xkey.y_root = e->global_y; - 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_x, e->local_y, &eventPtr->xkey.x, - &eventPtr->xkey.y); - - eventPtr->xkey.keycode = e->ch | ((savedKeyCode & charCodeMask) << 8) | - ((e->message&keyCodeMask) << 8); - return 1; -} - -/* - *---------------------------------------------------------------------- - * - * GetKeyboardLayout -- - * - * Queries the OS for a pointer to a keyboard resource. - * - * This function works with the keyboard layout switch menu. It uses - * Keyboard Layout Services, where available. - * - * Results: - * 1 if there is returned a Unicode 'uchr' resource in *resourcePtr, 0 if - * it is a classic 'KCHR' resource. A pointer to the actual resource data - * goes into *resourcePtr. If the resource is a 'KCHR' resource, the - * corresponding Mac encoding goes into *encodingPtr. - * - * Side effects: - * Sets some internal static variables. - * - *---------------------------------------------------------------------- - */ - -static int -GetKeyboardLayout( - Ptr *resourcePtr, - TextEncoding *encodingPtr) -{ - static KeyboardLayoutRef lastLayout = NULL; - static SInt32 lastLayoutId; - static TextEncoding lastEncoding = kTextEncodingMacRoman; - static Ptr uchr = NULL; - static Ptr KCHR = NULL; - int hasLayoutChanged = false; - KeyboardLayoutRef currentLayout = NULL; - SInt32 currentLayoutId = 0; - ScriptCode currentKeyScript; - - currentKeyScript = GetScriptManagerVariable(smKeyScript); - - /* - * Use the Keyboard Layout Services. - */ - - KLGetCurrentKeyboardLayout(¤tLayout); - - if (currentLayout != NULL) { - /* - * The layout pointer could in theory be the same for different - * layouts, only the id gives us the information that the - * keyboard has actually changed. OTOH the layout object can - * also change and it could still be the same layoutid. - */ - - KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier, - (const void **) ¤tLayoutId); - - if ((lastLayout != currentLayout) - || (lastLayoutId != currentLayoutId)) { -#ifdef TK_MAC_DEBUG_KEYBOARD - TkMacOSXDbgMsg("Use KLS"); -#endif - hasLayoutChanged = true; - - /* - * Reinitialize all relevant variables. - */ - - lastLayout = currentLayout; - lastLayoutId = currentLayoutId; - uchr = NULL; - KCHR = NULL; - - if ((KLGetKeyboardLayoutProperty(currentLayout, - kKLuchrData, (const void **) &uchr) == noErr) - && (uchr != NULL)) { - /* done */ - } else if ((KLGetKeyboardLayoutProperty(currentLayout, - kKLKCHRData, (const void**)&KCHR) == noErr) - && (KCHR != NULL)) { - /* done */ - } - } - } - - if (hasLayoutChanged) { -#ifdef TK_MAC_DEBUG_KEYBOARD - if (KCHR) { - TkMacOSXDbgMsg("New 'KCHR' layout %ld", currentLayoutId); - } else if (uchr) { - TkMacOSXDbgMsg("New 'uchr' layout %ld", currentLayoutId); + return theEvent; + } + + XEvent xEvent; + xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); + xEvent.xany.send_event = false; + xEvent.xany.display = Tk_Display(tkwin); + xEvent.xany.window = Tk_WindowId(tkwin); + + xEvent.xkey.root = XRootWindow(Tk_Display(tkwin), 0); + xEvent.xkey.subwindow = None; + xEvent.xkey.time = TkpGetMS(); + xEvent.xkey.state = state; + xEvent.xkey.same_screen = true; + xEvent.xkey.trans_chars[0] = 0; + xEvent.xkey.nbytes = 0; + + if (type == NSFlagsChanged) { + if (savedModifiers > modifiers) { + xEvent.xany.type = KeyRelease; } else { - TkMacOSXDbgMsg("Use cached layout (should have been %ld)", - currentLayoutId); + xEvent.xany.type = KeyPress; } -#endif - - deadKeyStateUp = deadKeyStateDown = 0; - - /* - * If we did get a new 'KCHR', compute its encoding and put it into - * lastEncoding. - * - * If we didn't get a new 'KCHR' and if we have no 'uchr' either, get - * some 'KCHR' from the OS cache and leave lastEncoding at its current - * value. This should better not happen, it doesn't really work. - */ - - if (KCHR) { - lastEncoding = GetKCHREncoding(currentKeyScript, currentLayoutId); -#ifdef TK_MAC_DEBUG_KEYBOARD - TkMacOSXDbgMsg("New 'KCHR' encoding %lu (%lu + 0x%lX)", - lastEncoding, lastEncoding & 0xFFFFL, - lastEncoding & ~0xFFFFL); -#endif - } else if (!uchr) { - KCHR = (Ptr)(intptr_t) GetScriptManagerVariable(smKCHRCache); - } - } - - if (uchr) { - *resourcePtr = uchr; - return 1; - } - *resourcePtr = KCHR; - *encodingPtr = lastEncoding; - return 0; -} - -/* - *---------------------------------------------------------------------- - * - * GetKCHREncoding -- - * - * Upgrade a WorldScript code to a TEC encoding based on the keyboard - * layout id. - * - * Results: - * The TEC code that corresponds best to the combination of WorldScript - * code and 'KCHR' id. - * - * Side effects: - * None. - * - * Rationale and Notes: - * WorldScript codes are sometimes not unique encodings. E.g. Icelandic - * uses script smRoman (0), but the actual encoding is - * kTextEncodingMacIcelandic (37). ftp://ftp.unicode.org/Public - * /MAPPINGS/VENDORS/APPLE/README.TXT has a good summary of these - * variants. So we need to upgrade the script to an encoding with - * GetTextEncodingFromScriptInfo(). - * - * 'KCHR' ids are usually region codes (see the comments in Script.h). - * Where they are not, we get a paramErr from the OS function and have - * appropriate fallbacks. - * - *---------------------------------------------------------------------- - */ - -static TextEncoding -GetKCHREncoding( - ScriptCode script, - SInt32 layoutid) -{ - RegionCode region = layoutid; - TextEncoding encoding = script; - - if (GetTextEncodingFromScriptInfo(script, kTextLanguageDontCare, region, - &encoding) == noErr) { - return encoding; - } - - /* - * GetTextEncodingFromScriptInfo() doesn't know about more exotic layouts. - * This provides a fallback for good measure. In an ideal world, exotic - * layouts would always provide a 'uchr' resource anyway, so we wouldn't - * need this. - * - * We can add more keyboard layouts, if we get actual complaints. Farsi or - * other Celtic/Gaelic layouts would be candidates. - */ - - switch (layoutid) { - /* - * Icelandic and Faroese (planned). These layouts are sold by Apple - * Iceland for legacy applications. - */ - - case 1800: case 1821: - return kTextEncodingMacIcelandic; /* - * Irish and Welsh. These layouts are mentioned in <Script.h>. - * - * FIXME: This may have to be kTextEncodingMacGaelic instead, but I - * can't locate layouts of this type for testing. - */ - - case 581: case 779: - return kTextEncodingMacCeltic; - } - - /* - * The valid script codes are also the valid default encoding codes, so if - * nothing else helps, fall back on those. - */ - - return script; -} - -/* - *---------------------------------------------------------------------- - * - * KeycodeToUnicodeViaUnicodeResource -- - * - * Given MacOS key event data this function generates the Unicode - * characters. It does this using a 'uchr' and the UCKeyTranslate API. - * - * 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: - * 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 - * - *---------------------------------------------------------------------- - */ - -static int -KeycodeToUnicodeViaUnicodeResource( - UniChar *uniChars, - int maxChars, - Ptr uchr, - EventKind eKind, - UInt32 keycode, - UInt32 modifiers, - UInt32 *deadKeyStatePtr) -{ - int action; - unsigned long keyboardType; - OptionBits options = 0; - UInt32 dummy_state; - UniCharCount actuallength; - OSStatus err; - - keycode &= 0xFF; - modifiers = (modifiers >> 8) & 0xFF; - keyboardType = LMGetKbdType(); - - 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: - TkMacOSXDbgMsg("Invalid parameter eKind %d", eKind); - return 0; - } - - err = ChkErr(UCKeyTranslate, (const UCKeyboardLayout *) uchr, keycode, - action, modifiers, keyboardType, options, deadKeyStatePtr, - maxChars, &actuallength, uniChars); - - if ((0 == actuallength) && (0 != *deadKeyStatePtr)) { - /* - * More data later - */ - - return 0; - } - - /* - * Some IMEs leave residue. :-( - */ - - *deadKeyStatePtr = 0; - - if (err != noErr) { - actuallength = 0; - } - - return actuallength; -} - -/* - *---------------------------------------------------------------------- - * - * KeycodeToUnicodeViaKCHRResource -- - * - * Given MacOS key event data this function generates the Unicode - * characters. It does this using a 'KCHR' and the KeyTranslate API. - * - * The parameter deadKeyStatePtr can be NULL, if no deadkey handling is - * needed. - * - * 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 - * - *---------------------------------------------------------------------- - */ - -static int -KeycodeToUnicodeViaKCHRResource( - UniChar *uniChars, - int maxChars, - Ptr kchr, - TextEncoding encoding, - EventKind eKind, - UInt32 keycode, - UInt32 modifiers, - UInt32 *deadKeyStatePtr) -{ - UInt32 result; - char macBuff[3]; - char *macStr; - int macStrLen, uniStrLen; - UInt32 dummy_state = 0; - CFStringRef cfString; - - 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 == result) && (0 != *deadKeyStatePtr)) { - /* - * More data later + * Use special '-1' to signify a special keycode to our platform + * specific code in tkMacOSXKeyboard.c. This is rather like what + * happens on Windows. */ - return 0; - } - - 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. - */ + xEvent.xany.send_event = -1; - macStr = macBuff; - macStrLen = 2; - } else if (0 != macBuff[1]) { /* - * Only the second is valid. + * Set keycode (which was zero) to the changed modifier */ - macStr = macBuff+1; - macStrLen = 1; + xEvent.xkey.keycode = (modifiers ^ savedModifiers); } else { - /* - * No valid bytes at all -- shouldn't happen. - */ - - macStr = NULL; - macStrLen = 0; - } - - if (macStrLen <= 0) { - return 0; - } - - /* - * 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. - * - * FIXME: The system ships with an Irish 'KCHR' but without the - * corresponding macCeltic encoding, which triggers the error below. Tcl - * doesn't have the macCeltic encoding either right now, so until we get - * that, we can just as well stick to this code. The right fix would be to - * use the Tcl encodings and add macCeltic and probably others - * there. Suitable Unicode data files for the missing encodings are - * available from www.evertype.com. - */ - - cfString = CFStringCreateWithCStringNoCopy(NULL, macStr, encoding, - kCFAllocatorNull); - if (cfString == NULL) { - TkMacOSXDbgMsg("CFString: Can't convert with encoding %ld", encoding); - return 0; - } - - uniStrLen = CFStringGetLength(cfString); - if (uniStrLen > maxChars) { - uniStrLen = maxChars; + if (type == NSKeyUp || repeat) { + xEvent.xany.type = KeyRelease; + } else { + xEvent.xany.type = KeyPress; + } + xEvent.xkey.keycode = (keyCode << 16) | (UInt16) + [characters characterAtIndex:0]; + if (![characters getCString:xEvent.xkey.trans_chars + maxLength:XMaxTransChars encoding:NSUTF8StringEncoding]) { + TkMacOSXDbgMsg("characters too long"); + return theEvent; + } + len = [charactersIgnoringModifiers length]; + if (len) { + xEvent.xkey.nbytes = [charactersIgnoringModifiers characterAtIndex:0]; + if (len > 1) { + TkMacOSXDbgMsg("more than one charactersIgnoringModifiers"); + } + } + if (repeat) { + Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); + xEvent.xany.type = KeyPress; + xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); + } } - CFStringGetCharacters(cfString, CFRangeMake(0,uniStrLen), uniChars); - CFRelease(cfString); + Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); + savedModifiers = modifiers; - return uniStrLen; + return theEvent; } - -/* - *---------------------------------------------------------------------- - * - * 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 - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE int -TkMacOSXKeycodeToUnicode( - UniChar *uniChars, - int maxChars, - EventKind eKind, - UInt32 keycode, - UInt32 modifiers, - UInt32 *deadKeyStatePtr) -{ - Ptr resource = NULL; - TextEncoding encoding; +@end - if (GetKeyboardLayout(&resource, &encoding)) { - return KeycodeToUnicodeViaUnicodeResource(uniChars, maxChars, - resource, eKind, keycode, modifiers, deadKeyStatePtr); - } else { - return KeycodeToUnicodeViaKCHRResource(uniChars, maxChars, resource, - encoding, eKind, keycode, modifiers, deadKeyStatePtr); - } -} +#pragma mark - /* *---------------------------------------------------------------------- @@ -1012,7 +201,17 @@ XGrabKeyboard( Time time) { keyboardGrabWinPtr = Tk_IdToWindow(display, grab_window); + if (keyboardGrabWinPtr && grabWinPtr) { + NSWindow *w = TkMacOSXDrawableWindow(grab_window); + MacDrawable *macWin = (MacDrawable *) grab_window; + if (w && macWin->toplevel->winPtr == (TkWindow*) grabWinPtr) { + if (modalSession) { + Tcl_Panic("XGrabKeyboard: already grabbed"); + } + modalSession = [NSApp beginModalSessionForWindow:[w retain]]; + } + } return GrabSuccess; } @@ -1037,6 +236,13 @@ XUngrabKeyboard( Display* display, Time time) { + if (modalSession) { + NSWindow *w = keyboardGrabWinPtr ? TkMacOSXDrawableWindow( + ((TkWindow *) keyboardGrabWinPtr)->window) : nil; + [NSApp endModalSession:modalSession]; + [w release]; + modalSession = NULL; + } keyboardGrabWinPtr = NULL; } @@ -1047,6 +253,7 @@ XUngrabKeyboard( * * Results: * Returns the current grab window + * * Side effects: * None. * @@ -1062,6 +269,26 @@ TkMacOSXGetCapture(void) /* *---------------------------------------------------------------------- * + * TkMacOSXGetModalSession -- + * + * Results: + * Returns the current modal session + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE NSModalSession +TkMacOSXGetModalSession(void) +{ + return modalSession; +} + +/* + *---------------------------------------------------------------------- + * * TkpSetCapture -- * * This function captures the mouse so that all future events will be @@ -1084,25 +311,6 @@ TkpSetCapture( while (winPtr && !Tk_IsTopLevel(winPtr)) { winPtr = winPtr->parentPtr; } -#if 0 - { - TkWindow *w = NULL; - WindowModality m; - - if (winPtr) { - w = winPtr; - m = kWindowModalityAppModal; - } else if (grabWinPtr) { - w = (TkWindow *) grabWinPtr; - m = kWindowModalityNone; - } - - if (w && w->window != None && TkMacOSXHostToplevelExists(w)) { - ChkErr(SetWindowModality, TkMacOSXDrawableWindow(w->window), m, - NULL); - } - } -#endif grabWinPtr = (Tk_Window) winPtr; } @@ -1134,32 +342,6 @@ Tk_SetCaretPos( } /* - *---------------------------------------------------------------------- - * - * TkMacOSXInitKeyboard -- - * - * This procedure initializes the keyboard layout. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE void -TkMacOSXInitKeyboard( - Tcl_Interp *interp) -{ - Ptr resource; - TextEncoding encoding; - - GetKeyboardLayout(&resource, &encoding); -} - -/* * Local Variables: * mode: c * c-basic-offset: 4 |