racket/src/gracket/gracketmsw.cxx
2010-05-16 23:46:05 -04:00

1004 lines
23 KiB
C++

/*
* File: mredmsw.cc
* Purpose: GRacket Windows event loop
* Author: Matthew Flatt
* Created: 1996
* Copyright: (c) 2004-2010 PLT Scheme Inc.
* Copyright: (c) 1996, Matthew Flatt
*/
#if defined(MZ_PRECISE_GC)
# include "wx.h"
#endif
#include "wx_main.h"
#include "wx_utils.h"
#include "scheme.h"
#include "wx_dialg.h"
#include "gracket.h"
#pragma optimize("", off)
#define wxLOG_EVENTS 0
#if wxLOG_EVENTS
static FILE *log;
#endif
#ifndef MZ_PRECISE_GC
# define HIDE_FROM_XFORM(x) x
#endif
void mred_log_msg(const char *msg, ...);
#define OS_SEMAPHORE_TYPE HANDLE
#include "../racket/src/schwinfd.h"
#include <winsock.h>
extern long last_msg_time;
extern "C" {
struct Scheme_Thread_Memory *scheme_remember_thread(void *);
void scheme_forget_thread(struct Scheme_Thread_Memory *);
};
static volatile int need_quit;
extern void wxDoPreGM(void);
extern void wxDoPostGM(void);
extern int wxCheckMousePosition();
extern void wxDoLeaveEvent(wxWindow *w, int x, int y, int flags);
extern LRESULT APIENTRY wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
extern struct MrEdContext *MrEdGetContext(wxObject *w);
extern void MrEdQueueInEventspace(void *context, Scheme_Object *thunk);
class LeaveEvent {
public:
wxWindow *wnd;
int x, y, flags;
LeaveEvent *next;
void *saferef;
};
#ifdef MZ_PRECISE_GC
# define WRAP_SAFEREF(x) (void *)GC_malloc_immobile_box(GC_malloc_weak_box(gcOBJ_TO_PTR(x), NULL, 0))
# define FREE_SAFEREF(x) GC_free_immobile_box((void **)x)
typedef struct {
short tag;
short filler_used_for_hashing;
void *val;
} wxWeak_Box;
# define GET_SAFEREF(x) ((*(void **)x) ? gcPTR_TO_OBJ((*(wxWeak_Box **)x)->val) : NULL)
#else
# define WRAP_SAFEREF(x) (scheme_dont_gc_ptr(x), x)
# define FREE_SAFEREF(x) scheme_gc_ptr_ok(x)
# define GET_SAFEREF(x) x
#endif
static void CALLBACK HETRunSome(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
static Scheme_Object *call_wnd_proc(void *data, int argc, Scheme_Object **argv);
static int WM_MRED_LEAVE;
#ifndef WM_MOUSEWHEEL
# define WM_MOUSEWHEEL 0x020A
#endif
void MrEdInitFirstContext(MrEdContext *c)
{
}
void MrEdInitNewContext(MrEdContext *c)
{
}
void MrEdDestroyContext(MrEdFinalizedContext *)
{
}
void MrEdSyncCurrentDir(void)
{
Scheme_Object *v;
v = scheme_get_param(scheme_current_config(), MZCONFIG_CURRENT_DIRECTORY);
scheme_os_setcwd(SCHEME_PATH_VAL(v), 0);
}
int MrEdGetDoubleTime(void)
{
return GetDoubleClickTime();
}
extern wxWindow *wxHWNDtoWindow(HWND);
static MrEdContext *GetContext(HWND hwnd)
{
wxWindow *w;
HWND next = hwnd, wnd;
do {
do {
wnd = next;
next = GetParent(next);
} while (next);
next = GetWindow(wnd, GW_OWNER);
} while (next);
w = wxHWNDtoWindow(wnd);
if (!w)
return NULL;
if (wxSubType(w->__type, wxTYPE_FRAME))
return (MrEdContext *)((wxFrame *)w)->context;
else if (wxSubType(w->__type, wxTYPE_DIALOG_BOX))
return (MrEdContext *)((wxDialogBox *)w)->context;
else
return NULL;
}
/**********************************************************************/
typedef struct {
MrEdContext *c, *c_return;
MSG *msg;
int remove;
HWND wnd;
} CheckInfo;
static BOOL CALLBACK CheckWindow(HWND wnd, LPARAM param)
{
CheckInfo *info = (CheckInfo *)param;
MrEdContext *c;
c = GetContext(wnd);
if ((!info->c && (!c || c->ready)) || (info->c == c)) {
if (c && c->queued_leaves) {
if (info->remove) {
if (!WM_MRED_LEAVE)
WM_MRED_LEAVE = RegisterWindowMessage("MrEd_Leave_" MRED_GUID);
info->wnd = wnd;
info->c_return = c;
info->msg->message = WM_MRED_LEAVE;
{
if (!c->queued_leaves->saferef) {
void *sr;
sr = WRAP_SAFEREF(c->queued_leaves);
c->queued_leaves->saferef = sr;
}
}
info->msg->lParam = (long)c->queued_leaves->saferef;
c->queued_leaves = c->queued_leaves->next;
}
return FALSE;
}
if (PeekMessage(info->msg, wnd, NULL, NULL,
info->remove ? PM_REMOVE : PM_NOREMOVE)) {
info->wnd = wnd;
info->c_return = c;
scheme_notify_sleep_progress();
return FALSE;
}
}
return TRUE;
}
int FindReady(MrEdContext *c, MSG *msg, int remove, MrEdContext **c_return)
{
MSG backup;
CheckInfo info;
int result = 0;
if (!msg)
msg = &backup;
info.c = c;
info.msg = msg;
info.remove = remove;
if (!EnumThreadWindows(GetCurrentThreadId(), (WNDENUMPROC)CheckWindow, (LPARAM)&info)) {
if (c_return)
*c_return = info.c_return;
result = 1;
}
/* XP uses messages above 0x4000 to hilite items in the task bar,
etc. In any case, these messages won't be handled by us, so they
can't trigger Scheme code. (If 0x4000 handling ends up sending a
window a message that we *do* handle, we'll end up ignoring it,
as we do for all unexpected messages that can call into
Scheme.) */
{
MSG pmsg;
while (PeekMessage(&pmsg, NULL, 0x4000, 0xFFFF, PM_REMOVE)) {
wxTranslateMessage(&pmsg);
DispatchMessage(&pmsg);
scheme_notify_sleep_progress();
}
}
return result;
}
int MrEdGetNextEvent(int check_only, int current_only,
MSG *event, MrEdContext **which)
{
MrEdContext *c;
if (which)
*which = NULL;
if (need_quit) {
/* This function can be called in any thread; it queues as necessary: */
need_quit = 0;
wxDrop_Quit();
}
if (current_only)
c = MrEdGetContext();
else
c = NULL;
wxCheckMousePosition();
return FindReady(c, event, !check_only, which);
}
static HWND can_trampoline_win;
static HWND need_trampoline_win;
static UINT need_trampoline_message;
static WPARAM need_trampoline_wparam;
static LPARAM need_trampoline_lparam;
static WNDPROC need_trampoline_proc;
int wx_trampolining;
static int HETDispatchMessage(void *_msg)
{
MSG *msg = (MSG *)_msg;
DispatchMessage(msg);
return 0;
}
void MrEdDispatchEvent(MSG *msg)
{
switch (msg->message) {
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
case WM_NCLBUTTONDOWN:
case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN:
case WM_NCLBUTTONDBLCLK:
case WM_NCRBUTTONDBLCLK:
case WM_NCMBUTTONDBLCLK:
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONUP:
case WM_NCRBUTTONUP:
case WM_NCMBUTTONUP:
wxUnhideCursor();
break;
default:
break;
}
if (WM_MRED_LEAVE && (msg->message == WM_MRED_LEAVE)) {
/* Queued leave event */
void *sr = (void *)msg->lParam;
LeaveEvent *e;
e = (LeaveEvent *)GET_SAFEREF(sr);
FREE_SAFEREF(sr);
if (e)
wxDoLeaveEvent(e->wnd, e->x, e->y, e->flags);
} else if (!wxTheApp->ProcessMessage(msg)) {
#if wxLOG_EVENTS
if (!log)
log = fopen("evtlog", "w");
fprintf(log, "{SEND %lx (%lx) %lx\n",
msg->hwnd, GetContext(msg->hwnd),
msg->message);
fflush(log);
#endif
wxTranslateMessage(msg);
can_trampoline_win = msg->hwnd;
last_msg_time = msg->time;
DispatchMessage(msg);
#if wxLOG_EVENTS
if (!log)
log = fopen("evtlog", "w");
fprintf(log, " SENT %lx (%lx) %lx %lx %lx}\n",
msg->hwnd, GetContext(msg->hwnd), msg->message,
need_trampoline_win, need_trampoline_message);
fflush(log);
#endif
can_trampoline_win = 0;
/* See wxEventTrampoline, below: */
{
int iterations;
for (iterations = 0; iterations < 10; iterations++) {
if (msg->hwnd && (need_trampoline_win == msg->hwnd)) {
HWND win = need_trampoline_win;
need_trampoline_win = 0;
wx_trampolining = 1;
if (iterations < 9)
can_trampoline_win = win;
else
can_trampoline_win = NULL;
need_trampoline_proc(win, need_trampoline_message,
need_trampoline_wparam, need_trampoline_lparam);
} else
break;
}
}
}
}
void wxCopyData(LPARAM lParam)
{
/* Is this a message from another GRacket? */
int len;
COPYDATASTRUCT *cd;
len = strlen(MRED_GUID);
cd = (COPYDATASTRUCT *)lParam;
if ((cd->cbData > len + 4 + sizeof(DWORD))
&& !strncmp((char *)cd->lpData, MRED_GUID, len)) {
if (!strncmp((char *)cd->lpData + len, "OPEN", 4)) {
/* This is an "OPEN" event, with a command line.
The command line's argv (sans argv[0]) is
expressed as a DWORD for the number of args,
followed by each arg. Each arg is a DWORD
for the number of chars and then the chars. */
DWORD w;
int cnt, i, pos;
char **argv, *s;
memcpy(&w, (char *)cd->lpData + len + 4, sizeof(DWORD));
cnt = w;
pos = len + 4 + sizeof(DWORD);
argv = new char*[cnt];
for (i = 0; i < cnt; i++) {
if (pos + sizeof(DWORD) <= cd->cbData) {
memcpy(&w, (char *)cd->lpData + pos, sizeof(DWORD));
pos += sizeof(DWORD);
if (w >= 0 && (pos + w <= cd->cbData)) {
s = new WXGC_ATOMIC char[w + 1];
memcpy(s, (char *)cd->lpData + pos, w);
s[w] = NULL;
argv[i] = s;
pos += w;
} else {
cnt = i;
break;
}
} else {
cnt = i;
break;
}
}
wxDrop_Runtime(argv, cnt);
}
}
}
int wxEventTrampoline(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
LRESULT *res, WNDPROC proc)
/* The Windows event dispatcher doesn't like GRacket's thread
implementation. In particular, if a message causes a thread
switch or kill, because it triggers Scheme code, then event
dispatches don't return in the way that Windows expects.
We therefore set up a special trampoline for events that trigger
Scheme code. For example, WM_LBUTTONDOWN. Other events, such as
WM_PAINT or WM_SETFOCUS messages, are handled through a
"trampoline" in the form of queued callbacks, since those events
may be received other than through an event dispatch (i.e., some
other Windows toolbox call triggers and event that it sends
directly).
For trampolined events, we return from the Windows-sponsored
message send, and then re-send the message (where the re-send
might trigger Scheme code). These events *cannot* be handled
without a trampoline. For example, if somehow the WM_LBUTTONDOWN
message is sent directly to a window, we can't handle it. The
wx_start_win_event() function returns 0 to say "give up".
For certain kinds of events, the callback queueing is most easily
implemented in Scheme within mred.ss. For those cases, we put
Racket into atomic mode while handling the event. The "mred.ss"
implementation promises to run quickly (and not call user code).
Scrolling is a special case. To implement interactive scrolling,
we jump into a special mode started by wxHiEventTrampoline().
This mode calls into Windows to implement scrolling, but handles
WM_HSCROLL and WM_VSCROLL messages specially. See mred.cxx for details
on wxHiEventTrampoline. */
{
int tramp;
#if wxLOG_EVENTS
if (!log)
log = fopen("evtlog", "w");
fprintf(log,
"[TCHECK %lx %lx (%lx) %lx"
# ifdef MZ_PRECISE_GC
" %lx"
# endif
"]\n",
scheme_current_thread,
hWnd, can_trampoline_win, message
# ifdef MZ_PRECISE_GC
, ((void **)__gc_var_stack__[0])[0]
# endif
);
fflush(log);
#endif
switch (message) {
case WM_HSCROLL:
case WM_VSCROLL:
/* Special cases */
tramp = 0;
break;
case WM_QUERYENDSESSION:
/* Always allow end-session here; see wx_pdf for the effective guardian */
tramp = 1;
*res = 1;
break;
case WM_ENDSESSION:
case WM_CLOSE:
tramp = 1;
*res = 1;
break;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
case WM_SYSKEYUP:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_KEYDOWN:
case WM_SYSCHAR:
case WM_CHAR:
case WM_INITMENU:
case WM_DROPFILES:
tramp = 1;
*res = 1;
break;
/* These three are for pre-emptive WM_INITMENU
and for on-pre-event over scrollbars plus interactive scrolling */
case WM_NCLBUTTONDOWN:
case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN:
case WM_NCLBUTTONDBLCLK:
case WM_NCRBUTTONDBLCLK:
case WM_NCMBUTTONDBLCLK:
if ((wParam == HTMENU) || (wParam == HTVSCROLL) || (wParam == HTHSCROLL)) {
tramp = 1;
*res = 1;
} else
tramp = 0;
break;
/* These are for on-pre-event over scrollbars plus interactive scrolling */
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONUP:
case WM_NCRBUTTONUP:
case WM_NCMBUTTONUP:
if ((wParam == HTVSCROLL) || (wParam == HTHSCROLL)) {
tramp = 1;
*res = 1;
} else
tramp = 1;
break;
default:
tramp = 0;
break;
}
if (can_trampoline_win != hWnd) {
if (tramp)
return 1;
return 0;
}
if (tramp) {
can_trampoline_win = 0;
need_trampoline_win = hWnd;
need_trampoline_proc = proc;
need_trampoline_message = message;
need_trampoline_wparam = wParam;
need_trampoline_lparam = lParam;
return 1;
} else
return 0;
}
int wx_start_win_event(const char *who, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, int tramp, LONG *_retval)
{
/* See wxEventTrampoline notes above. */
#if wxLOG_EVENTS
if (!log)
log = fopen("evtlog", "w");
fprintf(log, "(%lx %lx %lx[%d] %s %d"
# ifdef MZ_PRECISE_GC
" <%lx %lx %lx>"
# endif
"\n",
scheme_current_thread, hWnd, message, wParam, who, tramp
# ifdef MZ_PRECISE_GC
, GC_variable_stack
, ((void **)__gc_var_stack__[0])[0]
, ((void **)((void **)__gc_var_stack__[0])[0])[0]
# endif
);
fflush(log);
#endif
if (!tramp && scheme_current_thread) {
Scheme_Object *v;
HiEventTramp *het;
v = scheme_extract_one_cc_mark(NULL, mred_het_key);
if (!v)
het = NULL;
else
het = (HiEventTramp *)SCHEME_CAR(v);
if (het) {
/* we're in restricted mode; general calls into Scheme are bad */
switch (message) {
/* These shouldn't happen; reject them if they do! */
case WM_ACTIVATE:
case WM_NCACTIVATE:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_SIZE:
case WM_MOVE:
case WM_COMMAND:
case WM_MDIACTIVATE:
case WM_CLOSE:
#if wxLOG_EVENTS
fprintf(log, " RESTRICTED)\n");
fflush(log);
#endif
return 0;
case WM_VSCROLL:
case WM_HSCROLL:
/* need to re-queue the scroll event (in the MrEd middle queue) */
#if wxLOG_EVENTS
fprintf(log, "_scroll_ %lx\n", GetCurrentThreadId());
#endif
{
MSG *msg;
Scheme_Object *thunk;
msg = (MSG *)scheme_malloc_atomic(sizeof(MSG));
#if wxLOG_EVENTS
fprintf(log, "_scroll1_\n");
#endif
msg->hwnd = hWnd;
msg->message = message;
msg->wParam = wParam;
msg->lParam = lParam;
thunk = scheme_make_closed_prim(call_wnd_proc, (Scheme_Object *)msg);
#if wxLOG_EVENTS
fprintf(log, "_scroll2_\n");
#endif
MrEdQueueInEventspace(MrEdGetContext(NULL), thunk);
}
#if wxLOG_EVENTS
fprintf(log, "_scrolly_ %d\n", het->yielding);
#endif
if (!het->yielding) {
if (het->timer_on) {
het->timer_on = 0;
KillTimer(NULL, het->timer_id);
}
#if wxLOG_EVENTS
fprintf(log, "{HET\n");
#endif
mred_het_run_some(NULL, NULL);
#if wxLOG_EVENTS
fprintf(log, "HET}\n");
#endif
if (het->in_progress && !het->timer_on) {
/* Make a timer event so that we get more time... */
het->timer_on = 1;
het->timer_id = SetTimer(0, NULL, 100, HETRunSome);
}
#if wxLOG_EVENTS
if (het->in_progress)
fprintf(log, " HET_START)\n");
else
fprintf(log, " HET_DONE)\n");
fflush(log);
#endif
} else {
#if wxLOG_EVENTS
fprintf(log, " NESTED)\n");
fflush(log);
#endif
}
return 0;
default:
/* anything else is ok, because it doesn't call Scheme */
break;
}
}
}
if (!tramp) {
switch (message) {
case WM_QUERYENDSESSION:
*_retval = 1;
return 0;
case WM_NCRBUTTONDOWN:
case WM_NCRBUTTONUP:
case WM_NCRBUTTONDBLCLK:
case WM_NCMBUTTONDOWN:
case WM_NCMBUTTONUP:
case WM_NCMBUTTONDBLCLK:
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
case WM_NCLBUTTONDBLCLK:
case WM_NCMOUSEMOVE:
if ((wParam != HTVSCROLL) && (wParam != HTHSCROLL))
break;
case WM_CLOSE: /* ^^^^ fallthrough &&&& */
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
case WM_SYSKEYUP:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_KEYDOWN:
case WM_SYSCHAR:
case WM_CHAR:
case WM_INITMENU:
case WM_DROPFILES:
#if wxLOG_EVENTS
fprintf(log, " CAN'T HANDLE!)\n");
fflush(log);
#endif
return 0;
break;
default:
/* non-tramp ok */
break;
}
}
if (!tramp)
scheme_start_atomic();
#if wxLOG_EVENTS
fprintf(log, " ...\n");
#endif
return 1;
}
void wx_end_win_event(const char *who, HWND hWnd, UINT message, int tramp)
{
/* See wxEventTrampoline notes above. */
#if wxLOG_EVENTS
fprintf(log, " %lx %lx %lx %s %d)"
# ifdef MZ_PRECISE_GC
" <%lx %lx>"
# endif
"\n",
scheme_current_thread, hWnd, message, who, tramp
# ifdef MZ_PRECISE_GC
, GC_variable_stack
, ((void **)__gc_var_stack__[0])[0]
# endif
);
fflush(log);
#endif
if (!tramp)
scheme_end_atomic_no_swap();
if (!tramp && ((message == WM_VSCROLL) || (message == WM_HSCROLL)) && scheme_current_thread) {
HiEventTramp *het;
Scheme_Object *v;
v = scheme_extract_one_cc_mark(NULL, mred_het_key);
if (!v)
het = NULL;
else
het = (HiEventTramp *)SCHEME_CAR(v);
if (het) {
mred_het_run_some(NULL, NULL);
if (het->in_progress && !het->timer_on) {
/* Make a timer event so that we get more time... */
het->timer_on = 1;
het->timer_id = SetTimer(0, NULL, 100, HETRunSome);
}
}
}
}
static Scheme_Object *call_wnd_proc(void *data, int argc, Scheme_Object **argv)
{
MSG *msg = (MSG *)data;
#if wxLOG_EVENTS
fprintf(log, "{CWP\n");
#endif
wx_trampolining = 1;
wxWndProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
#if wxLOG_EVENTS
fprintf(log, " CWP}\n");
#endif
return scheme_void;
}
static void CALLBACK HETRunSome(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
HiEventTramp *het;
Scheme_Object *v;
v = scheme_extract_one_cc_mark(NULL, mred_het_key);
if (!v)
het = NULL;
else
het = (HiEventTramp *)SCHEME_CAR(v);
if (het) {
#if wxLOG_EVENTS
fprintf(log, "(HET_TIMER_CONT\n");
#endif
if (het->timer_on) {
het->timer_on = 0;
KillTimer(NULL, het->timer_id);
}
mred_het_run_some(NULL, NULL);
if (het->in_progress) {
het->timer_on = 1;
het->timer_id = SetTimer(0, NULL, 100, HETRunSome);
}
#if wxLOG_EVENTS
if (het->in_progress)
fprintf(log, " HET_TIMER_SUSPEND)\n");
else
fprintf(log, " HET_TIMER_DONE)\n");
#endif
}
}
/***************************************************************************/
void wxPostQueryEndSession()
/* Called from non-main Windows thread */
{
need_quit = 1;
}
/***************************************************************************/
int MrEdCheckForBreak(void)
{
HWND w;
w = GetActiveWindow();
if (MrEdGetContext() != GetContext(w))
return 0;
{
SHORT hit = (SHORT)0x8000;
SHORT hitnow = (SHORT)0x0001;
SHORT c, shift, control;
c = GetAsyncKeyState('C');
#if BREAKING_REQUIRES_SHIFT
shift = GetAsyncKeyState(VK_SHIFT);
#else
shift = hit;
#endif
control = GetAsyncKeyState(VK_CONTROL);
return ((c & hit) && (c & hitnow) && (control & hit) && (shift & hit));
}
}
void MrEdMSWSleep(float secs, void *fds, SLEEP_PROC_PTR mzsleep)
{
if (fds && ((win_extended_fd_set *)fds)->no_sleep)
return;
if (wxCheckMousePosition())
return;
scheme_add_fd_eventmask(fds, QS_ALLINPUT);
mzsleep(secs, fds);
}
void wxQueueLeaveEvent(void *ctx, wxWindow *wnd, int x, int y, int flags)
{
MrEdContext *c = (MrEdContext *)ctx;
LeaveEvent *e, *prev, *n;
e = new LeaveEvent();
e->wnd = wnd;
e->x = x;
e->y = y;
e->flags = flags;
e->next = NULL;
prev = NULL;
for (n = c->queued_leaves; n; n = n->next) {
prev = n;
}
if (prev)
prev->next = e;
else
c->queued_leaves = e;
}
/**********************************************************************/
/* For Windows 95/98/Me, it's important to release all GDI object
handles on exit. The gdi_objects table maps integers to pairs of
HANDLES. The integer is the handle | 0x1, which ensures that the
key looks like a Scheme fixnum. The pair of handles has something
in the first slot for !(handle & 0x1), and something in the second
slot for (handle & 0x1). */
static Scheme_Hash_Table *gdi_objects;
static void (*orig_exit)(int);
void mred_clean_up_gdi_objects(void)
{
int i;
if ((long)gdi_objects == 0x1)
return;
for (i = 0; i < gdi_objects->size; i++) {
if (gdi_objects->vals[i]) {
Scheme_Object *key;
HANDLE *val;
key = gdi_objects->keys[i];
val = (HANDLE *)gdi_objects->vals[i];
scheme_hash_set(gdi_objects, key, NULL);
if (val[0])
DeleteObject(val[0]);
if (val[1])
DeleteObject(val[1]);
}
}
}
static void clean_up_and_exit(int v)
{
mred_clean_up_gdi_objects();
wxGDIShutdown();
if (orig_exit)
orig_exit(v);
exit(v);
}
void RegisterGDIObject(HANDLE x)
{
if (!gdi_objects) {
/* Only need this table if we're in 95/98/Me */
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&info);
if (info.dwPlatformId != VER_PLATFORM_WIN32_NT) {
/* Need it... */
wxREGGLOB(gdi_objects);
gdi_objects = scheme_make_hash_table(SCHEME_hash_ptr);
orig_exit = scheme_exit;
scheme_exit = clean_up_and_exit;
} else
gdi_objects = (Scheme_Hash_Table *)0x1;
}
if ((long)gdi_objects == 0x1)
return;
if (x) {
Scheme_Object *key;
HANDLE *v;
key = (Scheme_Object *)(((long)x) | 0x1);
v = (HANDLE *)scheme_hash_get(gdi_objects, key);
if (!v) {
v = (HANDLE *)scheme_malloc_atomic(sizeof(HANDLE)*2);
v[0] = v[1] = NULL;
}
if (((long)x) & 1)
v[1] = x;
else
v[0] = x;
scheme_hash_set(gdi_objects, key, (Scheme_Object *)v);
}
}
void DeleteRegisteredGDIObject(HANDLE x)
{
Scheme_Object *key;
HANDLE *v;
if ((long)gdi_objects != 0x1) {
key = (Scheme_Object *)(((long)x) | 0x1);
v = (HANDLE *)scheme_hash_get(gdi_objects, key);
if (v) {
if (((long)x) & 1)
v[1] = NULL;
else
v[0] = NULL;
if (!v[0] && !v[1]) {
/* Remove from hash table: */
scheme_hash_set(gdi_objects, key, NULL);
}
}
}
DeleteObject(x);
}
/**************************************************/
void mred_log_msg(const char *msg, ...)
{
long len;
GC_CAN_IGNORE va_list args;
FILE *f;
f = fopen("mredlog", "a");
fprintf(f, "0x%lx ", scheme_current_thread);
HIDE_FROM_XFORM(va_start(args, msg));
len = vfprintf(f, msg, args);
HIDE_FROM_XFORM(va_end(args));
fclose(f);
}