tkMacOSXKeyEvent.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:31k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tkMacOSXKeyEvent.c --
  3.  *
  4.  * This file implements functions that decode & handle keyboard events
  5.  * on MacOS X.
  6.  *
  7.  * Copyright 2001, Apple Computer, Inc.
  8.  * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net>
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution of
  11.  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * The following terms apply to all files originating from Apple
  14.  * Computer, Inc. ("Apple") and associated with the software
  15.  * unless explicitly disclaimed in individual files.
  16.  *
  17.  *
  18.  * Apple hereby grants permission to use, copy, modify,
  19.  * distribute, and license this software and its documentation
  20.  * for any purpose, provided that existing copyright notices are
  21.  * retained in all copies and that this notice is included
  22.  * verbatim in any distributions. No written agreement, license,
  23.  * or royalty fee is required for any of the authorized
  24.  * uses. Modifications to this software may be copyrighted by
  25.  * their authors and need not follow the licensing terms
  26.  * described here, provided that the new terms are clearly
  27.  * indicated on the first page of each file where they apply.
  28.  *
  29.  *
  30.  * IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE
  31.  * SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
  32.  * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
  33.  * THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
  34.  * EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE
  35.  * POSSIBILITY OF SUCH DAMAGE.  APPLE, THE AUTHORS AND
  36.  * DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
  37.  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  38.  * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS
  39.  * SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE
  40.  * AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
  41.  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  42.  *
  43.  * GOVERNMENT USE: If you are acquiring this software on behalf
  44.  * of the U.S. government, the Government shall have only
  45.  * "Restricted Rights" in the software and related documentation
  46.  * as defined in the Federal Acquisition Regulations (FARs) in
  47.  * Clause 52.227.19 (c) (2).  If you are acquiring the software
  48.  * on behalf of the Department of Defense, the software shall be
  49.  * classified as "Commercial Computer Software" and the
  50.  * Government shall have only "Restricted Rights" as defined in
  51.  * Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
  52.  * foregoing, the authors grant the U.S. Government and others
  53.  * acting in its behalf permission to use and distribute the
  54.  * software in accordance with the terms specified in this
  55.  * license.
  56.  *
  57.  * RCS: @(#) $Id: tkMacOSXKeyEvent.c,v 1.6.2.15 2007/06/29 03:22:02 das Exp $
  58.  */
  59. #include "tkMacOSXPrivate.h"
  60. #include "tkMacOSXEvent.h"
  61. /*
  62. #ifdef TK_MAC_DEBUG
  63. #define TK_MAC_DEBUG_KEYBOARD
  64. #endif
  65. */
  66. typedef struct {
  67.     WindowRef whichWindow;
  68.     int global_x, global_y;
  69.     int local_x, local_y;
  70.     unsigned int state;
  71.     UInt32 keyCode;
  72.     UInt32 keyModifiers;
  73.     UInt32 message;
  74.     unsigned char ch;
  75. } KeyEventData;
  76. static Tk_Window grabWinPtr = NULL;
  77. /* Current grab window, NULL if no grab. */
  78. static Tk_Window keyboardGrabWinPtr = NULL;
  79. /* Current keyboard grab window. */
  80. static UInt32 deadKeyStateUp = 0;
  81. /* The deadkey state for the current sequence
  82.  * of keyup events or 0 if not in a deadkey
  83.  * sequence */
  84. static UInt32 deadKeyStateDown = 0;
  85. /* Ditto for keydown */
  86. /*
  87.  * Declarations for functions used only in this file.
  88.  */
  89. static int InitKeyData(KeyEventData *keyEventDataPtr);
  90. static int InitKeyEvent(XEvent *eventPtr, KeyEventData *e, UInt32 savedKeyCode,
  91.     UInt32 savedModifiers);
  92. static int GenerateKeyEvent(UInt32 eKind, KeyEventData *e, UInt32 savedKeyCode,
  93.     UInt32 savedModifiers, const UniChar *chars, int numChars);
  94. static int GetKeyboardLayout(Ptr *resourcePtr, TextEncoding *encodingPtr);
  95. static TextEncoding GetKCHREncoding(ScriptCode script, SInt32 layoutid);
  96. static int KeycodeToUnicodeViaUnicodeResource(UniChar *uniChars, int maxChars,
  97.     Ptr uchr, EventKind eKind, UInt32 keycode, UInt32 modifiers,
  98.     UInt32 *deadKeyStatePtr);
  99. static int KeycodeToUnicodeViaKCHRResource(UniChar *uniChars, int maxChars,
  100.     Ptr kchr, TextEncoding encoding, EventKind eKind, UInt32 keycode,
  101.     UInt32 modifiers, UInt32 *deadKeyStatePtr);
  102. /*
  103.  *----------------------------------------------------------------------
  104.  *
  105.  * TkMacOSXProcessKeyboardEvent --
  106.  *
  107.  * This routine processes the event in eventPtr, and
  108.  * generates the appropriate Tk events from it.
  109.  *
  110.  * Results:
  111.  * True if event(s) are generated - false otherwise.
  112.  *
  113.  * Side effects:
  114.  * Additional events may be place on the Tk event queue.
  115.  *
  116.  *----------------------------------------------------------------------
  117.  */
  118. MODULE_SCOPE int
  119. TkMacOSXProcessKeyboardEvent(
  120.     TkMacOSXEvent *eventPtr,
  121.     MacEventStatus *statusPtr)
  122. {
  123.     static UInt32 savedKeyCode = 0;
  124.     static UInt32 savedModifiers = 0;
  125.     static UniChar savedChar = 0;
  126.     OSStatus err;
  127.     KeyEventData keyEventData;
  128.     MenuRef menuRef;
  129.     MenuItemIndex menuItemIndex;
  130.     int eventGenerated;
  131.     UniChar uniChars[5]; /* make this larger, if needed */
  132.     UInt32 uniCharsLen = 0;
  133.     if (!InitKeyData(&keyEventData)) {
  134. statusPtr->err = 1;
  135. return false;
  136.     }
  137.     /*
  138.      * Because of the way that Tk operates, we can't in general funnel menu
  139.      * accelerators through IsMenuKeyEvent. Tk treats accelerators as mere
  140.      * decoration, and the user has to install bindings to get them to fire.
  141.      *
  142.      * However, the only way to trigger the Hide & Hide Others functions
  143.      * is by invoking the Menu command for Hide. So there is no nice way to
  144.      * provide a Tk command to hide the app which would be available for a
  145.      * binding. So I am going to hijack Command-H and Command-Shift-H
  146.      * here, and run the menu commands. Since the HI Guidelines explicitly
  147.      * reserve these for Hide, this isn't such a bad thing. Also, if you do
  148.      * rebind Command-H to another menu item, Hide will lose its binding.
  149.      *
  150.      * Note that I don't really do anything at this point,
  151.      * I just mark stopProcessing as 0 and return, and then the
  152.      * RecieveAndProcessEvent code will dispatch the event to the default
  153.      * handler.
  154.      */
  155.     if ((eventPtr->eKind == kEventRawKeyDown
  156.     || eventPtr->eKind == kEventRawKeyRepeat)
  157.     && IsMenuKeyEvent(tkCurrentAppleMenu, eventPtr->eventRef,
  158.     kMenuEventQueryOnly, &menuRef, &menuItemIndex)) {
  159. MenuCommand menuCmd;
  160. GetMenuItemCommandID (menuRef, menuItemIndex, &menuCmd);
  161. switch (menuCmd) {
  162.     case kHICommandHide:
  163.     case kHICommandHideOthers:
  164.     case kHICommandShowAll:
  165.     case kHICommandPreferences:
  166.     case kHICommandQuit:
  167. statusPtr->stopProcessing = 0;
  168. /*
  169.  * TODO: may not be on event on queue.
  170.  */
  171. return 0;
  172. break;
  173.     default:
  174. break;
  175. }
  176.     }
  177.     err = ChkErr(GetEventParameter, eventPtr->eventRef,
  178.     kEventParamKeyMacCharCodes, typeChar, NULL,
  179.     sizeof(keyEventData.ch), NULL, &keyEventData.ch);
  180.     if (err != noErr) {
  181. statusPtr->err = 1;
  182. return false;
  183.     }
  184.     err = ChkErr(GetEventParameter, eventPtr->eventRef, kEventParamKeyCode,
  185.     typeUInt32, NULL, sizeof(keyEventData.keyCode), NULL,
  186.     &keyEventData.keyCode);
  187.     if (err != noErr) {
  188. statusPtr->err = 1;
  189. return false;
  190.     }
  191.     err = ChkErr(GetEventParameter, eventPtr->eventRef,
  192.     kEventParamKeyModifiers, typeUInt32, NULL,
  193.     sizeof(keyEventData.keyModifiers), NULL,
  194.     &keyEventData.keyModifiers);
  195.     if (err != noErr) {
  196. statusPtr->err = 1;
  197. return false;
  198.     }
  199.     switch (eventPtr->eKind) {
  200. case kEventRawKeyUp:
  201. case kEventRawKeyDown:
  202. case kEventRawKeyRepeat: {
  203.     UInt32 *deadKeyStatePtr;
  204.     if (kEventRawKeyDown == eventPtr->eKind) {
  205. deadKeyStatePtr = &deadKeyStateDown;
  206.     } else {
  207. deadKeyStatePtr = &deadKeyStateUp;
  208.     }
  209.     uniCharsLen = TkMacOSXKeycodeToUnicode(uniChars,
  210.     sizeof(uniChars)/sizeof(*uniChars), eventPtr->eKind,
  211.     keyEventData.keyCode, keyEventData.keyModifiers,
  212.     deadKeyStatePtr);
  213.     break;
  214. }
  215.     }
  216.     if (kEventRawKeyUp == eventPtr->eKind) {
  217. /*
  218.  * For some reason the deadkey processing for KeyUp doesn't work
  219.  * sometimes, so we fudge and use the last detected KeyDown.
  220.  */
  221. if ((0 == uniCharsLen) && (0 != savedChar)) {
  222.     uniChars[0] = savedChar;
  223.     uniCharsLen = 1;
  224. }
  225. /*
  226.  * Suppress keyup events while we have a deadkey sequence on keydown.
  227.  * We still *do* want to collect deadkey state in this situation if
  228.  * the system provides it, that's why we do this only after
  229.  * TkMacOSXKeycodeToUnicode().
  230.  */
  231. if (0 != deadKeyStateDown) {
  232.     uniCharsLen = 0;
  233. }
  234.     }
  235.     keyEventData.message = keyEventData.ch|(keyEventData.keyCode << 8);
  236.     eventGenerated = GenerateKeyEvent(eventPtr->eKind, &keyEventData,
  237.     savedKeyCode, savedModifiers, uniChars, uniCharsLen);
  238.     savedModifiers = keyEventData.keyModifiers;
  239.     if ((kEventRawKeyDown == eventPtr->eKind) && (uniCharsLen > 0)) {
  240. savedChar = uniChars[0];
  241.     } else {
  242. savedChar = 0;
  243.     }
  244.     statusPtr->stopProcessing = 1;
  245.     if (eventGenerated == 0) {
  246. savedKeyCode = keyEventData.message;
  247. return false;
  248.     } else if (eventGenerated == -1) {
  249. savedKeyCode = 0;
  250. statusPtr->stopProcessing = 0;
  251. return false;
  252.     } else {
  253. savedKeyCode = 0;
  254. return true;
  255.     }
  256. }
  257. /*
  258.  *----------------------------------------------------------------------
  259.  *
  260.  * GenerateKeyEvent --
  261.  *
  262.  * Given Macintosh keyUp, keyDown & autoKey events (in their "raw"
  263.  * form) and a list of unicode characters this function generates the
  264.  * appropriate X key events.
  265.  *
  266.  * Parameter eKind is a raw keyboard event. e contains the data sent
  267.  * with the event. savedKeyCode and savedModifiers contain the values
  268.  * from the last event that came before (see
  269.  * TkMacOSXProcessKeyboardEvent()). chars/numChars has the Unicode
  270.  * characters for which we want to create events.
  271.  *
  272.  * Results:
  273.  * 1 if an event was generated, -1 for any error.
  274.  *
  275.  * Side effects:
  276.  * Additional events may be place on the Tk event queue.
  277.  *
  278.  *----------------------------------------------------------------------
  279.  */
  280. static int
  281. GenerateKeyEvent(
  282.     UInt32 eKind,
  283.     KeyEventData * e,
  284.     UInt32 savedKeyCode,
  285.     UInt32 savedModifiers,
  286.     const UniChar * chars,
  287.     int numChars)
  288. {
  289.     XEvent event;
  290.     int i;
  291.     if (-1 == InitKeyEvent(&event, e, savedKeyCode, savedModifiers)) {
  292. return -1;
  293.     }
  294.     if (kEventRawKeyModifiersChanged == eKind) {
  295. if (savedModifiers > e->keyModifiers) {
  296.     event.xany.type = KeyRelease;
  297. } else {
  298.     event.xany.type = KeyPress;
  299. }
  300. /*
  301.  * Use special '-1' to signify a special keycode to our
  302.  * platform specific code in tkMacOSXKeyboard.c. This is
  303.  * rather like what happens on Windows.
  304.  */
  305. event.xany.send_event = -1;
  306. /*
  307.  * Set keycode (which was zero) to the changed modifier
  308.  */
  309. event.xkey.keycode = (e->keyModifiers ^ savedModifiers);
  310. Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  311.     } else {
  312. for (i = 0; i < numChars; ++i) {
  313.     /*
  314.      * Encode one char in the trans_chars array that was already
  315.      * introduced for MS Windows. Don't encode the string, if it is
  316.      * a control character but was not generated with a real control
  317.      * modifier. Such control characters get generated by KeyTrans()
  318.      * for special keys, but we rather want to identify those by
  319.      * their KeySyms.
  320.      */
  321.     event.xkey.trans_chars[0] = 0;
  322.     if ((controlKey & e->keyModifiers) || (chars[i] >= ' ')) {
  323. int done;
  324. done = Tcl_UniCharToUtf(chars[i],event.xkey.trans_chars);
  325. event.xkey.trans_chars[done] = 0;
  326.     }
  327.     switch(eKind) {
  328. case kEventRawKeyDown:
  329.     event.xany.type = KeyPress;
  330.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  331.     break;
  332. case kEventRawKeyUp:
  333.     event.xany.type = KeyRelease;
  334.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  335.     break;
  336. case kEventRawKeyRepeat:
  337.     event.xany.type = KeyRelease;
  338.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  339.     event.xany.type = KeyPress;
  340.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  341.     break;
  342. default:
  343.     TkMacOSXDbgMsg("Invalid parameter eKind %ld", eKind);
  344.     return -1;
  345.     }
  346. }
  347.     }
  348.     return 1;
  349. }
  350. /*
  351.  *----------------------------------------------------------------------
  352.  *
  353.  * InitKeyData --
  354.  *
  355.  * This routine initializes a KeyEventData structure by asking the OS
  356.  * and Tk for all the global information needed here.
  357.  *
  358.  * Results:
  359.  * True if the current front window can be found in Tk data structures
  360.  * - false otherwise.
  361.  *
  362.  * Side Effects:
  363.  * None
  364.  *
  365.  *----------------------------------------------------------------------
  366.  */
  367. static int
  368. InitKeyData(
  369.     KeyEventData *keyEventDataPtr)
  370. {
  371.     memset(keyEventDataPtr, 0, sizeof(*keyEventDataPtr));
  372.     keyEventDataPtr->whichWindow = ActiveNonFloatingWindow();
  373.     if (keyEventDataPtr->whichWindow == NULL) {
  374. return false;
  375.     }
  376.     XQueryPointer(NULL, None, NULL, NULL, &keyEventDataPtr->global_x,
  377.     &keyEventDataPtr->global_y, &keyEventDataPtr->local_x,
  378.     &keyEventDataPtr->local_y, &keyEventDataPtr->state);
  379.     return true;
  380. }
  381. /*
  382.  *----------------------------------------------------------------------
  383.  *
  384.  * InitKeyEvent --
  385.  *
  386.  * Initialize an XEvent structure by asking Tk for global information.
  387.  * Also uses a KeyEventData structure and other current state.
  388.  *
  389.  * Results:
  390.  * 1 on success, -1 for any error.
  391.  *
  392.  * Side effects:
  393.  * Additional events may be place on the Tk event queue.
  394.  *
  395.  *----------------------------------------------------------------------
  396.  */
  397. /*
  398.  * We have a general problem here. How do we handle 'Option-char'
  399.  * keypresses? The problem is that we might want to bind to some of these
  400.  * (e.g. Cmd-Opt-d is 'uncomment' in Alpha). OTOH Option-d actually produces
  401.  * a real character on MacOS, namely a mathematical delta.
  402.  *
  403.  * The current behaviour is that a binding goes by the combinations of
  404.  * modifiers and base keysym, that is Option-d. The string value of the
  405.  * event is the mathematical delta character, so if no binding calls
  406.  * [break], the text widget will insert that character.
  407.  *
  408.  * Note that this is similar to control combinations on all platforms. They
  409.  * also generate events that have the base character as keysym and a real
  410.  * control character as character value. So Ctrl+C gets us the keysym XK_C,
  411.  * the modifier Control (so you can bind <Control-C>) and a string value as
  412.  * "u0003".
  413.  *
  414.  * For a different solutions we may want for the event to contain keysyms for
  415.  * *both* the 'Opt-d' side of things and the mathematical delta. Then a
  416.  * binding on Opt-d will trigger, but a binding on mathematical delta would
  417.  * also trigger. This would require changes in the core, though.
  418.  */
  419. static int
  420. InitKeyEvent(
  421.     XEvent * eventPtr,
  422.     KeyEventData * e,
  423.     UInt32 savedKeyCode,
  424.     UInt32 savedModifiers)
  425. {
  426.     Window window;
  427.     Tk_Window tkwin;
  428.     TkDisplay *dispPtr;
  429.     /*
  430.      * The focus must be in the FrontWindow on the Macintosh.
  431.      * We then query Tk to determine the exact Tk window
  432.      * that owns the focus.
  433.      */
  434.     window = TkMacOSXGetXWindow(e->whichWindow);
  435.     dispPtr = TkGetDisplayList();
  436.     tkwin = Tk_IdToWindow(dispPtr->display, window);
  437.     if (!tkwin) {
  438. TkMacOSXDbgMsg("tkwin == NULL");
  439. return -1;
  440.     }
  441.     tkwin = (Tk_Window) ((TkWindow *) tkwin)->dispPtr->focusPtr;
  442.     if (!tkwin) {
  443. TkMacOSXDbgMsg("tkwin == NULL");
  444. return -1;
  445.     }
  446.     eventPtr->xany.send_event = false;
  447.     eventPtr->xany.serial = Tk_Display(tkwin)->request;
  448.     eventPtr->xkey.same_screen = true;
  449.     eventPtr->xkey.subwindow = None;
  450.     eventPtr->xkey.time = TkpGetMS();
  451.     eventPtr->xkey.x_root = e->global_x;
  452.     eventPtr->xkey.y_root = e->global_y;
  453.     eventPtr->xkey.window = Tk_WindowId(tkwin);
  454.     eventPtr->xkey.display = Tk_Display(tkwin);
  455.     eventPtr->xkey.root = XRootWindow(Tk_Display(tkwin), 0);
  456.     eventPtr->xkey.state =  e->state;
  457.     eventPtr->xkey.trans_chars[0] = 0;
  458.     Tk_TopCoordsToWindow(tkwin, e->local_x, e->local_y, &eventPtr->xkey.x,
  459.     &eventPtr->xkey.y);
  460.     eventPtr->xkey.keycode = e->ch | ((savedKeyCode & charCodeMask) << 8) |
  461.     ((e->message&keyCodeMask) << 8);
  462.     return 1;
  463. }
  464. /*
  465.  *----------------------------------------------------------------------
  466.  *
  467.  * GetKeyboardLayout --
  468.  *
  469.  * Queries the OS for a pointer to a keyboard resource.
  470.  *
  471.  * This function works with the keyboard layout switch menu. It uses
  472.  * Keyboard Layout Services, where available.
  473.  *
  474.  * Results:
  475.  * 1 if there is returned a Unicode 'uchr' resource in *resourcePtr, 0
  476.  * if it is a classic 'KCHR' resource. A pointer to the actual resource
  477.  * data goes into *resourcePtr. If the resource is a 'KCHR' resource,
  478.  * the corresponding Mac encoding goes into *encodingPtr.
  479.  *
  480.  * Side effects:
  481.  * Sets some internal static variables.
  482.  *
  483.  *----------------------------------------------------------------------
  484.  */
  485. static int
  486. GetKeyboardLayout(
  487.     Ptr *resourcePtr,
  488.     TextEncoding *encodingPtr)
  489. {
  490.     static KeyboardLayoutRef lastLayout = NULL;
  491.     static SInt32 lastLayoutId;
  492.     static TextEncoding lastEncoding = kTextEncodingMacRoman;
  493.     static Ptr uchr = NULL;
  494.     static Ptr KCHR = NULL;
  495.     int hasLayoutChanged = false;
  496.     KeyboardLayoutRef currentLayout = NULL;
  497.     SInt32 currentLayoutId = 0;
  498.     ScriptCode currentKeyScript;
  499.     currentKeyScript = GetScriptManagerVariable(smKeyScript);
  500.     /*
  501.      * Use the Keyboard Layout Services.
  502.      */
  503.     KLGetCurrentKeyboardLayout(&currentLayout);
  504.     if (currentLayout != NULL) {
  505. /*
  506.  * The layout pointer could in theory be the same for different
  507.  * layouts, only the id gives us the information that the
  508.  * keyboard has actually changed. OTOH the layout object can
  509.  * also change and it could still be the same layoutid.
  510.  */
  511. KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier,
  512. (const void**)&currentLayoutId);
  513. if ((lastLayout != currentLayout)
  514. || (lastLayoutId != currentLayoutId)) {
  515. #ifdef TK_MAC_DEBUG_KEYBOARD
  516.     TkMacOSXDbgMsg("Use KLS");
  517. #endif
  518.     hasLayoutChanged = true;
  519.     /*
  520.      * Reinitialize all relevant variables.
  521.      */
  522.     lastLayout = currentLayout;
  523.     lastLayoutId = currentLayoutId;
  524.     uchr = NULL;
  525.     KCHR = NULL;
  526.     if ((KLGetKeyboardLayoutProperty(currentLayout,
  527.     kKLuchrData, (const void**)&uchr)
  528.     == noErr)
  529.     && (uchr != NULL)) {
  530. /* done */
  531.     } else if ((KLGetKeyboardLayoutProperty(currentLayout,
  532.     kKLKCHRData, (const void**)&KCHR)
  533.     == noErr)
  534.     && (KCHR != NULL)) {
  535. /* done */
  536.     }
  537. }
  538.     }
  539.     if (hasLayoutChanged) {
  540. #ifdef TK_MAC_DEBUG_KEYBOARD
  541. if (KCHR) {
  542.     TkMacOSXDbgMsg("New 'KCHR' layout %ld", currentLayoutId);
  543. } else if (uchr) {
  544.     TkMacOSXDbgMsg("New 'uchr' layout %ld", currentLayoutId);
  545. } else {
  546.     TkMacOSXDbgMsg("Use cached layout (should have been %ld)",
  547.     currentLayoutId);
  548. }
  549. #endif
  550. deadKeyStateUp = deadKeyStateDown = 0;
  551. /*
  552.  * If we did get a new 'KCHR', compute its encoding and put it into
  553.  * lastEncoding.
  554.  *
  555.  * If we didn't get a new 'KCHR' and if we have no 'uchr' either, get
  556.  * some 'KCHR' from the OS cache and leave lastEncoding at its
  557.  * current value. This should better not happen, it doesn't really
  558.  * work.
  559.  */
  560. if (KCHR) {
  561.     lastEncoding = GetKCHREncoding(currentKeyScript, currentLayoutId);
  562. #ifdef TK_MAC_DEBUG_KEYBOARD
  563.     TkMacOSXDbgMsg("New 'KCHR' encoding %lu (%lu + 0x%lX)",
  564.     lastEncoding, lastEncoding & 0xFFFFL,
  565.     lastEncoding & ~0xFFFFL);
  566. #endif
  567. } else if (!uchr) {
  568.     KCHR = (Ptr)(intptr_t)GetScriptManagerVariable(smKCHRCache);
  569. }
  570.     }
  571.     if (uchr) {
  572. *resourcePtr = uchr;
  573. return 1;
  574.     } else {
  575. *resourcePtr = KCHR;
  576. *encodingPtr = lastEncoding;
  577. return 0;
  578.     }
  579. }
  580. /*
  581.  *----------------------------------------------------------------------
  582.  *
  583.  * GetKCHREncoding --
  584.  *
  585.  * Upgrade a WorldScript code to a TEC encoding based on the keyboard
  586.  * layout id.
  587.  *
  588.  * Results:
  589.  * The TEC code that corresponds best to the combination of WorldScript
  590.  * code and 'KCHR' id.
  591.  *
  592.  * Side effects:
  593.  * None.
  594.  *
  595.  * Rationale and Notes:
  596.  * WorldScript codes are sometimes not unique encodings. E.g. Icelandic
  597.  * uses script smRoman (0), but the actual encoding is
  598.  * kTextEncodingMacIcelandic (37). ftp://ftp.unicode.org/Public
  599.  * /MAPPINGS/VENDORS/APPLE/README.TXT has a good summary of these
  600.  * variants. So we need to upgrade the script to an encoding with
  601.  * GetTextEncodingFromScriptInfo().
  602.  *
  603.  * 'KCHR' ids are usually region codes (see the comments in Script.h).
  604.  * Where they are not, we get a paramErr from the OS function and have
  605.  * appropriate fallbacks.
  606.  *
  607.  *----------------------------------------------------------------------
  608.  */
  609. static TextEncoding
  610. GetKCHREncoding(
  611.     ScriptCode script,
  612.     SInt32 layoutid)
  613. {
  614.     RegionCode region = layoutid;
  615.     TextEncoding encoding = script;
  616.     if (GetTextEncodingFromScriptInfo(script, kTextLanguageDontCare, region,
  617.     &encoding) == noErr) {
  618. return encoding;
  619.     }
  620.     /*
  621.      * GetTextEncodingFromScriptInfo() doesn't know about more exotic
  622.      * layouts. This provides a fallback for good measure. In an ideal
  623.      * world, exotic layouts would always provide a 'uchr' resource anyway,
  624.      * so we wouldn't need this.
  625.      *
  626.      * We can add more keyboard layouts, if we get actual complaints. Farsi
  627.      * or other Celtic/Gaelic layouts would be candidates.
  628.      */
  629.     switch (layoutid) {
  630. /*
  631.  * Icelandic and Faroese (planned). These layouts are sold by Apple
  632.  * Iceland for legacy applications.
  633.  */
  634. case 1800: case 1821:
  635.     return kTextEncodingMacIcelandic;
  636. /*
  637.  * Irish and Welsh. These layouts are mentioned in <Script.h>.
  638.  *
  639.  * FIXME: This may have to be kTextEncodingMacGaelic instead, but I
  640.  * can't locate layouts of this type for testing.
  641.  */
  642. case 581: case 779:
  643.     return kTextEncodingMacCeltic;
  644.     }
  645.     /*
  646.      * The valid script codes are also the valid default encoding codes, so
  647.      * if nothing else helps, fall back on those.
  648.      */
  649.     return script;
  650. }
  651. /*
  652.  *----------------------------------------------------------------------
  653.  *
  654.  * KeycodeToUnicodeViaUnicodeResource --
  655.  *
  656.  * Given MacOS key event data this function generates the Unicode
  657.  * characters. It does this using a 'uchr' and the UCKeyTranslate
  658.  * API.
  659.  *
  660.  * The parameter deadKeyStatePtr can be NULL, if no deadkey handling
  661.  * is needed.
  662.  *
  663.  * Tested and known to work with US, Hebrew, Greek and Russian layouts
  664.  * as well as "Unicode Hex Input".
  665.  *
  666.  * Results:
  667.  * The number of characters generated if any, 0 if we are waiting for
  668.  * another byte of a dead-key sequence. Fills in the uniChars array
  669.  * with a Unicode string.
  670.  *
  671.  * Side Effects:
  672.  * None
  673.  *
  674.  *----------------------------------------------------------------------
  675.  */
  676. static int
  677. KeycodeToUnicodeViaUnicodeResource(
  678.     UniChar *uniChars,
  679.     int maxChars,
  680.     Ptr uchr,
  681.     EventKind eKind,
  682.     UInt32 keycode,
  683.     UInt32 modifiers,
  684.     UInt32 *deadKeyStatePtr)
  685. {
  686.     int action;
  687.     unsigned long keyboardType;
  688.     OptionBits options = 0;
  689.     UInt32 dummy_state;
  690.     UniCharCount actuallength;
  691.     OSStatus err;
  692.     keycode &= 0xFF;
  693.     modifiers = (modifiers >> 8) & 0xFF;
  694.     keyboardType = LMGetKbdType();
  695.     if (NULL==deadKeyStatePtr) {
  696. options = kUCKeyTranslateNoDeadKeysMask;
  697. dummy_state = 0;
  698. deadKeyStatePtr = &dummy_state;
  699.     }
  700.     switch(eKind) {
  701. case kEventRawKeyDown:
  702.     action = kUCKeyActionDown;
  703.     break;
  704. case kEventRawKeyUp:
  705.     action = kUCKeyActionUp;
  706.     break;
  707. case kEventRawKeyRepeat:
  708.     action = kUCKeyActionAutoKey;
  709.     break;
  710. default:
  711.     TkMacOSXDbgMsg("Invalid parameter eKind %d", eKind);
  712.     return 0;
  713.     }
  714.     err = ChkErr(UCKeyTranslate, (const UCKeyboardLayout *) uchr, keycode,
  715.     action, modifiers, keyboardType, options, deadKeyStatePtr,
  716.     maxChars, &actuallength, uniChars);
  717.     if ((0 == actuallength) && (0 != *deadKeyStatePtr)) {
  718. /*
  719.  * More data later
  720.  */
  721. return 0;
  722.     }
  723.     /*
  724.      * some IMEs leave residue :-(
  725.      */
  726.     *deadKeyStatePtr = 0;
  727.     if (err != noErr) {
  728. actuallength = 0;
  729.     }
  730.     return actuallength;
  731. }
  732. /*
  733.  *----------------------------------------------------------------------
  734.  *
  735.  * KeycodeToUnicodeViaKCHRResource --
  736.  *
  737.  * Given MacOS key event data this function generates the Unicode
  738.  * characters. It does this using a 'KCHR' and the KeyTranslate API.
  739.  *
  740.  * The parameter deadKeyStatePtr can be NULL, if no deadkey handling
  741.  * is needed.
  742.  *
  743.  * Results:
  744.  * The number of characters generated if any, 0 if we are waiting for
  745.  * another byte of a dead-key sequence. Fills in the uniChars array
  746.  * with a Unicode string.
  747.  *
  748.  * Side Effects:
  749.  * None
  750.  *
  751.  *----------------------------------------------------------------------
  752.  */
  753. static int
  754. KeycodeToUnicodeViaKCHRResource(
  755.     UniChar *uniChars,
  756.     int maxChars,
  757.     Ptr kchr,
  758.     TextEncoding encoding,
  759.     EventKind eKind,
  760.     UInt32 keycode,
  761.     UInt32 modifiers,
  762.     UInt32 *deadKeyStatePtr)
  763. {
  764.     UInt32 result;
  765.     char macBuff[3];
  766.     char *macStr;
  767.     int macStrLen;
  768.     UInt32 dummy_state = 0;
  769.     if (NULL == deadKeyStatePtr) {
  770. deadKeyStatePtr = &dummy_state;
  771.     }
  772.     keycode |= modifiers;
  773.     result = KeyTranslate(kchr, keycode, deadKeyStatePtr);
  774.     if ((0 == result) && (0 != dummy_state)) {
  775. /*
  776.  * 'dummy_state' gets only filled if the caller did not want deadkey
  777.  * processing (deadKeyStatePtr was NULL originally), but we still
  778.  * have a deadkey. We just push the keycode for the space bar to get
  779.  * the real key value.
  780.  */
  781. result = KeyTranslate(kchr, 0x31, deadKeyStatePtr);
  782. *deadKeyStatePtr = 0;
  783.     }
  784.     if ((0 == result) && (0 != *deadKeyStatePtr)) {
  785. /*
  786.  * More data later
  787.  */
  788. return 0;
  789.     }
  790.     macBuff[0] = (char) (result >> 16);
  791.     macBuff[1] = (char)  result;
  792.     macBuff[2] = 0;
  793.     if (0 != macBuff[0]) {
  794. /*
  795.  * If the first byte is valid, the second is too
  796.  */
  797. macStr = macBuff;
  798. macStrLen = 2;
  799.     } else if (0 != macBuff[1]) {
  800. /*
  801.  * Only the second is valid
  802.  */
  803. macStr = macBuff+1;
  804. macStrLen = 1;
  805.     } else {
  806. /*
  807.  * No valid bytes at all -- shouldn't happen
  808.  */
  809. macStr = NULL;
  810. macStrLen = 0;
  811.     }
  812.     if (macStrLen <= 0) {
  813. return 0;
  814.     } else {
  815. /*
  816.  * Use the CFString conversion routines. This is the easiest and
  817.  * most compatible way to get from an 8-bit string and a MacOS script
  818.  * code to a Unicode string.
  819.  *
  820.  * FIXME: The system ships with an Irish 'KCHR' but without the
  821.  * corresponding macCeltic encoding, which triggers the error below.
  822.  * Tcl doesn't have the macCeltic encoding either right now, so until
  823.  * we get that, we can just as well stick to this code. The right
  824.  * fix would be to use the Tcl encodings and add macCeltic and
  825.  * probably others there. Suitable Unicode data files for the
  826.  * missing encodings are available from www.evertype.com.
  827.  */
  828. CFStringRef cfString;
  829. int uniStrLen;
  830. cfString = CFStringCreateWithCStringNoCopy(NULL, macStr, encoding,
  831. kCFAllocatorNull);
  832. if (cfString == NULL) {
  833.     TkMacOSXDbgMsg("CFString: Can't convert with encoding %ld",
  834.     encoding);
  835.     return 0;
  836. }
  837. uniStrLen = CFStringGetLength(cfString);
  838. if (uniStrLen > maxChars) {
  839.     uniStrLen = maxChars;
  840. }
  841. CFStringGetCharacters(cfString, CFRangeMake(0,uniStrLen), uniChars);
  842. CFRelease(cfString);
  843. return uniStrLen;
  844.     }
  845. }
  846. /*
  847.  *----------------------------------------------------------------------
  848.  *
  849.  * TkMacOSXKeycodeToUnicode --
  850.  *
  851.  * Given MacOS key event data this function generates the Unicode
  852.  * characters. It does this using OS resources and APIs.
  853.  *
  854.  * The parameter deadKeyStatePtr can be NULL, if no deadkey handling
  855.  * is needed.
  856.  *
  857.  * This function is called from XKeycodeToKeysym() in
  858.  * tkMacOSKeyboard.c.
  859.  *
  860.  * Results:
  861.  * The number of characters generated if any, 0 if we are waiting for
  862.  * another byte of a dead-key sequence. Fills in the uniChars array
  863.  * with a Unicode string.
  864.  *
  865.  * Side Effects:
  866.  * None
  867.  *
  868.  *----------------------------------------------------------------------
  869.  */
  870. MODULE_SCOPE int
  871. TkMacOSXKeycodeToUnicode(
  872.     UniChar *uniChars,
  873.     int maxChars,
  874.     EventKind eKind,
  875.     UInt32 keycode,
  876.     UInt32 modifiers,
  877.     UInt32 *deadKeyStatePtr)
  878. {
  879.     Ptr resource = NULL;
  880.     TextEncoding encoding;
  881.     int len;
  882.     if (GetKeyboardLayout(&resource,&encoding)) {
  883. len = KeycodeToUnicodeViaUnicodeResource(
  884.     uniChars, maxChars, resource, eKind,
  885.     keycode, modifiers, deadKeyStatePtr);
  886.     } else {
  887. len = KeycodeToUnicodeViaKCHRResource(
  888.     uniChars, maxChars, resource, encoding, eKind,
  889.     keycode, modifiers, deadKeyStatePtr);
  890.     }
  891.     return len;
  892. }
  893. /*
  894.  *----------------------------------------------------------------------
  895.  *
  896.  * XGrabKeyboard --
  897.  *
  898.  * Simulates a keyboard grab by setting the focus.
  899.  *
  900.  * Results:
  901.  * Always returns GrabSuccess.
  902.  *
  903.  * Side effects:
  904.  * Sets the keyboard focus to the specified window.
  905.  *
  906.  *----------------------------------------------------------------------
  907.  */
  908. int
  909. XGrabKeyboard(
  910.     Display* display,
  911.     Window grab_window,
  912.     Bool owner_events,
  913.     int pointer_mode,
  914.     int keyboard_mode,
  915.     Time time)
  916. {
  917.     keyboardGrabWinPtr = Tk_IdToWindow(display, grab_window);
  918.     return GrabSuccess;
  919. }
  920. /*
  921.  *----------------------------------------------------------------------
  922.  *
  923.  * XUngrabKeyboard --
  924.  *
  925.  * Releases the simulated keyboard grab.
  926.  *
  927.  * Results:
  928.  * None.
  929.  *
  930.  * Side effects:
  931.  * Sets the keyboard focus back to the value before the grab.
  932.  *
  933.  *----------------------------------------------------------------------
  934.  */
  935. void
  936. XUngrabKeyboard(
  937.     Display* display,
  938.     Time time)
  939. {
  940.     keyboardGrabWinPtr = NULL;
  941. }
  942. /*
  943.  *----------------------------------------------------------------------
  944.  *
  945.  * TkMacOSXGetCapture --
  946.  *
  947.  * Results:
  948.  * Returns the current grab window
  949.  * Side effects:
  950.  * None.
  951.  *
  952.  */
  953. Tk_Window
  954. TkMacOSXGetCapture(void)
  955. {
  956.     return grabWinPtr;
  957. }
  958. /*
  959.  *----------------------------------------------------------------------
  960.  *
  961.  * TkpSetCapture --
  962.  *
  963.  * This function captures the mouse so that all future events
  964.  * will be reported to this window, even if the mouse is outside
  965.  * the window. If the specified window is NULL, then the mouse
  966.  * is released.
  967.  *
  968.  * Results:
  969.  * None.
  970.  *
  971.  * Side effects:
  972.  * Sets the capture flag and captures the mouse.
  973.  *
  974.  *----------------------------------------------------------------------
  975.  */
  976. void
  977. TkpSetCapture(
  978.     TkWindow *winPtr) /* Capture window, or NULL. */
  979. {
  980.     while (winPtr && !Tk_IsTopLevel(winPtr)) {
  981. winPtr = winPtr->parentPtr;
  982.     }
  983. #if 0
  984.     {
  985. TkWindow *w = NULL;
  986. WindowModality m;
  987. if (winPtr) {
  988.     w = winPtr;
  989.     m = kWindowModalityAppModal;
  990. } else if (grabWinPtr) {
  991.     w = (TkWindow*)grabWinPtr;
  992.     m = kWindowModalityNone;
  993. }
  994. if (w && w->window != None && TkMacOSXHostToplevelExists(w)) {
  995.     ChkErr(SetWindowModality, TkMacOSXDrawableWindow(w->window), m,
  996.     NULL);
  997. }
  998.     }
  999. #endif
  1000.     grabWinPtr = (Tk_Window) winPtr;
  1001. }
  1002. /*
  1003.  *----------------------------------------------------------------------
  1004.  *
  1005.  * Tk_SetCaretPos --
  1006.  *
  1007.  * This enables correct placement of the XIM caret. This is called
  1008.  * by widgets to indicate their cursor placement, and the caret
  1009.  * location is used by TkpGetString to place the XIM caret.
  1010.  *
  1011.  * Results:
  1012.  * None
  1013.  *
  1014.  * Side effects:
  1015.  * None
  1016.  *
  1017.  *----------------------------------------------------------------------
  1018.  */
  1019. void
  1020. Tk_SetCaretPos(
  1021.     Tk_Window tkwin,
  1022.     int x,
  1023.     int y,
  1024.     int height)
  1025. {
  1026. }
  1027. /*
  1028.  *----------------------------------------------------------------------
  1029.  *
  1030.  * TkMacOSXInitKeyboard --
  1031.  *
  1032.  * This procedure initializes the keyboard layout.
  1033.  *
  1034.  * Results:
  1035.  * None.
  1036.  *
  1037.  * Side effects:
  1038.  * None.
  1039.  *
  1040.  *----------------------------------------------------------------------
  1041.  */
  1042. MODULE_SCOPE void
  1043. TkMacOSXInitKeyboard(
  1044.     Tcl_Interp *interp)
  1045. {
  1046.     Ptr resource;
  1047.     TextEncoding encoding;
  1048.     GetKeyboardLayout(&resource, &encoding);
  1049. }