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#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
331GtkWidget*
332gtk_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
424void
425gtk_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
436void
437gtk_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
471static void
472gtk_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
494static void
495gtk_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 */
537static void
538xt_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*  */
579static void
580xt_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
655static void
656xt_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
674static void
675xt_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
685static void
686xt_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
701static void
702xt_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
762static void
763xt_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
801static void
802send_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
836static int
837error_handler(Display *display, XErrorEvent *error)
838{
839  trapped_error_code = error->error_code;
840  return 0;
841}
842
843static void
844trap_errors(void)
845{
846  trapped_error_code =0;
847  old_error_handler = XSetErrorHandler(error_handler);
848}
849
850static int
851untrap_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
862static void
863xt_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
907static void
908xt_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
929static void
930xt_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
939static void
940xt_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