• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2: */
3 
4 /* ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is the Gtk2XtBin Widget Implementation.
18  *
19  * The Initial Developer of the Original Code is
20  * Sun Microsystems, Inc.
21  * Portions created by the Initial Developer are Copyright (C) 2002
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39 
40 /*
41  * The GtkXtBin widget allows for Xt toolkit code to be used
42  * inside a GTK application.
43  */
44 
45 #include "GtkVersioning.h"
46 #include "xembed.h"
47 #include "gtk2xtbin.h"
48 #include <gtk/gtk.h>
49 #ifdef GTK_API_VERSION_2
50 #include <gdk/gdkx.h>
51 #endif
52 #include <glib.h>
53 #include <assert.h>
54 #include <sys/time.h>
55 #include <sys/types.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 
60 /* Xlib/Xt stuff */
61 #include <X11/Xlib.h>
62 #include <X11/Xutil.h>
63 #include <X11/Shell.h>
64 #include <X11/Intrinsic.h>
65 #include <X11/StringDefs.h>
66 
67 /* uncomment this if you want debugging information about widget
68    creation and destruction */
69 #undef DEBUG_XTBIN
70 
71 #define XTBIN_MAX_EVENTS 30
72 
73 static void            gtk_xtbin_class_init (GtkXtBinClass *klass);
74 static void            gtk_xtbin_init       (GtkXtBin      *xtbin);
75 static void            gtk_xtbin_realize    (GtkWidget      *widget);
76 static void            gtk_xtbin_unrealize    (GtkWidget      *widget);
77 static void            gtk_xtbin_dispose    (GObject      *object);
78 
79 /* Xt aware XEmbed */
80 static void       xt_client_init      (XtClient * xtclient,
81                                        Visual *xtvisual,
82                                        Colormap xtcolormap,
83                                        int xtdepth);
84 static void       xt_client_create    (XtClient * xtclient,
85                                        Window embeder,
86                                        int height,
87                                        int width );
88 static void       xt_client_unrealize (XtClient* xtclient);
89 static void       xt_client_destroy   (XtClient* xtclient);
90 static void       xt_client_set_info  (Widget xtplug,
91                                        unsigned long flags);
92 static void       xt_client_event_handler (Widget w,
93                                            XtPointer client_data,
94                                            XEvent *event);
95 static void       xt_client_handle_xembed_message (Widget w,
96                                                    XtPointer client_data,
97                                                    XEvent *event);
98 static void       xt_client_focus_listener       (Widget w,
99                                                    XtPointer user_data,
100                                                    XEvent *event);
101 static void       xt_add_focus_listener( Widget w, XtPointer user_data );
102 static void       xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data);
103 static void       xt_remove_focus_listener(Widget w, XtPointer user_data);
104 static void       send_xembed_message (XtClient *xtclient,
105                                        long message,
106                                        long detail,
107                                        long data1,
108                                        long data2,
109                                        long time);
110 static int        error_handler       (Display *display,
111                                        XErrorEvent *error);
112 /* For error trap of XEmbed */
113 static void       trap_errors(void);
114 static int        untrap_error(void);
115 static int        (*old_error_handler) (Display *, XErrorEvent *);
116 static int        trapped_error_code = 0;
117 
118 static GtkWidgetClass *parent_class = NULL;
119 
120 static Display         *xtdisplay = NULL;
121 static String          *fallback = NULL;
122 static gboolean         xt_is_initialized = FALSE;
123 static gint             num_widgets = 0;
124 
125 static GPollFD          xt_event_poll_fd;
126 static gint             xt_polling_timer_id = 0;
127 static guint            tag = 0;
128 
129 static gboolean
xt_event_prepare(GSource * source_data,gint * timeout)130 xt_event_prepare (GSource*  source_data,
131                    gint     *timeout)
132 {
133   int mask;
134 
135   GDK_THREADS_ENTER();
136   mask = XPending(xtdisplay);
137   GDK_THREADS_LEAVE();
138 
139   return (gboolean)mask;
140 }
141 
142 static gboolean
xt_event_check(GSource * source_data)143 xt_event_check (GSource*  source_data)
144 {
145   GDK_THREADS_ENTER ();
146 
147   if (xt_event_poll_fd.revents & G_IO_IN) {
148     int mask;
149     mask = XPending(xtdisplay);
150     GDK_THREADS_LEAVE ();
151     return (gboolean)mask;
152   }
153 
154   GDK_THREADS_LEAVE ();
155   return FALSE;
156 }
157 
158 static gboolean
xt_event_dispatch(GSource * source_data,GSourceFunc call_back,gpointer user_data)159 xt_event_dispatch (GSource*  source_data,
160                     GSourceFunc call_back,
161                     gpointer  user_data)
162 {
163   XtAppContext ac;
164   int i = 0;
165 
166   ac = XtDisplayToApplicationContext(xtdisplay);
167 
168   GDK_THREADS_ENTER ();
169 
170   /* Process only real X traffic here.  We only look for data on the
171    * pipe, limit it to XTBIN_MAX_EVENTS and only call
172    * XtAppProcessEvent so that it will look for X events.  There's no
173    * timer processing here since we already have a timer callback that
174    * does it.  */
175   for (i=0; i < XTBIN_MAX_EVENTS && XPending(xtdisplay); i++) {
176     XtAppProcessEvent(ac, XtIMXEvent);
177   }
178 
179   GDK_THREADS_LEAVE ();
180 
181   return TRUE;
182 }
183 
184 typedef void (*GSourceFuncsFinalize) (GSource* source);
185 
186 static GSourceFuncs xt_event_funcs = {
187   xt_event_prepare,
188   xt_event_check,
189   xt_event_dispatch,
190   (GSourceFuncsFinalize)g_free,
191   (GSourceFunc)NULL,
192   (GSourceDummyMarshal)NULL
193 };
194 
195 static gboolean
xt_event_polling_timer_callback(gpointer user_data)196 xt_event_polling_timer_callback(gpointer user_data)
197 {
198   Display * display;
199   XtAppContext ac;
200   int eventsToProcess = 20;
201 
202   display = (Display *)user_data;
203   ac = XtDisplayToApplicationContext(display);
204 
205   /* We need to process many Xt events here. If we just process
206      one event we might starve one or more Xt consumers. On the other hand
207      this could hang the whole app if Xt events come pouring in. So process
208      up to 20 Xt events right now and save the rest for later. This is a hack,
209      but it oughta work. We *really* should have out of process plugins.
210   */
211   while (eventsToProcess-- && XtAppPending(ac))
212     XtAppProcessEvent(ac, XtIMAll);
213   return TRUE;
214 }
215 
216 GType
gtk_xtbin_get_type(void)217 gtk_xtbin_get_type (void)
218 {
219   static GType xtbin_type = 0;
220 
221   if (!xtbin_type) {
222       static const GTypeInfo xtbin_info =
223       {
224         sizeof (GtkXtBinClass),
225         NULL,
226         NULL,
227 
228         (GClassInitFunc)gtk_xtbin_class_init,
229         NULL,
230         NULL,
231 
232         sizeof (GtkXtBin),
233         0,
234         (GInstanceInitFunc)gtk_xtbin_init,
235         NULL
236       };
237       xtbin_type = g_type_register_static (GTK_TYPE_SOCKET,
238                                            "GtkXtBin",
239                                            &xtbin_info,
240                                            0);
241   }
242   return xtbin_type;
243 }
244 
245 static void
gtk_xtbin_class_init(GtkXtBinClass * klass)246 gtk_xtbin_class_init (GtkXtBinClass *klass)
247 {
248   GtkWidgetClass *widget_class;
249   GObjectClass   *object_class;
250 
251   parent_class = g_type_class_peek_parent (klass);
252 
253   widget_class = GTK_WIDGET_CLASS (klass);
254   widget_class->realize = gtk_xtbin_realize;
255   widget_class->unrealize = gtk_xtbin_unrealize;
256 
257   object_class = G_OBJECT_CLASS (klass);
258   object_class->dispose = gtk_xtbin_dispose;
259 }
260 
261 static void
gtk_xtbin_init(GtkXtBin * xtbin)262 gtk_xtbin_init (GtkXtBin *xtbin)
263 {
264   xtbin->xtdisplay = NULL;
265   xtbin->parent_window = NULL;
266   xtbin->xtwindow = 0;
267   xtbin->x = 0;
268   xtbin->y = 0;
269 }
270 
271 static void
gtk_xtbin_realize(GtkWidget * widget)272 gtk_xtbin_realize (GtkWidget *widget)
273 {
274   GtkXtBin     *xtbin;
275   GtkAllocation allocation = { 0, 0, 200, 200 };
276 #if GTK_CHECK_VERSION(2, 18, 0)
277   GtkAllocation widget_allocation;
278 #endif
279 
280 #ifdef DEBUG_XTBIN
281   printf("gtk_xtbin_realize()\n");
282 #endif
283 
284   g_return_if_fail (GTK_IS_XTBIN (widget));
285 
286   xtbin = GTK_XTBIN (widget);
287 
288   /* caculate the allocation before realize */
289 #if GTK_CHECK_VERSION(2, 24, 0)
290   allocation.width = gdk_window_get_width(xtbin->parent_window);
291   allocation.height = gdk_window_get_height(xtbin->parent_window);
292 #else
293   gint  x, y, w, h, d; /* geometry of window */
294   gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d);
295   allocation.width = w;
296   allocation.height = h;
297 #endif
298   gtk_widget_size_allocate (widget, &allocation);
299 
300 #ifdef DEBUG_XTBIN
301   printf("initial allocation %d %d %d %d\n", x, y, w, h);
302 #endif
303 
304 #if GTK_CHECK_VERSION(2, 18, 0)
305   gtk_widget_get_allocation(widget, &widget_allocation);
306   xtbin->width = widget_allocation.width;
307   xtbin->height = widget_allocation.height;
308 #else
309   xtbin->width = widget->allocation.width;
310   xtbin->height = widget->allocation.height;
311 #endif
312 
313   /* use GtkSocket's realize */
314   (*GTK_WIDGET_CLASS(parent_class)->realize)(widget);
315 
316   /* create the Xt client widget */
317   xt_client_create(&(xtbin->xtclient),
318        gtk_socket_get_id(GTK_SOCKET(xtbin)),
319        xtbin->height,
320        xtbin->width);
321   xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget);
322 
323   gdk_flush();
324 
325   /* now that we have created the xt client, add it to the socket. */
326   gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow);
327 }
328 
329 
330 
331 GtkWidget*
gtk_xtbin_new(GtkWidget * parent_widget,String * f)332 gtk_xtbin_new (GtkWidget *parent_widget, String *f)
333 {
334   GtkXtBin *xtbin;
335   gpointer user_data;
336   GdkScreen *screen;
337   GdkVisual* visual;
338   Colormap colormap;
339   GdkWindow* parent_window = gtk_widget_get_window(parent_widget);
340 
341   assert(parent_window != NULL);
342   xtbin = g_object_new (GTK_TYPE_XTBIN, NULL);
343 
344   if (!xtbin)
345     return (GtkWidget*)NULL;
346 
347   if (f)
348     fallback = f;
349 
350   /* Initialize the Xt toolkit */
351   xtbin->parent_window = parent_window;
352 
353   screen = gtk_widget_get_screen(parent_widget);
354   visual = gdk_screen_get_system_visual(screen);
355   colormap = XCreateColormap(GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(screen)),
356                              GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)),
357                              GDK_VISUAL_XVISUAL(visual), AllocNone);
358 
359   xt_client_init(&(xtbin->xtclient),
360                  GDK_VISUAL_XVISUAL(visual),
361                  colormap,
362                  gdk_visual_get_depth(visual));
363 
364   if (!xtbin->xtclient.xtdisplay) {
365     /* If XtOpenDisplay failed, we can't go any further.
366      *  Bail out.
367      */
368 #ifdef DEBUG_XTBIN
369     printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
370 #endif
371     g_free (xtbin);
372     return (GtkWidget *)NULL;
373   }
374 
375   /* If this is the first running widget, hook this display into the
376      mainloop */
377   if (0 == num_widgets) {
378     int           cnumber;
379     /*
380      * hook Xt event loop into the glib event loop.
381      */
382 
383     /* the assumption is that gtk_init has already been called */
384     GSource* gs = g_source_new(&xt_event_funcs, sizeof(GSource));
385       if (!gs) {
386        return NULL;
387       }
388 
389     g_source_set_priority(gs, GDK_PRIORITY_EVENTS);
390     g_source_set_can_recurse(gs, TRUE);
391     tag = g_source_attach(gs, (GMainContext*)NULL);
392 #ifdef VMS
393     cnumber = XConnectionNumber(xtdisplay);
394 #else
395     cnumber = ConnectionNumber(xtdisplay);
396 #endif
397     xt_event_poll_fd.fd = cnumber;
398     xt_event_poll_fd.events = G_IO_IN;
399     xt_event_poll_fd.revents = 0;    /* hmm... is this correct? */
400 
401     g_main_context_add_poll ((GMainContext*)NULL,
402                              &xt_event_poll_fd,
403                              G_PRIORITY_LOW);
404     /* add a timer so that we can poll and process Xt timers */
405     xt_polling_timer_id =
406       g_timeout_add(25,
407                       (GSourceFunc)xt_event_polling_timer_callback,
408                       xtdisplay);
409   }
410 
411   /* Bump up our usage count */
412   num_widgets++;
413 
414   /* Build the hierachy */
415   xtbin->xtdisplay = xtbin->xtclient.xtdisplay;
416   gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window);
417   gdk_window_get_user_data(xtbin->parent_window, &user_data);
418   if (user_data)
419     gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin));
420 
421   return GTK_WIDGET (xtbin);
422 }
423 
424 void
gtk_xtbin_set_position(GtkXtBin * xtbin,gint x,gint y)425 gtk_xtbin_set_position (GtkXtBin *xtbin,
426                         gint       x,
427                         gint       y)
428 {
429   xtbin->x = x;
430   xtbin->y = y;
431 
432   if (gtk_widget_get_realized (GTK_WIDGET(xtbin)))
433     gdk_window_move (gtk_widget_get_window(GTK_WIDGET (xtbin)), x, y);
434 }
435 
436 void
gtk_xtbin_resize(GtkWidget * widget,gint width,gint height)437 gtk_xtbin_resize (GtkWidget *widget,
438                   gint       width,
439                   gint       height)
440 {
441   Arg args[2];
442   GtkXtBin *xtbin = GTK_XTBIN (widget);
443   GtkAllocation allocation;
444 
445 #ifdef DEBUG_XTBIN
446   printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height);
447 #endif
448 
449   xtbin->height = height;
450   xtbin->width  = width;
451 
452   // Avoid BadValue errors in XtSetValues
453   if (height <= 0 || width <=0) {
454     height = 1;
455     width = 1;
456   }
457   XtSetArg(args[0], XtNheight, height);
458   XtSetArg(args[1], XtNwidth,  width);
459   XtSetValues(xtbin->xtclient.top_widget, args, 2);
460 
461   /* we need to send a size allocate so the socket knows about the
462      size changes */
463   allocation.x = xtbin->x;
464   allocation.y = xtbin->y;
465   allocation.width = xtbin->width;
466   allocation.height = xtbin->height;
467 
468   gtk_widget_size_allocate(widget, &allocation);
469 }
470 
471 static void
gtk_xtbin_unrealize(GtkWidget * object)472 gtk_xtbin_unrealize (GtkWidget *object)
473 {
474   GtkXtBin *xtbin;
475   GtkWidget *widget;
476 
477 #ifdef DEBUG_XTBIN
478   printf("gtk_xtbin_unrealize()\n");
479 #endif
480 
481   /* gtk_object_destroy() will already hold a refcount on object
482    */
483   xtbin = GTK_XTBIN(object);
484   widget = GTK_WIDGET(object);
485 
486   gtk_widget_set_visible(widget, FALSE);
487   if (gtk_widget_get_realized (widget)) {
488     xt_client_unrealize(&(xtbin->xtclient));
489   }
490 
491   (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget);
492 }
493 
494 static void
gtk_xtbin_dispose(GObject * object)495 gtk_xtbin_dispose (GObject *object)
496 {
497   GtkXtBin *xtbin;
498 
499 #ifdef DEBUG_XTBIN
500   printf("gtk_xtbin_destroy()\n");
501 #endif
502 
503   g_return_if_fail (object != NULL);
504   g_return_if_fail (GTK_IS_XTBIN (object));
505 
506   xtbin = GTK_XTBIN (object);
507 
508   if(xtbin->xtwindow) {
509     /* remove the event handler */
510     xt_client_destroy(&(xtbin->xtclient));
511     xtbin->xtwindow = 0;
512 
513     num_widgets--; /* reduce our usage count */
514 
515     /* If this is the last running widget, remove the Xt display
516        connection from the mainloop */
517     if (0 == num_widgets) {
518 #ifdef DEBUG_XTBIN
519       printf("removing the Xt connection from the main loop\n");
520 #endif
521       g_main_context_remove_poll((GMainContext*)NULL, &xt_event_poll_fd);
522       g_source_remove(tag);
523 
524       g_source_remove(xt_polling_timer_id);
525       xt_polling_timer_id = 0;
526     }
527   }
528 
529   G_OBJECT_CLASS(parent_class)->dispose(object);
530 }
531 
532 /*
533 * Following is the implementation of Xt XEmbedded for client side
534 */
535 
536 /* Initial Xt plugin */
537 static void
xt_client_init(XtClient * xtclient,Visual * xtvisual,Colormap xtcolormap,int xtdepth)538 xt_client_init( XtClient * xtclient,
539                 Visual *xtvisual,
540                 Colormap xtcolormap,
541                 int xtdepth)
542 {
543   XtAppContext  app_context;
544   char         *mArgv[1];
545   int           mArgc = 0;
546 
547   /*
548    * Initialize Xt stuff
549    */
550   xtclient->top_widget = NULL;
551   xtclient->child_widget = NULL;
552   xtclient->xtdisplay  = NULL;
553   xtclient->xtvisual   = NULL;
554   xtclient->xtcolormap = 0;
555   xtclient->xtdepth = 0;
556 
557   if (!xt_is_initialized) {
558 #ifdef DEBUG_XTBIN
559     printf("starting up Xt stuff\n");
560 #endif
561     XtToolkitInitialize();
562     app_context = XtCreateApplicationContext();
563     if (fallback)
564       XtAppSetFallbackResources(app_context, fallback);
565 
566     xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL,
567                             "Wrapper", NULL, 0, &mArgc, mArgv);
568     if (xtdisplay)
569       xt_is_initialized = TRUE;
570   }
571   xtclient->xtdisplay  = xtdisplay;
572   xtclient->xtvisual   = xtvisual;
573   xtclient->xtcolormap = xtcolormap;
574   xtclient->xtdepth    = xtdepth;
575 }
576 
577 /* Create the Xt client widgets
578 *  */
579 static void
xt_client_create(XtClient * xtclient,Window embedderid,int height,int width)580 xt_client_create ( XtClient* xtclient ,
581                    Window embedderid,
582                    int height,
583                    int width )
584 {
585   int           n;
586   Arg           args[6];
587   Widget        child_widget;
588   Widget        top_widget;
589 
590 #ifdef DEBUG_XTBIN
591   printf("xt_client_create() \n");
592 #endif
593   top_widget = XtAppCreateShell("drawingArea", "Wrapper",
594                                 applicationShellWidgetClass,
595                                 xtclient->xtdisplay,
596                                 NULL, 0);
597   xtclient->top_widget = top_widget;
598 
599   /* set size of Xt window */
600   n = 0;
601   XtSetArg(args[n], XtNheight,   height);n++;
602   XtSetArg(args[n], XtNwidth,    width);n++;
603   XtSetValues(top_widget, args, n);
604 
605   child_widget = XtVaCreateWidget("form",
606                                   compositeWidgetClass,
607                                   top_widget, NULL);
608 
609   n = 0;
610   XtSetArg(args[n], XtNheight,   height);n++;
611   XtSetArg(args[n], XtNwidth,    width);n++;
612   XtSetArg(args[n], XtNvisual,   xtclient->xtvisual ); n++;
613   XtSetArg(args[n], XtNdepth,    xtclient->xtdepth ); n++;
614   XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++;
615   XtSetArg(args[n], XtNborderWidth, 0); n++;
616   XtSetValues(child_widget, args, n);
617 
618   XSync(xtclient->xtdisplay, FALSE);
619   xtclient->oldwindow = top_widget->core.window;
620   top_widget->core.window = embedderid;
621 
622   /* this little trick seems to finish initializing the widget */
623 #if XlibSpecificationRelease >= 6
624   XtRegisterDrawable(xtclient->xtdisplay,
625                      embedderid,
626                      top_widget);
627 #else
628   _XtRegisterWindow( embedderid,
629                      top_widget);
630 #endif
631   XtRealizeWidget(child_widget);
632 
633   /* listen to all Xt events */
634   XSelectInput(xtclient->xtdisplay,
635                XtWindow(top_widget),
636                0x0FFFFF);
637   xt_client_set_info (child_widget, 0);
638 
639   XtManageChild(child_widget);
640   xtclient->child_widget = child_widget;
641 
642   /* set the event handler */
643   XtAddEventHandler(child_widget,
644                     0x0FFFFF & ~ResizeRedirectMask,
645                     TRUE,
646                     (XtEventHandler)xt_client_event_handler, xtclient);
647   XtAddEventHandler(child_widget,
648                     SubstructureNotifyMask | ButtonReleaseMask,
649                     TRUE,
650                     (XtEventHandler)xt_client_focus_listener,
651                     xtclient);
652   XSync(xtclient->xtdisplay, FALSE);
653 }
654 
655 static void
xt_client_unrealize(XtClient * xtclient)656 xt_client_unrealize ( XtClient* xtclient )
657 {
658 #if XlibSpecificationRelease >= 6
659   XtUnregisterDrawable(xtclient->xtdisplay,
660                        xtclient->top_widget->core.window);
661 #else
662   _XtUnregisterWindow(xtclient->top_widget->core.window,
663                       xtclient->top_widget);
664 #endif
665 
666   /* flush the queue before we returning origin top_widget->core.window
667      or we can get X error since the window is gone */
668   XSync(xtclient->xtdisplay, False);
669 
670   xtclient->top_widget->core.window = xtclient->oldwindow;
671   XtUnrealizeWidget(xtclient->top_widget);
672 }
673 
674 static void
xt_client_destroy(XtClient * xtclient)675 xt_client_destroy   (XtClient* xtclient)
676 {
677   if(xtclient->top_widget) {
678     XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE,
679                          (XtEventHandler)xt_client_event_handler, xtclient);
680     XtDestroyWidget(xtclient->top_widget);
681     xtclient->top_widget = NULL;
682   }
683 }
684 
685 static void
xt_client_set_info(Widget xtplug,unsigned long flags)686 xt_client_set_info (Widget xtplug, unsigned long flags)
687 {
688   unsigned long buffer[2];
689 
690   Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False);
691 
692   buffer[1] = 0;                /* Protocol version */
693   buffer[1] = flags;
694 
695   XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug),
696                    infoAtom, infoAtom, 32,
697                    PropModeReplace,
698                    (unsigned char *)buffer, 2);
699 }
700 
701 static void
xt_client_handle_xembed_message(Widget w,XtPointer client_data,XEvent * event)702 xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event)
703 {
704   XtClient *xtplug = (XtClient*)client_data;
705   switch (event->xclient.data.l[1])
706   {
707   case XEMBED_EMBEDDED_NOTIFY:
708     break;
709   case XEMBED_WINDOW_ACTIVATE:
710 #ifdef DEBUG_XTBIN
711     printf("Xt client get XEMBED_WINDOW_ACTIVATE\n");
712 #endif
713     break;
714   case XEMBED_WINDOW_DEACTIVATE:
715 #ifdef DEBUG_XTBIN
716     printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n");
717 #endif
718     break;
719   case XEMBED_MODALITY_ON:
720 #ifdef DEBUG_XTBIN
721     printf("Xt client get XEMBED_MODALITY_ON\n");
722 #endif
723     break;
724   case XEMBED_MODALITY_OFF:
725 #ifdef DEBUG_XTBIN
726     printf("Xt client get XEMBED_MODALITY_OFF\n");
727 #endif
728     break;
729   case XEMBED_FOCUS_IN:
730   case XEMBED_FOCUS_OUT:
731     {
732       XEvent xevent;
733       memset(&xevent, 0, sizeof(xevent));
734 
735       if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) {
736 #ifdef DEBUG_XTBIN
737         printf("XTEMBED got focus in\n");
738 #endif
739         xevent.xfocus.type = FocusIn;
740       }
741       else {
742 #ifdef DEBUG_XTBIN
743         printf("XTEMBED got focus out\n");
744 #endif
745         xevent.xfocus.type = FocusOut;
746       }
747 
748       xevent.xfocus.window = XtWindow(xtplug->child_widget);
749       xevent.xfocus.display = XtDisplay(xtplug->child_widget);
750       XSendEvent(XtDisplay(xtplug->child_widget),
751                  xevent.xfocus.window,
752                  False, NoEventMask,
753                  &xevent );
754       XSync( XtDisplay(xtplug->child_widget), False);
755     }
756     break;
757   default:
758     break;
759   } /* End of XEmbed Message */
760 }
761 
762 static void
xt_client_event_handler(Widget w,XtPointer client_data,XEvent * event)763 xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event)
764 {
765   XtClient *xtplug = (XtClient*)client_data;
766 
767   switch(event->type)
768     {
769     case ClientMessage:
770       /* Handle xembed message */
771       if (event->xclient.message_type==
772                  XInternAtom (XtDisplay(xtplug->child_widget),
773                               "_XEMBED", False)) {
774         xt_client_handle_xembed_message(w, client_data, event);
775       }
776       break;
777     case ReparentNotify:
778       break;
779     case MappingNotify:
780       xt_client_set_info (w, XEMBED_MAPPED);
781       break;
782     case UnmapNotify:
783       xt_client_set_info (w, 0);
784       break;
785     case FocusIn:
786       send_xembed_message ( xtplug,
787                             XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
788       break;
789     case FocusOut:
790       break;
791     case KeyPress:
792 #ifdef DEBUG_XTBIN
793       printf("Key Press Got!\n");
794 #endif
795       break;
796     default:
797       break;
798     } /* End of switch(event->type) */
799 }
800 
801 static void
send_xembed_message(XtClient * xtclient,long message,long detail,long data1,long data2,long time)802 send_xembed_message (XtClient  *xtclient,
803                      long      message,
804                      long      detail,
805                      long      data1,
806                      long      data2,
807                      long      time)
808 {
809   XEvent xevent;
810   Window w=XtWindow(xtclient->top_widget);
811   Display* dpy=xtclient->xtdisplay;
812   int errorcode;
813 
814   memset(&xevent,0,sizeof(xevent));
815   xevent.xclient.window = w;
816   xevent.xclient.type = ClientMessage;
817   xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False);
818   xevent.xclient.format = 32;
819   xevent.xclient.data.l[0] = time;
820   xevent.xclient.data.l[1] = message;
821   xevent.xclient.data.l[2] = detail;
822   xevent.xclient.data.l[3] = data1;
823   xevent.xclient.data.l[4] = data2;
824 
825   trap_errors ();
826   XSendEvent (dpy, w, False, NoEventMask, &xevent);
827   XSync (dpy,False);
828 
829   if((errorcode = untrap_error())) {
830 #ifdef DEBUG_XTBIN
831     printf("send_xembed_message error(%d)!!!\n",errorcode);
832 #endif
833   }
834 }
835 
836 static int
error_handler(Display * display,XErrorEvent * error)837 error_handler(Display *display, XErrorEvent *error)
838 {
839   trapped_error_code = error->error_code;
840   return 0;
841 }
842 
843 static void
trap_errors(void)844 trap_errors(void)
845 {
846   trapped_error_code =0;
847   old_error_handler = XSetErrorHandler(error_handler);
848 }
849 
850 static int
untrap_error(void)851 untrap_error(void)
852 {
853   XSetErrorHandler(old_error_handler);
854   if(trapped_error_code) {
855 #ifdef DEBUG_XTBIN
856     printf("Get X Window Error = %d\n", trapped_error_code);
857 #endif
858   }
859   return trapped_error_code;
860 }
861 
862 static void
xt_client_focus_listener(Widget w,XtPointer user_data,XEvent * event)863 xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event)
864 {
865   Display *dpy = XtDisplay(w);
866   XtClient *xtclient = user_data;
867   Window win = XtWindow(w);
868 
869   switch(event->type)
870     {
871     case CreateNotify:
872       if(event->xcreatewindow.parent == win) {
873         Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window);
874         if (child)
875           xt_add_focus_listener_tree(child, user_data);
876       }
877       break;
878     case DestroyNotify:
879       xt_remove_focus_listener( w, user_data);
880       break;
881     case ReparentNotify:
882       if(event->xreparent.parent == win) {
883         /* I am the new parent */
884         Widget child=XtWindowToWidget(dpy, event->xreparent.window);
885         if (child)
886           xt_add_focus_listener_tree( child, user_data);
887       }
888       else if(event->xreparent.window == win) {
889         /* I am the new child */
890       }
891       else {
892         /* I am the old parent */
893       }
894       break;
895     case ButtonRelease:
896 #if 0
897       XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time);
898 #endif
899       send_xembed_message ( xtclient,
900                             XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
901       break;
902     default:
903       break;
904     } /* End of switch(event->type) */
905 }
906 
907 static void
xt_add_focus_listener(Widget w,XtPointer user_data)908 xt_add_focus_listener( Widget w, XtPointer user_data)
909 {
910   XWindowAttributes attr;
911   long eventmask;
912   XtClient *xtclient = user_data;
913 
914   trap_errors ();
915   XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr);
916   eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask;
917   XSelectInput(XtDisplay(w),
918                XtWindow(w),
919                eventmask);
920 
921   XtAddEventHandler(w,
922                     SubstructureNotifyMask | ButtonReleaseMask,
923                     TRUE,
924                     (XtEventHandler)xt_client_focus_listener,
925                     xtclient);
926   untrap_error();
927 }
928 
929 static void
xt_remove_focus_listener(Widget w,XtPointer user_data)930 xt_remove_focus_listener(Widget w, XtPointer user_data)
931 {
932   trap_errors ();
933   XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE,
934                       (XtEventHandler)xt_client_focus_listener, user_data);
935 
936   untrap_error();
937 }
938 
939 static void
xt_add_focus_listener_tree(Widget treeroot,XtPointer user_data)940 xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data)
941 {
942   Window win = XtWindow(treeroot);
943   Window *children;
944   Window root, parent;
945   Display *dpy = XtDisplay(treeroot);
946   unsigned int i, nchildren;
947 
948   /* ensure we don't add more than once */
949   xt_remove_focus_listener( treeroot, user_data);
950   xt_add_focus_listener( treeroot, user_data);
951   trap_errors();
952   if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) {
953     untrap_error();
954     return;
955   }
956 
957   if(untrap_error())
958     return;
959 
960   for(i=0; i<nchildren; ++i) {
961     Widget child = XtWindowToWidget(dpy, children[i]);
962     if (child)
963       xt_add_focus_listener_tree( child, user_data);
964   }
965   XFree((void*)children);
966 
967   return;
968 }
969