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
73static void            gtk_xtbin_class_init (GtkXtBinClass *klass);
74static void            gtk_xtbin_init       (GtkXtBin      *xtbin);
75static void            gtk_xtbin_realize    (GtkWidget      *widget);
76static void            gtk_xtbin_unrealize    (GtkWidget      *widget);
77static void            gtk_xtbin_dispose    (GObject      *object);
78
79/* Xt aware XEmbed */
80static void       xt_client_init      (XtClient * xtclient,
81                                       Visual *xtvisual,
82                                       Colormap xtcolormap,
83                                       int xtdepth);
84static void       xt_client_create    (XtClient * xtclient,
85                                       Window embeder,
86                                       int height,
87                                       int width );
88static void       xt_client_unrealize (XtClient* xtclient);
89static void       xt_client_destroy   (XtClient* xtclient);
90static void       xt_client_set_info  (Widget xtplug,
91                                       unsigned long flags);
92static void       xt_client_event_handler (Widget w,
93                                           XtPointer client_data,
94                                           XEvent *event);
95static void       xt_client_handle_xembed_message (Widget w,
96                                                   XtPointer client_data,
97                                                   XEvent *event);
98static void       xt_client_focus_listener       (Widget w,
99                                                   XtPointer user_data,
100                                                   XEvent *event);
101static void       xt_add_focus_listener( Widget w, XtPointer user_data );
102static void       xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data);
103static void       xt_remove_focus_listener(Widget w, XtPointer user_data);
104static void       send_xembed_message (XtClient *xtclient,
105                                       long message,
106                                       long detail,
107                                       long data1,
108                                       long data2,
109                                       long time);
110static int        error_handler       (Display *display,
111                                       XErrorEvent *error);
112/* For error trap of XEmbed */
113static void       trap_errors(void);
114static int        untrap_error(void);
115static int        (*old_error_handler) (Display *, XErrorEvent *);
116static int        trapped_error_code = 0;
117
118static GtkWidgetClass *parent_class = NULL;
119
120static Display         *xtdisplay = NULL;
121static String          *fallback = NULL;
122static gboolean         xt_is_initialized = FALSE;
123static gint             num_widgets = 0;
124
125static GPollFD          xt_event_poll_fd;
126static gint             xt_polling_timer_id = 0;
127static guint            tag = 0;
128
129static gboolean
130xt_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
142static gboolean
143xt_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
158static gboolean
159xt_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
184typedef void (*GSourceFuncsFinalize) (GSource* source);
185
186static 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
195static gboolean
196xt_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
216GType
217gtk_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
245static void
246gtk_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
261static void
262gtk_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
271static void
272gtk_xtbin_realize (GtkWidget *widget)
273{
274  GtkXtBin     *xtbin;
275  GtkAllocation allocation = { 0, 0, 200, 200 };
276  GtkAllocation widget_allocation;
277
278#ifdef DEBUG_XTBIN
279  printf("gtk_xtbin_realize()\n");
280#endif
281
282  g_return_if_fail (GTK_IS_XTBIN (widget));
283
284  xtbin = GTK_XTBIN (widget);
285
286  /* caculate the allocation before realize */
287  allocation.width = gdk_window_get_width(xtbin->parent_window);
288  allocation.height = gdk_window_get_height(xtbin->parent_window);
289  gtk_widget_size_allocate (widget, &allocation);
290
291#ifdef DEBUG_XTBIN
292  printf("initial allocation %d %d %d %d\n", x, y, w, h);
293#endif
294
295  gtk_widget_get_allocation(widget, &widget_allocation);
296  xtbin->width = widget_allocation.width;
297  xtbin->height = widget_allocation.height;
298
299  /* use GtkSocket's realize */
300  (*GTK_WIDGET_CLASS(parent_class)->realize)(widget);
301
302  /* create the Xt client widget */
303  xt_client_create(&(xtbin->xtclient),
304       gtk_socket_get_id(GTK_SOCKET(xtbin)),
305       xtbin->height,
306       xtbin->width);
307  xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget);
308
309  gdk_flush();
310
311  /* now that we have created the xt client, add it to the socket. */
312  gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow);
313}
314
315
316
317GtkWidget*
318gtk_xtbin_new (GtkWidget *parent_widget, String *f)
319{
320  GtkXtBin *xtbin;
321  gpointer user_data;
322  GdkScreen *screen;
323  GdkVisual* visual;
324  Colormap colormap;
325  GdkWindow* parent_window = gtk_widget_get_window(parent_widget);
326
327  assert(parent_window != NULL);
328  xtbin = g_object_new (GTK_TYPE_XTBIN, NULL);
329
330  if (!xtbin)
331    return (GtkWidget*)NULL;
332
333  if (f)
334    fallback = f;
335
336  /* Initialize the Xt toolkit */
337  xtbin->parent_window = parent_window;
338
339  screen = gtk_widget_get_screen(parent_widget);
340  visual = gdk_screen_get_system_visual(screen);
341  colormap = XCreateColormap(GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(screen)),
342                             GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)),
343                             GDK_VISUAL_XVISUAL(visual), AllocNone);
344
345  xt_client_init(&(xtbin->xtclient),
346                 GDK_VISUAL_XVISUAL(visual),
347                 colormap,
348                 gdk_visual_get_depth(visual));
349
350  if (!xtbin->xtclient.xtdisplay) {
351    /* If XtOpenDisplay failed, we can't go any further.
352     *  Bail out.
353     */
354#ifdef DEBUG_XTBIN
355    printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
356#endif
357    g_free (xtbin);
358    return (GtkWidget *)NULL;
359  }
360
361  /* If this is the first running widget, hook this display into the
362     mainloop */
363  if (0 == num_widgets) {
364    int           cnumber;
365    /*
366     * hook Xt event loop into the glib event loop.
367     */
368
369    /* the assumption is that gtk_init has already been called */
370    GSource* gs = g_source_new(&xt_event_funcs, sizeof(GSource));
371      if (!gs) {
372       return NULL;
373      }
374
375    g_source_set_priority(gs, GDK_PRIORITY_EVENTS);
376    g_source_set_can_recurse(gs, TRUE);
377    tag = g_source_attach(gs, (GMainContext*)NULL);
378#ifdef VMS
379    cnumber = XConnectionNumber(xtdisplay);
380#else
381    cnumber = ConnectionNumber(xtdisplay);
382#endif
383    xt_event_poll_fd.fd = cnumber;
384    xt_event_poll_fd.events = G_IO_IN;
385    xt_event_poll_fd.revents = 0;    /* hmm... is this correct? */
386
387    g_main_context_add_poll ((GMainContext*)NULL,
388                             &xt_event_poll_fd,
389                             G_PRIORITY_LOW);
390    /* add a timer so that we can poll and process Xt timers */
391    xt_polling_timer_id =
392      g_timeout_add(25,
393                      (GSourceFunc)xt_event_polling_timer_callback,
394                      xtdisplay);
395  }
396
397  /* Bump up our usage count */
398  num_widgets++;
399
400  /* Build the hierachy */
401  xtbin->xtdisplay = xtbin->xtclient.xtdisplay;
402  gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window);
403  gdk_window_get_user_data(xtbin->parent_window, &user_data);
404  if (user_data)
405    gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin));
406
407  return GTK_WIDGET (xtbin);
408}
409
410void
411gtk_xtbin_set_position (GtkXtBin *xtbin,
412                        gint       x,
413                        gint       y)
414{
415  xtbin->x = x;
416  xtbin->y = y;
417
418  if (gtk_widget_get_realized (GTK_WIDGET(xtbin)))
419    gdk_window_move (gtk_widget_get_window(GTK_WIDGET (xtbin)), x, y);
420}
421
422void
423gtk_xtbin_resize (GtkWidget *widget,
424                  gint       width,
425                  gint       height)
426{
427  Arg args[2];
428  GtkXtBin *xtbin = GTK_XTBIN (widget);
429  GtkAllocation allocation;
430
431#ifdef DEBUG_XTBIN
432  printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height);
433#endif
434
435  xtbin->height = height;
436  xtbin->width  = width;
437
438  // Avoid BadValue errors in XtSetValues
439  if (height <= 0 || width <=0) {
440    height = 1;
441    width = 1;
442  }
443  XtSetArg(args[0], XtNheight, height);
444  XtSetArg(args[1], XtNwidth,  width);
445  XtSetValues(xtbin->xtclient.top_widget, args, 2);
446
447  /* we need to send a size allocate so the socket knows about the
448     size changes */
449  allocation.x = xtbin->x;
450  allocation.y = xtbin->y;
451  allocation.width = xtbin->width;
452  allocation.height = xtbin->height;
453
454  gtk_widget_size_allocate(widget, &allocation);
455}
456
457static void
458gtk_xtbin_unrealize (GtkWidget *object)
459{
460  GtkXtBin *xtbin;
461  GtkWidget *widget;
462
463#ifdef DEBUG_XTBIN
464  printf("gtk_xtbin_unrealize()\n");
465#endif
466
467  /* gtk_object_destroy() will already hold a refcount on object
468   */
469  xtbin = GTK_XTBIN(object);
470  widget = GTK_WIDGET(object);
471
472  gtk_widget_set_visible(widget, FALSE);
473  if (gtk_widget_get_realized (widget)) {
474    xt_client_unrealize(&(xtbin->xtclient));
475  }
476
477  (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget);
478}
479
480static void
481gtk_xtbin_dispose (GObject *object)
482{
483  GtkXtBin *xtbin;
484
485#ifdef DEBUG_XTBIN
486  printf("gtk_xtbin_destroy()\n");
487#endif
488
489  g_return_if_fail (object != NULL);
490  g_return_if_fail (GTK_IS_XTBIN (object));
491
492  xtbin = GTK_XTBIN (object);
493
494  if(xtbin->xtwindow) {
495    /* remove the event handler */
496    xt_client_destroy(&(xtbin->xtclient));
497    xtbin->xtwindow = 0;
498
499    num_widgets--; /* reduce our usage count */
500
501    /* If this is the last running widget, remove the Xt display
502       connection from the mainloop */
503    if (0 == num_widgets) {
504#ifdef DEBUG_XTBIN
505      printf("removing the Xt connection from the main loop\n");
506#endif
507      g_main_context_remove_poll((GMainContext*)NULL, &xt_event_poll_fd);
508      g_source_remove(tag);
509
510      g_source_remove(xt_polling_timer_id);
511      xt_polling_timer_id = 0;
512    }
513  }
514
515  G_OBJECT_CLASS(parent_class)->dispose(object);
516}
517
518/*
519* Following is the implementation of Xt XEmbedded for client side
520*/
521
522/* Initial Xt plugin */
523static void
524xt_client_init( XtClient * xtclient,
525                Visual *xtvisual,
526                Colormap xtcolormap,
527                int xtdepth)
528{
529  XtAppContext  app_context;
530  char         *mArgv[1];
531  int           mArgc = 0;
532
533  /*
534   * Initialize Xt stuff
535   */
536  xtclient->top_widget = NULL;
537  xtclient->child_widget = NULL;
538  xtclient->xtdisplay  = NULL;
539  xtclient->xtvisual   = NULL;
540  xtclient->xtcolormap = 0;
541  xtclient->xtdepth = 0;
542
543  if (!xt_is_initialized) {
544#ifdef DEBUG_XTBIN
545    printf("starting up Xt stuff\n");
546#endif
547    XtToolkitInitialize();
548    app_context = XtCreateApplicationContext();
549    if (fallback)
550      XtAppSetFallbackResources(app_context, fallback);
551
552    xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL,
553                            "Wrapper", NULL, 0, &mArgc, mArgv);
554    if (xtdisplay)
555      xt_is_initialized = TRUE;
556  }
557  xtclient->xtdisplay  = xtdisplay;
558  xtclient->xtvisual   = xtvisual;
559  xtclient->xtcolormap = xtcolormap;
560  xtclient->xtdepth    = xtdepth;
561}
562
563/* Create the Xt client widgets
564*  */
565static void
566xt_client_create ( XtClient* xtclient ,
567                   Window embedderid,
568                   int height,
569                   int width )
570{
571  int           n;
572  Arg           args[6];
573  Widget        child_widget;
574  Widget        top_widget;
575
576#ifdef DEBUG_XTBIN
577  printf("xt_client_create() \n");
578#endif
579  top_widget = XtAppCreateShell("drawingArea", "Wrapper",
580                                applicationShellWidgetClass,
581                                xtclient->xtdisplay,
582                                NULL, 0);
583  xtclient->top_widget = top_widget;
584
585  /* set size of Xt window */
586  n = 0;
587  XtSetArg(args[n], XtNheight,   height);n++;
588  XtSetArg(args[n], XtNwidth,    width);n++;
589  XtSetValues(top_widget, args, n);
590
591  child_widget = XtVaCreateWidget("form",
592                                  compositeWidgetClass,
593                                  top_widget, NULL);
594
595  n = 0;
596  XtSetArg(args[n], XtNheight,   height);n++;
597  XtSetArg(args[n], XtNwidth,    width);n++;
598  XtSetArg(args[n], XtNvisual,   xtclient->xtvisual ); n++;
599  XtSetArg(args[n], XtNdepth,    xtclient->xtdepth ); n++;
600  XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++;
601  XtSetArg(args[n], XtNborderWidth, 0); n++;
602  XtSetValues(child_widget, args, n);
603
604  XSync(xtclient->xtdisplay, FALSE);
605  xtclient->oldwindow = top_widget->core.window;
606  top_widget->core.window = embedderid;
607
608  /* this little trick seems to finish initializing the widget */
609#if XlibSpecificationRelease >= 6
610  XtRegisterDrawable(xtclient->xtdisplay,
611                     embedderid,
612                     top_widget);
613#else
614  _XtRegisterWindow( embedderid,
615                     top_widget);
616#endif
617  XtRealizeWidget(child_widget);
618
619  /* listen to all Xt events */
620  XSelectInput(xtclient->xtdisplay,
621               XtWindow(top_widget),
622               0x0FFFFF);
623  xt_client_set_info (child_widget, 0);
624
625  XtManageChild(child_widget);
626  xtclient->child_widget = child_widget;
627
628  /* set the event handler */
629  XtAddEventHandler(child_widget,
630                    0x0FFFFF & ~ResizeRedirectMask,
631                    TRUE,
632                    (XtEventHandler)xt_client_event_handler, xtclient);
633  XtAddEventHandler(child_widget,
634                    SubstructureNotifyMask | ButtonReleaseMask,
635                    TRUE,
636                    (XtEventHandler)xt_client_focus_listener,
637                    xtclient);
638  XSync(xtclient->xtdisplay, FALSE);
639}
640
641static void
642xt_client_unrealize ( XtClient* xtclient )
643{
644#if XlibSpecificationRelease >= 6
645  XtUnregisterDrawable(xtclient->xtdisplay,
646                       xtclient->top_widget->core.window);
647#else
648  _XtUnregisterWindow(xtclient->top_widget->core.window,
649                      xtclient->top_widget);
650#endif
651
652  /* flush the queue before we returning origin top_widget->core.window
653     or we can get X error since the window is gone */
654  XSync(xtclient->xtdisplay, False);
655
656  xtclient->top_widget->core.window = xtclient->oldwindow;
657  XtUnrealizeWidget(xtclient->top_widget);
658}
659
660static void
661xt_client_destroy   (XtClient* xtclient)
662{
663  if(xtclient->top_widget) {
664    XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE,
665                         (XtEventHandler)xt_client_event_handler, xtclient);
666    XtDestroyWidget(xtclient->top_widget);
667    xtclient->top_widget = NULL;
668  }
669}
670
671static void
672xt_client_set_info (Widget xtplug, unsigned long flags)
673{
674  unsigned long buffer[2];
675
676  Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False);
677
678  buffer[1] = 0;                /* Protocol version */
679  buffer[1] = flags;
680
681  XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug),
682                   infoAtom, infoAtom, 32,
683                   PropModeReplace,
684                   (unsigned char *)buffer, 2);
685}
686
687static void
688xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event)
689{
690  XtClient *xtplug = (XtClient*)client_data;
691  switch (event->xclient.data.l[1])
692  {
693  case XEMBED_EMBEDDED_NOTIFY:
694    break;
695  case XEMBED_WINDOW_ACTIVATE:
696#ifdef DEBUG_XTBIN
697    printf("Xt client get XEMBED_WINDOW_ACTIVATE\n");
698#endif
699    break;
700  case XEMBED_WINDOW_DEACTIVATE:
701#ifdef DEBUG_XTBIN
702    printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n");
703#endif
704    break;
705  case XEMBED_MODALITY_ON:
706#ifdef DEBUG_XTBIN
707    printf("Xt client get XEMBED_MODALITY_ON\n");
708#endif
709    break;
710  case XEMBED_MODALITY_OFF:
711#ifdef DEBUG_XTBIN
712    printf("Xt client get XEMBED_MODALITY_OFF\n");
713#endif
714    break;
715  case XEMBED_FOCUS_IN:
716  case XEMBED_FOCUS_OUT:
717    {
718      XEvent xevent;
719      memset(&xevent, 0, sizeof(xevent));
720
721      if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) {
722#ifdef DEBUG_XTBIN
723        printf("XTEMBED got focus in\n");
724#endif
725        xevent.xfocus.type = FocusIn;
726      }
727      else {
728#ifdef DEBUG_XTBIN
729        printf("XTEMBED got focus out\n");
730#endif
731        xevent.xfocus.type = FocusOut;
732      }
733
734      xevent.xfocus.window = XtWindow(xtplug->child_widget);
735      xevent.xfocus.display = XtDisplay(xtplug->child_widget);
736      XSendEvent(XtDisplay(xtplug->child_widget),
737                 xevent.xfocus.window,
738                 False, NoEventMask,
739                 &xevent );
740      XSync( XtDisplay(xtplug->child_widget), False);
741    }
742    break;
743  default:
744    break;
745  } /* End of XEmbed Message */
746}
747
748static void
749xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event)
750{
751  XtClient *xtplug = (XtClient*)client_data;
752
753  switch(event->type)
754    {
755    case ClientMessage:
756      /* Handle xembed message */
757      if (event->xclient.message_type==
758                 XInternAtom (XtDisplay(xtplug->child_widget),
759                              "_XEMBED", False)) {
760        xt_client_handle_xembed_message(w, client_data, event);
761      }
762      break;
763    case ReparentNotify:
764      break;
765    case MappingNotify:
766      xt_client_set_info (w, XEMBED_MAPPED);
767      break;
768    case UnmapNotify:
769      xt_client_set_info (w, 0);
770      break;
771    case FocusIn:
772      send_xembed_message ( xtplug,
773                            XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
774      break;
775    case FocusOut:
776      break;
777    case KeyPress:
778#ifdef DEBUG_XTBIN
779      printf("Key Press Got!\n");
780#endif
781      break;
782    default:
783      break;
784    } /* End of switch(event->type) */
785}
786
787static void
788send_xembed_message (XtClient  *xtclient,
789                     long      message,
790                     long      detail,
791                     long      data1,
792                     long      data2,
793                     long      time)
794{
795  XEvent xevent;
796  Window w=XtWindow(xtclient->top_widget);
797  Display* dpy=xtclient->xtdisplay;
798  int errorcode;
799
800  memset(&xevent,0,sizeof(xevent));
801  xevent.xclient.window = w;
802  xevent.xclient.type = ClientMessage;
803  xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False);
804  xevent.xclient.format = 32;
805  xevent.xclient.data.l[0] = time;
806  xevent.xclient.data.l[1] = message;
807  xevent.xclient.data.l[2] = detail;
808  xevent.xclient.data.l[3] = data1;
809  xevent.xclient.data.l[4] = data2;
810
811  trap_errors ();
812  XSendEvent (dpy, w, False, NoEventMask, &xevent);
813  XSync (dpy,False);
814
815  if((errorcode = untrap_error())) {
816#ifdef DEBUG_XTBIN
817    printf("send_xembed_message error(%d)!!!\n",errorcode);
818#endif
819  }
820}
821
822static int
823error_handler(Display *display, XErrorEvent *error)
824{
825  trapped_error_code = error->error_code;
826  return 0;
827}
828
829static void
830trap_errors(void)
831{
832  trapped_error_code =0;
833  old_error_handler = XSetErrorHandler(error_handler);
834}
835
836static int
837untrap_error(void)
838{
839  XSetErrorHandler(old_error_handler);
840  if(trapped_error_code) {
841#ifdef DEBUG_XTBIN
842    printf("Get X Window Error = %d\n", trapped_error_code);
843#endif
844  }
845  return trapped_error_code;
846}
847
848static void
849xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event)
850{
851  Display *dpy = XtDisplay(w);
852  XtClient *xtclient = user_data;
853  Window win = XtWindow(w);
854
855  switch(event->type)
856    {
857    case CreateNotify:
858      if(event->xcreatewindow.parent == win) {
859        Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window);
860        if (child)
861          xt_add_focus_listener_tree(child, user_data);
862      }
863      break;
864    case DestroyNotify:
865      xt_remove_focus_listener( w, user_data);
866      break;
867    case ReparentNotify:
868      if(event->xreparent.parent == win) {
869        /* I am the new parent */
870        Widget child=XtWindowToWidget(dpy, event->xreparent.window);
871        if (child)
872          xt_add_focus_listener_tree( child, user_data);
873      }
874      else if(event->xreparent.window == win) {
875        /* I am the new child */
876      }
877      else {
878        /* I am the old parent */
879      }
880      break;
881    case ButtonRelease:
882#if 0
883      XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time);
884#endif
885      send_xembed_message ( xtclient,
886                            XEMBED_REQUEST_FOCUS, 0, 0, 0, 0);
887      break;
888    default:
889      break;
890    } /* End of switch(event->type) */
891}
892
893static void
894xt_add_focus_listener( Widget w, XtPointer user_data)
895{
896  XWindowAttributes attr;
897  long eventmask;
898  XtClient *xtclient = user_data;
899
900  trap_errors ();
901  XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr);
902  eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask;
903  XSelectInput(XtDisplay(w),
904               XtWindow(w),
905               eventmask);
906
907  XtAddEventHandler(w,
908                    SubstructureNotifyMask | ButtonReleaseMask,
909                    TRUE,
910                    (XtEventHandler)xt_client_focus_listener,
911                    xtclient);
912  untrap_error();
913}
914
915static void
916xt_remove_focus_listener(Widget w, XtPointer user_data)
917{
918  trap_errors ();
919  XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE,
920                      (XtEventHandler)xt_client_focus_listener, user_data);
921
922  untrap_error();
923}
924
925static void
926xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data)
927{
928  Window win = XtWindow(treeroot);
929  Window *children;
930  Window root, parent;
931  Display *dpy = XtDisplay(treeroot);
932  unsigned int i, nchildren;
933
934  /* ensure we don't add more than once */
935  xt_remove_focus_listener( treeroot, user_data);
936  xt_add_focus_listener( treeroot, user_data);
937  trap_errors();
938  if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) {
939    untrap_error();
940    return;
941  }
942
943  if(untrap_error())
944    return;
945
946  for(i=0; i<nchildren; ++i) {
947    Widget child = XtWindowToWidget(dpy, children[i]);
948    if (child)
949      xt_add_focus_listener_tree( child, user_data);
950  }
951  XFree((void*)children);
952
953  return;
954}
955