1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set expandtab shiftwidth=2 tabstop=2: */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 * The GtkXtBin widget allows for Xt toolkit code to be used
10 * inside a GTK application.
14 #include "gtk2xtbin.h"
20 #include <sys/types.h>
27 #include <X11/Xutil.h>
28 #include <X11/Shell.h>
29 #include <X11/Intrinsic.h>
30 #include <X11/StringDefs.h>
32 /* uncomment this if you want debugging information about widget
33 creation and destruction */
36 #define XTBIN_MAX_EVENTS 30
38 static void gtk_xtbin_class_init (GtkXtBinClass
*klass
);
39 static void gtk_xtbin_init (GtkXtBin
*xtbin
);
40 static void gtk_xtbin_realize (GtkWidget
*widget
);
41 static void gtk_xtbin_unrealize (GtkWidget
*widget
);
42 static void gtk_xtbin_destroy (GtkObject
*object
);
43 static void gtk_xtbin_shutdown (GtkObject
*object
);
46 static void xt_client_handle_xembed_message (Widget w
,
47 XtPointer client_data
,
49 static void xt_add_focus_listener( Widget w
, XtPointer user_data
);
50 static void xt_add_focus_listener_tree ( Widget treeroot
, XtPointer user_data
);
51 static void xt_remove_focus_listener(Widget w
, XtPointer user_data
);
52 static void xt_client_event_handler (Widget w
, XtPointer client_data
, XEvent
*event
);
53 static void xt_client_focus_listener (Widget w
, XtPointer user_data
, XEvent
*event
);
54 static void xt_client_set_info (Widget xtplug
, unsigned long flags
);
55 static void send_xembed_message (XtClient
*xtclient
,
61 static int error_handler (Display
*display
,
63 /* For error trap of XEmbed */
64 static void trap_errors(void);
65 static int untrap_error(void);
66 static int (*old_error_handler
) (Display
*, XErrorEvent
*);
67 static int trapped_error_code
= 0;
69 static GtkWidgetClass
*parent_class
= NULL
;
71 static Display
*xtdisplay
= NULL
;
72 static String
*fallback
= NULL
;
73 static gboolean xt_is_initialized
= FALSE
;
74 static gint num_widgets
= 0;
76 static GPollFD xt_event_poll_fd
;
77 static gint xt_polling_timer_id
= 0;
81 xt_event_prepare (GSource
* source_data
,
87 mask
= XPending(xtdisplay
);
90 return (gboolean
)mask
;
94 xt_event_check (GSource
* source_data
)
98 if (xt_event_poll_fd
.revents
& G_IO_IN
) {
100 mask
= XPending(xtdisplay
);
101 GDK_THREADS_LEAVE ();
102 return (gboolean
)mask
;
105 GDK_THREADS_LEAVE ();
110 xt_event_dispatch (GSource
* source_data
,
111 GSourceFunc call_back
,
118 ac
= XtDisplayToApplicationContext(xtdisplay
);
120 GDK_THREADS_ENTER ();
122 /* Process only real X traffic here. We only look for data on the
123 * pipe, limit it to XTBIN_MAX_EVENTS and only call
124 * XtAppProcessEvent so that it will look for X events. There's no
125 * timer processing here since we already have a timer callback that
127 for (i
=0; i
< XTBIN_MAX_EVENTS
&& XPending(xtdisplay
); i
++) {
128 XtAppProcessEvent(ac
, XtIMXEvent
);
131 GDK_THREADS_LEAVE ();
136 static GSourceFuncs xt_event_funcs
= {
142 (GSourceDummyMarshal
)NULL
146 xt_event_polling_timer_callback(gpointer user_data
)
150 int eventsToProcess
= 20;
152 display
= (Display
*)user_data
;
153 ac
= XtDisplayToApplicationContext(display
);
155 /* We need to process many Xt events here. If we just process
156 one event we might starve one or more Xt consumers. On the other hand
157 this could hang the whole app if Xt events come pouring in. So process
158 up to 20 Xt events right now and save the rest for later. This is a hack,
159 but it oughta work. We *really* should have out of process plugins.
161 while (eventsToProcess
-- && XtAppPending(ac
))
162 XtAppProcessEvent(ac
, XtIMAll
);
167 gtk_xtbin_get_type (void)
169 static GType xtbin_type
= 0;
172 static const GTypeInfo xtbin_info
=
174 sizeof (GtkXtBinClass
), /* class_size */
175 NULL
, /* base_init */
176 NULL
, /* base_finalize */
177 (GClassInitFunc
) gtk_xtbin_class_init
, /* class_init */
178 NULL
, /* class_finalize */
179 NULL
, /* class_data */
180 sizeof (GtkXtBin
), /* instance_size */
182 (GInstanceInitFunc
) gtk_xtbin_init
, /* instance_init */
183 NULL
/* value_table */
185 xtbin_type
= g_type_register_static(GTK_TYPE_SOCKET
, "GtkXtBin",
192 gtk_xtbin_class_init (GtkXtBinClass
*klass
)
194 GtkWidgetClass
*widget_class
;
195 GtkObjectClass
*object_class
;
197 parent_class
= g_type_class_peek_parent(klass
);
199 widget_class
= GTK_WIDGET_CLASS (klass
);
200 widget_class
->realize
= gtk_xtbin_realize
;
201 widget_class
->unrealize
= gtk_xtbin_unrealize
;
203 object_class
= GTK_OBJECT_CLASS (klass
);
204 object_class
->destroy
= gtk_xtbin_destroy
;
208 gtk_xtbin_init (GtkXtBin
*xtbin
)
210 xtbin
->xtdisplay
= NULL
;
211 xtbin
->parent_window
= NULL
;
216 gtk_xtbin_realize (GtkWidget
*widget
)
219 GtkAllocation allocation
= { 0, 0, 200, 200 };
220 gint x
, y
, w
, h
, d
; /* geometry of window */
223 printf("gtk_xtbin_realize()\n");
226 g_return_if_fail (GTK_IS_XTBIN (widget
));
228 xtbin
= GTK_XTBIN (widget
);
230 /* caculate the allocation before realize */
231 gdk_window_get_geometry(xtbin
->parent_window
, &x
, &y
, &w
, &h
, &d
);
232 allocation
.width
= w
;
233 allocation
.height
= h
;
234 gtk_widget_size_allocate (widget
, &allocation
);
237 printf("initial allocation %d %d %d %d\n", x
, y
, w
, h
);
240 /* use GtkSocket's realize */
241 (*GTK_WIDGET_CLASS(parent_class
)->realize
)(widget
);
243 /* create the Xt client widget */
244 xt_client_create(&(xtbin
->xtclient
),
245 gtk_socket_get_id(GTK_SOCKET(xtbin
)),
247 xtbin
->xtwindow
= XtWindow(xtbin
->xtclient
.child_widget
);
251 /* now that we have created the xt client, add it to the socket. */
252 gtk_socket_add_id(GTK_SOCKET(widget
), xtbin
->xtwindow
);
258 gtk_xtbin_new (GdkWindow
*parent_window
, String
* f
)
263 assert(parent_window
!= NULL
);
264 xtbin
= g_object_new (GTK_TYPE_XTBIN
, NULL
);
267 return (GtkWidget
*)NULL
;
272 /* Initialize the Xt toolkit */
273 xtbin
->parent_window
= parent_window
;
275 xt_client_init(&(xtbin
->xtclient
),
276 GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()),
277 GDK_COLORMAP_XCOLORMAP(gdk_rgb_get_colormap()),
278 gdk_rgb_get_visual()->depth
);
280 if (!xtbin
->xtclient
.xtdisplay
) {
281 /* If XtOpenDisplay failed, we can't go any further.
285 printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
288 return (GtkWidget
*)NULL
;
291 /* Launch X event loop */
292 xt_client_xloop_create();
294 /* Build the hierachy */
295 xtbin
->xtdisplay
= xtbin
->xtclient
.xtdisplay
;
296 gtk_widget_set_parent_window(GTK_WIDGET(xtbin
), parent_window
);
297 gdk_window_get_user_data(xtbin
->parent_window
, &user_data
);
299 gtk_container_add(GTK_CONTAINER(user_data
), GTK_WIDGET(xtbin
));
301 /* This GtkSocket has a visible window, but the Xt plug will cover this
302 * window. Normally GtkSockets let the X server paint their background and
303 * this would happen immediately (before the plug is mapped). Setting the
304 * background to None prevents the server from painting this window,
307 gtk_widget_realize(GTK_WIDGET(xtbin
));
308 gdk_window_set_back_pixmap(GTK_WIDGET(xtbin
)->window
, NULL
, FALSE
);
310 return GTK_WIDGET (xtbin
);
314 gtk_xtbin_unrealize (GtkWidget
*object
)
320 printf("gtk_xtbin_unrealize()\n");
323 /* gtk_object_destroy() will already hold a refcount on object
325 xtbin
= GTK_XTBIN(object
);
326 widget
= GTK_WIDGET(object
);
328 GTK_WIDGET_UNSET_FLAGS (widget
, GTK_VISIBLE
);
329 if (GTK_WIDGET_REALIZED (widget
)) {
330 xt_client_unrealize(&(xtbin
->xtclient
));
333 (*GTK_WIDGET_CLASS (parent_class
)->unrealize
)(widget
);
337 gtk_xtbin_destroy (GtkObject
*object
)
342 printf("gtk_xtbin_destroy()\n");
345 g_return_if_fail (object
!= NULL
);
346 g_return_if_fail (GTK_IS_XTBIN (object
));
348 xtbin
= GTK_XTBIN (object
);
350 if(xtbin
->xtwindow
) {
351 /* remove the event handler */
352 xt_client_destroy(&(xtbin
->xtclient
));
355 /* stop X event loop */
356 xt_client_xloop_destroy();
359 GTK_OBJECT_CLASS(parent_class
)->destroy(object
);
363 * Following is the implementation of Xt XEmbedded for client side
366 /* Initial Xt plugin */
368 xt_client_init( XtClient
* xtclient
,
373 XtAppContext app_context
;
378 * Initialize Xt stuff
380 xtclient
->top_widget
= NULL
;
381 xtclient
->child_widget
= NULL
;
382 xtclient
->xtdisplay
= NULL
;
383 xtclient
->xtvisual
= NULL
;
384 xtclient
->xtcolormap
= 0;
385 xtclient
->xtdepth
= 0;
387 if (!xt_is_initialized
) {
389 printf("starting up Xt stuff\n");
391 XtToolkitInitialize();
392 app_context
= XtCreateApplicationContext();
394 XtAppSetFallbackResources(app_context
, fallback
);
396 xtdisplay
= XtOpenDisplay(app_context
, gdk_get_display(), NULL
,
397 "Wrapper", NULL
, 0, &mArgc
, mArgv
);
399 xt_is_initialized
= TRUE
;
401 xtclient
->xtdisplay
= xtdisplay
;
402 xtclient
->xtvisual
= xtvisual
;
403 xtclient
->xtcolormap
= xtcolormap
;
404 xtclient
->xtdepth
= xtdepth
;
408 xt_client_xloop_create(void)
410 /* If this is the first running widget, hook this display into the
412 if (0 == num_widgets
) {
416 /* Set up xtdisplay in case we're missing one */
418 (void)xt_client_get_display();
422 * hook Xt event loop into the glib event loop.
424 /* the assumption is that gtk_init has already been called */
425 gs
= g_source_new(&xt_event_funcs
, sizeof(GSource
));
430 g_source_set_priority(gs
, GDK_PRIORITY_EVENTS
);
431 g_source_set_can_recurse(gs
, TRUE
);
432 tag
= g_source_attach(gs
, (GMainContext
*)NULL
);
435 cnumber
= XConnectionNumber(xtdisplay
);
437 cnumber
= ConnectionNumber(xtdisplay
);
439 xt_event_poll_fd
.fd
= cnumber
;
440 xt_event_poll_fd
.events
= G_IO_IN
;
441 xt_event_poll_fd
.revents
= 0; /* hmm... is this correct? */
443 g_main_context_add_poll ((GMainContext
*)NULL
,
446 /* add a timer so that we can poll and process Xt timers */
447 xt_polling_timer_id
=
449 (GtkFunction
)xt_event_polling_timer_callback
,
453 /* Bump up our usage count */
458 xt_client_xloop_destroy(void)
460 num_widgets
--; /* reduce our usage count */
462 /* If this is the last running widget, remove the Xt display
463 connection from the mainloop */
464 if (0 == num_widgets
) {
466 printf("removing the Xt connection from the main loop\n");
468 g_main_context_remove_poll((GMainContext
*)NULL
, &xt_event_poll_fd
);
469 g_source_remove(tag
);
471 g_source_remove(xt_polling_timer_id
);
472 xt_polling_timer_id
= 0;
476 /* Get Xt Client display */
478 xt_client_get_display(void)
482 xt_client_init(&tmp
,NULL
,0,0);
487 /* Create the Xt client widgets
490 xt_client_create ( XtClient
* xtclient
,
501 printf("xt_client_create() \n");
503 top_widget
= XtAppCreateShell("drawingArea", "Wrapper",
504 applicationShellWidgetClass
,
507 xtclient
->top_widget
= top_widget
;
509 /* set size of Xt window */
511 XtSetArg(args
[n
], XtNheight
, height
);n
++;
512 XtSetArg(args
[n
], XtNwidth
, width
);n
++;
513 XtSetValues(top_widget
, args
, n
);
515 child_widget
= XtVaCreateWidget("form",
516 compositeWidgetClass
,
520 XtSetArg(args
[n
], XtNheight
, height
);n
++;
521 XtSetArg(args
[n
], XtNwidth
, width
);n
++;
522 XtSetArg(args
[n
], XtNvisual
, xtclient
->xtvisual
); n
++;
523 XtSetArg(args
[n
], XtNdepth
, xtclient
->xtdepth
); n
++;
524 XtSetArg(args
[n
], XtNcolormap
, xtclient
->xtcolormap
); n
++;
525 XtSetArg(args
[n
], XtNborderWidth
, 0); n
++;
526 XtSetValues(child_widget
, args
, n
);
528 XSync(xtclient
->xtdisplay
, FALSE
);
529 xtclient
->oldwindow
= top_widget
->core
.window
;
530 top_widget
->core
.window
= embedderid
;
532 /* this little trick seems to finish initializing the widget */
533 #if XlibSpecificationRelease >= 6
534 XtRegisterDrawable(xtclient
->xtdisplay
,
538 _XtRegisterWindow( embedderid
,
541 XtRealizeWidget(child_widget
);
543 /* listen to all Xt events */
544 XSelectInput(xtclient
->xtdisplay
,
546 XtBuildEventMask(top_widget
));
547 xt_client_set_info (child_widget
, 0);
549 XtManageChild(child_widget
);
550 xtclient
->child_widget
= child_widget
;
552 /* set the event handler */
553 XtAddEventHandler(child_widget
,
554 StructureNotifyMask
| KeyPressMask
,
556 (XtEventHandler
)xt_client_event_handler
, xtclient
);
557 XtAddEventHandler(child_widget
,
558 SubstructureNotifyMask
| ButtonReleaseMask
,
560 (XtEventHandler
)xt_client_focus_listener
,
562 XSync(xtclient
->xtdisplay
, FALSE
);
566 xt_client_unrealize ( XtClient
* xtclient
)
568 /* Explicitly destroy the child_widget window because this is actually a
569 child of the socket window. It is not a child of top_widget's window
570 when that is destroyed. */
571 XtUnrealizeWidget(xtclient
->child_widget
);
573 #if XlibSpecificationRelease >= 6
574 XtUnregisterDrawable(xtclient
->xtdisplay
,
575 xtclient
->top_widget
->core
.window
);
577 _XtUnregisterWindow(xtclient
->top_widget
->core
.window
,
578 xtclient
->top_widget
);
581 /* flush the queue before we returning origin top_widget->core.window
582 or we can get X error since the window is gone */
583 XSync(xtclient
->xtdisplay
, False
);
585 xtclient
->top_widget
->core
.window
= xtclient
->oldwindow
;
586 XtUnrealizeWidget(xtclient
->top_widget
);
590 xt_client_destroy (XtClient
* xtclient
)
592 if(xtclient
->top_widget
) {
593 XtRemoveEventHandler(xtclient
->child_widget
,
594 StructureNotifyMask
| KeyPressMask
,
596 (XtEventHandler
)xt_client_event_handler
, xtclient
);
597 XtDestroyWidget(xtclient
->top_widget
);
598 xtclient
->top_widget
= NULL
;
603 xt_client_set_info (Widget xtplug
, unsigned long flags
)
605 unsigned long buffer
[2];
607 Atom infoAtom
= XInternAtom(XtDisplay(xtplug
), "_XEMBED_INFO", False
);
609 buffer
[1] = 0; /* Protocol version */
612 XChangeProperty (XtDisplay(xtplug
), XtWindow(xtplug
),
613 infoAtom
, infoAtom
, 32,
615 (unsigned char *)buffer
, 2);
619 xt_client_handle_xembed_message(Widget w
, XtPointer client_data
, XEvent
*event
)
621 XtClient
*xtplug
= (XtClient
*)client_data
;
622 switch (event
->xclient
.data
.l
[1])
624 case XEMBED_EMBEDDED_NOTIFY
:
626 case XEMBED_WINDOW_ACTIVATE
:
628 printf("Xt client get XEMBED_WINDOW_ACTIVATE\n");
631 case XEMBED_WINDOW_DEACTIVATE
:
633 printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n");
636 case XEMBED_MODALITY_ON
:
638 printf("Xt client get XEMBED_MODALITY_ON\n");
641 case XEMBED_MODALITY_OFF
:
643 printf("Xt client get XEMBED_MODALITY_OFF\n");
646 case XEMBED_FOCUS_IN
:
647 case XEMBED_FOCUS_OUT
:
650 memset(&xevent
, 0, sizeof(xevent
));
652 if(event
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) {
654 printf("XTEMBED got focus in\n");
656 xevent
.xfocus
.type
= FocusIn
;
660 printf("XTEMBED got focus out\n");
662 xevent
.xfocus
.type
= FocusOut
;
665 xevent
.xfocus
.window
= XtWindow(xtplug
->child_widget
);
666 xevent
.xfocus
.display
= XtDisplay(xtplug
->child_widget
);
667 XSendEvent(XtDisplay(xtplug
->child_widget
),
668 xevent
.xfocus
.window
,
671 XSync( XtDisplay(xtplug
->child_widget
), False
);
676 } /* End of XEmbed Message */
680 xt_client_event_handler( Widget w
, XtPointer client_data
, XEvent
*event
)
682 XtClient
*xtplug
= (XtClient
*)client_data
;
687 /* Handle xembed message */
688 if (event
->xclient
.message_type
==
689 XInternAtom (XtDisplay(xtplug
->child_widget
),
691 xt_client_handle_xembed_message(w
, client_data
, event
);
697 xt_client_set_info (w
, XEMBED_MAPPED
);
700 xt_client_set_info (w
, 0);
704 printf("Key Press Got!\n");
709 } /* End of switch(event->type) */
713 send_xembed_message (XtClient
*xtclient
,
721 Window w
=XtWindow(xtclient
->top_widget
);
722 Display
* dpy
=xtclient
->xtdisplay
;
725 memset(&xevent
,0,sizeof(xevent
));
726 xevent
.xclient
.window
= w
;
727 xevent
.xclient
.type
= ClientMessage
;
728 xevent
.xclient
.message_type
= XInternAtom(dpy
,"_XEMBED",False
);
729 xevent
.xclient
.format
= 32;
730 xevent
.xclient
.data
.l
[0] = time
;
731 xevent
.xclient
.data
.l
[1] = message
;
732 xevent
.xclient
.data
.l
[2] = detail
;
733 xevent
.xclient
.data
.l
[3] = data1
;
734 xevent
.xclient
.data
.l
[4] = data2
;
737 XSendEvent (dpy
, w
, False
, NoEventMask
, &xevent
);
740 if((errorcode
= untrap_error())) {
742 printf("send_xembed_message error(%d)!!!\n",errorcode
);
748 error_handler(Display
*display
, XErrorEvent
*error
)
750 trapped_error_code
= error
->error_code
;
757 trapped_error_code
=0;
758 old_error_handler
= XSetErrorHandler(error_handler
);
764 XSetErrorHandler(old_error_handler
);
765 if(trapped_error_code
) {
767 printf("Get X Window Error = %d\n", trapped_error_code
);
770 return trapped_error_code
;
774 xt_client_focus_listener( Widget w
, XtPointer user_data
, XEvent
*event
)
776 Display
*dpy
= XtDisplay(w
);
777 XtClient
*xtclient
= user_data
;
778 Window win
= XtWindow(w
);
783 if(event
->xcreatewindow
.parent
== win
) {
784 Widget child
=XtWindowToWidget( dpy
, event
->xcreatewindow
.window
);
786 xt_add_focus_listener_tree(child
, user_data
);
790 xt_remove_focus_listener( w
, user_data
);
793 if(event
->xreparent
.parent
== win
) {
794 /* I am the new parent */
795 Widget child
=XtWindowToWidget(dpy
, event
->xreparent
.window
);
797 xt_add_focus_listener_tree( child
, user_data
);
799 else if(event
->xreparent
.window
== win
) {
800 /* I am the new child */
803 /* I am the old parent */
808 XSetInputFocus(dpy
, XtWindow(xtclient
->child_widget
), RevertToParent
, event
->xbutton
.time
);
810 send_xembed_message ( xtclient
,
811 XEMBED_REQUEST_FOCUS
, 0, 0, 0, 0);
815 } /* End of switch(event->type) */
819 xt_add_focus_listener( Widget w
, XtPointer user_data
)
821 XtClient
*xtclient
= user_data
;
825 SubstructureNotifyMask
| ButtonReleaseMask
,
827 (XtEventHandler
)xt_client_focus_listener
,
833 xt_remove_focus_listener(Widget w
, XtPointer user_data
)
836 XtRemoveEventHandler(w
, SubstructureNotifyMask
| ButtonReleaseMask
, FALSE
,
837 (XtEventHandler
)xt_client_focus_listener
, user_data
);
843 xt_add_focus_listener_tree ( Widget treeroot
, XtPointer user_data
)
845 Window win
= XtWindow(treeroot
);
848 Display
*dpy
= XtDisplay(treeroot
);
849 unsigned int i
, nchildren
;
851 /* ensure we don't add more than once */
852 xt_remove_focus_listener( treeroot
, user_data
);
853 xt_add_focus_listener( treeroot
, user_data
);
855 if(!XQueryTree(dpy
, win
, &root
, &parent
, &children
, &nchildren
)) {
863 for(i
=0; i
<nchildren
; ++i
) {
864 Widget child
= XtWindowToWidget(dpy
, children
[i
]);
866 xt_add_focus_listener_tree( child
, user_data
);
868 XFree((void*)children
);