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 "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
    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
    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
    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
    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
    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
    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
    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
    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*
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    837 error_handler(Display *display, XErrorEvent *error)
    838 {
    839   trapped_error_code = error->error_code;
    840   return 0;
    841 }
    842 
    843 static void
    844 trap_errors(void)
    845 {
    846   trapped_error_code =0;
    847   old_error_handler = XSetErrorHandler(error_handler);
    848 }
    849 
    850 static int
    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
    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
    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
    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
    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