hopefully fix XCheckPred problem (PR 8547), and add support with -singleInstance

svn: r5701
This commit is contained in:
Matthew Flatt 2007-02-27 23:33:31 +00:00
parent 75a0a02355
commit efe9e73e8e
8 changed files with 415 additions and 32 deletions

View File

@ -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,6 +3419,13 @@ void MrEdApp::RealInit(void)
scheme_exit = CAST_EXIT MrEdExit;
#endif
#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

View File

@ -228,4 +228,5 @@ extern void WakeUpMrEd();
#if defined(wx_xt)
extern void wxUnhideAllCursors();
int wxCheckSingleInstance(Scheme_Env *global_env);
#endif

View File

@ -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;
Check_Ungrab_Record *cur;
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? */
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);
}

View File

@ -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");
}
#endif

View File

@ -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 }
};
@ -189,6 +194,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);

View File

@ -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)

View File

@ -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);
}