fix Mac input to work with the character palette and CJK input

svn: r4624
This commit is contained in:
Matthew Flatt 2006-10-18 03:29:46 +00:00
parent 8798784bd5
commit a9ab05d0f5
3 changed files with 283 additions and 250 deletions

View File

@ -41,6 +41,9 @@ static void MrDequeue(MrQueueElem *q);
WindowPtr MrEdMouseWindow(Point where); WindowPtr MrEdMouseWindow(Point where);
WindowPtr MrEdKeyWindow(); WindowPtr MrEdKeyWindow();
extern int wxTranslateRawKey(int key);
extern short wxMacDisableMods;
typedef MrQueueElem *MrQueueRef; typedef MrQueueElem *MrQueueRef;
typedef int (*Checker_Func)(EventRecord *evt, MrQueueRef q, int check_only, typedef int (*Checker_Func)(EventRecord *evt, MrQueueRef q, int check_only,
@ -276,9 +279,15 @@ static int pending_self_ae;
static void EnsureWNEReturn() static void EnsureWNEReturn()
{ {
/* Generate an event that WaitNextEvent will return, but /* Generate an event that WaitNextEvent() will return, but that we can
that we can recognize and ignore. An AppleEvent is a recognize and ignore. (Note that window handlers can run nested
heavyweight but reliable way to do that. */ event handlers, such as the resize handler for the little
OS-provided window to implement Chinese text via pinyin. We need
something that doesn't break those loops.) An AppleEvent is a
heavyweight(?) but apparently reliable way to get WaitNextEvent() to
return. Of course, don't install the standard handlers that are put
in place by RunApplicationEventLoop(), because they'll dispatch the
dummy AppleEvent and defeat the purpose. */
if (!pending_self_ae) { if (!pending_self_ae) {
ProcessSerialNumber psn; ProcessSerialNumber psn;
AEAddressDesc target; AEAddressDesc target;
@ -332,20 +341,34 @@ void wxSmuggleOutEvent(EventRef ref)
&& (GetEventKind(ref) == kEventTextInputUnicodeForKeyEvent)) { && (GetEventKind(ref) == kEventTextInputUnicodeForKeyEvent)) {
UniChar *text; UniChar *text;
UInt32 actualSize; UInt32 actualSize;
EventRef kref;
GetEventParameter(ref, kEventParamTextInputSendText, GetEventParameter(ref, kEventParamTextInputSendKeyboardEvent,
typeUnicodeText, NULL, 0, &actualSize, NULL); typeEventRef, NULL, sizeof(EventRef), NULL, &kref);
if (actualSize) { if (ConvertEventRefToEventRecord(kref, &e)) {
text = (UniChar*)scheme_malloc_atomic(actualSize); ok = TRUE;
GetEventParameter(ref, kEventParamTextInputSendText, } else {
typeUnicodeText, NULL, actualSize, NULL, text);
e.what = unicodeEvt;
e.message = text[0];
e.modifiers = 0; e.modifiers = 0;
e.message = 0;
e.where.h = 0; e.where.h = 0;
e.where.v = 0; e.where.v = 0;
ok = TRUE; }
if ((e.modifiers & (wxMacDisableMods | cmdKey))
|| wxTranslateRawKey((e.message & keyCodeMask) >> 8)) {
/* keep the raw event */
} else {
GetEventParameter(ref, kEventParamTextInputSendText,
typeUnicodeText, NULL, 0, &actualSize, NULL);
if (actualSize) {
text = (UniChar*)scheme_malloc_atomic(actualSize);
GetEventParameter(ref, kEventParamTextInputSendText,
typeUnicodeText, NULL, actualSize, NULL, text);
e.what = unicodeEvt;
e.message = text[0];
ok = TRUE;
}
} }
} else { } else {
ok = ConvertEventRefToEventRecord(ref, &e); ok = ConvertEventRefToEventRecord(ref, &e);
@ -379,7 +402,7 @@ static pascal OSErr HandleSmug(const AppleEvent *evt, AppleEvent *rae, long k)
return 0; return 0;
} }
/* WNE: a small wrapper for WaitNextEvent, mostly to manage /* WNE: a small wrapper for WaitNextEvent(), mostly to manage
wake-up activities. wake-up activities.
It's tempting to try to use ReceiveNextEvent() to filter It's tempting to try to use ReceiveNextEvent() to filter
the raw events. Don't do that, because WaitNextEvent() is the raw events. Don't do that, because WaitNextEvent() is
@ -424,7 +447,7 @@ int WNE(EventRecord *e, double sleep_secs)
::InstallEventHandler(GetEventDispatcherTarget(), ::InstallEventHandler(GetEventDispatcherTarget(),
smuggle_handler, smuggle_handler,
2, 3,
evts, evts,
NULL, NULL,
NULL); NULL);
@ -465,7 +488,7 @@ static int TransferQueue(int all)
int sleep_time = 0; int sleep_time = 0;
int delay_time = 0; int delay_time = 0;
/* Don't call WaitNextEvent too often. */ /* Don't call WaitNextEvent() too often. */
static unsigned long lastTime; static unsigned long lastTime;
if (TickCount() <= lastTime + delay_time) if (TickCount() <= lastTime + delay_time)
return 0; return 0;
@ -726,6 +749,7 @@ static int CheckForMouseOrKey(EventRecord *e, MrQueueRef osq, int check_only,
} }
break; break;
case wheelEvt: case wheelEvt:
case unicodeEvt:
case keyDown: case keyDown:
case autoKey: case autoKey:
case keyUp: case keyUp:
@ -754,10 +778,6 @@ static int CheckForActivate(EventRecord *evt, MrQueueRef q, int check_only,
WindowPtr window; WindowPtr window;
switch (evt->what) { switch (evt->what) {
#ifndef OS_X
// OS X does not support the diskEvt event.
case diskEvt:
#endif
case kHighLevelEvent: case kHighLevelEvent:
{ {
MrEdContext *fc; MrEdContext *fc;
@ -904,6 +924,7 @@ int MrEdGetNextEvent(int check_only, int current_only,
case mouseMenuDown: case mouseMenuDown:
case mouseDown: case mouseDown:
case wheelEvt: case wheelEvt:
case unicodeEvt:
case keyDown: case keyDown:
case keyUp: case keyUp:
case autoKey: case autoKey:
@ -1071,14 +1092,12 @@ int MrEdCheckForBreak(void)
/* sleep */ /* sleep */
/***************************************************************************/ /***************************************************************************/
#ifdef OS_X
#include <pthread.h> #include <pthread.h>
static volatile int thread_running; static volatile int thread_running;
static volatile int need_post; /* 0=>1 transition has a benign race condition, an optimization */ static volatile int need_post; /* 0=>1 transition has a benign race condition, an optimization */
static SLEEP_PROC_PTR mzsleep; static SLEEP_PROC_PTR mzsleep;
static pthread_t watcher; static pthread_t watcher;
static volatile float sleep_secs; static volatile float sleep_secs;
static ProcessSerialNumber psn;
/* These file descriptors act as semaphores: */ /* These file descriptors act as semaphores: */
static int watch_read_fd, watch_write_fd; static int watch_read_fd, watch_write_fd;
@ -1103,7 +1122,6 @@ static void *do_watch(void *fds)
mzsleep(sleep_secs, fds); mzsleep(sleep_secs, fds);
if (need_post) { if (need_post) {
need_post = 0; need_post = 0;
WakeUpProcess(&psn);
if (cb_socket_ready) { if (cb_socket_ready) {
/* Sometimes WakeUpProcess() doesn't work. /* Sometimes WakeUpProcess() doesn't work.
Try a notification socket as a backup. Try a notification socket as a backup.
@ -1145,7 +1163,6 @@ static int StartFDWatcher(void (*mzs)(float secs, void *fds), float secs, void *
} }
if (!watcher) { if (!watcher) {
GetCurrentProcess(&psn);
if (pthread_create(&watcher, NULL, do_watch, fds)) { if (pthread_create(&watcher, NULL, do_watch, fds)) {
return 0; return 0;
} }
@ -1175,32 +1192,26 @@ static void EndFDWatcher(void)
} }
} }
/* See ARGH below. */
void socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) void socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{ {
WakeUpProcess(&psn); EnsureWNEReturn();
} }
/* See ARGH below. */
static const void *sock_retain(const void *info) static const void *sock_retain(const void *info)
{ {
return NULL; return NULL;
} }
/* See ARGH below. */
static void sock_release(const void *info) static void sock_release(const void *info)
{ {
/* do nothing */ /* do nothing */
} }
/* See ARGH below. */
static CFStringRef sock_copy_desc(const void *info) static CFStringRef sock_copy_desc(const void *info)
{ {
return CFSTR("sock"); return CFSTR("sock");
} }
#endif
static int going, reported_recursive_sleep; static int going, reported_recursive_sleep;
void MrEdMacSleep(float secs, void *fds, SLEEP_PROC_PTR mzsleep) void MrEdMacSleep(float secs, void *fds, SLEEP_PROC_PTR mzsleep)
@ -1215,24 +1226,24 @@ void MrEdMacSleep(float secs, void *fds, SLEEP_PROC_PTR mzsleep)
} }
/* If we're asked to sleep less than 1/60 of a second, then don't /* If we're asked to sleep less than 1/60 of a second, then don't
bother with WaitNextEvent. */ bother with WaitNextEvent(). */
if ((secs > 0) && (secs < 1.0/60)) { if ((secs > 0) && (secs < 1.0/60)) {
mzsleep(secs, fds); mzsleep(secs, fds);
} else { } else {
EventRecord e; EventRecord e;
if (!cb_socket_ready) { if (!cb_socket_ready) {
/* ARGH: We set up a pipe for the purpose of breaking the Carbon /* We set up a pipe for the purpose of breaking the Carbon
event manager out of its loop. When the watcher thread sees event manager out of its loop. When the watcher thread sees
that an fd is ready, it writes to write_sock_ready, which that an fd is ready, it writes to write_sock_ready, which
means that sock_ready is ready to read, which means that means that sock_ready is ready to read, which means that
socket_callback is invoked, and it calls WakeUpProcess(). socket_callback is invoked, and it calls EnsureWNEReturn().
None of this would be necessary if WakeUpProcess() worked With the current implementation of EnsureWNEReturn(), this is
correctly, because the watcher thread also calls probably overkill. I think the watcher thread could call
WakeUpProcess(). It seems to have become broken in OS X 10.2, EnsureWNEReturn() directly. Doing it this way moves the call
where WakeUpprocess() doesn't work when called before the WNE into this thread, though, which seems more robust in the long
starts (reminiscent of OS 7.1.2 or so). */ run (i.e., if EnsureWNEReturn() changes). */
int fds[2]; int fds[2];
if (!pipe(fds)) { if (!pipe(fds)) {
CFRunLoopRef rl; CFRunLoopRef rl;

View File

@ -46,6 +46,8 @@ extern WindowPtr MrEdKeyWindow();
extern void wxCheckRootFrame(WindowPtr w); extern void wxCheckRootFrame(WindowPtr w);
int wxTranslateRawKey(int key);
int wxMenuBarHeight; int wxMenuBarHeight;
extern wxApp *wxTheApp; extern wxApp *wxTheApp;
@ -649,232 +651,141 @@ void wxApp::doMacKeyUpDown(Bool down)
} else if (cCurrentEvent.what == unicodeEvt) { } else if (cCurrentEvent.what == unicodeEvt) {
key = cCurrentEvent.message; key = cCurrentEvent.message;
} else { } else {
int newkey;
key = (cCurrentEvent.message & keyCodeMask) >> 8; key = (cCurrentEvent.message & keyCodeMask) >> 8;
/* Better way than to use hard-wired key codes? */ newkey = wxTranslateRawKey(key);
switch (key) {
# define wxFKEY(code, wxk) case code: key = wxk; break
wxFKEY(122, WXK_F1);
wxFKEY(120, WXK_F2);
wxFKEY(99, WXK_F3);
wxFKEY(118, WXK_F4);
wxFKEY(96, WXK_F5);
wxFKEY(97, WXK_F6);
wxFKEY(98, WXK_F7);
wxFKEY(100, WXK_F8);
wxFKEY(101, WXK_F9);
wxFKEY(109, WXK_F10);
wxFKEY(103, WXK_F11);
wxFKEY(111, WXK_F12);
wxFKEY(105, WXK_F13);
wxFKEY(107, WXK_F14);
wxFKEY(113, WXK_F15);
case 0x7e:
case 0x3e:
key = WXK_UP;
break;
case 0x7d:
case 0x3d:
key = WXK_DOWN;
break;
case 0x7b:
case 0x3b:
key = WXK_LEFT;
break;
case 0x7c:
case 0x3c:
key = WXK_RIGHT;
break;
case 0x24:
key = WXK_RETURN;
break;
case 0x30:
key = WXK_TAB;
break;
case 0x33:
key = WXK_BACK;
break;
case 0x75:
key = WXK_DELETE;
break;
case 0x73:
key = WXK_HOME;
break;
case 0x77:
key = WXK_END;
break;
case 0x74:
key = WXK_PRIOR;
break;
case 0x79:
key = WXK_NEXT;
break;
case 0x45:
key = WXK_ADD;
break;
case 78:
key = WXK_SUBTRACT;
break;
case 0x43:
key = WXK_MULTIPLY;
break;
case 0x4B:
key = WXK_DIVIDE;
break;
case 71:
key = WXK_SEPARATOR;
break;
case 65:
key = WXK_DECIMAL;
break;
case 76:
key = 3; /* numpad enter */
break;
case 82:
case 83:
case 84:
case 85:
case 86:
case 87:
case 88:
case 89:
key = WXK_NUMPAD0 + (key - 82);
break;
case 91:
key = WXK_NUMPAD8;
break;
case 92:
key = WXK_NUMPAD9;
break;
default:
{
int iter, akey, orig_key = key;
key = 0; /* let compiler know that key is assigned */ if (newkey) {
for (iter = 0; iter < ((cCurrentEvent.modifiers & cmdKey) ? 2 : 1); iter++) { key = newkey;
char cstr[3]; } else {
int from_str = 0; int iter, akey, orig_key = key;
akey = orig_key; key = 0; /* let compiler know that key is assigned */
for (iter = 0; iter < ((cCurrentEvent.modifiers & cmdKey) ? 2 : 1); iter++) {
char cstr[3];
int from_str = 0;
if (cCurrentEvent.modifiers & (wxMacDisableMods | cmdKey)) { akey = orig_key;
/* The following code manually translates the virtual key event
into a character. We'd use this code all the time, except
that dead keys have already been filtered before we get here,
which means that option-e-e doesn't produce an accented e.
So, instead, we only use this code to find out what would
happen if the control/option key wasn't pressed. */
int mods;
OSStatus status;
UniCharCount len;
UniChar keys[1];
SInt16 currentKeyLayoutID;
static UCKeyboardLayout *key_layout;
mods = cCurrentEvent.modifiers; if (cCurrentEvent.modifiers & (wxMacDisableMods | cmdKey)) {
if (mods & cmdKey) { /* The following code manually translates the virtual key event
/* Strip control and option modifiers when command is pressed: */ into a character. We'd use this code all the time, except
mods -= (mods & (optionKey | controlKey | cmdKey | shiftKey)); that dead keys have already been filtered before we get here,
/* On second iteration, set the shift key: */ which means that option-e-e doesn't produce an accented e.
if (iter) So, instead, we only use this code to find out what would
mods |= shiftKey; happen if the control/option key wasn't pressed. */
} else { int mods;
/* Remove effect of anything in wxMacDisableMods: */ OSStatus status;
mods -= (mods & wxMacDisableMods); UniCharCount len;
} UniChar keys[1];
SInt16 currentKeyLayoutID;
static UCKeyboardLayout *key_layout;
currentKeyLayoutID = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptKeys); mods = cCurrentEvent.modifiers;
if (!keyLayoutSet || (currentKeyLayoutID != lastKeyLayoutID)) { if (mods & cmdKey) {
KeyboardLayoutRef kl; /* Strip control and option modifiers when command is pressed: */
key_state = 0; mods -= (mods & (optionKey | controlKey | cmdKey | shiftKey));
if (KLGetCurrentKeyboardLayout(&kl) == noErr) { /* On second iteration, set the shift key: */
void *p; if (iter)
if (KLGetKeyboardLayoutProperty(kl, kKLKCHRData, (const void **)&p) == noErr) { mods |= shiftKey;
KCHRPtr = p; } else {
} else /* Remove effect of anything in wxMacDisableMods: */
KCHRPtr = NULL; mods -= (mods & wxMacDisableMods);
if (KLGetKeyboardLayoutProperty(kl, kKLuchrData, (const void **)&p) == noErr) }
uchrPtr = p;
else
uchrPtr = NULL;
}
lastKeyLayoutID = currentKeyLayoutID;
keyLayoutSet = 1;
}
if (!uchrPtr) { currentKeyLayoutID = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptKeys);
if (!KCHRPtr) { if (!keyLayoutSet || (currentKeyLayoutID != lastKeyLayoutID)) {
akey = '?'; KeyboardLayoutRef kl;
} else { key_state = 0;
int trans; if (KLGetCurrentKeyboardLayout(&kl) == noErr) {
trans = KeyTranslate(KCHRPtr, akey | mods, &key_state); void *p;
if (trans & 0xFF0000) { if (KLGetKeyboardLayoutProperty(kl, kKLKCHRData, (const void **)&p) == noErr) {
/* 2-byte result */ KCHRPtr = p;
cstr[0] = (trans & 0xFF0000) >> 16; } else
cstr[1] = trans & 0xFF; KCHRPtr = NULL;
cstr[2] = 0; if (KLGetKeyboardLayoutProperty(kl, kKLuchrData, (const void **)&p) == noErr)
} else { uchrPtr = p;
/* 1-byte result */ else
cstr[0] = trans & 0xFF; uchrPtr = NULL;
cstr[1] = 0; }
} lastKeyLayoutID = currentKeyLayoutID;
keyLayoutSet = 1;
}
akey = '?'; /* temporary */ if (!uchrPtr) {
from_str = 1; if (!KCHRPtr) {
} akey = '?';
} else { } else {
key_layout = (UCKeyboardLayout *)uchrPtr; int trans;
trans = KeyTranslate(KCHRPtr, akey | mods, &key_state);
if (trans & 0xFF0000) {
/* 2-byte result */
cstr[0] = (trans & 0xFF0000) >> 16;
cstr[1] = trans & 0xFF;
cstr[2] = 0;
} else {
/* 1-byte result */
cstr[0] = trans & 0xFF;
cstr[1] = 0;
}
status = UCKeyTranslate(key_layout, akey = '?'; /* temporary */
akey, from_str = 1;
cCurrentEvent.what - keyDown, }
mods >> 8, } else {
LMGetKbdType(), key_layout = (UCKeyboardLayout *)uchrPtr;
0 /* options */,
&key_state,
1,
&len,
keys);
if (status == noErr) status = UCKeyTranslate(key_layout,
akey = keys[0]; akey,
else cCurrentEvent.what - keyDown,
akey = '?'; mods >> 8,
} LMGetKbdType(),
} else { 0 /* options */,
akey = '?'; /* temporary */ &key_state,
cstr[0] = cCurrentEvent.message & charCodeMask; 1,
cstr[1] = 0; &len,
from_str = 1; keys);
}
if (from_str) { if (status == noErr)
CFStringRef str; akey = keys[0];
UniChar keys[1]; else
akey = '?';
}
} else {
akey = '?'; /* temporary */
cstr[0] = cCurrentEvent.message & charCodeMask;
cstr[1] = 0;
from_str = 1;
}
if (from_str) {
CFStringRef str;
UniChar keys[1];
str = CFStringCreateWithCStringNoCopy(NULL, cstr, str = CFStringCreateWithCStringNoCopy(NULL, cstr,
GetScriptManagerVariable(smKeyScript), GetScriptManagerVariable(smKeyScript),
kCFAllocatorNull); kCFAllocatorNull);
if (str) { if (str) {
if (CFStringGetLength(str) > 0) { if (CFStringGetLength(str) > 0) {
GC_CAN_IGNORE CFRange rng; GC_CAN_IGNORE CFRange rng;
rng = CFRangeMake(0, 1); rng = CFRangeMake(0, 1);
CFStringGetCharacters(str, rng, keys); CFStringGetCharacters(str, rng, keys);
} else } else
keys[0] = '?'; keys[0] = '?';
CFRelease(str); CFRelease(str);
} else } else
keys[0] = '?'; keys[0] = '?';
akey = keys[0]; akey = keys[0];
} }
if (!iter) if (!iter)
key = akey; key = akey;
else else
otherKey = akey; otherKey = akey;
}
} }
} // end switch }
} }
if (down) { if (down) {
@ -915,6 +826,111 @@ void wxApp::doMacAutoKey(void)
doMacKeyUpDown(true); doMacKeyUpDown(true);
} }
int wxTranslateRawKey(int key)
{
/* Better way than to use hard-wired key codes? */
switch (key) {
# define wxFKEY(code, wxk) case code: key = wxk; break
wxFKEY(122, WXK_F1);
wxFKEY(120, WXK_F2);
wxFKEY(99, WXK_F3);
wxFKEY(118, WXK_F4);
wxFKEY(96, WXK_F5);
wxFKEY(97, WXK_F6);
wxFKEY(98, WXK_F7);
wxFKEY(100, WXK_F8);
wxFKEY(101, WXK_F9);
wxFKEY(109, WXK_F10);
wxFKEY(103, WXK_F11);
wxFKEY(111, WXK_F12);
wxFKEY(105, WXK_F13);
wxFKEY(107, WXK_F14);
wxFKEY(113, WXK_F15);
case 0x7e:
case 0x3e:
key = WXK_UP;
break;
case 0x7d:
case 0x3d:
key = WXK_DOWN;
break;
case 0x7b:
case 0x3b:
key = WXK_LEFT;
break;
case 0x7c:
case 0x3c:
key = WXK_RIGHT;
break;
case 0x24:
key = WXK_RETURN;
break;
case 0x30:
key = WXK_TAB;
break;
case 0x33:
key = WXK_BACK;
break;
case 0x75:
key = WXK_DELETE;
break;
case 0x73:
key = WXK_HOME;
break;
case 0x77:
key = WXK_END;
break;
case 0x74:
key = WXK_PRIOR;
break;
case 0x79:
key = WXK_NEXT;
break;
case 0x45:
key = WXK_ADD;
break;
case 78:
key = WXK_SUBTRACT;
break;
case 0x43:
key = WXK_MULTIPLY;
break;
case 0x4B:
key = WXK_DIVIDE;
break;
case 71:
key = WXK_SEPARATOR;
break;
case 65:
key = WXK_DECIMAL;
break;
case 76:
key = 3; /* numpad enter */
break;
case 82:
case 83:
case 84:
case 85:
case 86:
case 87:
case 88:
case 89:
key = WXK_NUMPAD0 + (key - 82);
break;
case 91:
key = WXK_NUMPAD8;
break;
case 92:
key = WXK_NUMPAD9;
break;
default:
key = 0;
break;
}
return key;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void wxApp::doMacActivateEvt(void) void wxApp::doMacActivateEvt(void)
{ {

View File

@ -263,6 +263,12 @@ wxFrame::wxFrame // Constructor (for frame window)
spec[2].eventKind = kEventWindowBoundsChanging; spec[2].eventKind = kEventWindowBoundsChanging;
InstallEventHandler(GetWindowEventTarget(theMacWindow), window_evt_handler, 3, spec, refcon, NULL); InstallEventHandler(GetWindowEventTarget(theMacWindow), window_evt_handler, 3, spec, refcon, NULL);
} }
{
/* In case we need to recognize MrEd windows: */
UInt32 val = 1;
SetWindowProperty (theMacWindow, 'mReD', 'Ello', sizeof(UInt32), &val);
}
} }
static void userPaneDrawFunction(ControlRef controlRef, SInt16 thePart) static void userPaneDrawFunction(ControlRef controlRef, SInt16 thePart)