/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim:expandtab:shiftwidth=2:tabstop=2: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Gtk2XtBin Widget Implementation. * * The Initial Developer of the Original Code is * Sun Microsystems, Inc. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * The GtkXtBin widget allows for Xt toolkit code to be used * inside a GTK application. */ #include "xembed.h" #include "gtk2xtbin.h" #include #include #include #include #include #include #include #include #include #include /* Xlib/Xt stuff */ #include #include #include #include #include /* uncomment this if you want debugging information about widget creation and destruction */ #undef DEBUG_XTBIN #define XTBIN_MAX_EVENTS 30 static void gtk_xtbin_class_init (GtkXtBinClass *klass); static void gtk_xtbin_init (GtkXtBin *xtbin); static void gtk_xtbin_realize (GtkWidget *widget); static void gtk_xtbin_unrealize (GtkWidget *widget); static void gtk_xtbin_destroy (GtkObject *object); static void gtk_xtbin_shutdown (GtkObject *object); /* Xt aware XEmbed */ static void xt_client_init (XtClient * xtclient, Visual *xtvisual, Colormap xtcolormap, int xtdepth); static void xt_client_create (XtClient * xtclient, Window embeder, int height, int width ); static void xt_client_unrealize (XtClient* xtclient); static void xt_client_destroy (XtClient* xtclient); static void xt_client_set_info (Widget xtplug, unsigned long flags); static void xt_client_event_handler (Widget w, XtPointer client_data, XEvent *event); static void xt_client_handle_xembed_message (Widget w, XtPointer client_data, XEvent *event); static void xt_client_focus_listener (Widget w, XtPointer user_data, XEvent *event); static void xt_add_focus_listener( Widget w, XtPointer user_data ); static void xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data); static void xt_remove_focus_listener(Widget w, XtPointer user_data); static void send_xembed_message (XtClient *xtclient, long message, long detail, long data1, long data2, long time); static int error_handler (Display *display, XErrorEvent *error); /* For error trap of XEmbed */ static void trap_errors(void); static int untrap_error(void); static int (*old_error_handler) (Display *, XErrorEvent *); static int trapped_error_code = 0; static GtkWidgetClass *parent_class = NULL; static Display *xtdisplay = NULL; static String *fallback = NULL; static gboolean xt_is_initialized = FALSE; static gint num_widgets = 0; static GPollFD xt_event_poll_fd; static gint xt_polling_timer_id = 0; static guint tag = 0; static gboolean xt_event_prepare (GSource* source_data, gint *timeout) { int mask; GDK_THREADS_ENTER(); mask = XPending(xtdisplay); GDK_THREADS_LEAVE(); return (gboolean)mask; } static gboolean xt_event_check (GSource* source_data) { GDK_THREADS_ENTER (); if (xt_event_poll_fd.revents & G_IO_IN) { int mask; mask = XPending(xtdisplay); GDK_THREADS_LEAVE (); return (gboolean)mask; } GDK_THREADS_LEAVE (); return FALSE; } static gboolean xt_event_dispatch (GSource* source_data, GSourceFunc call_back, gpointer user_data) { XEvent event; XtAppContext ac; int i = 0; ac = XtDisplayToApplicationContext(xtdisplay); GDK_THREADS_ENTER (); /* Process only real X traffic here. We only look for data on the * pipe, limit it to XTBIN_MAX_EVENTS and only call * XtAppProcessEvent so that it will look for X events. There's no * timer processing here since we already have a timer callback that * does it. */ for (i=0; i < XTBIN_MAX_EVENTS && XPending(xtdisplay); i++) { XtAppProcessEvent(ac, XtIMXEvent); } GDK_THREADS_LEAVE (); return TRUE; } static GSourceFuncs xt_event_funcs = { xt_event_prepare, xt_event_check, xt_event_dispatch, g_free, (GSourceFunc)NULL, (GSourceDummyMarshal)NULL }; static gboolean xt_event_polling_timer_callback(gpointer user_data) { Display * display; XtAppContext ac; display = (Display *)user_data; ac = XtDisplayToApplicationContext(display); /* don't starve the primary event queue - just process one event */ if (XtAppPending(ac)) XtAppProcessEvent(ac, XtIMAll); return TRUE; } GtkType gtk_xtbin_get_type (void) { static GtkType xtbin_type = 0; if (!xtbin_type) { static const GtkTypeInfo xtbin_info = { "GtkXtBin", sizeof (GtkXtBin), sizeof (GtkXtBinClass), (GtkClassInitFunc) gtk_xtbin_class_init, (GtkObjectInitFunc) gtk_xtbin_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL }; xtbin_type = gtk_type_unique (GTK_TYPE_SOCKET, &xtbin_info); } return xtbin_type; } static void gtk_xtbin_class_init (GtkXtBinClass *klass) { GtkWidgetClass *widget_class; GtkObjectClass *object_class; parent_class = gtk_type_class (GTK_TYPE_SOCKET); widget_class = GTK_WIDGET_CLASS (klass); widget_class->realize = gtk_xtbin_realize; widget_class->unrealize = gtk_xtbin_unrealize; object_class = GTK_OBJECT_CLASS (klass); object_class->destroy = gtk_xtbin_destroy; } static void gtk_xtbin_init (GtkXtBin *xtbin) { xtbin->xtdisplay = NULL; xtbin->parent_window = NULL; xtbin->xtwindow = 0; xtbin->x = 0; xtbin->y = 0; } static void gtk_xtbin_realize (GtkWidget *widget) { GtkXtBin *xtbin; GtkAllocation allocation = { 0, 0, 200, 200 }; gint x, y, w, h, d; /* geometry of window */ #ifdef DEBUG_XTBIN printf("gtk_xtbin_realize()\n"); #endif g_return_if_fail (GTK_IS_XTBIN (widget)); xtbin = GTK_XTBIN (widget); /* caculate the allocation before realize */ gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d); allocation.width = w; allocation.height = h; gtk_widget_size_allocate (widget, &allocation); #ifdef DEBUG_XTBIN printf("initial allocation %d %d %d %d\n", x, y, w, h); #endif xtbin->width = widget->allocation.width; xtbin->height = widget->allocation.height; /* use GtkSocket's realize */ (*GTK_WIDGET_CLASS(parent_class)->realize)(widget); /* create the Xt client widget */ xt_client_create(&(xtbin->xtclient), gtk_socket_get_id(GTK_SOCKET(xtbin)), xtbin->height, xtbin->width); xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget); gdk_flush(); /* now that we have created the xt client, add it to the socket. */ gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow); } GtkWidget* gtk_xtbin_new (GdkWindow *parent_window, String * f) { GtkXtBin *xtbin; gpointer user_data; assert(parent_window != NULL); xtbin = gtk_type_new (GTK_TYPE_XTBIN); if (!xtbin) return (GtkWidget*)NULL; if (f) fallback = f; /* Initialize the Xt toolkit */ xtbin->parent_window = parent_window; xt_client_init(&(xtbin->xtclient), GDK_VISUAL_XVISUAL(gdk_window_get_visual(parent_window )), GDK_COLORMAP_XCOLORMAP(gdk_window_get_colormap(parent_window)), gdk_window_get_visual(parent_window )->depth); if (!xtbin->xtclient.xtdisplay) { /* If XtOpenDisplay failed, we can't go any further. * Bail out. */ #ifdef DEBUG_XTBIN printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n"); #endif g_free (xtbin); return (GtkWidget *)NULL; } /* If this is the first running widget, hook this display into the mainloop */ if (0 == num_widgets) { int cnumber; /* * hook Xt event loop into the glib event loop. */ /* the assumption is that gtk_init has already been called */ GSource* gs = g_source_new(&xt_event_funcs, sizeof(GSource)); if (!gs) { return NULL; } g_source_set_priority(gs, GDK_PRIORITY_EVENTS); g_source_set_can_recurse(gs, TRUE); tag = g_source_attach(gs, (GMainContext*)NULL); #ifdef VMS cnumber = XConnectionNumber(xtdisplay); #else cnumber = ConnectionNumber(xtdisplay); #endif xt_event_poll_fd.fd = cnumber; xt_event_poll_fd.events = G_IO_IN; xt_event_poll_fd.revents = 0; /* hmm... is this correct? */ g_main_context_add_poll ((GMainContext*)NULL, &xt_event_poll_fd, G_PRIORITY_LOW); /* add a timer so that we can poll and process Xt timers */ xt_polling_timer_id = gtk_timeout_add(25, (GtkFunction)xt_event_polling_timer_callback, xtdisplay); } /* Bump up our usage count */ num_widgets++; /* Build the hierachy */ xtbin->xtdisplay = xtbin->xtclient.xtdisplay; gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window); gdk_window_get_user_data(xtbin->parent_window, &user_data); if (user_data) gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin)); return GTK_WIDGET (xtbin); } void gtk_xtbin_set_position (GtkXtBin *xtbin, gint x, gint y) { xtbin->x = x; xtbin->y = y; if (GTK_WIDGET_REALIZED (xtbin)) gdk_window_move (GTK_WIDGET (xtbin)->window, x, y); } void gtk_xtbin_resize (GtkWidget *widget, gint width, gint height) { Arg args[2]; GtkXtBin *xtbin = GTK_XTBIN (widget); GtkAllocation allocation; #ifdef DEBUG_XTBIN printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height); #endif XtSetArg(args[0], XtNheight, height); XtSetArg(args[1], XtNwidth, width); XtSetValues(xtbin->xtclient.top_widget, args, 2); xtbin->height = height; xtbin->width = width; } static void gtk_xtbin_unrealize (GtkWidget *object) { GtkXtBin *xtbin; GtkWidget *widget; #ifdef DEBUG_XTBIN printf("gtk_xtbin_unrealize()\n"); #endif /* gtk_object_destroy() will already hold a refcount on object */ xtbin = GTK_XTBIN(object); widget = GTK_WIDGET(object); GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE); if (GTK_WIDGET_REALIZED (widget)) { xt_client_unrealize(&(xtbin->xtclient)); } (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget); } static void gtk_xtbin_destroy (GtkObject *object) { GtkXtBin *xtbin; #ifdef DEBUG_XTBIN printf("gtk_xtbin_destroy()\n"); #endif g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_XTBIN (object)); xtbin = GTK_XTBIN (object); if(xtbin->xtwindow) { /* remove the event handler */ xt_client_destroy(&(xtbin->xtclient)); xtbin->xtwindow = 0; num_widgets--; /* reduce our usage count */ /* If this is the last running widget, remove the Xt display connection from the mainloop */ if (0 == num_widgets) { #ifdef DEBUG_XTBIN printf("removing the Xt connection from the main loop\n"); #endif g_main_context_remove_poll((GMainContext*)NULL, &xt_event_poll_fd); g_source_remove(tag); gtk_timeout_remove(xt_polling_timer_id); xt_polling_timer_id = 0; } } GTK_OBJECT_CLASS(parent_class)->destroy(object); } /* * Following is the implementation of Xt XEmbeded for client side */ /* Initial Xt plugin */ static void xt_client_init( XtClient * xtclient, Visual *xtvisual, Colormap xtcolormap, int xtdepth) { XtAppContext app_context; char *mArgv[1]; int mArgc = 0; /* * Initialize Xt stuff */ xtclient->top_widget = NULL; xtclient->child_widget = NULL; xtclient->xtdisplay = NULL; xtclient->xtvisual = NULL; xtclient->xtcolormap = 0; xtclient->xtdepth = 0; if (!xt_is_initialized) { #ifdef DEBUG_XTBIN printf("starting up Xt stuff\n"); #endif XtToolkitInitialize(); app_context = XtCreateApplicationContext(); if (fallback) XtAppSetFallbackResources(app_context, fallback); xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL, "Wrapper", NULL, 0, &mArgc, mArgv); if (xtdisplay) xt_is_initialized = TRUE; } xtclient->xtdisplay = xtdisplay; xtclient->xtvisual = xtvisual; xtclient->xtcolormap = xtcolormap; xtclient->xtdepth = xtdepth; } /* Create the Xt client widgets * */ static void xt_client_create ( XtClient* xtclient , Window embedderid, int height, int width ) { int n; Arg args[6]; Widget child_widget; Widget top_widget; #ifdef DEBUG_XTBIN printf("xt_client_create() \n"); #endif top_widget = XtAppCreateShell("drawingArea", "Wrapper", applicationShellWidgetClass, xtclient->xtdisplay, NULL, 0); xtclient->top_widget = top_widget; /* set size of Xt window */ n = 0; XtSetArg(args[n], XtNheight, height);n++; XtSetArg(args[n], XtNwidth, width);n++; XtSetValues(top_widget, args, n); child_widget = XtVaCreateWidget("form", compositeWidgetClass, top_widget, NULL); n = 0; XtSetArg(args[n], XtNheight, height);n++; XtSetArg(args[n], XtNwidth, width);n++; XtSetArg(args[n], XtNvisual, xtclient->xtvisual ); n++; XtSetArg(args[n], XtNdepth, xtclient->xtdepth ); n++; XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++; XtSetArg(args[n], XtNborderWidth, 0); n++; XtSetValues(child_widget, args, n); XSync(xtclient->xtdisplay, FALSE); xtclient->oldwindow = top_widget->core.window; top_widget->core.window = embedderid; /* this little trick seems to finish initializing the widget */ #if XlibSpecificationRelease >= 6 XtRegisterDrawable(xtclient->xtdisplay, embedderid, top_widget); #else _XtRegisterWindow( embedderid, top_widget); #endif XtRealizeWidget(child_widget); /* listen to all Xt events */ XSelectInput(xtclient->xtdisplay, XtWindow(top_widget), 0x0FFFFF); XSelectInput(xtclient->xtdisplay, XtWindow(child_widget), 0x0FFFFF); xt_client_set_info (child_widget, 0); XtManageChild(child_widget); xtclient->child_widget = child_widget; /* set the event handler */ XtAddEventHandler(child_widget, 0x0FFFFF, TRUE, (XtEventHandler)xt_client_event_handler, xtclient); XtAddEventHandler(child_widget, SubstructureNotifyMask | ButtonReleaseMask, TRUE, (XtEventHandler)xt_client_focus_listener, xtclient); XSync(xtclient->xtdisplay, FALSE); } static void xt_client_unrealize ( XtClient* xtclient ) { #if XlibSpecificationRelease >= 6 XtUnregisterDrawable(xtclient->xtdisplay, xtclient->top_widget->core.window); #else _XtUnregisterWindow(xtclient->top_widget->core.window, xtclient->top_widget); #endif /* flush the queue before we returning origin top_widget->core.window or we can get X error since the window is gone */ XSync(xtclient->xtdisplay, False); xtclient->top_widget->core.window = xtclient->oldwindow; XtUnrealizeWidget(xtclient->top_widget); } static void xt_client_destroy (XtClient* xtclient) { if(xtclient->top_widget) { XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE, (XtEventHandler)xt_client_event_handler, xtclient); XtDestroyWidget(xtclient->top_widget); xtclient->top_widget = NULL; } } static void xt_client_set_info (Widget xtplug, unsigned long flags) { unsigned long buffer[2]; Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False); buffer[1] = 0; /* Protocol version */ buffer[1] = flags; XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug), infoAtom, infoAtom, 32, PropModeReplace, (unsigned char *)buffer, 2); } static void xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event) { XtClient *xtplug = (XtClient*)client_data; switch (event->xclient.data.l[1]) { case XEMBED_EMBEDDED_NOTIFY: break; case XEMBED_WINDOW_ACTIVATE: #ifdef DEBUG_XTBIN printf("Xt client get XEMBED_WINDOW_ACTIVATE\n"); #endif break; case XEMBED_WINDOW_DEACTIVATE: #ifdef DEBUG_XTBIN printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n"); #endif break; case XEMBED_MODALITY_ON: #ifdef DEBUG_XTBIN printf("Xt client get XEMBED_MODALITY_ON\n"); #endif break; case XEMBED_MODALITY_OFF: #ifdef DEBUG_XTBIN printf("Xt client get XEMBED_MODALITY_OFF\n"); #endif break; case XEMBED_FOCUS_IN: case XEMBED_FOCUS_OUT: { XEvent xevent; memset(&xevent, 0, sizeof(xevent)); if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) { #ifdef DEBUG_XTBIN printf("XTEMBED got focus in\n"); #endif xevent.xfocus.type = FocusIn; } else { #ifdef DEBUG_XTBIN printf("XTEMBED got focus out\n"); #endif xevent.xfocus.type = FocusOut; } xevent.xfocus.window = XtWindow(xtplug->child_widget); xevent.xfocus.display = XtDisplay(xtplug->child_widget); XSendEvent(XtDisplay(xtplug->child_widget), xevent.xfocus.window, False, NoEventMask, &xevent ); XSync( XtDisplay(xtplug->child_widget), False); } break; default: break; } /* End of XEmbed Message */ } static void xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event) { XtClient *xtplug = (XtClient*)client_data; switch(event->type) { case ClientMessage: /* Handle xembed message */ if (event->xclient.message_type== XInternAtom (XtDisplay(xtplug->child_widget), "_XEMBED", False)) { xt_client_handle_xembed_message(w, client_data, event); } break; case ReparentNotify: break; case MappingNotify: xt_client_set_info (w, XEMBED_MAPPED); break; case UnmapNotify: xt_client_set_info (w, 0); break; case FocusIn: send_xembed_message ( xtplug, XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); break; case FocusOut: break; case KeyPress: #ifdef DEBUG_XTBIN printf("Key Press Got!\n"); #endif break; default: break; } /* End of switch(event->type) */ } static void send_xembed_message (XtClient *xtclient, long message, long detail, long data1, long data2, long time) { XEvent xevent; Window w=XtWindow(xtclient->top_widget); Display* dpy=xtclient->xtdisplay; int errorcode; memset(&xevent,0,sizeof(xevent)); xevent.xclient.window = w; xevent.xclient.type = ClientMessage; xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False); xevent.xclient.format = 32; xevent.xclient.data.l[0] = time; xevent.xclient.data.l[1] = message; xevent.xclient.data.l[2] = detail; xevent.xclient.data.l[3] = data1; xevent.xclient.data.l[4] = data2; trap_errors (); XSendEvent (dpy, w, False, NoEventMask, &xevent); XSync (dpy,False); if((errorcode = untrap_error())) { #ifdef DEBUG_XTBIN printf("send_xembed_message error(%d)!!!\n",errorcode); #endif } } static int error_handler(Display *display, XErrorEvent *error) { trapped_error_code = error->error_code; return 0; } static void trap_errors(void) { trapped_error_code =0; old_error_handler = XSetErrorHandler(error_handler); } static int untrap_error(void) { XSetErrorHandler(old_error_handler); if(trapped_error_code) { #ifdef DEBUG_XTBIN printf("Get X Window Error = %d\n", trapped_error_code); #endif } return trapped_error_code; } static void xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event) { Display *dpy = XtDisplay(w); XtClient *xtclient = user_data; Window win = XtWindow(w); switch(event->type) { case CreateNotify: if(event->xcreatewindow.parent == win) { Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window); if (child) xt_add_focus_listener_tree(child, user_data); } break; case DestroyNotify: xt_remove_focus_listener( w, user_data); break; case ReparentNotify: if(event->xreparent.parent == win) { /* I am the new parent */ Widget child=XtWindowToWidget(dpy, event->xreparent.window); if (child) xt_add_focus_listener_tree( child, user_data); } else if(event->xreparent.window == win) { /* I am the new child */ } else { /* I am the old parent */ } break; case ButtonRelease: #if 0 XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time); #endif send_xembed_message ( xtclient, XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); break; default: break; } /* End of switch(event->type) */ } static void xt_add_focus_listener( Widget w, XtPointer user_data) { XWindowAttributes attr; long eventmask; XtClient *xtclient = user_data; int errorcode; trap_errors (); XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr); eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask; XSelectInput(XtDisplay(w), XtWindow(w), eventmask); XtAddEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE, (XtEventHandler)xt_client_focus_listener, xtclient); untrap_error(); } static void xt_remove_focus_listener(Widget w, XtPointer user_data) { int errorcode; trap_errors (); XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE, (XtEventHandler)xt_client_focus_listener, user_data); untrap_error(); } static void xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data) { Window win = XtWindow(treeroot); Window *children; Window root, parent; Display *dpy = XtDisplay(treeroot); unsigned int i, nchildren; /* ensure we don't add more than once */ xt_remove_focus_listener( treeroot, user_data); xt_add_focus_listener( treeroot, user_data); trap_errors(); if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) { untrap_error(); return; } if(untrap_error()) return; for(i=0; i