summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXKeyEvent.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tkMacOSXKeyEvent.c')
-rw-r--r--macosx/tkMacOSXKeyEvent.c1114
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(&currentLayout);
-
- 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 **) &currentLayoutId);
-
- 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