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