From efe9e73e8e05faafcebc8e28ed7c6e048da0fff6 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 27 Feb 2007 23:33:31 +0000 Subject: [PATCH] hopefully fix XCheckPred problem (PR 8547), and add support with -singleInstance svn: r5701 --- src/mred/mred.cxx | 20 ++- src/mred/mred.h | 1 + src/mred/mredx.cxx | 260 +++++++++++++++++++++++++--- src/mred/wxs/wxscheme.cxx | 5 +- src/wxxt/src/Application/AppMain.cc | 22 ++- src/wxxt/src/GDI-Classes/Font.cc | 2 +- src/wxxt/src/Misc/Clipboard.cc | 8 + src/wxxt/src/Windows/Window.cc | 129 ++++++++++++++ 8 files changed, 415 insertions(+), 32 deletions(-) diff --git a/src/mred/mred.cxx b/src/mred/mred.cxx index 1afd6539e0..ab9bd96864 100644 --- a/src/mred/mred.cxx +++ b/src/mred/mred.cxx @@ -118,6 +118,9 @@ static Scheme_Thread *user_main_thread; extern void wxMediaIOCheckLSB(void); extern void wxMouseEventHandled(void); +#ifdef wx_xt +extern int wx_single_instance; +#endif #include "mred.h" @@ -3416,7 +3419,14 @@ void MrEdApp::RealInit(void) scheme_exit = CAST_EXIT MrEdExit; #endif - exit_val = mred_finish_cmd_line_run(); +#ifdef wx_xt + if (wx_single_instance) { + exit_val = wxCheckSingleInstance(global_env); + } +#endif + + if (!exit_val) + exit_val = mred_finish_cmd_line_run(); scheme_kill_thread(scheme_current_thread); } @@ -3809,10 +3819,9 @@ static unsigned long get_deeper_base() #endif /****************************************************************************/ -/* Mac AE support */ +/* AE-like support */ /****************************************************************************/ -#if defined(wx_mac) || defined(wx_msw) static void wxDo(Scheme_Object *proc, int argc, Scheme_Object **argv) { mz_jmp_buf * volatile save, newbuf; @@ -3852,11 +3861,16 @@ void wxDrop_Runtime(char **argv, int argc) for (i = 0; i < argc; i++) { Scheme_Object *p[1]; +#ifdef wx_xt + p[0] = scheme_char_string_to_path(scheme_make_utf8_string(argv[i])); +#else p[0] = scheme_make_path(argv[i]); +#endif wxDo(wxs_app_file_proc, 1, p); } } +#if defined(wx_mac) || defined(wx_msw) void wxDrop_Quit() { #if WINDOW_STDIO diff --git a/src/mred/mred.h b/src/mred/mred.h index aceb983f8c..7d9917da05 100644 --- a/src/mred/mred.h +++ b/src/mred/mred.h @@ -228,4 +228,5 @@ extern void WakeUpMrEd(); #if defined(wx_xt) extern void wxUnhideAllCursors(); +int wxCheckSingleInstance(Scheme_Env *global_env); #endif diff --git a/src/mred/mredx.cxx b/src/mred/mredx.cxx index 77efa40a70..15438530cf 100644 --- a/src/mred/mredx.cxx +++ b/src/mred/mredx.cxx @@ -35,6 +35,9 @@ static int grab_stack_pos = 0, grab_stack_size = 0; extern Widget wx_clipWindow, wx_selWindow; +Window wxAddClipboardWindowProperty(Atom prop); +extern Atom wx_single_instance_tag; + wxWindow *wxLocationToWindow(int x, int y); extern "C" { @@ -152,6 +155,41 @@ static Window GetEventWindow(XEvent *e) static unsigned long lastUngrabTime; static unsigned long lastUnhideTime; +class Check_Ungrab_Record { +public: + Window window; + int x, y, x_root, y_root; + Check_Ungrab_Record *next; +}; + +static int cur_registered = 0; +static Check_Ungrab_Record *first_cur = NULL, *last_cur = NULL; + +static void CheckUngrab(Display *dpy, Check_Ungrab_Record *cur) +{ + Window root; + int x, y; + unsigned w, h, b, d; + + XGetGeometry(dpy, cur->window, + &root, &x, &y, &w, &h, + &b, &d); + if ((cur->x < 0) || (cur->y < 0) + || ((unsigned int)cur->x > w) || ((unsigned int)cur->y > h)) { + /* Looks bad, but is it a click in a MrEd window + that we could care about? */ + + wxWindow *w; + w = wxLocationToWindow(cur->x_root, cur->y_root); + + if (w) { + /* Looks like we need to ungrab */ + XUngrabPointer(dpy, 0); + XUngrabKeyboard(dpy, 0); + } + } +} + static Bool CheckPred(Display *display, XEvent *e, char *args) { Window window; @@ -197,28 +235,23 @@ static Bool CheckPred(Display *display, XEvent *e, char *args) /* lastUngrabTime keeps us from checking the same events over and over again. */ if (e->xbutton.time > lastUngrabTime) { - Window root; - int x, y; - unsigned w, h, b, d; - - XGetGeometry(XtDisplay(widget), e->xbutton.window, - &root, &x, &y, &w, &h, - &b, &d); - if ((e->xbutton.x < 0) || (e->xbutton.y < 0) - || ((unsigned int)e->xbutton.x > w) || ((unsigned int)e->xbutton.y > h)) { - /* Looks bad, but is it a click in a MrEd window - that we could care about? */ + Check_Ungrab_Record *cur; - wxWindow *w; - w = wxLocationToWindow(e->xbutton.x_root, e->xbutton.y_root); - - if (w) { - /* Looks like we need to ungrab */ - XUngrabPointer(XtDisplay(widget), 0); - XUngrabKeyboard(XtDisplay(widget), 0); - } + if (!cur_registered) { + wxREGGLOB(first_cur); + wxREGGLOB(last_cur); } - + cur = new WXGC_PTRS Check_Ungrab_Record; + cur->window = e->xbutton.window; + cur->x = e->xbutton.x; + cur->y = e->xbutton.y; + cur->x_root = e->xbutton.x_root; + cur->y_root = e->xbutton.y_root; + if (last_cur) + last_cur->next = cur; + else + first_cur = cur; + last_cur = cur; lastUngrabTime = e->xbutton.time; } } @@ -339,6 +372,7 @@ int MrEdGetNextEvent(int check_only, int current_only, XEvent *event, MrEdContext **which) { Display *d; + int got; if (which) *which = NULL; @@ -351,7 +385,15 @@ int MrEdGetNextEvent(int check_only, int current_only, else d = XtDisplay(orig_top_level); - if (XCheckIfEvent(d, event, CheckPred, (char *)which)) { + got = XCheckIfEvent(d, event, CheckPred, (char *)which); + + while (first_cur) { + CheckUngrab(d, first_cur); + first_cur = first_cur->next; + } + last_cur = NULL; + + if (got) { just_check = 0; return 1; } else if (short_circuit) { @@ -722,3 +764,179 @@ int wxUTF8StringToChar(char *str, int slen) NULL, 0, '?'); return (int)s[0]; } + +/***********************************************************************/ + +static int has_property(Display *d, Window w, Atom atag) +{ + Atom actual; + int format; + unsigned long count, remaining; + unsigned char *data = 0; + + XGetWindowProperty(d, w, atag, + 0, 0x8000000L, FALSE, + AnyPropertyType, &actual, &format, + &count, &remaining, &data); + + if (data) + XFree(data); + + return (actual != None); +} + +static int wxSendOrSetTag(char *pre_tag, char *tag, char *msg) +{ + Display *d; + Window root, parent, *children; + unsigned int n, i; + Atom atag, apre_tag; + Window target = 0, me; + int try_again = 0; + + /* Elect a leader, relying on the fact that the X server serializes + its interactions. + + Each client sets a pre-tag, and then checks all windows. If any + window has a (non-pre) tag already, then that's the leader. If no + one else has a pre tag, then this client is elected, and it sets + the tag on itself. If someone else has a pre tag, we try again; + if the other window id is lower, this client drops it pre tag, so + that the other will be elected eventually. Note that if two + clients set a pre tag, then one must see the other (because + neither looks until its tag is set). Livelock is a possibility if + clients continuously appear with ever higher window ids, but that + possibility is exceedingly remote. */ + + if (!orig_top_level) + d = XtDisplay(save_top_level); + else + d = XtDisplay(orig_top_level); + + apre_tag = XInternAtom(d, pre_tag, False); + atag = XInternAtom(d, tag, False); + + wx_single_instance_tag = atag; + + me = wxAddClipboardWindowProperty(apre_tag); + + XFlush(d); + + do { + XSync(d, FALSE); + if (XQueryTree(d, DefaultRootWindow(d), + &root, &parent, &children, &n)) { + for (i = n; i--; ) { + if (children[i] != me) { + if (has_property(d, children[i], atag)) { + target = children[i]; + try_again = 0; + break; + } else if (has_property(d, children[i], apre_tag)) { + if ((long)me >= (long)children[i]) + XDeleteProperty(d, me, apre_tag); + try_again = 1; + } + } + } + + if (children) + XFree(children); + } + } while (try_again); + + if (target) { + GC_CAN_IGNORE XEvent xevent; + long mlen, offset = 0; + int sent_last = 0; + + mlen = strlen(msg); + + /* Send the message(s): */ + while (!sent_last) { + memset(&xevent, 0, sizeof (xevent)); + + xevent.xany.type = ClientMessage; + xevent.xany.display = d; + xevent.xclient.window = target; + xevent.xclient.message_type = atag; + xevent.xclient.format = 8; + + { + int i = sizeof(Window); + long w = (long)me; + + while (i--) { + xevent.xclient.data.b[i] = (char)(w & 0xFF); + w = w >> 8; + } + } + + if (offset < mlen) { + long amt; + amt = mlen - offset; + if (amt > (int)(20 - sizeof(Window))) + amt = 20 - sizeof(Window); + memcpy(xevent.xclient.data.b + sizeof(Window), msg + offset, amt); + offset += amt; + sent_last = (amt < (int)(20 - sizeof(Window))); + } else + sent_last = 1; + + XSendEvent(d, target, 0, 0, &xevent); + } + + XFlush(d); + XSync(d, FALSE); + + return 1; + } else { + /* Set the property on the clipboard window */ + wxAddClipboardWindowProperty(atag); + + return 0; + } +} + +# define SINGLE_INSTANCE_HANDLER_CODE \ +"(lambda (f)" \ +" (let ([path (simplify-path" \ +" (path->complete-path" \ +" (or (find-executable-path (find-system-path 'run-file) #f)" \ +" (find-system-path 'run-file))" \ +" (current-directory)))])" \ +" (let ([tag (string->bytes/utf-8" \ +" (format \"~a_~a\" path (version)))])" \ +" (f tag " \ +" (bytes-append #\"pre\" tag)" \ +" (apply" \ +" bytes-append" \ +" (map (lambda (s)" \ +" (let ([s (path->string" \ +" (path->complete-path s (current-directory)))])" \ +" (string->bytes/utf-8" \ +" (format \"~a:~a\"" \ +" (string-length s)" \ +" s))))" \ +" (vector->list" \ +" (current-command-line-arguments))))))))" + +static Scheme_Object *prep_single_instance(int argc, Scheme_Object **argv) +{ + return (wxSendOrSetTag(SCHEME_BYTE_STR_VAL(argv[0]), + SCHEME_BYTE_STR_VAL(argv[1]), + SCHEME_BYTE_STR_VAL(argv[2])) + ? scheme_true + : scheme_false); +} + +int wxCheckSingleInstance(Scheme_Env *global_env) +{ + Scheme_Object *a[1], *v; + a[0] = scheme_make_prim(prep_single_instance); + v = scheme_apply(scheme_eval_string(SINGLE_INSTANCE_HANDLER_CODE, + global_env), + 1, + a); + return SCHEME_TRUEP(v); +} diff --git a/src/mred/wxs/wxscheme.cxx b/src/mred/wxs/wxscheme.cxx index 567b0848a6..a6cfa68f7e 100644 --- a/src/mred/wxs/wxscheme.cxx +++ b/src/mred/wxs/wxscheme.cxx @@ -2359,10 +2359,7 @@ static char *win_find_home() static char *x_display_str; extern void wxsRememberDisplay(char *str) { - if (str) - x_display_str = str; - else - x_display_str = getenv("DISPLAY"); + x_display_str = str; } #endif diff --git a/src/wxxt/src/Application/AppMain.cc b/src/wxxt/src/Application/AppMain.cc index 4e09362b54..72affa26e8 100644 --- a/src/wxxt/src/Application/AppMain.cc +++ b/src/wxxt/src/Application/AppMain.cc @@ -53,6 +53,8 @@ int wx_visual_depth; Colormap wx_default_colormap; unsigned long wx_white_pixel, wx_black_pixel; +int wx_single_instance = 0; + //----------------------------------------------------------------------------- // wxApp implementation //----------------------------------------------------------------------------- @@ -115,6 +117,8 @@ typedef struct { int arg_count; } X_flag_entry; +#define SINGLE_INSTANCE "-singleInstance" + X_flag_entry X_flags[] = { { "-display", 1 }, { "-geometry", 1 }, @@ -134,6 +138,7 @@ X_flag_entry X_flags[] = { { "-title", 1 }, { "-xnllanguage", 1 }, { "-xrm", 1 }, + { SINGLE_INSTANCE, 0}, { NULL, 0 } }; @@ -188,6 +193,9 @@ int wxEntry(int argc, char *argv[]) xargc = filter_x_readable(argv, argc, &x_display_str); ate = xargc - 1; + + if (!x_display_str) + x_display_str = getenv("DISPLAY"); /* Remember -display or DISPLAY, in case someone needs it: */ wxsRememberDisplay(x_display_str); @@ -199,8 +207,6 @@ int wxEntry(int argc, char *argv[]) &xargc, argv); // command line arguments if (!wxAPP_DISPLAY) { - if (!x_display_str) - x_display_str = getenv("DISPLAY"); if (!x_display_str) { printf("DISPLAY environment variable not set and no -display argument\n"); } else { @@ -209,6 +215,15 @@ int wxEntry(int argc, char *argv[]) exit(1); } + + if ((xargc > 1) && !strcmp("-singleInstance", argv[1])) { + wx_single_instance = 1; + --xargc; + if (xargc > 1) { + argv[1] = argv[2]; + } + } + if (xargc != 1) { printf("%s: standard X Window System flag \"%s\" was rejected\n", argv[0], argv[1]); @@ -236,7 +251,8 @@ int wxEntry(int argc, char *argv[]) wxAPP_VISUAL = vi2.visual; wx_visual_depth = 24; wx_default_colormap = XCreateColormap(wxAPP_DISPLAY, - RootWindow(wxAPP_DISPLAY, DefaultScreen(wxAPP_DISPLAY)), + RootWindow(wxAPP_DISPLAY, + DefaultScreen(wxAPP_DISPLAY)), wxAPP_VISUAL, AllocNone); diff --git a/src/wxxt/src/GDI-Classes/Font.cc b/src/wxxt/src/GDI-Classes/Font.cc index 63ff6617ee..6e08b9b446 100644 --- a/src/wxxt/src/GDI-Classes/Font.cc +++ b/src/wxxt/src/GDI-Classes/Font.cc @@ -918,7 +918,7 @@ static XFontStruct *wxLoadQueryFont(const char *name, weight, underlined, 0, sip, angle); } - + return s; } diff --git a/src/wxxt/src/Misc/Clipboard.cc b/src/wxxt/src/Misc/Clipboard.cc index 59f40f07f0..ffb06fa5b0 100644 --- a/src/wxxt/src/Misc/Clipboard.cc +++ b/src/wxxt/src/Misc/Clipboard.cc @@ -108,6 +108,14 @@ void wxInitClipboard(void) xa_clipboard = ATOM("CLIPBOARD"); } +Window wxAddClipboardWindowProperty(Atom prop) +{ + unsigned char data[1] = { 'm' }; + XChangeProperty(XtDisplay(wx_clipWindow), XtWindow(wx_clipWindow), + prop, prop, 8, PropModeReplace, data, 1); + return XtWindow(wx_clipWindow); +} + static void AddClipboardFrame(wxClipboard *cb, int on) { if (!on) diff --git a/src/wxxt/src/Windows/Window.cc b/src/wxxt/src/Windows/Window.cc index b5cadaffd3..8ade8beb93 100644 --- a/src/wxxt/src/Windows/Window.cc +++ b/src/wxxt/src/Windows/Window.cc @@ -74,10 +74,24 @@ static Bool grabbing_panel_regsitered; static int dnd_inited = 0; static DndClass dnd; +Atom wx_single_instance_tag = 0; + #ifndef NO_XMB_LOOKUP_STRING static XIM the_im; #endif +class Accum_Single_Instance_Message { +public: + long src; + char *accum; + int len, size; + Accum_Single_Instance_Message *next; +}; +static int si_registered; +static Accum_Single_Instance_Message *si_msgs; +static void parse_and_drop_runtime(int len, char *s); +extern void wxDrop_Runtime(char **argv, int argc); + //----------------------------------------------------------------------------- // wxWindow constructor //----------------------------------------------------------------------------- @@ -1408,6 +1422,75 @@ void wxWindow::FrameEventHandler(Widget w, if (win->OnClose()) win->Show(FALSE); } + if (wx_single_instance_tag) { + if (xev->xclient.message_type == wx_single_instance_tag) { + /* Accumulate msg data */ + long src = 0; + int i; + Accum_Single_Instance_Message *msg, *prev = NULL; + + if (!si_registered) { + wxREGGLOB(si_msgs); + } + + for (i = sizeof(Window); i--; ) { + src = (src << 8) | ((int)xev->xclient.data.b[i]); + } + + for (msg = si_msgs; msg; msg = msg->next) { + if (msg->src == src) + break; + } + if (!msg) { + char *s; + s = new WXGC_ATOMIC char[128]; + msg = new WXGC_PTRS Accum_Single_Instance_Message; + msg->next = si_msgs; + si_msgs = msg; + msg->src = src; + msg->accum = s; + msg->len = 0; + msg->size = 128; + } + + { + int len = sizeof(Window); + while (len < 20 && xev->xclient.data.b[len]) { + len++; + } + len -= sizeof(Window); + + if (len) { + /* accumulate data */ + if (msg->size < msg->len + 1 + len) { + char *naya; + int new_size = msg->size * 2; + naya = new WXGC_ATOMIC char[new_size]; + memcpy(naya, msg->accum, msg->len); + msg->accum = naya; + msg->size = new_size; + } + memcpy(msg->accum + msg->len, + xev->xclient.data.b + sizeof(Window), + len); + msg->len += len; + if (len < (int)(20 - sizeof(Window))) + len = 0; /* inidicate that we're done */ + } + + if (!len) { + /* done */ + if (prev) + prev->next = msg->next; + else + si_msgs = msg->next; + msg->accum[msg->len] = 0; + + parse_and_drop_runtime(msg->len, msg->accum); + } + } + } + } if (dnd_inited) { if (xev->xclient.message_type == dnd.XdndEnter) { /* Ok... */ @@ -2413,3 +2496,49 @@ wxWindow *wxWindow::FindChildByWidget(Widget w) return NULL; } + +static void parse_and_drop_runtime(int len, char *s) +{ + char **argv, *a; + int cnt = 0, pos = 0; + int sz; + + while (pos < len) { + sz = 0; + while ((pos < len) && (s[pos] != ':')) { + sz = (sz * 10) + (s[pos] - '0'); + pos++; + } + pos++; + if (sz > 0) + pos += sz; + cnt++; + } + + argv = new WXGC_PTRS char*[cnt]; + + pos = cnt = 0; + while (pos < len) { + sz = 0; + while ((pos < len) && (s[pos] != ':')) { + sz = (sz * 10) + (s[pos] - '0'); + pos++; + } + pos++; + + if (sz > len - pos) + sz = len - pos; + if (sz < 0) + sz = 0; + a = new WXGC_ATOMIC char[sz + 1]; + memcpy(a, s + pos, sz); + a[sz] = 0; + argv[cnt] = a; + + if (sz > 0) + pos += sz; + cnt++; + } + + wxDrop_Runtime(argv, cnt); +}