1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set expandtab shiftwidth=2 tabstop=2: */
3
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 /*
9 * The GtkXtBin widget allows for Xt toolkit code to be used
10 * inside a GTK application.
11 */
12
13 #include "xembed.h"
14 #include "gtk2xtbin.h"
15 #include <gtk/gtk.h>
16 #include <gdk/gdkx.h>
17 #include <glib.h>
18 #include <assert.h>
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 /* Xlib/Xt stuff */
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/Shell.h>
29 #include <X11/Intrinsic.h>
30 #include <X11/StringDefs.h>
31
32 /* uncomment this if you want debugging information about widget
33 creation and destruction */
34 #undef DEBUG_XTBIN
35
36 #define XTBIN_MAX_EVENTS 30
37
38 static void gtk_xtbin_class_init(GtkXtBinClass *klass);
39 static void gtk_xtbin_init(GtkXtBin *xtbin);
40 static void gtk_xtbin_realize(GtkWidget *widget);
41 static void gtk_xtbin_unrealize(GtkWidget *widget);
42 static void gtk_xtbin_destroy(GtkObject *object);
43
44 /* Xt aware XEmbed */
45 static void xt_client_handle_xembed_message(Widget w, XtPointer client_data,
46 XEvent *event);
47 static void xt_add_focus_listener(Widget w, XtPointer user_data);
48 static void xt_add_focus_listener_tree(Widget treeroot, XtPointer user_data);
49 static void xt_remove_focus_listener(Widget w, XtPointer user_data);
50 static void xt_client_event_handler(Widget w, XtPointer client_data,
51 XEvent *event);
52 static void xt_client_focus_listener(Widget w, XtPointer user_data,
53 XEvent *event);
54 static void xt_client_set_info(Widget xtplug, unsigned long flags);
55 static void send_xembed_message(XtClient *xtclient, long message, long detail,
56 long data1, long data2, long time);
57 static int error_handler(Display *display, XErrorEvent *error);
58 /* For error trap of XEmbed */
59 static void trap_errors(void);
60 static int untrap_error(void);
61 static int (*old_error_handler)(Display *, XErrorEvent *);
62 static int trapped_error_code = 0;
63
64 static GtkWidgetClass *parent_class = NULL;
65
66 static Display *xtdisplay = NULL;
67 static String *fallback = NULL;
68 static gboolean xt_is_initialized = FALSE;
69 static gint num_widgets = 0;
70
71 static GPollFD xt_event_poll_fd;
72 static gint xt_polling_timer_id = 0;
73 static guint tag = 0;
74
xt_event_prepare(GSource * source_data,gint * timeout)75 static gboolean xt_event_prepare(GSource *source_data, gint *timeout) {
76 int mask;
77
78 mask = XPending(xtdisplay);
79
80 return (gboolean)mask;
81 }
82
xt_event_check(GSource * source_data)83 static gboolean xt_event_check(GSource *source_data) {
84 if (xt_event_poll_fd.revents & G_IO_IN) {
85 int mask;
86 mask = XPending(xtdisplay);
87 return (gboolean)mask;
88 }
89
90 return FALSE;
91 }
92
xt_event_dispatch(GSource * source_data,GSourceFunc call_back,gpointer user_data)93 static gboolean xt_event_dispatch(GSource *source_data, GSourceFunc call_back,
94 gpointer user_data) {
95 XtAppContext ac;
96 int i = 0;
97
98 ac = XtDisplayToApplicationContext(xtdisplay);
99
100 /* Process only real X traffic here. We only look for data on the
101 * pipe, limit it to XTBIN_MAX_EVENTS and only call
102 * XtAppProcessEvent so that it will look for X events. There's no
103 * timer processing here since we already have a timer callback that
104 * does it. */
105 for (i = 0; i < XTBIN_MAX_EVENTS && XPending(xtdisplay); i++) {
106 XtAppProcessEvent(ac, XtIMXEvent);
107 }
108
109 return TRUE;
110 }
111
112 static GSourceFuncs xt_event_funcs = {
113 xt_event_prepare, xt_event_check, xt_event_dispatch, NULL,
114 (GSourceFunc)NULL, (GSourceDummyMarshal)NULL};
115
xt_event_polling_timer_callback(gpointer user_data)116 static gboolean xt_event_polling_timer_callback(gpointer user_data) {
117 Display *display;
118 XtAppContext ac;
119 int eventsToProcess = 20;
120
121 display = (Display *)user_data;
122 ac = XtDisplayToApplicationContext(display);
123
124 /* We need to process many Xt events here. If we just process
125 one event we might starve one or more Xt consumers. On the other hand
126 this could hang the whole app if Xt events come pouring in. So process
127 up to 20 Xt events right now and save the rest for later. This is a hack,
128 but it oughta work. We *really* should have out of process plugins.
129 */
130 while (eventsToProcess-- && XtAppPending(ac)) XtAppProcessEvent(ac, XtIMAll);
131 return TRUE;
132 }
133
gtk_xtbin_get_type(void)134 GType gtk_xtbin_get_type(void) {
135 static GType xtbin_type = 0;
136
137 if (!xtbin_type) {
138 static const GTypeInfo xtbin_info = {
139 sizeof(GtkXtBinClass), /* class_size */
140 NULL, /* base_init */
141 NULL, /* base_finalize */
142 (GClassInitFunc)gtk_xtbin_class_init, /* class_init */
143 NULL, /* class_finalize */
144 NULL, /* class_data */
145 sizeof(GtkXtBin), /* instance_size */
146 0, /* n_preallocs */
147 (GInstanceInitFunc)gtk_xtbin_init, /* instance_init */
148 NULL /* value_table */
149 };
150 xtbin_type =
151 g_type_register_static(GTK_TYPE_SOCKET, "GtkXtBin", &xtbin_info, 0);
152 }
153 return xtbin_type;
154 }
155
gtk_xtbin_class_init(GtkXtBinClass * klass)156 static void gtk_xtbin_class_init(GtkXtBinClass *klass) {
157 GtkWidgetClass *widget_class;
158 GtkObjectClass *object_class;
159
160 parent_class = g_type_class_peek_parent(klass);
161
162 widget_class = GTK_WIDGET_CLASS(klass);
163 widget_class->realize = gtk_xtbin_realize;
164 widget_class->unrealize = gtk_xtbin_unrealize;
165
166 object_class = GTK_OBJECT_CLASS(klass);
167 object_class->destroy = gtk_xtbin_destroy;
168 }
169
gtk_xtbin_init(GtkXtBin * xtbin)170 static void gtk_xtbin_init(GtkXtBin *xtbin) {
171 xtbin->xtdisplay = NULL;
172 xtbin->parent_window = NULL;
173 xtbin->xtwindow = 0;
174 }
175
gtk_xtbin_realize(GtkWidget * widget)176 static void gtk_xtbin_realize(GtkWidget *widget) {
177 GtkXtBin *xtbin;
178 GtkAllocation allocation = {0, 0, 200, 200};
179 gint x, y, w, h, d; /* geometry of window */
180
181 #ifdef DEBUG_XTBIN
182 printf("gtk_xtbin_realize()\n");
183 #endif
184
185 g_return_if_fail(GTK_IS_XTBIN(widget));
186
187 xtbin = GTK_XTBIN(widget);
188
189 /* caculate the allocation before realize */
190 gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d);
191 allocation.width = w;
192 allocation.height = h;
193 gtk_widget_size_allocate(widget, &allocation);
194
195 #ifdef DEBUG_XTBIN
196 printf("initial allocation %d %d %d %d\n", x, y, w, h);
197 #endif
198
199 /* use GtkSocket's realize */
200 (*GTK_WIDGET_CLASS(parent_class)->realize)(widget);
201
202 /* create the Xt client widget */
203 xt_client_create(&(xtbin->xtclient), gtk_socket_get_id(GTK_SOCKET(xtbin)), h,
204 w);
205 xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget);
206
207 gdk_flush();
208
209 /* now that we have created the xt client, add it to the socket. */
210 gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow);
211 }
212
gtk_xtbin_new(GdkWindow * parent_window,String * f)213 GtkWidget *gtk_xtbin_new(GdkWindow *parent_window, String *f) {
214 GtkXtBin *xtbin;
215 gpointer user_data;
216
217 assert(parent_window != NULL);
218 xtbin = g_object_new(GTK_TYPE_XTBIN, NULL);
219
220 if (!xtbin) return (GtkWidget *)NULL;
221
222 if (f) fallback = f;
223
224 /* Initialize the Xt toolkit */
225 xtbin->parent_window = parent_window;
226
227 xt_client_init(&(xtbin->xtclient), GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()),
228 GDK_COLORMAP_XCOLORMAP(gdk_rgb_get_colormap()),
229 gdk_rgb_get_visual()->depth);
230
231 if (!xtbin->xtclient.xtdisplay) {
232 /* If XtOpenDisplay failed, we can't go any further.
233 * Bail out.
234 */
235 #ifdef DEBUG_XTBIN
236 printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
237 #endif
238 g_free(xtbin);
239 return (GtkWidget *)NULL;
240 }
241
242 /* Launch X event loop */
243 xt_client_xloop_create();
244
245 /* Build the hierachy */
246 xtbin->xtdisplay = xtbin->xtclient.xtdisplay;
247 gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window);
248 gdk_window_get_user_data(xtbin->parent_window, &user_data);
249 if (user_data) gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin));
250
251 /* This GtkSocket has a visible window, but the Xt plug will cover this
252 * window. Normally GtkSockets let the X server paint their background and
253 * this would happen immediately (before the plug is mapped). Setting the
254 * background to None prevents the server from painting this window,
255 * avoiding flicker.
256 */
257 gtk_widget_realize(GTK_WIDGET(xtbin));
258 gdk_window_set_back_pixmap(GTK_WIDGET(xtbin)->window, NULL, FALSE);
259
260 return GTK_WIDGET(xtbin);
261 }
262
gtk_xtbin_unrealize(GtkWidget * object)263 static void gtk_xtbin_unrealize(GtkWidget *object) {
264 GtkXtBin *xtbin;
265 GtkWidget *widget;
266
267 #ifdef DEBUG_XTBIN
268 printf("gtk_xtbin_unrealize()\n");
269 #endif
270
271 /* gtk_object_destroy() will already hold a refcount on object
272 */
273 xtbin = GTK_XTBIN(object);
274 widget = GTK_WIDGET(object);
275
276 GTK_WIDGET_UNSET_FLAGS(widget, GTK_VISIBLE);
277 if (GTK_WIDGET_REALIZED(widget)) {
278 xt_client_unrealize(&(xtbin->xtclient));
279 }
280
281 (*GTK_WIDGET_CLASS(parent_class)->unrealize)(widget);
282 }
283
gtk_xtbin_destroy(GtkObject * object)284 static void gtk_xtbin_destroy(GtkObject *object) {
285 GtkXtBin *xtbin;
286
287 #ifdef DEBUG_XTBIN
288 printf("gtk_xtbin_destroy()\n");
289 #endif
290
291 g_return_if_fail(object != NULL);
292 g_return_if_fail(GTK_IS_XTBIN(object));
293
294 xtbin = GTK_XTBIN(object);
295
296 if (xtbin->xtwindow) {
297 /* remove the event handler */
298 xt_client_destroy(&(xtbin->xtclient));
299 xtbin->xtwindow = 0;
300
301 /* stop X event loop */
302 xt_client_xloop_destroy();
303 }
304
305 GTK_OBJECT_CLASS(parent_class)->destroy(object);
306 }
307
308 /*
309 * Following is the implementation of Xt XEmbedded for client side
310 */
311
312 /* Initial Xt plugin */
xt_client_init(XtClient * xtclient,Visual * xtvisual,Colormap xtcolormap,int xtdepth)313 void xt_client_init(XtClient *xtclient, Visual *xtvisual, Colormap xtcolormap,
314 int xtdepth) {
315 XtAppContext app_context;
316 char *mArgv[1];
317 int mArgc = 0;
318
319 /*
320 * Initialize Xt stuff
321 */
322 xtclient->top_widget = NULL;
323 xtclient->child_widget = NULL;
324 xtclient->xtdisplay = NULL;
325 xtclient->xtvisual = NULL;
326 xtclient->xtcolormap = 0;
327 xtclient->xtdepth = 0;
328
329 if (!xt_is_initialized) {
330 #ifdef DEBUG_XTBIN
331 printf("starting up Xt stuff\n");
332 #endif
333 XtToolkitInitialize();
334 app_context = XtCreateApplicationContext();
335 if (fallback) XtAppSetFallbackResources(app_context, fallback);
336
337 xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL, "Wrapper",
338 NULL, 0, &mArgc, mArgv);
339 if (xtdisplay) xt_is_initialized = TRUE;
340 }
341 xtclient->xtdisplay = xtdisplay;
342 xtclient->xtvisual = xtvisual;
343 xtclient->xtcolormap = xtcolormap;
344 xtclient->xtdepth = xtdepth;
345 }
346
xt_client_xloop_create(void)347 void xt_client_xloop_create(void) {
348 /* If this is the first running widget, hook this display into the
349 mainloop */
350 if (0 == num_widgets) {
351 int cnumber;
352 GSource *gs;
353
354 /* Set up xtdisplay in case we're missing one */
355 if (!xtdisplay) {
356 (void)xt_client_get_display();
357 }
358
359 /*
360 * hook Xt event loop into the glib event loop.
361 */
362 /* the assumption is that gtk_init has already been called */
363 gs = g_source_new(&xt_event_funcs, sizeof(GSource));
364 if (!gs) {
365 return;
366 }
367
368 g_source_set_priority(gs, GDK_PRIORITY_EVENTS);
369 g_source_set_can_recurse(gs, TRUE);
370 tag = g_source_attach(gs, (GMainContext *)NULL);
371 g_source_unref(gs);
372 #ifdef VMS
373 cnumber = XConnectionNumber(xtdisplay);
374 #else
375 cnumber = ConnectionNumber(xtdisplay);
376 #endif
377 xt_event_poll_fd.fd = cnumber;
378 xt_event_poll_fd.events = G_IO_IN;
379 xt_event_poll_fd.revents = 0; /* hmm... is this correct? */
380
381 g_main_context_add_poll((GMainContext *)NULL, &xt_event_poll_fd,
382 G_PRIORITY_LOW);
383 /* add a timer so that we can poll and process Xt timers */
384 xt_polling_timer_id = g_timeout_add(
385 25, (GtkFunction)xt_event_polling_timer_callback, xtdisplay);
386 }
387
388 /* Bump up our usage count */
389 num_widgets++;
390 }
391
xt_client_xloop_destroy(void)392 void xt_client_xloop_destroy(void) {
393 num_widgets--; /* reduce our usage count */
394
395 /* If this is the last running widget, remove the Xt display
396 connection from the mainloop */
397 if (0 == num_widgets) {
398 #ifdef DEBUG_XTBIN
399 printf("removing the Xt connection from the main loop\n");
400 #endif
401 g_main_context_remove_poll((GMainContext *)NULL, &xt_event_poll_fd);
402 g_source_remove(tag);
403
404 g_source_remove(xt_polling_timer_id);
405 xt_polling_timer_id = 0;
406 }
407 }
408
409 /* Get Xt Client display */
xt_client_get_display(void)410 Display *xt_client_get_display(void) {
411 if (!xtdisplay) {
412 XtClient tmp;
413 xt_client_init(&tmp, NULL, 0, 0);
414 }
415 return xtdisplay;
416 }
417
418 /* Create the Xt client widgets
419 * */
xt_client_create(XtClient * xtclient,Window embedderid,int height,int width)420 void xt_client_create(XtClient *xtclient, Window embedderid, int height,
421 int width) {
422 int n;
423 Arg args[6];
424 Widget child_widget;
425 Widget top_widget;
426
427 #ifdef DEBUG_XTBIN
428 printf("xt_client_create() \n");
429 #endif
430 top_widget =
431 XtAppCreateShell("drawingArea", "Wrapper", applicationShellWidgetClass,
432 xtclient->xtdisplay, NULL, 0);
433 xtclient->top_widget = top_widget;
434
435 /* set size of Xt window */
436 n = 0;
437 XtSetArg(args[n], XtNheight, height);
438 n++;
439 XtSetArg(args[n], XtNwidth, width);
440 n++;
441 XtSetValues(top_widget, args, n);
442
443 child_widget =
444 XtVaCreateWidget("form", compositeWidgetClass, top_widget, NULL);
445
446 n = 0;
447 XtSetArg(args[n], XtNheight, height);
448 n++;
449 XtSetArg(args[n], XtNwidth, width);
450 n++;
451 XtSetArg(args[n], XtNvisual, xtclient->xtvisual);
452 n++;
453 XtSetArg(args[n], XtNdepth, xtclient->xtdepth);
454 n++;
455 XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap);
456 n++;
457 XtSetArg(args[n], XtNborderWidth, 0);
458 n++;
459 XtSetValues(child_widget, args, n);
460
461 XSync(xtclient->xtdisplay, FALSE);
462 xtclient->oldwindow = top_widget->core.window;
463 top_widget->core.window = embedderid;
464
465 /* this little trick seems to finish initializing the widget */
466 #if XlibSpecificationRelease >= 6
467 XtRegisterDrawable(xtclient->xtdisplay, embedderid, top_widget);
468 #else
469 _XtRegisterWindow(embedderid, top_widget);
470 #endif
471 XtRealizeWidget(child_widget);
472
473 /* listen to all Xt events */
474 XSelectInput(xtclient->xtdisplay, embedderid, XtBuildEventMask(top_widget));
475 xt_client_set_info(child_widget, 0);
476
477 XtManageChild(child_widget);
478 xtclient->child_widget = child_widget;
479
480 /* set the event handler */
481 XtAddEventHandler(child_widget, StructureNotifyMask | KeyPressMask, TRUE,
482 (XtEventHandler)xt_client_event_handler, xtclient);
483 XtAddEventHandler(child_widget, SubstructureNotifyMask | ButtonReleaseMask,
484 FALSE, (XtEventHandler)xt_client_focus_listener, xtclient);
485 XSync(xtclient->xtdisplay, FALSE);
486 }
487
xt_client_unrealize(XtClient * xtclient)488 void xt_client_unrealize(XtClient *xtclient) {
489 /* Explicitly destroy the child_widget window because this is actually a
490 child of the socket window. It is not a child of top_widget's window
491 when that is destroyed. */
492 XtUnrealizeWidget(xtclient->child_widget);
493
494 #if XlibSpecificationRelease >= 6
495 XtUnregisterDrawable(xtclient->xtdisplay, xtclient->top_widget->core.window);
496 #else
497 _XtUnregisterWindow(xtclient->top_widget->core.window, xtclient->top_widget);
498 #endif
499
500 /* flush the queue before we returning origin top_widget->core.window
501 or we can get X error since the window is gone */
502 XSync(xtclient->xtdisplay, False);
503
504 xtclient->top_widget->core.window = xtclient->oldwindow;
505 XtUnrealizeWidget(xtclient->top_widget);
506 }
507
xt_client_destroy(XtClient * xtclient)508 void xt_client_destroy(XtClient *xtclient) {
509 if (xtclient->top_widget) {
510 XtRemoveEventHandler(xtclient->child_widget,
511 StructureNotifyMask | KeyPressMask, TRUE,
512 (XtEventHandler)xt_client_event_handler, xtclient);
513 XtDestroyWidget(xtclient->top_widget);
514 xtclient->top_widget = NULL;
515 }
516 }
517
xt_client_set_info(Widget xtplug,unsigned long flags)518 void xt_client_set_info(Widget xtplug, unsigned long flags) {
519 unsigned long buffer[2];
520
521 Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False);
522
523 buffer[1] = 0; /* Protocol version */
524 buffer[1] = flags;
525
526 XChangeProperty(XtDisplay(xtplug), XtWindow(xtplug), infoAtom, infoAtom, 32,
527 PropModeReplace, (unsigned char *)buffer, 2);
528 }
529
xt_client_handle_xembed_message(Widget w,XtPointer client_data,XEvent * event)530 static void xt_client_handle_xembed_message(Widget w, XtPointer client_data,
531 XEvent *event) {
532 XtClient *xtplug = (XtClient *)client_data;
533 switch (event->xclient.data.l[1]) {
534 case XEMBED_EMBEDDED_NOTIFY:
535 break;
536 case XEMBED_WINDOW_ACTIVATE:
537 #ifdef DEBUG_XTBIN
538 printf("Xt client get XEMBED_WINDOW_ACTIVATE\n");
539 #endif
540 break;
541 case XEMBED_WINDOW_DEACTIVATE:
542 #ifdef DEBUG_XTBIN
543 printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n");
544 #endif
545 break;
546 case XEMBED_MODALITY_ON:
547 #ifdef DEBUG_XTBIN
548 printf("Xt client get XEMBED_MODALITY_ON\n");
549 #endif
550 break;
551 case XEMBED_MODALITY_OFF:
552 #ifdef DEBUG_XTBIN
553 printf("Xt client get XEMBED_MODALITY_OFF\n");
554 #endif
555 break;
556 case XEMBED_FOCUS_IN:
557 case XEMBED_FOCUS_OUT: {
558 XEvent xevent;
559 memset(&xevent, 0, sizeof(xevent));
560
561 if (event->xclient.data.l[1] == XEMBED_FOCUS_IN) {
562 #ifdef DEBUG_XTBIN
563 printf("XTEMBED got focus in\n");
564 #endif
565 xevent.xfocus.type = FocusIn;
566 } else {
567 #ifdef DEBUG_XTBIN
568 printf("XTEMBED got focus out\n");
569 #endif
570 xevent.xfocus.type = FocusOut;
571 }
572
573 xevent.xfocus.window = XtWindow(xtplug->child_widget);
574 xevent.xfocus.display = XtDisplay(xtplug->child_widget);
575 XSendEvent(XtDisplay(xtplug->child_widget), xevent.xfocus.window, False,
576 NoEventMask, &xevent);
577 XSync(XtDisplay(xtplug->child_widget), False);
578 } break;
579 default:
580 break;
581 } /* End of XEmbed Message */
582 }
583
xt_client_event_handler(Widget w,XtPointer client_data,XEvent * event)584 void xt_client_event_handler(Widget w, XtPointer client_data, XEvent *event) {
585 XtClient *xtplug = (XtClient *)client_data;
586
587 switch (event->type) {
588 case ClientMessage:
589 /* Handle xembed message */
590 if (event->xclient.message_type ==
591 XInternAtom(XtDisplay(xtplug->child_widget), "_XEMBED", False)) {
592 xt_client_handle_xembed_message(w, client_data, event);
593 }
594 break;
595 case ReparentNotify:
596 break;
597 case MappingNotify:
598 xt_client_set_info(w, XEMBED_MAPPED);
599 break;
600 case UnmapNotify:
601 xt_client_set_info(w, 0);
602 break;
603 case KeyPress:
604 #ifdef DEBUG_XTBIN
605 printf("Key Press Got!\n");
606 #endif
607 break;
608 default:
609 break;
610 } /* End of switch(event->type) */
611 }
612
send_xembed_message(XtClient * xtclient,long message,long detail,long data1,long data2,long time)613 static void send_xembed_message(XtClient *xtclient, long message, long detail,
614 long data1, long data2, long time) {
615 XEvent xevent;
616 Window w = XtWindow(xtclient->top_widget);
617 Display *dpy = xtclient->xtdisplay;
618 int errorcode;
619
620 memset(&xevent, 0, sizeof(xevent));
621 xevent.xclient.window = w;
622 xevent.xclient.type = ClientMessage;
623 xevent.xclient.message_type = XInternAtom(dpy, "_XEMBED", False);
624 xevent.xclient.format = 32;
625 xevent.xclient.data.l[0] = time;
626 xevent.xclient.data.l[1] = message;
627 xevent.xclient.data.l[2] = detail;
628 xevent.xclient.data.l[3] = data1;
629 xevent.xclient.data.l[4] = data2;
630
631 trap_errors();
632 XSendEvent(dpy, w, False, NoEventMask, &xevent);
633 XSync(dpy, False);
634
635 if ((errorcode = untrap_error())) {
636 #ifdef DEBUG_XTBIN
637 printf("send_xembed_message error(%d)!!!\n", errorcode);
638 #endif
639 }
640 }
641
error_handler(Display * display,XErrorEvent * error)642 static int error_handler(Display *display, XErrorEvent *error) {
643 trapped_error_code = error->error_code;
644 return 0;
645 }
646
trap_errors(void)647 static void trap_errors(void) {
648 trapped_error_code = 0;
649 old_error_handler = XSetErrorHandler(error_handler);
650 }
651
untrap_error(void)652 static int untrap_error(void) {
653 XSetErrorHandler(old_error_handler);
654 if (trapped_error_code) {
655 #ifdef DEBUG_XTBIN
656 printf("Get X Window Error = %d\n", trapped_error_code);
657 #endif
658 }
659 return trapped_error_code;
660 }
661
xt_client_focus_listener(Widget w,XtPointer user_data,XEvent * event)662 void xt_client_focus_listener(Widget w, XtPointer user_data, XEvent *event) {
663 Display *dpy = XtDisplay(w);
664 XtClient *xtclient = user_data;
665 Window win = XtWindow(w);
666
667 switch (event->type) {
668 case CreateNotify:
669 if (event->xcreatewindow.parent == win) {
670 Widget child = XtWindowToWidget(dpy, event->xcreatewindow.window);
671 if (child) xt_add_focus_listener_tree(child, user_data);
672 }
673 break;
674 case DestroyNotify:
675 xt_remove_focus_listener(w, user_data);
676 break;
677 case ReparentNotify:
678 if (event->xreparent.parent == win) {
679 /* I am the new parent */
680 Widget child = XtWindowToWidget(dpy, event->xreparent.window);
681 if (child) xt_add_focus_listener_tree(child, user_data);
682 } else if (event->xreparent.window == win) {
683 /* I am the new child */
684 } else {
685 /* I am the old parent */
686 }
687 break;
688 case ButtonRelease:
689 #if 0
690 XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time);
691 #endif
692 send_xembed_message(xtclient, XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
693 break;
694 default:
695 break;
696 } /* End of switch(event->type) */
697 }
698
xt_add_focus_listener(Widget w,XtPointer user_data)699 static void xt_add_focus_listener(Widget w, XtPointer user_data) {
700 XtClient *xtclient = user_data;
701
702 trap_errors();
703 XtAddEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, FALSE,
704 (XtEventHandler)xt_client_focus_listener, xtclient);
705 untrap_error();
706 }
707
xt_remove_focus_listener(Widget w,XtPointer user_data)708 static void xt_remove_focus_listener(Widget w, XtPointer user_data) {
709 trap_errors();
710 XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, FALSE,
711 (XtEventHandler)xt_client_focus_listener, user_data);
712
713 untrap_error();
714 }
715
xt_add_focus_listener_tree(Widget treeroot,XtPointer user_data)716 static void xt_add_focus_listener_tree(Widget treeroot, XtPointer user_data) {
717 Window win = XtWindow(treeroot);
718 Window *children;
719 Window root, parent;
720 Display *dpy = XtDisplay(treeroot);
721 unsigned int i, nchildren;
722
723 /* ensure we don't add more than once */
724 xt_remove_focus_listener(treeroot, user_data);
725 xt_add_focus_listener(treeroot, user_data);
726 trap_errors();
727 if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) {
728 untrap_error();
729 return;
730 }
731
732 if (untrap_error()) return;
733
734 for (i = 0; i < nchildren; ++i) {
735 Widget child = XtWindowToWidget(dpy, children[i]);
736 if (child) xt_add_focus_listener_tree(child, user_data);
737 }
738 XFree((void *)children);
739 }
740