summaryrefslogtreecommitdiffstats
path: root/carbon/tkMacOSXKeyEvent.c
diff options
context:
space:
mode:
Diffstat (limited to 'carbon/tkMacOSXKeyEvent.c')
-rw-r--r--carbon/tkMacOSXKeyEvent.c1168
1 files changed, 1168 insertions, 0 deletions
diff --git a/carbon/tkMacOSXKeyEvent.c b/carbon/tkMacOSXKeyEvent.c
new file mode 100644
index 0000000..d801f68
--- /dev/null
+++ b/carbon/tkMacOSXKeyEvent.c
@@ -0,0 +1,1168 @@
+/*
+ * tkMacOSXKeyEvent.c --
+ *
+ * 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>
+ *
+ * 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.
+ */
+
+#include "tkMacOSXPrivate.h"
+#include "tkMacOSXEvent.h"
+
+/*
+#ifdef TK_MAC_DEBUG
+#define TK_MAC_DEBUG_KEYBOARD
+#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;
+ }
+
+ 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);
+ 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.
+ */
+
+ 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);
+
+ 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;
+ }
+ if (eventGenerated == -1) {
+ savedKeyCode = 0;
+ statusPtr->stopProcessing = 0;
+ return false;
+ }
+ 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 (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;
+ }
+ }
+ }
+
+ 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;
+ }
+
+ 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);
+
+ if (!tkwin) {
+ TkMacOSXDbgMsg("tkwin == NULL");
+ return -1;
+ }
+
+ tkwin = (Tk_Window) ((TkWindow *) tkwin)->dispPtr->focusPtr;
+ if (!tkwin) {
+ TkMacOSXDbgMsg("tkwin == NULL");
+ return -1;
+ }
+
+ memset(eventPtr, 0, sizeof(XEvent));
+ 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);
+ } else {
+ TkMacOSXDbgMsg("Use cached layout (should have been %ld)",
+ currentLayoutId);
+ }
+#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
+ */
+
+ 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.
+ */
+
+ 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 -- 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;
+ }
+ CFStringGetCharacters(cfString, CFRangeMake(0,uniStrLen), uniChars);
+ CFRelease(cfString);
+
+ return uniStrLen;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+
+ if (GetKeyboardLayout(&resource, &encoding)) {
+ return KeycodeToUnicodeViaUnicodeResource(uniChars, maxChars,
+ resource, eKind, keycode, modifiers, deadKeyStatePtr);
+ } else {
+ return KeycodeToUnicodeViaKCHRResource(uniChars, maxChars, resource,
+ encoding, eKind, keycode, modifiers, deadKeyStatePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGrabKeyboard --
+ *
+ * Simulates a keyboard grab by setting the focus.
+ *
+ * Results:
+ * Always returns GrabSuccess.
+ *
+ * Side effects:
+ * Sets the keyboard focus to the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XGrabKeyboard(
+ Display* display,
+ Window grab_window,
+ Bool owner_events,
+ int pointer_mode,
+ int keyboard_mode,
+ Time time)
+{
+ keyboardGrabWinPtr = Tk_IdToWindow(display, grab_window);
+
+ return GrabSuccess;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XUngrabKeyboard --
+ *
+ * Releases the simulated keyboard grab.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets the keyboard focus back to the value before the grab.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XUngrabKeyboard(
+ Display* display,
+ Time time)
+{
+ keyboardGrabWinPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXGetCapture --
+ *
+ * Results:
+ * Returns the current grab window
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tk_Window
+TkMacOSXGetCapture(void)
+{
+ return grabWinPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetCapture --
+ *
+ * This function captures the mouse so that all future events will be
+ * reported to this window, even if the mouse is outside the window. If
+ * the specified window is NULL, then the mouse is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets the capture flag and captures the mouse.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSetCapture(
+ TkWindow *winPtr) /* Capture window, or NULL. */
+{
+ 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_SetCaretPos(
+ Tk_Window tkwin,
+ int x,
+ int y,
+ int height)
+{
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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
+ * fill-column: 79
+ * coding: utf-8
+ * End:
+ */