1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim:expandtab:shiftwidth=2:tabstop=2: */
4 /* ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is the Gtk2XtBin Widget Implementation.
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.
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.
38 * ***** END LICENSE BLOCK ***** */
41 * The GtkXtBin widget allows for Xt toolkit code to be used
42 * inside a GTK application.
46 #include "gtk2xtbin.h"
47 #include <gtk/gtkmain.h>
48 #include <gtk/gtkprivate.h>
53 #include <sys/types.h>
60 #include <X11/Xutil.h>
61 #include <X11/Shell.h>
62 #include <X11/Intrinsic.h>
63 #include <X11/StringDefs.h>
65 /* uncomment this if you want debugging information about widget
66 creation and destruction */
69 #define XTBIN_MAX_EVENTS 30
71 static void gtk_xtbin_class_init (GtkXtBinClass
*klass
);
72 static void gtk_xtbin_init (GtkXtBin
*xtbin
);
73 static void gtk_xtbin_realize (GtkWidget
*widget
);
74 static void gtk_xtbin_unrealize (GtkWidget
*widget
);
75 static void gtk_xtbin_destroy (GtkObject
*object
);
76 static void gtk_xtbin_shutdown (GtkObject
*object
);
79 static void xt_client_init (XtClient
* xtclient
,
83 static void xt_client_create (XtClient
* xtclient
,
87 static void xt_client_unrealize (XtClient
* xtclient
);
88 static void xt_client_destroy (XtClient
* xtclient
);
89 static void xt_client_set_info (Widget xtplug
,
91 static void xt_client_event_handler (Widget w
,
92 XtPointer client_data
,
94 static void xt_client_handle_xembed_message (Widget w
,
95 XtPointer client_data
,
97 static void xt_client_focus_listener (Widget w
,
100 static void xt_add_focus_listener( Widget w
, XtPointer user_data
);
101 static void xt_add_focus_listener_tree ( Widget treeroot
, XtPointer user_data
);
102 static void xt_remove_focus_listener(Widget w
, XtPointer user_data
);
103 static void send_xembed_message (XtClient
*xtclient
,
109 static int error_handler (Display
*display
,
111 /* For error trap of XEmbed */
112 static void trap_errors(void);
113 static int untrap_error(void);
114 static int (*old_error_handler
) (Display
*, XErrorEvent
*);
115 static int trapped_error_code
= 0;
117 static GtkWidgetClass
*parent_class
= NULL
;
119 static Display
*xtdisplay
= NULL
;
120 static String
*fallback
= NULL
;
121 static gboolean xt_is_initialized
= FALSE
;
122 static gint num_widgets
= 0;
124 static GPollFD xt_event_poll_fd
;
125 static gint xt_polling_timer_id
= 0;
126 static guint tag
= 0;
129 xt_event_prepare (GSource
* source_data
,
135 mask
= XPending(xtdisplay
);
138 return (gboolean
)mask
;
142 xt_event_check (GSource
* source_data
)
144 GDK_THREADS_ENTER ();
146 if (xt_event_poll_fd
.revents
& G_IO_IN
) {
148 mask
= XPending(xtdisplay
);
149 GDK_THREADS_LEAVE ();
150 return (gboolean
)mask
;
153 GDK_THREADS_LEAVE ();
158 xt_event_dispatch (GSource
* source_data
,
159 GSourceFunc call_back
,
166 ac
= XtDisplayToApplicationContext(xtdisplay
);
168 GDK_THREADS_ENTER ();
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
175 for (i
=0; i
< XTBIN_MAX_EVENTS
&& XPending(xtdisplay
); i
++) {
176 XtAppProcessEvent(ac
, XtIMXEvent
);
179 GDK_THREADS_LEAVE ();
184 static GSourceFuncs xt_event_funcs
= {
190 (GSourceDummyMarshal
)NULL
194 xt_event_polling_timer_callback(gpointer user_data
)
198 int eventsToProcess
= 20;
200 display
= (Display
*)user_data
;
201 ac
= XtDisplayToApplicationContext(display
);
203 /* We need to process many Xt events here. If we just process
204 one event we might starve one or more Xt consumers. On the other hand
205 this could hang the whole app if Xt events come pouring in. So process
206 up to 20 Xt events right now and save the rest for later. This is a hack,
207 but it oughta work. We *really* should have out of process plugins.
209 while (eventsToProcess
-- && XtAppPending(ac
))
210 XtAppProcessEvent(ac
, XtIMAll
);
215 gtk_xtbin_get_type (void)
217 static GtkType xtbin_type
= 0;
220 static const GtkTypeInfo xtbin_info
=
224 sizeof (GtkXtBinClass
),
225 (GtkClassInitFunc
) gtk_xtbin_class_init
,
226 (GtkObjectInitFunc
) gtk_xtbin_init
,
227 /* reserved_1 */ NULL
,
228 /* reserved_2 */ NULL
,
229 (GtkClassInitFunc
) NULL
231 xtbin_type
= gtk_type_unique (GTK_TYPE_SOCKET
, &xtbin_info
);
237 gtk_xtbin_class_init (GtkXtBinClass
*klass
)
239 GtkWidgetClass
*widget_class
;
240 GtkObjectClass
*object_class
;
242 parent_class
= gtk_type_class (GTK_TYPE_SOCKET
);
244 widget_class
= GTK_WIDGET_CLASS (klass
);
245 widget_class
->realize
= gtk_xtbin_realize
;
246 widget_class
->unrealize
= gtk_xtbin_unrealize
;
248 object_class
= GTK_OBJECT_CLASS (klass
);
249 object_class
->destroy
= gtk_xtbin_destroy
;
253 gtk_xtbin_init (GtkXtBin
*xtbin
)
255 xtbin
->xtdisplay
= NULL
;
256 xtbin
->parent_window
= NULL
;
263 gtk_xtbin_realize (GtkWidget
*widget
)
266 GtkAllocation allocation
= { 0, 0, 200, 200 };
267 gint x
, y
, w
, h
, d
; /* geometry of window */
270 printf("gtk_xtbin_realize()\n");
273 g_return_if_fail (GTK_IS_XTBIN (widget
));
275 xtbin
= GTK_XTBIN (widget
);
277 /* caculate the allocation before realize */
278 gdk_window_get_geometry(xtbin
->parent_window
, &x
, &y
, &w
, &h
, &d
);
279 allocation
.width
= w
;
280 allocation
.height
= h
;
281 gtk_widget_size_allocate (widget
, &allocation
);
284 printf("initial allocation %d %d %d %d\n", x
, y
, w
, h
);
287 xtbin
->width
= widget
->allocation
.width
;
288 xtbin
->height
= widget
->allocation
.height
;
290 /* use GtkSocket's realize */
291 (*GTK_WIDGET_CLASS(parent_class
)->realize
)(widget
);
293 /* create the Xt client widget */
294 xt_client_create(&(xtbin
->xtclient
),
295 gtk_socket_get_id(GTK_SOCKET(xtbin
)),
298 xtbin
->xtwindow
= XtWindow(xtbin
->xtclient
.child_widget
);
302 /* now that we have created the xt client, add it to the socket. */
303 gtk_socket_add_id(GTK_SOCKET(widget
), xtbin
->xtwindow
);
309 gtk_xtbin_new (GdkWindow
*parent_window
, String
* f
)
314 assert(parent_window
!= NULL
);
315 xtbin
= gtk_type_new (GTK_TYPE_XTBIN
);
318 return (GtkWidget
*)NULL
;
323 /* Initialize the Xt toolkit */
324 xtbin
->parent_window
= parent_window
;
326 xt_client_init(&(xtbin
->xtclient
),
327 GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()),
328 GDK_COLORMAP_XCOLORMAP(gdk_rgb_get_colormap()),
329 gdk_rgb_get_visual()->depth
);
331 if (!xtbin
->xtclient
.xtdisplay
) {
332 /* If XtOpenDisplay failed, we can't go any further.
336 printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
339 return (GtkWidget
*)NULL
;
342 /* If this is the first running widget, hook this display into the
344 if (0 == num_widgets
) {
347 * hook Xt event loop into the glib event loop.
350 /* the assumption is that gtk_init has already been called */
351 GSource
* gs
= g_source_new(&xt_event_funcs
, sizeof(GSource
));
356 g_source_set_priority(gs
, GDK_PRIORITY_EVENTS
);
357 g_source_set_can_recurse(gs
, TRUE
);
358 tag
= g_source_attach(gs
, (GMainContext
*)NULL
);
360 cnumber
= XConnectionNumber(xtdisplay
);
362 cnumber
= ConnectionNumber(xtdisplay
);
364 xt_event_poll_fd
.fd
= cnumber
;
365 xt_event_poll_fd
.events
= G_IO_IN
;
366 xt_event_poll_fd
.revents
= 0; /* hmm... is this correct? */
368 g_main_context_add_poll ((GMainContext
*)NULL
,
371 /* add a timer so that we can poll and process Xt timers */
372 xt_polling_timer_id
=
374 (GtkFunction
)xt_event_polling_timer_callback
,
378 /* Bump up our usage count */
381 /* Build the hierachy */
382 xtbin
->xtdisplay
= xtbin
->xtclient
.xtdisplay
;
383 gtk_widget_set_parent_window(GTK_WIDGET(xtbin
), parent_window
);
384 gdk_window_get_user_data(xtbin
->parent_window
, &user_data
);
386 gtk_container_add(GTK_CONTAINER(user_data
), GTK_WIDGET(xtbin
));
388 return GTK_WIDGET (xtbin
);
392 gtk_xtbin_set_position (GtkXtBin
*xtbin
,
399 if (GTK_WIDGET_REALIZED (xtbin
))
400 gdk_window_move (GTK_WIDGET (xtbin
)->window
, x
, y
);
404 gtk_xtbin_resize (GtkWidget
*widget
,
409 GtkXtBin
*xtbin
= GTK_XTBIN (widget
);
410 GtkAllocation allocation
;
413 printf("gtk_xtbin_resize %p %d %d\n", (void *)widget
, width
, height
);
416 xtbin
->height
= height
;
417 xtbin
->width
= width
;
419 // Avoid BadValue errors in XtSetValues
420 if (height
<= 0 || width
<=0) {
424 XtSetArg(args
[0], XtNheight
, height
);
425 XtSetArg(args
[1], XtNwidth
, width
);
426 XtSetValues(xtbin
->xtclient
.top_widget
, args
, 2);
428 /* we need to send a size allocate so the socket knows about the
430 allocation
.x
= xtbin
->x
;
431 allocation
.y
= xtbin
->y
;
432 allocation
.width
= xtbin
->width
;
433 allocation
.height
= xtbin
->height
;
435 gtk_widget_size_allocate(widget
, &allocation
);
439 gtk_xtbin_unrealize (GtkWidget
*object
)
445 printf("gtk_xtbin_unrealize()\n");
448 /* gtk_object_destroy() will already hold a refcount on object
450 xtbin
= GTK_XTBIN(object
);
451 widget
= GTK_WIDGET(object
);
453 GTK_WIDGET_UNSET_FLAGS (widget
, GTK_VISIBLE
);
454 if (GTK_WIDGET_REALIZED (widget
)) {
455 xt_client_unrealize(&(xtbin
->xtclient
));
458 (*GTK_WIDGET_CLASS (parent_class
)->unrealize
)(widget
);
462 gtk_xtbin_destroy (GtkObject
*object
)
467 printf("gtk_xtbin_destroy()\n");
470 g_return_if_fail (object
!= NULL
);
471 g_return_if_fail (GTK_IS_XTBIN (object
));
473 xtbin
= GTK_XTBIN (object
);
475 if(xtbin
->xtwindow
) {
476 /* remove the event handler */
477 xt_client_destroy(&(xtbin
->xtclient
));
480 num_widgets
--; /* reduce our usage count */
482 /* If this is the last running widget, remove the Xt display
483 connection from the mainloop */
484 if (0 == num_widgets
) {
486 printf("removing the Xt connection from the main loop\n");
488 g_main_context_remove_poll((GMainContext
*)NULL
, &xt_event_poll_fd
);
489 g_source_remove(tag
);
491 gtk_timeout_remove(xt_polling_timer_id
);
492 xt_polling_timer_id
= 0;
496 GTK_OBJECT_CLASS(parent_class
)->destroy(object
);
500 * Following is the implementation of Xt XEmbedded for client side
503 /* Initial Xt plugin */
505 xt_client_init( XtClient
* xtclient
,
510 XtAppContext app_context
;
515 * Initialize Xt stuff
517 xtclient
->top_widget
= NULL
;
518 xtclient
->child_widget
= NULL
;
519 xtclient
->xtdisplay
= NULL
;
520 xtclient
->xtvisual
= NULL
;
521 xtclient
->xtcolormap
= 0;
522 xtclient
->xtdepth
= 0;
524 if (!xt_is_initialized
) {
526 printf("starting up Xt stuff\n");
528 XtToolkitInitialize();
529 app_context
= XtCreateApplicationContext();
531 XtAppSetFallbackResources(app_context
, fallback
);
533 xtdisplay
= XtOpenDisplay(app_context
, gdk_get_display(), NULL
,
534 "Wrapper", NULL
, 0, &mArgc
, mArgv
);
536 xt_is_initialized
= TRUE
;
538 xtclient
->xtdisplay
= xtdisplay
;
539 xtclient
->xtvisual
= xtvisual
;
540 xtclient
->xtcolormap
= xtcolormap
;
541 xtclient
->xtdepth
= xtdepth
;
544 /* Create the Xt client widgets
547 xt_client_create ( XtClient
* xtclient
,
558 printf("xt_client_create() \n");
560 top_widget
= XtAppCreateShell("drawingArea", "Wrapper",
561 applicationShellWidgetClass
,
564 xtclient
->top_widget
= top_widget
;
566 /* set size of Xt window */
568 XtSetArg(args
[n
], XtNheight
, height
);n
++;
569 XtSetArg(args
[n
], XtNwidth
, width
);n
++;
570 XtSetValues(top_widget
, args
, n
);
572 child_widget
= XtVaCreateWidget("form",
573 compositeWidgetClass
,
577 XtSetArg(args
[n
], XtNheight
, height
);n
++;
578 XtSetArg(args
[n
], XtNwidth
, width
);n
++;
579 XtSetArg(args
[n
], XtNvisual
, xtclient
->xtvisual
); n
++;
580 XtSetArg(args
[n
], XtNdepth
, xtclient
->xtdepth
); n
++;
581 XtSetArg(args
[n
], XtNcolormap
, xtclient
->xtcolormap
); n
++;
582 XtSetArg(args
[n
], XtNborderWidth
, 0); n
++;
583 XtSetValues(child_widget
, args
, n
);
585 XSync(xtclient
->xtdisplay
, FALSE
);
586 xtclient
->oldwindow
= top_widget
->core
.window
;
587 top_widget
->core
.window
= embedderid
;
589 /* this little trick seems to finish initializing the widget */
590 #if XlibSpecificationRelease >= 6
591 XtRegisterDrawable(xtclient
->xtdisplay
,
595 _XtRegisterWindow( embedderid
,
598 XtRealizeWidget(child_widget
);
600 /* listen to all Xt events */
601 XSelectInput(xtclient
->xtdisplay
,
602 XtWindow(top_widget
),
604 xt_client_set_info (child_widget
, 0);
606 XtManageChild(child_widget
);
607 xtclient
->child_widget
= child_widget
;
609 /* set the event handler */
610 XtAddEventHandler(child_widget
,
611 0x0FFFFF & ~ResizeRedirectMask
,
613 (XtEventHandler
)xt_client_event_handler
, xtclient
);
614 XtAddEventHandler(child_widget
,
615 SubstructureNotifyMask
| ButtonReleaseMask
,
617 (XtEventHandler
)xt_client_focus_listener
,
619 XSync(xtclient
->xtdisplay
, FALSE
);
623 xt_client_unrealize ( XtClient
* xtclient
)
625 #if XlibSpecificationRelease >= 6
626 XtUnregisterDrawable(xtclient
->xtdisplay
,
627 xtclient
->top_widget
->core
.window
);
629 _XtUnregisterWindow(xtclient
->top_widget
->core
.window
,
630 xtclient
->top_widget
);
633 /* flush the queue before we returning origin top_widget->core.window
634 or we can get X error since the window is gone */
635 XSync(xtclient
->xtdisplay
, False
);
637 xtclient
->top_widget
->core
.window
= xtclient
->oldwindow
;
638 XtUnrealizeWidget(xtclient
->top_widget
);
642 xt_client_destroy (XtClient
* xtclient
)
644 if(xtclient
->top_widget
) {
645 XtRemoveEventHandler(xtclient
->child_widget
, 0x0FFFFF, TRUE
,
646 (XtEventHandler
)xt_client_event_handler
, xtclient
);
647 XtDestroyWidget(xtclient
->top_widget
);
648 xtclient
->top_widget
= NULL
;
653 xt_client_set_info (Widget xtplug
, unsigned long flags
)
655 unsigned long buffer
[2];
657 Atom infoAtom
= XInternAtom(XtDisplay(xtplug
), "_XEMBED_INFO", False
);
659 buffer
[1] = 0; /* Protocol version */
662 XChangeProperty (XtDisplay(xtplug
), XtWindow(xtplug
),
663 infoAtom
, infoAtom
, 32,
665 (unsigned char *)buffer
, 2);
669 xt_client_handle_xembed_message(Widget w
, XtPointer client_data
, XEvent
*event
)
671 XtClient
*xtplug
= (XtClient
*)client_data
;
672 switch (event
->xclient
.data
.l
[1])
674 case XEMBED_EMBEDDED_NOTIFY
:
676 case XEMBED_WINDOW_ACTIVATE
:
678 printf("Xt client get XEMBED_WINDOW_ACTIVATE\n");
681 case XEMBED_WINDOW_DEACTIVATE
:
683 printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n");
686 case XEMBED_MODALITY_ON
:
688 printf("Xt client get XEMBED_MODALITY_ON\n");
691 case XEMBED_MODALITY_OFF
:
693 printf("Xt client get XEMBED_MODALITY_OFF\n");
696 case XEMBED_FOCUS_IN
:
697 case XEMBED_FOCUS_OUT
:
700 memset(&xevent
, 0, sizeof(xevent
));
702 if(event
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) {
704 printf("XTEMBED got focus in\n");
706 xevent
.xfocus
.type
= FocusIn
;
710 printf("XTEMBED got focus out\n");
712 xevent
.xfocus
.type
= FocusOut
;
715 xevent
.xfocus
.window
= XtWindow(xtplug
->child_widget
);
716 xevent
.xfocus
.display
= XtDisplay(xtplug
->child_widget
);
717 XSendEvent(XtDisplay(xtplug
->child_widget
),
718 xevent
.xfocus
.window
,
721 XSync( XtDisplay(xtplug
->child_widget
), False
);
726 } /* End of XEmbed Message */
730 xt_client_event_handler( Widget w
, XtPointer client_data
, XEvent
*event
)
732 XtClient
*xtplug
= (XtClient
*)client_data
;
737 /* Handle xembed message */
738 if (event
->xclient
.message_type
==
739 XInternAtom (XtDisplay(xtplug
->child_widget
),
741 xt_client_handle_xembed_message(w
, client_data
, event
);
747 xt_client_set_info (w
, XEMBED_MAPPED
);
750 xt_client_set_info (w
, 0);
753 send_xembed_message ( xtplug
,
754 XEMBED_REQUEST_FOCUS
, 0, 0, 0, 0);
760 printf("Key Press Got!\n");
765 } /* End of switch(event->type) */
769 send_xembed_message (XtClient
*xtclient
,
777 Window w
=XtWindow(xtclient
->top_widget
);
778 Display
* dpy
=xtclient
->xtdisplay
;
781 memset(&xevent
,0,sizeof(xevent
));
782 xevent
.xclient
.window
= w
;
783 xevent
.xclient
.type
= ClientMessage
;
784 xevent
.xclient
.message_type
= XInternAtom(dpy
,"_XEMBED",False
);
785 xevent
.xclient
.format
= 32;
786 xevent
.xclient
.data
.l
[0] = time
;
787 xevent
.xclient
.data
.l
[1] = message
;
788 xevent
.xclient
.data
.l
[2] = detail
;
789 xevent
.xclient
.data
.l
[3] = data1
;
790 xevent
.xclient
.data
.l
[4] = data2
;
793 XSendEvent (dpy
, w
, False
, NoEventMask
, &xevent
);
796 if((errorcode
= untrap_error())) {
798 printf("send_xembed_message error(%d)!!!\n",errorcode
);
804 error_handler(Display
*display
, XErrorEvent
*error
)
806 trapped_error_code
= error
->error_code
;
813 trapped_error_code
=0;
814 old_error_handler
= XSetErrorHandler(error_handler
);
820 XSetErrorHandler(old_error_handler
);
821 if(trapped_error_code
) {
823 printf("Get X Window Error = %d\n", trapped_error_code
);
826 return trapped_error_code
;
830 xt_client_focus_listener( Widget w
, XtPointer user_data
, XEvent
*event
)
832 Display
*dpy
= XtDisplay(w
);
833 XtClient
*xtclient
= user_data
;
834 Window win
= XtWindow(w
);
839 if(event
->xcreatewindow
.parent
== win
) {
840 Widget child
=XtWindowToWidget( dpy
, event
->xcreatewindow
.window
);
842 xt_add_focus_listener_tree(child
, user_data
);
846 xt_remove_focus_listener( w
, user_data
);
849 if(event
->xreparent
.parent
== win
) {
850 /* I am the new parent */
851 Widget child
=XtWindowToWidget(dpy
, event
->xreparent
.window
);
853 xt_add_focus_listener_tree( child
, user_data
);
855 else if(event
->xreparent
.window
== win
) {
856 /* I am the new child */
859 /* I am the old parent */
864 XSetInputFocus(dpy
, XtWindow(xtclient
->child_widget
), RevertToParent
, event
->xbutton
.time
);
866 send_xembed_message ( xtclient
,
867 XEMBED_REQUEST_FOCUS
, 0, 0, 0, 0);
871 } /* End of switch(event->type) */
875 xt_add_focus_listener( Widget w
, XtPointer user_data
)
877 XWindowAttributes attr
;
879 XtClient
*xtclient
= user_data
;
883 XGetWindowAttributes(XtDisplay(w
), XtWindow(w
), &attr
);
884 eventmask
= attr
.your_event_mask
| SubstructureNotifyMask
| ButtonReleaseMask
;
885 XSelectInput(XtDisplay(w
),
890 SubstructureNotifyMask
| ButtonReleaseMask
,
892 (XtEventHandler
)xt_client_focus_listener
,
898 xt_remove_focus_listener(Widget w
, XtPointer user_data
)
903 XtRemoveEventHandler(w
, SubstructureNotifyMask
| ButtonReleaseMask
, TRUE
,
904 (XtEventHandler
)xt_client_focus_listener
, user_data
);
910 xt_add_focus_listener_tree ( Widget treeroot
, XtPointer user_data
)
912 Window win
= XtWindow(treeroot
);
915 Display
*dpy
= XtDisplay(treeroot
);
916 unsigned int i
, nchildren
;
918 /* ensure we don't add more than once */
919 xt_remove_focus_listener( treeroot
, user_data
);
920 xt_add_focus_listener( treeroot
, user_data
);
922 if(!XQueryTree(dpy
, win
, &root
, &parent
, &children
, &nchildren
)) {
930 for(i
=0; i
<nchildren
; ++i
) {
931 Widget child
= XtWindowToWidget(dpy
, children
[i
]);
933 xt_add_focus_listener_tree( child
, user_data
);
935 XFree((void*)children
);