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