Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 0970a92038
Fetching contributors…

Cannot retrieve contributors at this time

1169 lines (1037 sloc) 31.899 kb
/*
* 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:
*/
Jump to Line
Something went wrong with that request. Please try again.