racket/src/mred/mredmac.cxx
Eli Barzilay ddc068c52b 2006->2007
svn: r5201
2006-12-31 10:05:55 +00:00

2285 lines
53 KiB
C++

/*
* File: mredmac.cc
* Purpose: MrEd MacOS event loop
* Author: Matthew Flatt
* Created: 1996
* Copyright: (c) 2004-2007 PLT Scheme Inc.
* Copyright: (c) 1996, Matthew Flatt
*/
#include "common.h"
#include "wx_main.h"
#include "wx_media.h"
#include "scheme.h"
#include "wx_macevents.h"
#include "wx_het.h"
#include "mred.h"
#include <unistd.h>
#include <fcntl.h>
#ifdef __i386__
# include <CoreServices/CoreServices.h>
# define wxNATIVE_LONG(x) EndianS32_BtoN((x).bigEndianValue)
#else
# define wxNATIVE_LONG(x) x
#endif
static int dispatched = 1;
extern "C" {
typedef void (*HANDLE_AE)(EventRecord *e);
}
class MrQueueElem; /* defined below */
static void QueueTransferredEvent(EventRecord *e);
static void MrDequeue(MrQueueElem *q);
WindowPtr MrEdMouseWindow(Point where);
WindowPtr MrEdKeyWindow();
int wx_leave_all_input_alone;
extern int wxTranslateRawKey(int key);
extern short wxMacDisableMods;
typedef MrQueueElem *MrQueueRef;
typedef int (*Checker_Func)(EventRecord *evt, MrQueueRef q, int check_only,
MrEdContext *c, MrEdContext *keyOk,
EventRecord *event, MrEdContext **which);
typedef struct EventFinderClosure {
int check_only;
MrEdContext *c;
MrEdContext *keyOk;
EventRecord *event;
MrEdContext **which;
Checker_Func checker;
} EventFinderClosure;
static int queue_size, max_queue_size;
static int mouse_down_in_flight;
Bool wx_ignore_key; /* used in wxItem */
void MrEdInitFirstContext(MrEdContext *)
{
}
void MrEdInitNewContext(MrEdContext *)
{
}
void MrEdDestroyContext(MrEdFinalizedContext *)
{
}
int MrEdGetDoubleTime(void)
{
return (int)(GetDblTime() * 16.67);
}
static wxFrame *_wxWindowPtrToFrame(WindowPtr w, wxChildList *l)
{
wxChildNode *n;
for (n = l->First(); n; n = n->Next()) {
wxFrame *f;
f = (wxFrame *)n->Data();
if (f->macWindow() == w)
return f;
}
return NULL;
}
static wxFrame *wxWindowPtrToFrame(WindowPtr w, MrEdContext *c)
{
if (c)
return _wxWindowPtrToFrame(w, c->topLevelWindowList);
else {
for (c = mred_contexts; c; c = c->next) {
wxFrame *f;
if ((f = _wxWindowPtrToFrame(w, c->topLevelWindowList)))
return f;
}
}
return NULL;
}
static void UpdateRgnToWindowCoords(WindowPtr w, RgnHandle updateRgn)
{
Rect windowBounds;
RgnHandle contentRgn;
GetWindowBounds(w, kWindowGlobalPortRgn, &windowBounds);
/* Avoid overflow in offset: */
contentRgn = NewRgn();
if (contentRgn) {
GetWindowRegion(w, kWindowContentRgn, contentRgn);
SectRgn(contentRgn, updateRgn, updateRgn);
DisposeRgn(contentRgn);
}
OffsetRgn(updateRgn, -1 * windowBounds.left, -1 * windowBounds.top);
}
/***************************************************************************/
/* shadow event queue */
/***************************************************************************/
/*
We need two things from the event queue:
* We need to handle the event queue non-sequentially. That is, we
want to handle certain kinds of events before handling other
kinds of events.
* We need to be able to sleep until a new (potentially ready to
handle) event arrives in the queue.
The only solution appears to be sucking all of the events into a
queue of our own, and dealing with them there. This causes certain
problems, but not horrible ones.
*/
class MrQueueElem {
public:
EventRecord event;
RgnHandle rgn;
int half_done; /* e.g., window brought to front */
MrQueueElem *next, *prev;
};
static MrQueueElem *first, *last;
/* QueueTransferredEvent takes an event and puts it
* in the MrEd queue, with several exceptions.
* 1. Update events. Update events are sent by the OS
* whenever the OS queue does not contain an update
* event and the update region is not empty. That is,
* the OS will keep poking you until the update region
* is empty. To get around this, QTE clears the update
* region manually (and then must reinstate it when it's
* time to handle the event. ick.
* 2. high level events. Dispatched immediately, and the
* handlers queue work in MzScheme threads.
* 3. suspendResumeMessage. See comment at top.
*/
static void QueueTransferredEvent(EventRecord *e)
{
MrQueueElem *q;
int done;
dispatched = 0;
done = 0;
if (e->what == updateEvt) {
WindowPtr w = (WindowPtr)e->message;
for (q = first; q; q = q->next) {
if ((q->event.what == updateEvt)
&& (w == ((WindowPtr)q->event.message))) {
RgnHandle updateRgn;
updateRgn = NewRgn();
GetWindowRegion(w, kWindowUpdateRgn, updateRgn);
/* Shift to window coords, because the window might
move before we handle the update */
UpdateRgnToWindowCoords(w, updateRgn);
UnionRgn(updateRgn, q->rgn, q->rgn);
DisposeRgn(updateRgn);
BeginUpdate(w);
EndUpdate(w);
return;
}
}
}
if (e->what == kHighLevelEvent) {
/* We have to dispatch the event immediately */
AEProcessAppleEvent(e);
return;
}
if ((e->what == osEvt) && !(((e->message >> 24) & 0x0ff) == suspendResumeMessage))
return;
q = new WXGC_PTRS MrQueueElem;
memcpy(&q->event, e, sizeof(EventRecord));
q->next = NULL;
q->prev = last;
if (last)
last->next = q;
else
first = q;
last = q;
queue_size++;
if (queue_size > max_queue_size) {
max_queue_size = queue_size;
}
if ((e->what == mouseDown)
|| (e->what == mouseMenuDown)) {
mouse_down_in_flight = 1;
}
q->rgn = NULL;
if (e->what == updateEvt) {
WindowPtr w = (WindowPtr)e->message;
q->rgn = NewRgn();
GetWindowRegion(w, kWindowUpdateRgn, q->rgn);
BeginUpdate(w);
EndUpdate(w);
/* Shift to window coords, because the window might
move before we handle the update */
UpdateRgnToWindowCoords(w, q->rgn);
} else if (e->what == osEvt) {
/* Must be a suspend/resume event */
int we_are_front = e->message & resumeFlag;
WindowPtr front;
front = ActiveNonFloatingWindow();
/* Generate an activate event */
q->event.what = activateEvt;
q->event.modifiers = we_are_front ? activeFlag : 0;
q->event.message = (long)front;
}
}
/* Called by wxWindows to queue leave and activate events: */
void QueueMrEdEvent(EventRecord *e)
{
QueueTransferredEvent(e);
}
void DequeueMrEdEvents(int type, long message)
{
/* Remove matching events: */
MrQueueElem *qq, *next;
for (qq = first; qq; qq = next) {
next = qq->next;
if ((qq->event.what == type)
&& ((long)qq->event.message == message))
MrDequeue(qq);
}
}
static RgnHandle mouseRgn;
static int waiting_for_next_event;
static int wne_handlersInstalled;
static int pending_self_ae;
static void EnsureWNEReturn()
{
/* 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;
AppleEvent ae;
pending_self_ae = 1;
GetCurrentProcess(&psn);
AECreateDesc(typeProcessSerialNumber, &psn, sizeof(psn), &target);
AECreateAppleEvent('MrEd', 'Smug', &target, kAutoGenerateReturnID, kAnyTransactionID, &ae);
AESend(&ae, NULL, kAENoReply, kAENormalPriority, kNoTimeOut, NULL, NULL);
}
}
void wxSmuggleOutEvent(EventRef ref)
{
EventRecord e;
int ok = 0;
if ((GetEventClass(ref) == kEventClassMouse)
&& (GetEventKind(ref) == 11 /* kEventMouseScroll */)) {
GetEventParameter(ref, kEventParamEventRef, typeEventRef,
NULL, sizeof(ref), NULL, &ref);
}
if ((GetEventClass(ref) == kEventClassMouse)
&& (GetEventKind(ref) == kEventMouseWheelMoved)) {
UInt32 modifiers;
EventMouseWheelAxis axis;
SInt32 delta;
Point pos;
GetEventParameter(ref, kEventParamKeyModifiers, typeUInt32,
NULL, sizeof(modifiers), NULL, &modifiers);
GetEventParameter(ref, kEventParamMouseWheelAxis,
typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
GetEventParameter(ref, kEventParamMouseWheelDelta,
typeLongInteger, NULL, sizeof(delta), NULL, &delta);
GetEventParameter(ref, kEventParamMouseLocation,
typeQDPoint, NULL, sizeof(Point), NULL, &pos);
if (axis == kEventMouseWheelAxisY) {
e.what = wheelEvt;
e.message = (delta > 0);
e.modifiers = modifiers;
e.where.h = pos.h;
e.where.v = pos.v;
ok = TRUE;
}
} else if ((GetEventClass(ref) == kEventClassTextInput)
&& (GetEventKind(ref) == kEventTextInputUnicodeForKeyEvent)) {
UniChar *text;
UInt32 actualSize;
EventRef kref;
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;
}
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);
}
if (ok) {
QueueTransferredEvent(&e);
EnsureWNEReturn();
}
}
static OSStatus unhide_cursor_handler(EventHandlerCallRef inHandlerCallRef,
EventRef inEvent,
void *inUserData)
{
wxUnhideCursor();
return eventNotHandledErr;
}
static OSStatus smuggle_handler(EventHandlerCallRef inHandlerCallRef,
EventRef inEvent,
void *inUserData)
{
if (wx_leave_all_input_alone)
return eventNotHandledErr;
wxSmuggleOutEvent(inEvent);
return noErr;
}
static pascal OSErr HandleSmug(const AppleEvent *evt, AppleEvent *rae, long k)
{
pending_self_ae = 0;
return 0;
}
/* 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
magic. In particular, WaitNextEvent() properly handles
Cmd-~, Cmd-Q, dead keys like option-e on a U.S. keyboard,
clicking that brings the application to the foreground,
and the character palette. (We used ReceiveNextEvent()
until version 352.7, and finally gave up when trying
to get the character palette to work.) */
int WNE(EventRecord *e, double sleep_secs)
{
int r;
if (mouse_down_in_flight) {
/* Try hard to handle a mouse-down event before calling
WaitNextEvent again. Otherwise, mouse events for tracking
(e.g., menu clicks, close-window clicks, window-drag clicks,
and button clicks) can get lost. We can't wait forever, though;
the target eventspace might be stuck for some reason. If MrEd
is idle enough to sleep, take that as a sign that it's ok to
get new events. Another sign is if there's a new mouse-down or
key-down event. Some other cases, such as a `yield' or waiting
on an AppleEvent, are handled by explicitly turning off
mouse_down_in_flight before we get here. */
EventRef eref;
EventTypeSpec poll_evts[2];
if (!sleep_secs) {
poll_evts[0].eventClass = kEventClassMouse;
poll_evts[0].eventKind = kEventMouseDown;
poll_evts[1].eventClass = kEventClassKeyboard;
poll_evts[1].eventKind = kEventRawKeyDown;
eref = AcquireFirstMatchingEventInQueue(GetCurrentEventQueue(),
2,
poll_evts,
kEventQueueOptionsNone);
if (eref) {
ReleaseEvent(eref);
} else {
/* Looks like we should wait... */
return 0;
}
}
}
wxResetCanvasBackgrounds();
if (!wne_handlersInstalled) {
EventTypeSpec evts[4];
wne_handlersInstalled = TRUE;
evts[0].eventClass = kEventClassMouse;
evts[0].eventKind = kEventMouseDown;
evts[1].eventClass = kEventClassMouse;
evts[1].eventKind = kEventMouseMoved;
evts[2].eventClass = kEventClassMouse;
evts[2].eventKind = kEventMouseUp;
evts[3].eventClass = kEventClassMouse;
evts[3].eventKind = kEventMouseDragged;
::InstallEventHandler(GetEventDispatcherTarget(),
unhide_cursor_handler,
4,
evts,
NULL,
NULL);
evts[0].eventClass = kEventClassMouse;
evts[0].eventKind = 11 /* kEventMouseScroll */;
evts[1].eventClass = kEventClassMouse;
evts[1].eventKind = kEventMouseWheelMoved;
evts[2].eventClass = kEventClassTextInput;
evts[2].eventKind = kEventTextInputUnicodeForKeyEvent;
::InstallEventHandler(GetEventDispatcherTarget(),
smuggle_handler,
3,
evts,
NULL,
NULL);
AEInstallEventHandler('MrEd', 'Smug', HandleSmug, 0, 0);
mouseRgn = NewRgn();
SetRectRgn(mouseRgn, 0, 0, 1, 1);
}
waiting_for_next_event = 1;
r = WaitNextEvent(everyEvent, e, sleep_secs * 60, mouseRgn);
waiting_for_next_event = 0;
return r;
}
void WakeUpMrEd()
{
/* Make sure we wake up a sleep, if this is a callback through
a window painter. */
if (waiting_for_next_event) {
EnsureWNEReturn();
waiting_for_next_event = 0;
}
}
/* TransferQueue sucks all of the pending events out of the
Application queue, sticks them in the MrEd queue, and returns 1,
unless it was called less than delay_time ago, in which case do
nothing and return 0. */
static unsigned long lastTime;
static int TransferQueue(int all)
{
EventRecord e;
int sleep_time = 0;
int delay_time = 0;
/* Don't call WaitNextEvent() too often. */
if (TickCount() <= lastTime + delay_time)
return 0;
while (WNE(&e, dispatched ? ((double)sleep_time/60.0) : 0)) {
QueueTransferredEvent(&e);
}
lastTime = TickCount();
return 1;
}
static void MrDequeue(MrQueueElem *q)
{
if (q->prev)
q->prev->next = q->next;
else
first = q->next;
if (q->next)
q->next->prev = q->prev;
else
last = q->prev;
--queue_size;
}
static MrQueueRef Find(EventFinderClosure *closure)
{
MrQueueRef osq, next;
osq = first;
while (osq) {
next = osq->next;
if (closure->checker(&osq->event, osq, closure->check_only,
closure->c, closure->keyOk,
closure->event, closure->which)) {
return osq;
}
osq = next;
}
return NULL;
}
/***************************************************************************/
/* state finder */
/***************************************************************************/
static MrEdContext *KeyOk(int current_only)
{
WindowPtr w;
wxFrame *fr;
MrEdContext *c;
c = current_only ? MrEdGetContext() : NULL;
fr = wxGetFocusFrame();
if (!fr) {
w = ActiveNonFloatingWindow();
fr = wxWindowPtrToFrame(w, c);
}
if (!fr || (c && (fr->context != (void *)c))
|| (!c && !((MrEdContext *)fr->context)->ready))
return NULL;
return (fr ? (MrEdContext *)fr->context : c);
}
static int WindowStillHere(WindowPtr win)
{
return IsValidWindowPtr(win);
}
static int GetMods(void)
{
KeyMap km;
int mods = 0;
GetKeys(km);
if (wxNATIVE_LONG(km[1]) & 32768)
mods |= cmdKey;
if (wxNATIVE_LONG(km[1]) & 1)
mods |= shiftKey;
if (wxNATIVE_LONG(km[1]) & 4)
mods |= optionKey;
if (wxNATIVE_LONG(km[1]) & 8)
mods |= controlKey;
return mods;
}
/* the cont_mouse_context is used to keep information about mouse-downs around so
* that later mouse-ups can be properly handled.
*/
static MrEdContext *cont_mouse_context;
static WindowPtr cont_mouse_context_window;
static Point last_mouse;
static WindowPtr last_front_window;
void wxTracking()
{
/* This function is called whenever wxMac lets the toolbox process
events, normally to track some button click. In that case, we
assume that a mouse-up event won't come through the event
queue. */
cont_mouse_context = NULL;
cont_mouse_context_window = NULL;
}
void wxMouseEventHandled(void)
{
mouse_down_in_flight = 0;
}
#ifdef RECORD_HISTORY
FILE *history;
#endif
/***************************************************************************/
/* event finders */
/***************************************************************************/
static int CheckForLeave(EventRecord *evt, MrQueueRef q, int check_only,
MrEdContext *c, MrEdContext *keyOk,
EventRecord *event, MrEdContext **which) {
switch (evt->what) {
case leaveEvt:
{
wxWindow *win;
wxFrame *fr;
MrEdContext *fc;
void *refcon;
refcon = (void *)evt->message;
win = (wxWindow *)GET_SAFEREF(refcon);
if ((win->__type != -1) && win->IsShown()) {
fr = (wxFrame *)win->GetRootFrame();
fc = fr ? (MrEdContext *)fr->context : NULL;
if ((!c && !fr) || (!c && fc->ready) || (fc == c)) {
if (which)
*which = fc;
#ifdef RECORD_HISTORY
fprintf(history, "leave\n");
fflush(history);
#endif
if (check_only)
return TRUE;
MrDequeue(q);
memcpy(event, evt, sizeof(EventRecord));
return TRUE;
}
} else {
MrDequeue(q);
}
}
}
return FALSE;
}
static int saw_mdown = 0, mdown_was_ctl = 0, saw_kdown = 0;
static int CheckForMouseOrKey(EventRecord *e, MrQueueRef osq, int check_only,
MrEdContext *c, MrEdContext *keyOk,
EventRecord *event, MrEdContext **foundc) {
int found = 0;
wxFrame *fr;
MrEdContext *fc;
switch (e->what) {
case mouseMenuDown:
case mouseDown:
{
WindowPtr window, front = NULL;
int part;
saw_mdown = 1;
part = FindWindow(e->where, &window);
if (part == inMenuBar) {
front = ActiveNonFloatingWindow();
window = front;
}
if (!window) {
MrDequeue(osq);
found = 1;
*foundc = keyOk;
cont_mouse_context = NULL;
} else if (!WindowStillHere(window)) {
MrDequeue(osq);
} else {
MrEdContext *clickOk;
fr = wxWindowPtrToFrame(window, c);
fc = fr ? (MrEdContext *)fr->context : NULL;
if (!fr || (c && (fr->context != (void *)c))
|| (!c && !((MrEdContext *)fr->context)->ready))
clickOk = NULL;
else
clickOk = fc;
if (!front)
front = ActiveNonFloatingWindow();
if (window != front) {
WindowClass wc;
GetWindowClass(window, &wc);
if ((wc != kFloatingWindowClass)
&& (wc != kUtilityWindowClass)
&& (wc != kToolbarWindowClass)) {
/* Handle bring-window-to-front click immediately */
if (!osq->half_done) {
if (fc && (!fc->modal_window || (fr == fc->modal_window))) {
if ((part == inContent) || !(e->modifiers & cmdKey))
SelectWindow(window);
cont_mouse_context = NULL;
} else if (fc && fc->modal_window) {
wxFrame *mfr;
mfr = (wxFrame *)fc->modal_window;
cont_mouse_context = NULL;
if ((part == inContent) || !(e->modifiers & cmdKey))
SelectWindow(mfr->macWindow());
}
osq->half_done = 1;
}
}
}
*foundc = clickOk;
if (*foundc) {
last_mouse.h = -1;
found = 1;
if (!check_only && (part != inMenuBar)) {
cont_mouse_context = *foundc;
cont_mouse_context_window = window;
mdown_was_ctl = (e->modifiers & controlKey);
} else
cont_mouse_context = NULL;
}
}
}
break;
case mouseUp:
if (!cont_mouse_context) {
if (!saw_mdown) {
MrDequeue(osq);
}
} else if (keyOk == cont_mouse_context) {
*foundc = keyOk;
if (*foundc) {
found = 1;
if (!check_only)
cont_mouse_context = NULL;
}
}
break;
case wheelEvt:
case unicodeEvt:
case keyDown:
case autoKey:
case keyUp:
*foundc = keyOk;
if (*foundc) {
found = 1;
}
break;
}
if (found) {
memcpy(event, e, sizeof(EventRecord));
/* Preserve rightness (as opposed to leftness) of mouse clicks */
if ((e->what == mouseUp) && mdown_was_ctl)
event->modifiers |= controlKey;
}
return found;
}
static int CheckForActivate(EventRecord *evt, MrQueueRef q, int check_only,
MrEdContext *c, MrEdContext *keyOk,
EventRecord *event, MrEdContext **which)
{
WindowPtr window;
switch (evt->what) {
case kHighLevelEvent:
{
MrEdContext *fc;
fc = NULL;
if ((!c && !fc) || (!c && fc->ready) || (fc == c)) {
if (which)
*which = fc;
if (check_only)
return TRUE;
memcpy(event, evt, sizeof(EventRecord));
MrDequeue(q);
return TRUE;
}
}
break;
case activateEvt:
window = (WindowPtr)evt->message;
if (WindowStillHere(window)) {
wxFrame *fr;
MrEdContext *fc;
fr = wxWindowPtrToFrame(window, c);
fc = fr ? (MrEdContext *)fr->context : NULL;
if ((!c && !fr) || (!c && fc->ready) || (fc == c)) {
if (which)
*which = fc;
#ifdef RECORD_HISTORY
fprintf(history, "activate\n");
fflush(history);
#endif
if (check_only)
return TRUE;
memcpy(event, evt, sizeof(EventRecord));
MrDequeue(q);
return TRUE;
}
} else
MrDequeue(q);
break;
}
return FALSE;
}
/***************************************************************************/
/* get next event */
/***************************************************************************/
int MrEdGetNextEvent(int check_only, int current_only,
EventRecord *event, MrEdContext **which)
{
/* Search for an event. Handle clicks in non-frontmost windows
immediately. */
MrQueueRef osq;
EventFinderClosure closure;
EventRecord ebuf;
MrEdContext *c, *keyOk, *foundc;
int found = 0;
saw_mdown = 0; saw_kdown = 0;
if (!event)
event = &ebuf;
c = current_only ? MrEdGetContext() : NULL;
wxResetCanvasBackgrounds();
keyOk = KeyOk(current_only);
#ifdef RECORD_HISTORY
if (!history) history = fopen("history3", "w");
fprintf(history, "%lx %lx %lx\n",
c, keyOk, cont_event_context);
#endif
#if 0
/* Update events are supposed to happen after mouse events, etc.
However, OS X refreshes window displays when WNE is called. In
particular, it looks nicer to update the frontmost window before
calling WNE. We must do this infrequenty, though, to avoid
dispatching only update events when other sorts of events should
get handled. */
static RgnHandle quickUpdateRgn;
static UInt32 quickUpdateTimeout;
static UInt32 quickUpdateWait;
if (!quickUpdateWait || (quickUpdateWait <= TickCount())) {
WindowPtr front;
quickUpdateWait = 0;
front = FrontNonFloatingWindow();
if (front) {
if (!quickUpdateRgn)
quickUpdateRgn = NewRgn();
GetWindowRegion(front, kWindowUpdateRgn, quickUpdateRgn);
if (!EmptyRgn(quickUpdateRgn)) {
/* Setup a trampoline and call WNE if the current thread
if the handler thread for the front window? */
quickUpdateWait = TickCount() + 15;
}
}
}
#endif
TransferQueue(0);
if (cont_mouse_context)
if (!WindowStillHere(cont_mouse_context_window))
cont_mouse_context = NULL;
closure.c = c;
closure.check_only = check_only;
closure.keyOk = keyOk;
closure.event = event;
closure.which = which;
/* First, service leave events: */
closure.checker = CheckForLeave;
if (Find(&closure))
return TRUE;
/* Next, service mouse & key events: */
closure.checker = CheckForMouseOrKey;
closure.which = &foundc;
if ((osq = Find(&closure))) {
found = 1;
}
closure.which = which;
if (found) {
/* Remove intervening mouse/key events: */
MrQueueElem *qq, *next;
for (qq = first; qq && (qq != osq); qq = next) {
next = qq->next;
switch (qq->event.what) {
case mouseUp:
cont_mouse_context = NULL;
/* fallthrough... */
case mouseMenuDown:
case mouseDown:
case wheelEvt:
case unicodeEvt:
case keyDown:
case keyUp:
case autoKey:
MrDequeue(qq);
break;
}
}
if (which)
*which = foundc;
#ifdef RECORD_HISTORY
fprintf(history, "mouse or key\n");
fflush(history);
#endif
if (check_only)
return TRUE;
MrDequeue(osq);
return TRUE;
}
// TransferQueue(0);
/* Try activate and high-level events: */
closure.checker = CheckForActivate;
if (Find(&closure))
return TRUE;
/* Generate a motion event? */
if (keyOk) {
WindowPtr front;
GetMouse(&event->where);
LocalToGlobal(&event->where);
front = MrEdMouseWindow(event->where);
if (((event->where.v != last_mouse.v)
|| (event->where.h != last_mouse.h)
|| last_front_window != front)
&& (!cont_mouse_context || (cont_mouse_context == keyOk))) {
long ticks;
if (which)
*which = (cont_mouse_context ? cont_mouse_context : keyOk);
if (check_only) {
#ifdef RECORD_HISTORY
fprintf(history, "move or drag\n");
fflush(history);
#endif
return TRUE;
}
last_mouse.v = event->where.v;
last_mouse.h = event->where.h;
last_front_window = front;
event->what = nullEvent;
ticks = TickCount();
event->when = ticks;
if (cont_mouse_context) {
/* Dragging... */
int mods;
mods = GetMods();
if (mdown_was_ctl)
mods |= controlKey;
event->modifiers = mods | btnState;
event->message = 1;
#ifdef RECORD_HISTORY
fprintf(history, "drag\n");
fflush(history);
#endif
} else {
if (keyOk) {
int mods;
mods = GetMods();
event->modifiers = mods;
} else {
event->modifiers = 0;
}
event->message = (keyOk ? 1 : 0);
#ifdef RECORD_HISTORY
fprintf(history, "move\n");
fflush(history);
#endif
}
return TRUE;
}
}
#ifdef RECORD_HISTORY
fprintf(history, "no event\n");
fflush(history);
#endif
return FALSE;
}
extern void wxCheckFinishedSounds(void);
void MrEdDispatchEvent(EventRecord *e)
{
dispatched = 1;
if (e->what == updateEvt) {
/* Find the update event for this window: */
RgnHandle rgn = NULL;
MrQueueElem *q;
WindowPtr w;
w = (WindowPtr)e->message;
for (q = first; q; q = q->next) {
if ((q->event.what == updateEvt)
&& (w == ((WindowPtr)q->event.message))) {
rgn = q->rgn;
MrDequeue(q);
break;
}
}
if (rgn) {
/* rgn is in window co-ords */
InvalWindowRgn(w, rgn);
DisposeRgn(rgn);
}
}
wxTheApp->doMacPreEvent();
wxTheApp->doMacDispatch(e);
wxTheApp->doMacPostEvent();
wxCheckFinishedSounds();
}
int MrEdCheckForBreak(void)
{
MrQueueElem *q;
if (!KeyOk(TRUE))
return 0;
TransferQueue(0);
for (q = first; q; q = q->next) {
if (q->event.what == keyDown) {
if ((((q->event.message & charCodeMask) == '.')
&& (q->event.modifiers & cmdKey))
|| (((q->event.message & charCodeMask) == 3)
&& (q->event.modifiers & controlKey))) {
MrDequeue(q);
return TRUE;
}
}
}
return FALSE;
}
/***************************************************************************/
/* sleep */
/***************************************************************************/
#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;
/* These file descriptors act as semaphores: */
static int watch_read_fd, watch_write_fd;
static int watch_done_read_fd, watch_done_write_fd;
/* These file descriptors are used for breaking the event loop.
See ARGH below. */
static int cb_socket_ready;
static int ready_sock, write_ready_sock;
#ifdef MZ_PRECISE_GC
START_XFORM_SKIP;
#endif
static void *do_watch(void *fds)
{
while (1) {
char buf[1];
read(watch_read_fd, buf, 1);
mzsleep(sleep_secs, fds);
if (need_post) {
need_post = 0;
if (cb_socket_ready) {
/* Sometimes WakeUpProcess() doesn't work.
Try a notification socket as a backup.
See ARGH below. */
write(write_ready_sock, "y", 1);
}
}
write(watch_done_write_fd, "y", 1);
}
return NULL;
}
#ifdef MZ_PRECISE_GC
END_XFORM_SKIP;
#endif
static int StartFDWatcher(void (*mzs)(float secs, void *fds), float secs, void *fds)
{
if (!watch_write_fd) {
int fds[2];
if (!pipe(fds)) {
watch_read_fd = fds[0];
watch_write_fd = fds[1];
} else {
return 0;
}
}
if (!watch_done_write_fd) {
int fds[2];
if (!pipe(fds)) {
watch_done_read_fd = fds[0];
watch_done_write_fd = fds[1];
} else {
return 0;
}
}
if (!watcher) {
if (pthread_create(&watcher, NULL, do_watch, fds)) {
return 0;
}
}
mzsleep = mzs;
sleep_secs = secs;
thread_running = 1;
need_post = 1;
write(watch_write_fd, "x", 1);
return 1;
}
static void EndFDWatcher(void)
{
char buf[1];
if (thread_running) {
if (need_post) {
need_post = 0;
scheme_signal_received();
}
read(watch_done_read_fd, buf, 1);
thread_running = 0;
}
}
void socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
EnsureWNEReturn();
}
static const void *sock_retain(const void *info)
{
return NULL;
}
static void sock_release(const void *info)
{
/* do nothing */
}
static CFStringRef sock_copy_desc(const void *info)
{
return CFSTR("sock");
}
static int going, reported_recursive_sleep;
void MrEdMacSleep(float secs, void *fds, SLEEP_PROC_PTR mzsleep)
{
if (going) {
if (!reported_recursive_sleep) {
fprintf(stderr, "BUG: recursive sleep! Please submit a bug report that explains how\n");
fprintf(stderr, "you got this message. (It won't appear again until you restart.)\n");
reported_recursive_sleep = 1;
}
return;
}
/* If we're asked to sleep less than 1/60 of a second, then don't
bother with WaitNextEvent(). */
if ((secs > 0) && (secs < 1.0/60)) {
mzsleep(secs, fds);
} else {
EventRecord e;
if (!cb_socket_ready) {
/* 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 EnsureWNEReturn().
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;
CFSocketRef cfs;
CFRunLoopSourceRef source;
CFSocketContext context;
/* True, they're not really sockets... */
ready_sock = fds[0];
write_ready_sock = fds[1];
/* The code below simply says says "please call
socket_callback from WNE when there's data to read on
ready_sock" */
context.version = 0; /* ? */
context.info = NULL;
context.retain = sock_retain;
context.release = sock_release;
context.copyDescription = sock_copy_desc;
rl = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
cfs = CFSocketCreateWithNative(CFAllocatorGetDefault(), ready_sock, kCFSocketReadCallBack, socket_callback, &context);
source = CFSocketCreateRunLoopSource(CFAllocatorGetDefault(), cfs, 0);
CFRunLoopAddSource(rl, source, kCFRunLoopDefaultMode);
fcntl(ready_sock, F_SETFL, O_NONBLOCK);
cb_socket_ready = 1;
}
}
/* Starts a watcher thread, which runs select() on the fds,
and also breaks when SIGINT is received. */
if (!StartFDWatcher(mzsleep, secs, fds)) {
secs = 0;
}
going++;
if (need_post) /* useless check in principle, but an optimization
in the case that the select() succeeds before
we even start */
if (WNE(&e, secs ? secs : kEventDurationForever))
QueueTransferredEvent(&e);
--going;
/* Shut down the watcher thread */
EndFDWatcher();
if (cb_socket_ready) {
/* clear out the pipe: */
char buf[1];
read(ready_sock, buf, 1);
}
}
}
/***************************************************************************/
/* location->window (used for send-message) */
/***************************************************************************/
wxWindow *wxLocationToWindow(int x, int y)
{
Point p;
WindowPtr f;
Rect bounds;
int part;
p.h = x;
p.v = y;
part = FindWindow(p, &f);
GetWindowBounds(f, kWindowContentRgn, &bounds);
if (IsWindowVisible(f)
&& (bounds.left <= x)
&& (bounds.right >= x)
&& (bounds.top <= y)
&& (bounds.bottom >= y)) {
/* Found it */
wxFrame *frame;
void *refcon;
refcon = (void *)GetWRefCon(f);
frame = (wxFrame *)GET_SAFEREF(refcon);
if (frame) {
/* Mac: some frames really represent dialogs. Any modal frame is
a dialog, so extract its only child. */
if (frame->IsModal()) {
wxChildNode *node2;
wxChildList *cl;
cl = frame->GetChildren();
node2 = cl->First();
if (node2)
return (wxWindow *)node2->Data();
} else
return frame;
} else
return NULL;
}
return NULL;
}
WindowPtr MrEdMouseWindow(Point where)
{
WindowPtr win;
WindowClass wc;
int part;
part = FindWindow(where, &win);
if (part == inMenuBar)
return FrontNonFloatingWindow();
GetWindowClass(win, &wc);
if ((wc == kFloatingWindowClass)
|| (wc == kUtilityWindowClass)
|| (wc == kToolbarWindowClass)) {
/* Floating windows always receive events: */
return win;
} else {
return FrontNonFloatingWindow();
}
}
WindowPtr MrEdKeyWindow()
{
wxFrame *f;
f = wxGetFocusFrame();
if (f)
return f->macWindow();
else
return FrontWindow();
}
/***************************************************************************/
/* gc */
/***************************************************************************/
void wxmac_reg_globs(void)
{
wxREGGLOB(first);
wxREGGLOB(last);
wxREGGLOB(cont_mouse_context);
}
/***************************************************************************/
/* AppleEvents */
/***************************************************************************/
static Scheme_Object *record_symbol, *file_symbol;
static long check_four(char *name, int which, int argc, Scheme_Object **argv)
{
Scheme_Object *o = argv[which];
if (!SCHEME_BYTE_STRINGP(o) || (SCHEME_BYTE_STRTAG_VAL(o) != 4))
scheme_wrong_type(name, "MacOS type/creator 4-character byte string", which, argc, argv);
#ifdef __POWERPC__
return *(int *)SCHEME_BYTE_STR_VAL(o);
#else
{
int v;
char tmp[4], *bs;
bs = SCHEME_BYTE_STR_VAL(o);
tmp[3] = bs[0];
tmp[2] = bs[1];
tmp[1] = bs[2];
tmp[0] = bs[3];
memcpy(&v, tmp, 4);
return v;
}
#endif
}
static int has_null(const char *s, long l)
{
if (!l)
return 1;
while (l--) {
if (!s[l])
return 1;
}
return 0;
}
#ifdef OS_X
/* Provided by MzScheme for Classic: */
int scheme_mac_path_to_spec(const char *filename, FSSpec *spec)
{
FSRef fsref;
OSErr err;
// first, convert to an FSRef
err = FSPathMakeRef((const UInt8 *)filename,&fsref,NULL);
if (err != noErr) {
return 0;
}
// then, convert to an FSSpec
err = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, spec, NULL);
if (err != noErr) {
return 0;
}
return 1;
}
char *scheme_mac_spec_to_path(FSSpec *spec)
{
FSRef fileRef;
int longEnough = FALSE;
int strLen = 256;
char *str;
str = (char *)scheme_malloc_atomic(strLen);
// first, convert to an FSRef
if (FSpMakeFSRef(spec,&fileRef) != noErr) {
return NULL;
}
while (! longEnough) {
if (FSRefMakePath(&fileRef,(unsigned char *)str,strLen) == pathTooLongErr) {
strLen *= 2;
str = (char *)scheme_malloc_atomic(strLen);
} else {
longEnough = TRUE;
}
}
return str;
}
#endif // OS_X
static int ae_marshall(AEDescList *ae, AEDescList *list_in, AEKeyword kw, Scheme_Object *v,
char *name, OSErr *err, char **stage)
{
DescType type;
Ptr data;
Size size;
Boolean x_b;
long x_i;
double x_d;
FSSpec x_fss;
Handle alias = NULL;
int retval = 1;
OSErr _err;
switch (SCHEME_TYPE(v)) {
case scheme_true_type:
case scheme_false_type:
x_b = SCHEME_TRUEP(v) ? TRUE : FALSE;
type = typeBoolean;
data = (char *)&x_b;
size = sizeof(Boolean);
break;
case scheme_integer_type:
x_i = SCHEME_INT_VAL(v);
type = typeLongInteger;
data = (char *)&x_i;
size = sizeof(long);
break;
case scheme_byte_string_type:
type = typeChar;
data = SCHEME_BYTE_STR_VAL(v);
size = SCHEME_BYTE_STRTAG_VAL(v);
break;
case scheme_char_string_type:
type = typeChar;
v = scheme_char_string_to_byte_string(v);
data = SCHEME_BYTE_STR_VAL(v);
size = SCHEME_BYTE_STRTAG_VAL(v);
break;
case scheme_float_type:
case scheme_double_type:
x_d = SCHEME_FLOAT_VAL(v);
type = typeFloat;
data = (char *)&x_d;
size = sizeof(double);
break;
case scheme_vector_type: /* vector => record */
if ((SCHEME_VEC_SIZE(v) >= 1)
&& ((SCHEME_VEC_ELS(v)[0] == record_symbol)
|| (SCHEME_VEC_ELS(v)[0] == file_symbol))) {
if (SCHEME_VEC_ELS(v)[0] == file_symbol) {
if ((SCHEME_VEC_SIZE(v) == 2)
&& SCHEME_PATH_STRINGP(SCHEME_VEC_ELS(v)[1])) {
Scheme_Object *bs;
char *s;
long l;
bs = SCHEME_VEC_ELS(v)[1];
if (!SCHEME_PATHP(bs))
bs = scheme_char_string_to_byte_string(bs);
s = SCHEME_BYTE_STR_VAL(bs);
l = SCHEME_BYTE_STRTAG_VAL(bs);
if (!has_null(s, l)) {
if (scheme_mac_path_to_spec(s, &x_fss)) {
_err = NewAliasMinimal(&x_fss, (AliasHandle *)&alias);
*err = _err;
if (_err == -43) {
/* Can't make alias; make FSSpec, instead */
type = typeFSS;
data = (char *)&x_fss;
size = sizeof(FSSpec);
break;
} else if (_err) {
*stage = "converting file to alias: ";
return 0;
}
type = typeAlias;
HLock(alias);
data = (char *)*alias;
size = GetHandleSize(alias);
break;
}
}
}
scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
"%s: cannot interpret vector as a file specification: %V",
name,
v);
}
/* record case falls through to list */
} else {
scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
"%s: cannot convert ill-tagged or untagged vector: %V",
name,
v);
}
case scheme_pair_type: /* /\ falls through */
case scheme_null_type:
{
int l;
int isrec = SCHEME_VECTORP(v);
if (isrec)
v = SCHEME_CDR(scheme_vector_to_list(v));
l = scheme_proper_list_length(v);
if (l >= 0) {
AEDescList *list;
list = (AEDescList *)scheme_malloc_atomic(sizeof(AEDescList));
list->descriptorType = typeNull;
list->dataHandle = NULL;
_err = AECreateList(NULL, 0, isrec, list);
if (_err) {
*err = _err;
*stage = "cannot create list/record: ";
return 0;
}
while (!SCHEME_NULLP(v)) {
Scheme_Object *a = SCHEME_CAR(v);
AEKeyword rkw;
if (isrec) {
Scheme_Object *k;
if (!SCHEME_PAIRP(a)
|| !SCHEME_PAIRP(SCHEME_CDR(a))
|| !SCHEME_NULLP(SCHEME_CDR(SCHEME_CDR(a)))
|| !SCHEME_BYTE_STRINGP(SCHEME_CAR(a))) {
/* Bad record form. */
scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
"%s: cannot interpret vector part as a record field: %s",
name,
scheme_make_provided_string(a, 1, NULL));
}
k = SCHEME_CAR(a);
a = SCHEME_CADR(a);
rkw = check_four(name, 0, 1, &k);
} else
rkw = 0;
if (!ae_marshall(NULL, list, rkw, a, name, err, stage)) {
AEDisposeDesc(list);
return 0;
}
v = SCHEME_CDR(v);
}
if (list_in) {
if (kw)
_err = AEPutKeyDesc(list_in, kw, list);
else
_err = AEPutDesc(list_in, 0, list);
if (_err) {
*err = _err;
*stage = "cannot add list item: ";
AEDisposeDesc(list);
return 0;
}
} else {
if (kw)
_err = AEPutParamDesc(ae, kw, list);
else
_err = AEPutParamDesc(ae, keyDirectObject, list);
if (_err) {
*err = _err;
*stage = "cannot install argument: ";
AEDisposeDesc(list);
return 0;
}
}
AEDisposeDesc(list);
return 1;
}
}
default:
/* Don't know how to marshall */
scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
"%s: cannot convert value for sending: %s",
name,
scheme_make_provided_string(v, 1, NULL));
return 0;
}
if (list_in) {
if (kw)
_err = AEPutKeyPtr(list_in, kw, type, data, size);
else
_err = AEPutPtr(list_in, 0, type, data, size);
if (_err) {
*err = _err;
*stage = "cannot add list item: ";
retval = 0;
}
} else {
if (kw)
_err = AEPutParamPtr(ae, kw, type, data, size);
else
_err = AEPutParamPtr(ae, keyDirectObject, type, data, size);
if (_err) {
*err = _err;
*stage = "cannot install argument: ";
retval = 0;
}
}
if (alias)
DisposeHandle(alias);
return retval;
}
static Scheme_Object *ae_unmarshall(AppleEvent *reply, AEDescList *list_in, int pos,
OSErr *err, char **stage, Scheme_Object **record)
{
DescType rtype;
long sz;
AEKeyword kw;
Scheme_Object *result = NULL;
OSErr _err;
if (list_in) {
if (AEGetNthPtr(list_in, pos, typeWildCard, &kw, &rtype, NULL, 0, &sz))
return scheme_void;
} else {
if (AEGetParamPtr(reply, keyDirectObject, typeWildCard, &rtype, NULL, 0, &sz))
return scheme_void;
}
{
Boolean x_b;
long x_i;
double x_d;
char *x_s = NULL;
FSSpec x_f;
Ptr data;
switch (rtype) {
case typeBoolean:
data = (char *)&x_b;
break;
case typeLongInteger:
case typeShortInteger:
rtype = typeLongInteger;
data = (char *)&x_i;
sz = sizeof(long);
break;
case typeLongFloat:
case typeShortFloat:
case typeExtended:
rtype = typeFloat;
data = (char *)&x_d;
sz = sizeof(double);
break;
case typeChar:
x_s = (char *)scheme_malloc_atomic(sz + 1);
data = x_s;
x_s[0] = 0;
break;
case typeAlias:
case typeFSS:
rtype = typeFSS;
data = (char *)&x_f;
sz = sizeof(FSSpec);
break;
case typeAEList:
case typeAERecord:
{
AEDescList *list;
Scheme_Object *first = scheme_null, *last = NULL, *v, *rec, **recp;
int i;
list = (AEDescList *)scheme_malloc_atomic(sizeof(AEDescList));
if (list_in) {
if (AEGetNthDesc(list_in, pos, rtype, &kw, list))
return NULL;
if (record) {
rec = scheme_make_sized_utf8_string((char *)&kw, sizeof(long));
*record = rec;
}
} else {
if (AEGetParamDesc(reply, keyDirectObject, rtype, list))
return NULL;
}
if (rtype == typeAERecord)
recp = &rec;
else
recp = NULL;
for (i = 1; (v = ae_unmarshall(NULL, list, i, err, stage, recp)); i++) {
if (v == scheme_void)
break;
else if (!v) {
AEDisposeDesc(list);
return NULL;
} else {
Scheme_Object *pr;
pr = scheme_make_pair(v, scheme_null);
if (recp) {
pr = scheme_make_pair(rec, pr);
pr = scheme_make_pair(pr, scheme_null);
}
if (last)
SCHEME_CDR(last) = pr;
else
first = pr;
last = pr;
}
}
if (recp)
first = scheme_list_to_vector(scheme_make_pair(record_symbol, first));
AEDisposeDesc(list);
return first;
}
default:
/* Don't know how to un-marshall */
*err = -1;
*stage = "error translating the reply to a Scheme value: ";
return NULL;
}
if (list_in) {
_err = AEGetNthPtr(list_in, pos, rtype, &kw, &rtype, data, sz, &sz);
if (record) {
Scheme_Object *rec;
rec = scheme_make_sized_utf8_string((char *)&kw, sizeof(long));
*record = rec;
}
if (_err) {
*err = _err;
*stage = "lost a list value: ";
return NULL;
}
} else {
_err = AEGetParamPtr(reply, keyDirectObject, rtype, &rtype, data, sz, &sz);
if (_err) {
*err = _err;
*stage = "lost the return value: ";
return NULL;
}
}
switch (rtype) {
case typeBoolean:
result = (x_b ? scheme_true : scheme_false);
break;
case typeLongInteger:
result = scheme_make_integer(x_i);
break;
case typeFloat:
result = scheme_make_double(x_d);
break;
case typeChar:
result = scheme_make_sized_utf8_string(x_s, sz);
break;
case typeFSS:
result = scheme_make_sized_utf8_string(scheme_mac_spec_to_path(&x_f), -1);
break;
}
}
return result;
}
/* Single-threaded ok: */
static int escaped = 0;
static int handlerInstalled = 0;
class ReplyItem;
class ReplyItem {
public:
long id;
AppleEvent *ae;
ReplyItem *next;
};
static ReplyItem *reply_queue;
static pascal Boolean while_waiting(EventRecord *e, long *sleeptime, RgnHandle *rgn)
{
mz_jmp_buf *save, newbuf;
if (escaped) return TRUE;
QueueTransferredEvent(e);
save = scheme_current_thread->error_buf;
scheme_current_thread->error_buf = &newbuf;
if (scheme_setjmp(newbuf)) {
scheme_current_thread->error_buf = save;
escaped = 1;
return TRUE; /* Immediately return to AESend */
} else {
scheme_thread_block(0);
scheme_current_thread->ran_some = 1;
scheme_current_thread->error_buf = save;
}
return FALSE;
}
static pascal OSErr HandleAnswer(const AppleEvent *evt, AppleEvent *rae, long k)
{
ReplyItem *r;
DescType rtype;
long sz;
AppleEvent *ae;
r = new WXGC_PTRS ReplyItem;
ae = (AppleEvent *)scheme_malloc_atomic(sizeof(AppleEvent));
r->ae = ae;
AEGetAttributePtr(evt, keyReturnIDAttr, typeLongInteger, &rtype, &r->id, sizeof(long), &sz);
AEDuplicateDesc(evt, r->ae);
r->next = reply_queue;
reply_queue = r;
return 0;
}
static void wait_for_reply(AppleEvent *ae, AppleEvent *reply)
{
EventRecord e;
DescType rtype;
long id, sz;
ReplyItem *r, *prev;
if (!handlerInstalled) {
handlerInstalled = TRUE;
AEInstallEventHandler(kCoreEventClass, kAEAnswer, NewAEEventHandlerUPP(HandleAnswer), 0, 0);
wxREGGLOB(reply_queue);
}
AEGetAttributePtr(ae, keyReturnIDAttr, typeLongInteger, &rtype, &id, sizeof(long), &sz);
while (1) {
wxMouseEventHandled();
WNE(&e, 1.0);
if (e.what == kHighLevelEvent)
AEProcessAppleEvent(&e);
else {
if (while_waiting(&e, NULL, NULL))
break;
}
prev = NULL;
for (r = reply_queue; r; r = r->next) {
if (r->id == id) {
/* Got the reply */
memcpy(reply, r->ae, sizeof(AppleEvent));
if (prev)
prev->next = r->next;
else
reply_queue = r->next;
return;
}
prev = r;
}
}
}
int scheme_mac_send_event(char *name, int argc, Scheme_Object **argv,
Scheme_Object **result, int *err, char **stage)
{
OSErr oerr;
AEEventClass classid;
AEEventID eventid;
AppleEvent *ae = NULL, *reply = NULL;
AEAddressDesc *target = NULL;
DescType rtype;
int retval;
long ret, sz, dst;
Scheme_Object *res;
if (!record_symbol) {
wxREGGLOB(record_symbol);
wxREGGLOB(file_symbol);
record_symbol = scheme_intern_symbol("record");
file_symbol = scheme_intern_symbol("file");
}
dst = check_four(name, 0, argc, argv);
classid = check_four(name, 1, argc, argv);
eventid = check_four(name, 2, argc, argv);
target = (AEAddressDesc *)malloc(sizeof(AEAddressDesc));
oerr = AECreateDesc(typeApplSignature, &dst, sizeof(long), target);
if (oerr) {
free(target);
target = NULL;
*err = (int)oerr;
*stage = "application not found: ";
goto fail;
}
ae = (AppleEvent *)malloc(sizeof(AppleEvent));
oerr = AECreateAppleEvent(classid, eventid, target, kAutoGenerateReturnID,
kAnyTransactionID, ae);
if (oerr) {
free(ae);
ae = NULL;
*err = (int)oerr;
*stage = "cannot create event: ";
ae = NULL;
goto fail;
}
if ((argc > 3) && !SCHEME_VOIDP(argv[3])) {
if (!ae_marshall(ae, NULL, 0, argv[3], name, &oerr, stage)) {
*err = (int)oerr;
goto fail;
}
}
if (argc > 4) {
Scheme_Object *l = argv[4];
char *expected = "list of pairs containing a type-string and a value";
while (SCHEME_PAIRP(l)) {
Scheme_Object *a = SCHEME_CAR(l), *k, *v;
AEKeyword kw;
/* Must be a list of 2-item lists: keyword and value */
if (!SCHEME_PAIRP(a)
|| !SCHEME_PAIRP(SCHEME_CDR(a))
|| !SCHEME_NULLP(SCHEME_CDR(SCHEME_CDR(a)))
|| !SCHEME_BYTE_STRINGP(SCHEME_CAR(a)))
break; /* => type error */
k = SCHEME_CAR(a);
v = SCHEME_CADR(a);
kw = check_four(name, 0, 1, &k);
if (!ae_marshall(ae, NULL, kw, v, name, &oerr, stage)) {
*err = (int)oerr;
goto fail;
}
l = SCHEME_CDR(l);
}
if (!SCHEME_NULLP(l))
scheme_wrong_type(name, expected, 4, argc, argv);
}
reply = (AppleEvent *)malloc(sizeof(AppleEvent));
oerr = AESend(ae, reply, kAEQueueReply | kAECanInteract, kAENormalPriority, kNoTimeOut, NULL, NULL);
if (oerr) {
free(reply);
reply = NULL;
*err = (int)oerr;
*stage = "send failed: ";
reply = NULL;
goto fail;
}
wait_for_reply(ae, reply);
if (escaped) {
reply = NULL;
escaped = 0;
goto escape;
}
if (!AEGetParamPtr(reply, keyErrorString, typeChar, &rtype, NULL, 0, &sz) && sz) {
char *st;
*err = -1;
if (sz > 256) sz = 256;
st = (char *)scheme_malloc_atomic(sz + 1);
*stage = st;
(*stage)[sz] = 0;
AEGetParamPtr(reply, keyErrorString, typeChar, &rtype, *stage, sz, &sz);
goto fail;
}
if (!AEGetParamPtr(reply, keyErrorNumber, typeLongInteger, &rtype, &ret, sizeof(long), &sz)
&& ret) {
*err = (int)ret;
*stage = "application replied with error: ";
goto fail;
}
res = ae_unmarshall(reply, NULL, 0, &oerr, stage, NULL);
*result = res;
if (!*result) {
*err = (int)oerr;
goto fail;
}
retval = 1;
goto done;
escape:
retval = -1;
goto done;
fail:
retval = 0;
done:
if (ae) {
AEDisposeDesc(ae);
free(ae);
}
if (reply) {
AEDisposeDesc(reply);
free(reply);
}
if (target) {
AEDisposeDesc(target);
free(target);
}
if (retval < 0) {
scheme_longjmp(scheme_error_buf, 1);
}
return retval;
}
/**********************************************************************/
/* Generic control tracking with callbacks */
/* or frame painting on show */
/**********************************************************************/
static RgnHandle clipRgn;
class wxTC_Closure {
public:
ControlRef ctl;
Point start;
ControlActionUPP proc;
};
static int call_tc(void *_c)
{
wxTC_Closure *c;
c = (wxTC_Closure *)_c;
return TrackControl(c->ctl, c->start, c->proc);
}
ControlPartCode wxHETTrackControl(ControlRef theControl, Point startPoint, ControlActionUPP actionProc)
{
wxTC_Closure *c;
int v;
c = new WXGC_PTRS wxTC_Closure;
c->ctl = theControl;
c->start = startPoint;
c->proc = actionProc;
v = wxHiEventTrampoline(call_tc, (void *)c);
return v;
}
class wxSW_Closure {
public:
WindowPtr w, pw;
};
static int call_sw(void *_c)
{
wxSW_Closure *c;
c = (wxSW_Closure *)_c;
if (c->pw)
ShowSheetWindow(c->w, c->pw);
else
ShowWindow(c->w);
return 0;
}
extern void wxHETShowWindow(WindowPtr w)
{
wxHETShowSheetWindow(w, NULL);
}
extern void wxHETShowSheetWindow(WindowPtr w, WindowPtr pw)
{
wxSW_Closure *c;
c = new WXGC_PTRS wxSW_Closure;
c->w = w;
c->pw = pw;
wxHiEventTrampoline(call_sw, (void *)c);
}
int wxHETYield(wxWindow *win, HiEventTrampProc do_f, void *do_data)
{
CGrafPtr savep;
GDHandle savegd;
ThemeDrawingState s;
int more;
wxMacDC *mdc;
if (!clipRgn)
clipRgn = NewRgn();
GetGWorld(&savep, &savegd);
GetThemeDrawingState(&s);
GetClip(clipRgn);
/* We assume that win was the old MacDC user, and savep is win's
MacDC. But control tracking has changed properties of the
grafport, so indicate the need for a reset: */
mdc = win->MacDC();
mdc->setCurrentUser(NULL);
more = mred_het_run_some(do_f, do_data);
wxResetCanvasBackgrounds();
SetGWorld(savep, savegd);
SetThemeDrawingState(s, TRUE);
SetClip(clipRgn);
/* Again. win may not be the current user, but whoever
is the current user for savep needs a reset. */
mdc->setCurrentUser(NULL);
return more;
}
void MrEdAtomicallyPaint(wxCanvas *win)
{
int block_descriptor;
block_descriptor = scheme_current_thread->block_descriptor;
scheme_current_thread->block_descriptor = 0;
scheme_start_atomic();
win->OnPaint();
scheme_end_atomic_no_swap();
scheme_current_thread->block_descriptor = block_descriptor;
}