Import Tk 8.6.11
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
* Copyright 2001-2009, Apple Inc.
|
||||
* Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
|
||||
* Copyright (c) 2012 Adrian Robert.
|
||||
* Copyright 2015-2019 Marc Culler.
|
||||
* Copyright 2015-2020 Marc Culler.
|
||||
*
|
||||
* See the file "license.terms" for information on usage and redistribution of
|
||||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
@@ -16,28 +16,29 @@
|
||||
#include "tkMacOSXPrivate.h"
|
||||
#include "tkMacOSXInt.h"
|
||||
#include "tkMacOSXConstants.h"
|
||||
#include "tkMacOSXWm.h"
|
||||
|
||||
/*
|
||||
* See tkMacOSXPrivate.h for macros related to key event processing.
|
||||
*/
|
||||
|
||||
/*
|
||||
#ifdef TK_MAC_DEBUG
|
||||
#define TK_MAC_DEBUG_KEYBOARD
|
||||
#endif
|
||||
*/
|
||||
#define NS_KEYLOG 0
|
||||
|
||||
static Tk_Window keyboardGrabWinPtr = NULL;
|
||||
/* Current keyboard grab window. */
|
||||
static NSWindow *keyboardGrabNSWindow = nil;
|
||||
/* NSWindow for the current keyboard grab
|
||||
* window. */
|
||||
#define NS_KEYLOG 0
|
||||
#define XEVENT_MOD_MASK (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask)
|
||||
static Tk_Window keyboardGrabWinPtr = NULL; /* Current keyboard grab window. */
|
||||
static NSWindow *keyboardGrabNSWindow = nil; /* Its underlying NSWindow.*/
|
||||
static NSModalSession modalSession = nil;
|
||||
static BOOL processingCompose = NO;
|
||||
static Tk_Window composeWin = NULL;
|
||||
static int caret_x = 0, caret_y = 0, caret_height = 0;
|
||||
static unsigned short releaseCode;
|
||||
|
||||
static void setupXEvent(XEvent *xEvent, NSWindow *w, unsigned int state);
|
||||
static unsigned isFunctionKey(unsigned int code);
|
||||
|
||||
static void setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers);
|
||||
static void setXEventPoint(XEvent *xEvent, Tk_Window tkwin, NSWindow *w);
|
||||
static NSUInteger textInputModifiers;
|
||||
|
||||
#pragma mark TKApplication(TKKeyEvent)
|
||||
|
||||
@@ -48,233 +49,226 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
#ifdef TK_MAC_DEBUG_EVENTS
|
||||
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent);
|
||||
#endif
|
||||
NSWindow *w;
|
||||
NSWindow *w = [theEvent window];
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow(w), *grabWinPtr, *focusWinPtr;
|
||||
Tk_Window tkwin = (Tk_Window)winPtr;
|
||||
NSEventType type = [theEvent type];
|
||||
NSUInteger virtual = [theEvent keyCode];
|
||||
NSUInteger modifiers = ([theEvent modifierFlags] &
|
||||
NSDeviceIndependentModifierFlagsMask);
|
||||
NSUInteger len = 0;
|
||||
BOOL repeat = NO;
|
||||
unsigned short keyCode = [theEvent keyCode];
|
||||
NSString *characters = nil, *charactersIgnoringModifiers = nil;
|
||||
XEvent xEvent;
|
||||
MacKeycode macKC;
|
||||
UniChar keychar = 0;
|
||||
Bool can_input_text, has_modifiers = NO, use_text_input = NO;
|
||||
static NSUInteger savedModifiers = 0;
|
||||
static NSMutableArray *nsEvArray;
|
||||
static NSMutableArray *nsEvArray = nil;
|
||||
|
||||
if (nsEvArray == nil) {
|
||||
nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
|
||||
processingCompose = NO;
|
||||
}
|
||||
|
||||
w = [theEvent window];
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow(w);
|
||||
Tk_Window tkwin = (Tk_Window) winPtr;
|
||||
XEvent xEvent;
|
||||
|
||||
if (!winPtr) {
|
||||
return theEvent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Control-Tab and Control-Shift-Tab are used to switch tabs in a tabbed
|
||||
* window. We do not want to generate an Xevent for these since that might
|
||||
* cause the deselected tab to be reactivated.
|
||||
* If a local grab is in effect, key events for windows in the
|
||||
* grabber's application are redirected to the grabber. Key events
|
||||
* for other applications are delivered normally. If a global
|
||||
* grab is in effect all key events are redirected to the grabber.
|
||||
*/
|
||||
|
||||
if (keyCode == 48 && (modifiers & NSControlKeyMask) == NSControlKeyMask) {
|
||||
return theEvent;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NSKeyUp:
|
||||
/*Fix for bug #1ba71a86bb: key release firing on key press.*/
|
||||
setupXEvent(&xEvent, w, 0);
|
||||
xEvent.xany.type = KeyRelease;
|
||||
xEvent.xkey.keycode = releaseCode;
|
||||
xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
|
||||
case NSKeyDown:
|
||||
repeat = [theEvent isARepeat];
|
||||
characters = [theEvent characters];
|
||||
charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers];
|
||||
len = [charactersIgnoringModifiers length];
|
||||
case NSFlagsChanged:
|
||||
|
||||
#if defined(TK_MAC_DEBUG_EVENTS) || NS_KEYLOG == 1
|
||||
TKLog(@"-[%@(%p) %s] r=%d mods=%u '%@' '%@' code=%u c=%d %@ %d", [self class], self, _cmd, repeat, modifiers, characters, charactersIgnoringModifiers, keyCode,([charactersIgnoringModifiers length] == 0) ? 0 : [charactersIgnoringModifiers characterAtIndex: 0], w, type);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
return theEvent; /* Unrecognized key event. */
|
||||
grabWinPtr = winPtr->dispPtr->grabWinPtr;
|
||||
if (grabWinPtr) {
|
||||
if (winPtr->dispPtr->grabFlags || /* global grab */
|
||||
grabWinPtr->mainPtr == winPtr->mainPtr){ /* same application */
|
||||
winPtr =winPtr->dispPtr->focusPtr;
|
||||
tkwin = (Tk_Window)winPtr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an Xevent to add to the Tk queue.
|
||||
* Extract the unicode character from KeyUp and KeyDown events.
|
||||
*/
|
||||
|
||||
if (!processingCompose) {
|
||||
unsigned int state = 0;
|
||||
|
||||
if (modifiers & NSAlphaShiftKeyMask) {
|
||||
state |= LockMask;
|
||||
}
|
||||
if (modifiers & NSShiftKeyMask) {
|
||||
state |= ShiftMask;
|
||||
}
|
||||
if (modifiers & NSControlKeyMask) {
|
||||
state |= ControlMask;
|
||||
}
|
||||
if (modifiers & NSCommandKeyMask) {
|
||||
state |= Mod1Mask; /* command key */
|
||||
}
|
||||
if (modifiers & NSAlternateKeyMask) {
|
||||
state |= Mod2Mask; /* option key */
|
||||
}
|
||||
if (modifiers & NSNumericPadKeyMask) {
|
||||
state |= Mod3Mask;
|
||||
}
|
||||
if (modifiers & NSFunctionKeyMask) {
|
||||
state |= Mod4Mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Key events are only received for the front Window on the Macintosh. So
|
||||
* to build an XEvent we look up the Tk window associated to the Front
|
||||
* window.
|
||||
*/
|
||||
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow(w);
|
||||
Tk_Window tkwin = (Tk_Window) winPtr;
|
||||
|
||||
if (tkwin) {
|
||||
TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr;
|
||||
if (type == NSKeyUp || type == NSKeyDown) {
|
||||
if ([[theEvent characters] length] > 0) {
|
||||
keychar = [[theEvent characters] characterAtIndex:0];
|
||||
|
||||
/*
|
||||
* If a local grab is in effect, key events for windows in the
|
||||
* grabber's application are redirected to the grabber. Key events
|
||||
* for other applications are delivered normally. If a global
|
||||
* grab is in effect all key events are redirected to the grabber.
|
||||
* Currently, real keys always send BMP characters, but who knows?
|
||||
*/
|
||||
|
||||
if (grabWinPtr) {
|
||||
if (winPtr->dispPtr->grabFlags || /* global grab */
|
||||
grabWinPtr->mainPtr == winPtr->mainPtr){ /* same appl. */
|
||||
tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
|
||||
}
|
||||
if (CFStringIsSurrogateHighCharacter(keychar)) {
|
||||
UniChar lowChar = [[theEvent characters] characterAtIndex:1];
|
||||
keychar = CFStringGetLongCharacterForSurrogatePair(
|
||||
keychar, lowChar);
|
||||
}
|
||||
} else {
|
||||
tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
|
||||
}
|
||||
if (!tkwin) {
|
||||
TkMacOSXDbgMsg("tkwin == NULL");
|
||||
return theEvent; /* Give up. No window for this event. */
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's a function key, or we have modifiers other than Shift or
|
||||
* Alt, pass it straight to Tk. Otherwise we'll send for input
|
||||
* processing.
|
||||
*/
|
||||
/*
|
||||
* This is a dead key, such as Option-e, so it should go to the
|
||||
* TextInputClient.
|
||||
*/
|
||||
|
||||
int code = (len == 0) ? 0 :
|
||||
[charactersIgnoringModifiers characterAtIndex: 0];
|
||||
if (type != NSKeyDown || isFunctionKey(code)
|
||||
|| (len > 0 && state & (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask))) {
|
||||
XEvent xEvent;
|
||||
|
||||
setupXEvent(&xEvent, w, state);
|
||||
if (type == NSFlagsChanged) {
|
||||
if (savedModifiers > modifiers) {
|
||||
xEvent.xany.type = KeyRelease;
|
||||
} else {
|
||||
xEvent.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.
|
||||
*/
|
||||
|
||||
xEvent.xany.send_event = -1;
|
||||
|
||||
/*
|
||||
* Set keycode (which was zero) to the changed modifier
|
||||
*/
|
||||
|
||||
xEvent.xkey.keycode = (modifiers ^ savedModifiers);
|
||||
} else {
|
||||
if (type == NSKeyUp || repeat) {
|
||||
xEvent.xany.type = KeyRelease;
|
||||
} else {
|
||||
xEvent.xany.type = KeyPress;
|
||||
}
|
||||
|
||||
if ([characters length] > 0) {
|
||||
xEvent.xkey.keycode = (keyCode << 16) |
|
||||
(UInt16) [characters characterAtIndex:0];
|
||||
if (![characters getCString:xEvent.xkey.trans_chars
|
||||
maxLength:XMaxTransChars encoding:NSUTF8StringEncoding]) {
|
||||
/* prevent SF bug 2907388 (crash on some composite chars) */
|
||||
//PENDING: we might not need this anymore
|
||||
TkMacOSXDbgMsg("characters too long");
|
||||
return theEvent;
|
||||
}
|
||||
}
|
||||
|
||||
if (repeat) {
|
||||
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
|
||||
xEvent.xany.type = KeyPress;
|
||||
xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
|
||||
}
|
||||
}
|
||||
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
|
||||
savedModifiers = modifiers;
|
||||
return theEvent;
|
||||
} /* if this is a function key or has modifiers */
|
||||
} /* if not processing compose */
|
||||
|
||||
if (type == NSKeyDown) {
|
||||
if (NS_KEYLOG) {
|
||||
TKLog(@"keyDown: %s compose sequence.\n",
|
||||
processingCompose == YES ? "Continue" : "Begin");
|
||||
use_text_input = YES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the interpretKeyEvents method to interpret composition key
|
||||
* strokes. When it detects a complete composition sequence it will
|
||||
* call our implementation of insertText: replacementRange, which
|
||||
* generates a key down XEvent with the appropriate character. In IME
|
||||
* when multiple characters have the same composition sequence and the
|
||||
* chosen character is not the default it may be necessary to hit the
|
||||
* enter key multiple times before the character is accepted and
|
||||
* rendered. We send enter key events until inputText has cleared
|
||||
* the processingCompose flag.
|
||||
* Apple uses 0x10 for unrecognized keys.
|
||||
*/
|
||||
|
||||
processingCompose = YES;
|
||||
while(processingCompose) {
|
||||
if (keychar == 0x10) {
|
||||
keychar = UNKNOWN_KEYCHAR;
|
||||
}
|
||||
|
||||
#if defined(TK_MAC_DEBUG_EVENTS) || NS_KEYLOG == 1
|
||||
TKLog(@"-[%@(%p) %s] repeat=%d mods=%x char=%x code=%lu c=%d type=%d",
|
||||
[self class], self, _cmd,
|
||||
(type == NSKeyDown) && [theEvent isARepeat], modifiers, keychar,
|
||||
virtual, w, type);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a skeleton XEvent. We need to build it here, even if we will not
|
||||
* send it, so we can pass it to TkFocusKeyEvent to determine whether the
|
||||
* target widget can input text.
|
||||
*/
|
||||
|
||||
setupXEvent(&xEvent, tkwin, modifiers);
|
||||
has_modifiers = xEvent.xkey.state & XEVENT_MOD_MASK;
|
||||
focusWinPtr = TkFocusKeyEvent(winPtr, &xEvent);
|
||||
if (focusWinPtr == NULL) {
|
||||
TKContentView *contentView = [w contentView];
|
||||
|
||||
/*
|
||||
* This NSEvent is being sent to a window which does not have focus.
|
||||
* This could mean, for example, that the user deactivated the Tk app
|
||||
* while the NSTextInputClient's popup character selection window was
|
||||
* still open. We attempt to abandon any ongoing composition operation
|
||||
* and discard the event.
|
||||
*/
|
||||
|
||||
[contentView cancelComposingText];
|
||||
return theEvent;
|
||||
}
|
||||
can_input_text = ((focusWinPtr->flags & TK_CAN_INPUT_TEXT) != 0);
|
||||
|
||||
#if (NS_KEYLOG)
|
||||
TKLog(@"keyDown: %s compose sequence.\n",
|
||||
processingCompose == YES ? "Continue" : "Begin");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Decide whether this event should be processed with the NSTextInputClient
|
||||
* protocol.
|
||||
*/
|
||||
|
||||
if (processingCompose ||
|
||||
(type == NSKeyDown && can_input_text && !has_modifiers &&
|
||||
IS_PRINTABLE(keychar))
|
||||
) {
|
||||
use_text_input = YES;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are processing this KeyDown event as an NSTextInputClient we do
|
||||
* not queue an XEvent. We pass the NSEvent to our interpretKeyEvents
|
||||
* method. When the composition sequence is complete, the callback method
|
||||
* insertText: replacementRange will be called. That method generates a
|
||||
* keyPress XEvent with the selected character.
|
||||
*/
|
||||
|
||||
if (use_text_input) {
|
||||
textInputModifiers = modifiers;
|
||||
|
||||
/*
|
||||
* In IME the Enter key is used to terminate a composition sequence.
|
||||
* When there are multiple choices of input text available, and the
|
||||
* user's selected choice is not the default, it may be necessary to
|
||||
* hit the Enter key multiple times before the text is accepted and
|
||||
* rendered (See ticket 39de9677aa]). So when sending an Enter key
|
||||
* during composition, we continue sending Enter keys until the
|
||||
* inputText method has cleared the processingCompose flag.
|
||||
*/
|
||||
|
||||
if (processingCompose && [theEvent keyCode] == 36) {
|
||||
[nsEvArray addObject: theEvent];
|
||||
while(processingCompose) {
|
||||
[[w contentView] interpretKeyEvents: nsEvArray];
|
||||
}
|
||||
[nsEvArray removeObject: theEvent];
|
||||
} else {
|
||||
[nsEvArray addObject: theEvent];
|
||||
[[w contentView] interpretKeyEvents: nsEvArray];
|
||||
[nsEvArray removeObject: theEvent];
|
||||
if ([theEvent keyCode] != 36) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return theEvent;
|
||||
}
|
||||
savedModifiers = modifiers;
|
||||
|
||||
/*
|
||||
* We are not handling this event as an NSTextInputClient, so we need to
|
||||
* finish constructing the XEvent and queue it.
|
||||
*/
|
||||
|
||||
macKC.v.o_s = ((modifiers & NSShiftKeyMask ? INDEX_SHIFT : 0) |
|
||||
(modifiers & NSAlternateKeyMask ? INDEX_OPTION : 0));
|
||||
macKC.v.virtual = virtual;
|
||||
switch (type) {
|
||||
case NSFlagsChanged:
|
||||
|
||||
/*
|
||||
* This XEvent is a simulated KeyPress or KeyRelease event for a
|
||||
* modifier key. To determine the type, note that the highest bit
|
||||
* where the flags differ is 1 if and only if it is a KeyPress. The
|
||||
* modifiers are saved so we can detect the next flag change.
|
||||
*/
|
||||
|
||||
xEvent.xany.type = modifiers > savedModifiers ? KeyPress : KeyRelease;
|
||||
savedModifiers = modifiers;
|
||||
|
||||
/*
|
||||
* Set the keychar to MOD_KEYCHAR as a signal to TkpGetKeySym (see
|
||||
* tkMacOSXKeyboard.c) that this is a modifier key event.
|
||||
*/
|
||||
|
||||
keychar = MOD_KEYCHAR;
|
||||
break;
|
||||
case NSKeyUp:
|
||||
xEvent.xany.type = KeyRelease;
|
||||
break;
|
||||
case NSKeyDown:
|
||||
xEvent.xany.type = KeyPress;
|
||||
break;
|
||||
default:
|
||||
return theEvent; /* Unrecognized key event. */
|
||||
}
|
||||
macKC.v.keychar = keychar;
|
||||
xEvent.xkey.keycode = macKC.uint;
|
||||
setXEventPoint(&xEvent, tkwin, w);
|
||||
|
||||
/*
|
||||
* Finally we can queue the XEvent, inserting a KeyRelease before a
|
||||
* repeated KeyPress.
|
||||
*/
|
||||
|
||||
if (type == NSKeyDown && [theEvent isARepeat]) {
|
||||
|
||||
xEvent.xany.type = KeyRelease;
|
||||
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
|
||||
xEvent.xany.type = KeyPress;
|
||||
}
|
||||
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
|
||||
return theEvent;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation TKContentView
|
||||
|
||||
-(id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_needsRedisplay = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@synthesize tkDirtyRect = _tkDirtyRect;
|
||||
@synthesize tkNeedsDisplay = _tkNeedsDisplay;
|
||||
|
||||
/*
|
||||
* Implementation of the NSTextInputClient protocol.
|
||||
@@ -282,15 +276,18 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
|
||||
/* [NSTextInputClient inputText: replacementRange:] is called by
|
||||
* interpretKeyEvents when a composition sequence is complete. It is also
|
||||
* called when we delete over working text. In that case the call is followed
|
||||
* called when we delete working text. In that case the call is followed
|
||||
* immediately by doCommandBySelector: deleteBackward:
|
||||
*/
|
||||
- (void)insertText: (id)aString
|
||||
replacementRange: (NSRange)repRange
|
||||
{
|
||||
int i, len;
|
||||
int i, len, state;
|
||||
XEvent xEvent;
|
||||
NSString *str;
|
||||
NSString *str, *keystr, *lower;
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
|
||||
Tk_Window tkwin = (Tk_Window)winPtr;
|
||||
Bool sendingIMEText = NO;
|
||||
|
||||
str = ([aString isKindOfClass: [NSAttributedString class]]) ?
|
||||
[aString string] : aString;
|
||||
@@ -300,13 +297,12 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
TKLog(@"insertText '%@'\tlen = %d", aString, len);
|
||||
}
|
||||
|
||||
processingCompose = NO;
|
||||
|
||||
/*
|
||||
* Clear any working text.
|
||||
*/
|
||||
|
||||
if (privateWorkingText != nil) {
|
||||
sendingIMEText = YES;
|
||||
[self deleteWorkingText];
|
||||
}
|
||||
|
||||
@@ -314,7 +310,8 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
* Insert the string as a sequence of keystrokes.
|
||||
*/
|
||||
|
||||
setupXEvent(&xEvent, [self window], 0);
|
||||
setupXEvent(&xEvent, tkwin, textInputModifiers);
|
||||
setXEventPoint(&xEvent, tkwin, [self window]);
|
||||
xEvent.xany.type = KeyPress;
|
||||
|
||||
/*
|
||||
@@ -325,37 +322,54 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
*/
|
||||
|
||||
if (repRange.location == 0) {
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
|
||||
Tk_Window focusWin = (Tk_Window) winPtr->dispPtr->focusPtr;
|
||||
Tk_Window focusWin = (Tk_Window)winPtr->dispPtr->focusPtr;
|
||||
TkSendVirtualEvent(focusWin, "TkAccentBackspace", NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Next we generate an XEvent for each unicode character in our string.
|
||||
* This string could contain non-BMP characters, for example if the
|
||||
* emoji palette was used.
|
||||
*
|
||||
* NSString uses UTF-16 internally, which means that a non-BMP character is
|
||||
* represented by a sequence of two 16-bit "surrogates". In principle we
|
||||
* could record this in the XEvent by setting the keycode to the 32-bit
|
||||
* unicode code point and setting the trans_chars string to the 4-byte
|
||||
* UTF-8 string for the non-BMP character. However, that will not work
|
||||
* when TCL_UTF_MAX is set to 3, as is the case for Tcl 8.6. A workaround
|
||||
* used internally by Tcl 8.6 is to encode each surrogate as a 3-byte
|
||||
* sequence using the UTF-8 algorithm (ignoring the fact that the UTF-8
|
||||
* encoding specification does not allow encoding UTF-16 surrogates).
|
||||
* This gives a 6-byte encoding of the non-BMP character which we write into
|
||||
* the trans_chars field of the XEvent.
|
||||
* represented by a sequence of two 16-bit "surrogates". We record this in
|
||||
* the XEvent by setting the low order 21-bits of the keycode to the UCS-32
|
||||
* value value of the character and the virtual keycode in the high order
|
||||
* byte to the special value NON_BMP.
|
||||
*/
|
||||
|
||||
state = xEvent.xkey.state;
|
||||
for (i = 0; i < len; i++) {
|
||||
xEvent.xkey.nbytes = TclUniAtIndex(str, i, xEvent.xkey.trans_chars,
|
||||
&xEvent.xkey.keycode);
|
||||
if (xEvent.xkey.keycode > 0xffff){
|
||||
i++;
|
||||
UniChar keychar;
|
||||
MacKeycode macKC = {0};
|
||||
|
||||
keychar = [str characterAtIndex:i];
|
||||
macKC.v.keychar = keychar;
|
||||
if (CFStringIsSurrogateHighCharacter(keychar)) {
|
||||
UniChar lowChar = [str characterAtIndex:++i];
|
||||
macKC.v.keychar = CFStringGetLongCharacterForSurrogatePair(
|
||||
(UniChar)keychar, lowChar);
|
||||
macKC.v.virtual = NON_BMP_VIRTUAL;
|
||||
} else if (repRange.location == 0 || sendingIMEText) {
|
||||
macKC.v.virtual = REPLACEMENT_VIRTUAL;
|
||||
} else {
|
||||
macKC.uint = TkMacOSXAddVirtual(macKC.uint);
|
||||
xEvent.xkey.state |= INDEX2STATE(macKC.x.xvirtual);
|
||||
}
|
||||
keystr = [[NSString alloc] initWithCharacters:&keychar length:1];
|
||||
lower = [keystr lowercaseString];
|
||||
if (![keystr isEqual: lower]) {
|
||||
macKC.v.o_s |= INDEX_SHIFT;
|
||||
xEvent.xkey.state |= ShiftMask;
|
||||
}
|
||||
if (xEvent.xkey.state & Mod2Mask) {
|
||||
macKC.v.o_s |= INDEX_OPTION;
|
||||
}
|
||||
xEvent.xkey.keycode = macKC.uint;
|
||||
xEvent.xany.type = KeyPress;
|
||||
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
|
||||
xEvent.xkey.state = state;
|
||||
}
|
||||
releaseCode = (UInt16) [str characterAtIndex: 0];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -379,13 +393,12 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
replacementRange: (NSRange)repRange
|
||||
{
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
|
||||
Tk_Window focusWin = (Tk_Window) winPtr->dispPtr->focusPtr;
|
||||
Tk_Window focusWin = (Tk_Window)winPtr->dispPtr->focusPtr;
|
||||
NSString *temp;
|
||||
NSString *str;
|
||||
|
||||
str = ([aString isKindOfClass: [NSAttributedString class]]) ?
|
||||
[aString string] : aString;
|
||||
|
||||
if (focusWin) {
|
||||
|
||||
/*
|
||||
@@ -416,10 +429,10 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
*/
|
||||
|
||||
TkSendVirtualEvent(focusWin, "TkStartIMEMarkedText", NULL);
|
||||
processingCompose = YES;
|
||||
temp = [str copy];
|
||||
[self insertText: temp replacementRange:repRange];
|
||||
privateWorkingText = temp;
|
||||
processingCompose = YES;
|
||||
TkSendVirtualEvent(focusWin, "TkEndIMEMarkedText", NULL);
|
||||
}
|
||||
|
||||
@@ -428,7 +441,6 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
return privateWorkingText != nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSRange)markedRange
|
||||
{
|
||||
NSRange rng = privateWorkingText != nil
|
||||
@@ -441,15 +453,6 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
return rng;
|
||||
}
|
||||
|
||||
- (void)cancelComposingText
|
||||
{
|
||||
if (NS_KEYLOG) {
|
||||
TKLog(@"cancelComposingText");
|
||||
}
|
||||
[self deleteWorkingText];
|
||||
processingCompose = NO;
|
||||
}
|
||||
|
||||
- (void)unmarkText
|
||||
{
|
||||
if (NS_KEYLOG) {
|
||||
@@ -459,7 +462,6 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
processingCompose = NO;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called by the system to get a position for popup character selection windows
|
||||
* such as a Character Palette, or a selection menu for IME.
|
||||
@@ -496,7 +498,7 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
processingCompose = NO;
|
||||
if (aSelector == @selector (deleteBackward:)) {
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
|
||||
Tk_Window focusWin = (Tk_Window) winPtr->dispPtr->focusPtr;
|
||||
Tk_Window focusWin = (Tk_Window)winPtr->dispPtr->focusPtr;
|
||||
TkSendVirtualEvent(focusWin, "TkAccentBackspace", NULL);
|
||||
}
|
||||
}
|
||||
@@ -543,7 +545,6 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
}
|
||||
/* End of NSTextInputClient implementation. */
|
||||
|
||||
@synthesize needsRedisplay = _needsRedisplay;
|
||||
@end
|
||||
|
||||
|
||||
@@ -573,6 +574,16 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancelComposingText
|
||||
{
|
||||
if (NS_KEYLOG) {
|
||||
TKLog(@"cancelComposingText");
|
||||
}
|
||||
[self deleteWorkingText];
|
||||
processingCompose = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
@@ -580,21 +591,29 @@ static unsigned isFunctionKey(unsigned int code);
|
||||
*/
|
||||
|
||||
static void
|
||||
setupXEvent(XEvent *xEvent, NSWindow *w, unsigned int state)
|
||||
setupXEvent(XEvent *xEvent, Tk_Window tkwin, NSUInteger modifiers)
|
||||
{
|
||||
TkWindow *winPtr = TkMacOSXGetTkWindow(w);
|
||||
Tk_Window tkwin = (Tk_Window) winPtr;
|
||||
unsigned int state = 0;
|
||||
Display *display = Tk_Display(tkwin);
|
||||
|
||||
if (!winPtr) {
|
||||
if (tkwin == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (modifiers) {
|
||||
state = (modifiers & NSAlphaShiftKeyMask ? LockMask : 0) |
|
||||
(modifiers & NSShiftKeyMask ? ShiftMask : 0) |
|
||||
(modifiers & NSControlKeyMask ? ControlMask : 0) |
|
||||
(modifiers & NSCommandKeyMask ? Mod1Mask : 0) |
|
||||
(modifiers & NSAlternateKeyMask ? Mod2Mask : 0) |
|
||||
(modifiers & NSNumericPadKeyMask ? Mod3Mask : 0) |
|
||||
(modifiers & NSFunctionKeyMask ? Mod4Mask : 0) ;
|
||||
}
|
||||
memset(xEvent, 0, sizeof(XEvent));
|
||||
xEvent->xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
|
||||
xEvent->xany.serial = LastKnownRequestProcessed(display);
|
||||
xEvent->xany.display = Tk_Display(tkwin);
|
||||
xEvent->xany.window = Tk_WindowId(tkwin);
|
||||
|
||||
xEvent->xkey.root = XRootWindow(Tk_Display(tkwin), 0);
|
||||
xEvent->xkey.root = XRootWindow(display, 0);
|
||||
xEvent->xkey.time = TkpGetMS();
|
||||
xEvent->xkey.state = state;
|
||||
xEvent->xkey.same_screen = true;
|
||||
@@ -602,6 +621,41 @@ setupXEvent(XEvent *xEvent, NSWindow *w, unsigned int state)
|
||||
* because of the memset() above. */
|
||||
}
|
||||
|
||||
static void
|
||||
setXEventPoint(
|
||||
XEvent *xEvent,
|
||||
Tk_Window tkwin,
|
||||
NSWindow *w)
|
||||
{
|
||||
TkWindow *winPtr = (TkWindow *) tkwin;
|
||||
NSPoint local = [w mouseLocationOutsideOfEventStream];
|
||||
NSPoint global = [w tkConvertPointToScreen: local];
|
||||
int win_x, win_y;
|
||||
|
||||
if (Tk_IsEmbedded(winPtr)) {
|
||||
TkWindow *contPtr = TkpGetOtherWindow(winPtr);
|
||||
if (Tk_IsTopLevel(contPtr)) {
|
||||
local.x -= contPtr->wmInfoPtr->xInParent;
|
||||
local.y -= contPtr->wmInfoPtr->yInParent;
|
||||
} else {
|
||||
TkWindow *topPtr = TkMacOSXGetHostToplevel(winPtr)->winPtr;
|
||||
local.x -= (topPtr->wmInfoPtr->xInParent + contPtr->changes.x);
|
||||
local.y -= (topPtr->wmInfoPtr->yInParent + contPtr->changes.y);
|
||||
}
|
||||
} else if (winPtr->wmInfoPtr != NULL) {
|
||||
local.x -= winPtr->wmInfoPtr->xInParent;
|
||||
local.y -= winPtr->wmInfoPtr->yInParent;
|
||||
}
|
||||
tkwin = Tk_TopCoordsToWindow(tkwin, local.x, local.y, &win_x, &win_y);
|
||||
local.x = win_x;
|
||||
local.y = win_y;
|
||||
global.y = TkMacOSXZeroScreenHeight() - global.y;
|
||||
xEvent->xbutton.x = local.x;
|
||||
xEvent->xbutton.y = local.y;
|
||||
xEvent->xbutton.x_root = global.x;
|
||||
xEvent->xbutton.y_root = global.y;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*
|
||||
@@ -633,8 +687,8 @@ XGrabKeyboard(
|
||||
TkWindow *captureWinPtr = (TkWindow *) TkpGetCapture();
|
||||
|
||||
if (keyboardGrabWinPtr && captureWinPtr) {
|
||||
NSWindow *w = TkMacOSXDrawableWindow(grab_window);
|
||||
MacDrawable *macWin = (MacDrawable *) grab_window;
|
||||
NSWindow *w = TkMacOSXGetNSWindowForDrawable(grab_window);
|
||||
MacDrawable *macWin = (MacDrawable *)grab_window;
|
||||
|
||||
if (w && macWin->toplevel->winPtr == (TkWindow *) captureWinPtr) {
|
||||
if (modalSession) {
|
||||
@@ -706,15 +760,22 @@ TkMacOSXGetModalSession(void)
|
||||
*
|
||||
* 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.
|
||||
* This enables correct placement of the popups used for character
|
||||
* selection by the NSTextInputClient. It gets called by text entry
|
||||
* widgets whenever the cursor is drawn. It does nothing if the widget's
|
||||
* NSWindow is not the current KeyWindow. Otherwise it updates the
|
||||
* display's caret structure and records the caret geometry in static
|
||||
* variables for use by the NSTextInputClient implementation. Any
|
||||
* widget passed to this function will be marked as being able to input
|
||||
* text by setting the TK_CAN_INPUT_TEXT flag.
|
||||
*
|
||||
* Results:
|
||||
* None
|
||||
*
|
||||
* Side effects:
|
||||
* None
|
||||
* Sets the CAN_INPUT_TEXT flag on the widget passed as tkwin. May update
|
||||
* the display's caret structure as well as the static variables caret_x,
|
||||
* caret_y and caret_height.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
@@ -726,27 +787,40 @@ Tk_SetCaretPos(
|
||||
int y,
|
||||
int height)
|
||||
{
|
||||
TkCaret *caretPtr = &(((TkWindow *) tkwin)->dispPtr->caret);
|
||||
TkWindow *winPtr = (TkWindow *) tkwin;
|
||||
TkCaret *caretPtr = &(winPtr->dispPtr->caret);
|
||||
NSWindow *w = TkMacOSXGetNSWindowForDrawable(Tk_WindowId(tkwin));
|
||||
|
||||
/*
|
||||
* Prevent processing anything if the values haven't changed. Windows only
|
||||
* has one display, so we can do this with statics.
|
||||
* Register this widget as being capable of text input, so we know we
|
||||
* should process (appropriate) key events for this window with the
|
||||
* NSTextInputClient protocol.
|
||||
*/
|
||||
|
||||
if ((caretPtr->winPtr == ((TkWindow *) tkwin))
|
||||
&& (caretPtr->x == x) && (caretPtr->y == y)) {
|
||||
winPtr->flags |= TK_CAN_INPUT_TEXT;
|
||||
if (w && ![w isKeyWindow]) {
|
||||
return;
|
||||
}
|
||||
if ((caretPtr->winPtr == winPtr
|
||||
&& caretPtr->x == x) && (caretPtr->y == y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
caretPtr->winPtr = ((TkWindow *) tkwin);
|
||||
/*
|
||||
* Update the display's caret information.
|
||||
*/
|
||||
|
||||
caretPtr->winPtr = winPtr;
|
||||
caretPtr->x = x;
|
||||
caretPtr->y = y;
|
||||
caretPtr->height = height;
|
||||
|
||||
/*
|
||||
* As in Windows, adjust to the toplevel to get the coords right.
|
||||
* Record the caret geometry in static variables for use when processing
|
||||
* key events. We use the TKContextView coordinate system for this.
|
||||
*/
|
||||
|
||||
caret_height = height;
|
||||
while (!Tk_IsTopLevel(tkwin)) {
|
||||
x += Tk_X(tkwin);
|
||||
y += Tk_Y(tkwin);
|
||||
@@ -755,94 +829,8 @@ Tk_SetCaretPos(
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* But adjust for fact that NS uses flipped view.
|
||||
*/
|
||||
|
||||
y = Tk_Height(tkwin) - y;
|
||||
|
||||
caret_x = x;
|
||||
caret_y = y;
|
||||
caret_height = height;
|
||||
}
|
||||
|
||||
|
||||
static unsigned convert_ns_to_X_keysym[] =
|
||||
{
|
||||
NSHomeFunctionKey, 0x50,
|
||||
NSLeftArrowFunctionKey, 0x51,
|
||||
NSUpArrowFunctionKey, 0x52,
|
||||
NSRightArrowFunctionKey, 0x53,
|
||||
NSDownArrowFunctionKey, 0x54,
|
||||
NSPageUpFunctionKey, 0x55,
|
||||
NSPageDownFunctionKey, 0x56,
|
||||
NSEndFunctionKey, 0x57,
|
||||
NSBeginFunctionKey, 0x58,
|
||||
NSSelectFunctionKey, 0x60,
|
||||
NSPrintFunctionKey, 0x61,
|
||||
NSExecuteFunctionKey, 0x62,
|
||||
NSInsertFunctionKey, 0x63,
|
||||
NSUndoFunctionKey, 0x65,
|
||||
NSRedoFunctionKey, 0x66,
|
||||
NSMenuFunctionKey, 0x67,
|
||||
NSFindFunctionKey, 0x68,
|
||||
NSHelpFunctionKey, 0x6A,
|
||||
NSBreakFunctionKey, 0x6B,
|
||||
|
||||
NSF1FunctionKey, 0xBE,
|
||||
NSF2FunctionKey, 0xBF,
|
||||
NSF3FunctionKey, 0xC0,
|
||||
NSF4FunctionKey, 0xC1,
|
||||
NSF5FunctionKey, 0xC2,
|
||||
NSF6FunctionKey, 0xC3,
|
||||
NSF7FunctionKey, 0xC4,
|
||||
NSF8FunctionKey, 0xC5,
|
||||
NSF9FunctionKey, 0xC6,
|
||||
NSF10FunctionKey, 0xC7,
|
||||
NSF11FunctionKey, 0xC8,
|
||||
NSF12FunctionKey, 0xC9,
|
||||
NSF13FunctionKey, 0xCA,
|
||||
NSF14FunctionKey, 0xCB,
|
||||
NSF15FunctionKey, 0xCC,
|
||||
NSF16FunctionKey, 0xCD,
|
||||
NSF17FunctionKey, 0xCE,
|
||||
NSF18FunctionKey, 0xCF,
|
||||
NSF19FunctionKey, 0xD0,
|
||||
NSF20FunctionKey, 0xD1,
|
||||
NSF21FunctionKey, 0xD2,
|
||||
NSF22FunctionKey, 0xD3,
|
||||
NSF23FunctionKey, 0xD4,
|
||||
NSF24FunctionKey, 0xD5,
|
||||
|
||||
NSBackspaceCharacter, 0x08, /* 8: Not on some KBs. */
|
||||
NSDeleteCharacter, 0xFF, /* 127: Big 'delete' key upper right. */
|
||||
NSDeleteFunctionKey, 0x9F, /* 63272: Del forw key off main array. */
|
||||
|
||||
NSTabCharacter, 0x09,
|
||||
0x19, 0x09, /* left tab->regular since pass shift */
|
||||
NSCarriageReturnCharacter, 0x0D,
|
||||
NSNewlineCharacter, 0x0D,
|
||||
NSEnterCharacter, 0x8D,
|
||||
|
||||
0x1B, 0x1B /* escape */
|
||||
};
|
||||
|
||||
|
||||
static unsigned
|
||||
isFunctionKey(
|
||||
unsigned code)
|
||||
{
|
||||
const unsigned last_keysym = (sizeof(convert_ns_to_X_keysym)
|
||||
/ sizeof(convert_ns_to_X_keysym[0]));
|
||||
unsigned keysym;
|
||||
|
||||
for (keysym = 0; keysym < last_keysym; keysym += 2) {
|
||||
if (code == convert_ns_to_X_keysym[keysym]) {
|
||||
return 0xFF00 | convert_ns_to_X_keysym[keysym + 1];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
caret_y = Tk_Height(tkwin) - y;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user