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 MrEdKeyWindow();
extern int wxTranslateRawKey(int key);
extern short wxMacDisableMods;
typedef MrQueueElem *MrQueueRef;
typedef int (*Checker_Func)(EventRecord *evt, MrQueueRef q, int check_only,
@ -276,9 +279,15 @@ static int pending_self_ae;
static void EnsureWNEReturn()
{
/* Generate an event that WaitNextEvent will return, but
that we can recognize and ignore. An AppleEvent is a
heavyweight but reliable way to do that. */
/* Generate an event that WaitNextEvent() will return, but that we can
recognize and ignore. (Note that window handlers can run nested
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) {
ProcessSerialNumber psn;
AEAddressDesc target;
@ -332,20 +341,34 @@ void wxSmuggleOutEvent(EventRef ref)
&& (GetEventKind(ref) == kEventTextInputUnicodeForKeyEvent)) {
UniChar *text;
UInt32 actualSize;
EventRef kref;
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];
GetEventParameter(ref, kEventParamTextInputSendKeyboardEvent,
typeEventRef, NULL, sizeof(EventRef), NULL, &kref);
if (ConvertEventRefToEventRecord(kref, &e)) {
ok = TRUE;
} else {
e.modifiers = 0;
e.message = 0;
e.where.h = 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 {
ok = ConvertEventRefToEventRecord(ref, &e);
@ -379,7 +402,7 @@ static pascal OSErr HandleSmug(const AppleEvent *evt, AppleEvent *rae, long k)
return 0;
}
/* WNE: a small wrapper for WaitNextEvent, mostly to manage
/* WNE: a small wrapper for WaitNextEvent(), mostly to manage
wake-up activities.
It's tempting to try to use ReceiveNextEvent() to filter
the raw events. Don't do that, because WaitNextEvent() is
@ -424,7 +447,7 @@ int WNE(EventRecord *e, double sleep_secs)
::InstallEventHandler(GetEventDispatcherTarget(),
smuggle_handler,
2,
3,
evts,
NULL,
NULL);
@ -465,7 +488,7 @@ static int TransferQueue(int all)
int sleep_time = 0;
int delay_time = 0;
/* Don't call WaitNextEvent too often. */
/* Don't call WaitNextEvent() too often. */
static unsigned long lastTime;
if (TickCount() <= lastTime + delay_time)
return 0;
@ -726,6 +749,7 @@ static int CheckForMouseOrKey(EventRecord *e, MrQueueRef osq, int check_only,
}
break;
case wheelEvt:
case unicodeEvt:
case keyDown:
case autoKey:
case keyUp:
@ -754,10 +778,6 @@ static int CheckForActivate(EventRecord *evt, MrQueueRef q, int check_only,
WindowPtr window;
switch (evt->what) {
#ifndef OS_X
// OS X does not support the diskEvt event.
case diskEvt:
#endif
case kHighLevelEvent:
{
MrEdContext *fc;
@ -904,6 +924,7 @@ int MrEdGetNextEvent(int check_only, int current_only,
case mouseMenuDown:
case mouseDown:
case wheelEvt:
case unicodeEvt:
case keyDown:
case keyUp:
case autoKey:
@ -1071,14 +1092,12 @@ int MrEdCheckForBreak(void)
/* sleep */
/***************************************************************************/
#ifdef OS_X
#include <pthread.h>
static volatile int thread_running;
static volatile int need_post; /* 0=>1 transition has a benign race condition, an optimization */
static SLEEP_PROC_PTR mzsleep;
static pthread_t watcher;
static volatile float sleep_secs;
static ProcessSerialNumber psn;
/* These file descriptors act as semaphores: */
static int watch_read_fd, watch_write_fd;
@ -1103,7 +1122,6 @@ static void *do_watch(void *fds)
mzsleep(sleep_secs, fds);
if (need_post) {
need_post = 0;
WakeUpProcess(&psn);
if (cb_socket_ready) {
/* Sometimes WakeUpProcess() doesn't work.
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) {
GetCurrentProcess(&psn);
if (pthread_create(&watcher, NULL, do_watch, fds)) {
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)
{
WakeUpProcess(&psn);
EnsureWNEReturn();
}
/* See ARGH below. */
static const void *sock_retain(const void *info)
{
return NULL;
}
/* See ARGH below. */
static void sock_release(const void *info)
{
/* do nothing */
}
/* See ARGH below. */
static CFStringRef sock_copy_desc(const void *info)
{
return CFSTR("sock");
}
#endif
static int going, reported_recursive_sleep;
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
bother with WaitNextEvent. */
bother with WaitNextEvent(). */
if ((secs > 0) && (secs < 1.0/60)) {
mzsleep(secs, fds);
} else {
EventRecord e;
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
that an fd is ready, it writes to write_sock_ready, which
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
correctly, because the watcher thread also calls
WakeUpProcess(). It seems to have become broken in OS X 10.2,
where WakeUpprocess() doesn't work when called before the WNE
starts (reminiscent of OS 7.1.2 or so). */
With the current implementation of EnsureWNEReturn(), this is
probably overkill. I think the watcher thread could call
EnsureWNEReturn() directly. Doing it this way moves the call
into this thread, though, which seems more robust in the long
run (i.e., if EnsureWNEReturn() changes). */
int fds[2];
if (!pipe(fds)) {
CFRunLoopRef rl;

View File

@ -46,6 +46,8 @@ extern WindowPtr MrEdKeyWindow();
extern void wxCheckRootFrame(WindowPtr w);
int wxTranslateRawKey(int key);
int wxMenuBarHeight;
extern wxApp *wxTheApp;
@ -649,232 +651,141 @@ void wxApp::doMacKeyUpDown(Bool down)
} else if (cCurrentEvent.what == unicodeEvt) {
key = cCurrentEvent.message;
} else {
int newkey;
key = (cCurrentEvent.message & keyCodeMask) >> 8;
/* 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:
{
int iter, akey, orig_key = key;
newkey = wxTranslateRawKey(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 (newkey) {
key = newkey;
} else {
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)) {
/* 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;
akey = orig_key;
mods = cCurrentEvent.modifiers;
if (mods & cmdKey) {
/* Strip control and option modifiers when command is pressed: */
mods -= (mods & (optionKey | controlKey | cmdKey | shiftKey));
/* On second iteration, set the shift key: */
if (iter)
mods |= shiftKey;
} else {
/* Remove effect of anything in wxMacDisableMods: */
mods -= (mods & wxMacDisableMods);
}
if (cCurrentEvent.modifiers & (wxMacDisableMods | cmdKey)) {
/* 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;
currentKeyLayoutID = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptKeys);
if (!keyLayoutSet || (currentKeyLayoutID != lastKeyLayoutID)) {
KeyboardLayoutRef kl;
key_state = 0;
if (KLGetCurrentKeyboardLayout(&kl) == noErr) {
void *p;
if (KLGetKeyboardLayoutProperty(kl, kKLKCHRData, (const void **)&p) == noErr) {
KCHRPtr = p;
} else
KCHRPtr = NULL;
if (KLGetKeyboardLayoutProperty(kl, kKLuchrData, (const void **)&p) == noErr)
uchrPtr = p;
else
uchrPtr = NULL;
}
lastKeyLayoutID = currentKeyLayoutID;
keyLayoutSet = 1;
}
mods = cCurrentEvent.modifiers;
if (mods & cmdKey) {
/* Strip control and option modifiers when command is pressed: */
mods -= (mods & (optionKey | controlKey | cmdKey | shiftKey));
/* On second iteration, set the shift key: */
if (iter)
mods |= shiftKey;
} else {
/* Remove effect of anything in wxMacDisableMods: */
mods -= (mods & wxMacDisableMods);
}
if (!uchrPtr) {
if (!KCHRPtr) {
akey = '?';
} else {
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;
}
currentKeyLayoutID = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptKeys);
if (!keyLayoutSet || (currentKeyLayoutID != lastKeyLayoutID)) {
KeyboardLayoutRef kl;
key_state = 0;
if (KLGetCurrentKeyboardLayout(&kl) == noErr) {
void *p;
if (KLGetKeyboardLayoutProperty(kl, kKLKCHRData, (const void **)&p) == noErr) {
KCHRPtr = p;
} else
KCHRPtr = NULL;
if (KLGetKeyboardLayoutProperty(kl, kKLuchrData, (const void **)&p) == noErr)
uchrPtr = p;
else
uchrPtr = NULL;
}
lastKeyLayoutID = currentKeyLayoutID;
keyLayoutSet = 1;
}
akey = '?'; /* temporary */
from_str = 1;
}
} else {
key_layout = (UCKeyboardLayout *)uchrPtr;
if (!uchrPtr) {
if (!KCHRPtr) {
akey = '?';
} else {
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,
cCurrentEvent.what - keyDown,
mods >> 8,
LMGetKbdType(),
0 /* options */,
&key_state,
1,
&len,
keys);
akey = '?'; /* temporary */
from_str = 1;
}
} else {
key_layout = (UCKeyboardLayout *)uchrPtr;
if (status == noErr)
akey = keys[0];
else
akey = '?';
}
} else {
akey = '?'; /* temporary */
cstr[0] = cCurrentEvent.message & charCodeMask;
cstr[1] = 0;
from_str = 1;
}
status = UCKeyTranslate(key_layout,
akey,
cCurrentEvent.what - keyDown,
mods >> 8,
LMGetKbdType(),
0 /* options */,
&key_state,
1,
&len,
keys);
if (from_str) {
CFStringRef str;
UniChar keys[1];
if (status == noErr)
akey = keys[0];
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,
GetScriptManagerVariable(smKeyScript),
kCFAllocatorNull);
if (str) {
if (CFStringGetLength(str) > 0) {
GC_CAN_IGNORE CFRange rng;
rng = CFRangeMake(0, 1);
CFStringGetCharacters(str, rng, keys);
} else
keys[0] = '?';
CFRelease(str);
} else
keys[0] = '?';
str = CFStringCreateWithCStringNoCopy(NULL, cstr,
GetScriptManagerVariable(smKeyScript),
kCFAllocatorNull);
if (str) {
if (CFStringGetLength(str) > 0) {
GC_CAN_IGNORE CFRange rng;
rng = CFRangeMake(0, 1);
CFStringGetCharacters(str, rng, keys);
} else
keys[0] = '?';
CFRelease(str);
} else
keys[0] = '?';
akey = keys[0];
}
akey = keys[0];
}
if (!iter)
key = akey;
else
otherKey = akey;
}
if (!iter)
key = akey;
else
otherKey = akey;
}
} // end switch
}
}
if (down) {
@ -915,6 +826,111 @@ void wxApp::doMacAutoKey(void)
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)
{

View File

@ -263,6 +263,12 @@ wxFrame::wxFrame // Constructor (for frame window)
spec[2].eventKind = kEventWindowBoundsChanging;
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)