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