/* -*- 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 GtkXtBin Widget Implementation. * * The Initial Developer of the Original Code is * Intel Corporation. * Portions created by the Initial Developer are Copyright (C) 2000 * 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 "gtkxtbin.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 */ /* #define 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_destroy (GtkObject *object); static void gtk_xtbin_shutdown (GtkObject *object); static void gtk_xtbin_show (GtkWidget *widget); static GtkWidgetClass *parent_class = NULL; static String *fallback = NULL; static gboolean xt_is_initialized = FALSE; static gint num_widgets = 0; static GPollFD xt_event_poll_fd; static gboolean xt_event_prepare (gpointer source_data, GTimeVal *current_time, gint *timeout, gpointer user_data) { int mask; GDK_THREADS_ENTER(); mask = XPending((Display *)user_data); GDK_THREADS_LEAVE(); return (gboolean)mask; } static gboolean xt_event_check (gpointer source_data, GTimeVal *current_time, gpointer user_data) { GDK_THREADS_ENTER (); if (xt_event_poll_fd.revents & G_IO_IN) { int mask; mask = XPending((Display *)user_data); GDK_THREADS_LEAVE (); return (gboolean)mask; } GDK_THREADS_LEAVE (); return FALSE; } static gboolean xt_event_dispatch (gpointer source_data, GTimeVal *current_time, gpointer user_data) { XEvent event; Display * display; XtAppContext ac; int i = 0; display = (Display *)user_data; ac = XtDisplayToApplicationContext(display); 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(display); i++) { XtAppProcessEvent(ac, XtIMXEvent); } GDK_THREADS_LEAVE (); return TRUE; } static GSourceFuncs xt_event_funcs = { xt_event_prepare, xt_event_check, xt_event_dispatch, (GDestroyNotify)g_free }; static gint xt_polling_timer_id = 0; static gboolean xt_event_polling_timer_callback(gpointer user_data) { Display * display; XtAppContext ac; int eventsToProcess = 20; display = (Display *)user_data; ac = XtDisplayToApplicationContext(display); /* We need to process many Xt events here. If we just process one event we might starve one or more Xt consumers. On the other hand this could hang the whole app if Xt events come pouring in. So process up to 20 Xt events right now and save the rest for later. This is a hack, but it oughta work. We *really* should have out of process plugins. */ while (eventsToProcess-- && XtAppPending(ac)) XtAppProcessEvent(ac, XtIMAll); /* restart the timer */ 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_WIDGET, &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_widget_get_type ()); widget_class = GTK_WIDGET_CLASS (klass); widget_class->realize = gtk_xtbin_realize; widget_class->show = gtk_xtbin_show; object_class = GTK_OBJECT_CLASS (klass); object_class->shutdown= gtk_xtbin_shutdown; object_class->destroy = gtk_xtbin_destroy; } static void gtk_xtbin_init (GtkXtBin *xtbin) { xtbin->xtdisplay = NULL; xtbin->xtwidget = NULL; xtbin->parent_window = NULL; xtbin->xtwindow = 0; xtbin->x = 0; xtbin->y = 0; } static void gtk_xtbin_realize (GtkWidget *widget) { GdkWindowAttr attributes; gint attributes_mask, n; GtkXtBin *xtbin; Arg args[5]; gint width, height; Widget top_widget; Window win; Widget embedded; #ifdef DEBUG_XTBIN printf("gtk_xtbin_realize()\n"); #endif g_return_if_fail (GTK_IS_XTBIN (widget)); gdk_flush(); xtbin = GTK_XTBIN (widget); if (widget->allocation.x == -1 && widget->allocation.y == -1 && widget->allocation.width == 1 && widget->allocation.height == 1) { GtkRequisition requisition; GtkAllocation allocation = { 0, 0, 200, 200 }; gtk_widget_size_request (widget, &requisition); if (requisition.width || requisition.height) { /* non-empty window */ allocation.width = requisition.width; allocation.height = requisition.height; } gtk_widget_size_allocate (widget, &allocation); } GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = xtbin->x; attributes.y = xtbin->y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gdk_window_get_visual (xtbin->parent_window); attributes.colormap = gdk_window_get_colormap (xtbin->parent_window); attributes.event_mask = gdk_window_get_events (xtbin->parent_window); attributes.event_mask |= GDK_EXPOSURE_MASK; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; xtbin->width = attributes.width; xtbin->height = attributes.height; widget->window = gdk_window_new (xtbin->parent_window, &attributes, attributes_mask); /* Turn off any event catching for this window by */ /* the Gtk/Gdk event loop... otherwise some strange */ /* things happen */ XSelectInput(GDK_WINDOW_XDISPLAY(widget->window), GDK_WINDOW_XWINDOW(widget->window), 0); gdk_window_set_user_data (widget->window, xtbin); widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); /* ensure all the outgoing events are flushed */ /* before we try this crazy dual toolkit stuff */ gdk_flush(); /*** * I'm sure there is a better way, but for now I'm * just creating a new application shell (since it doesn't * need a parent widget,) and then swapping out the XWindow * from under it. This seems to mostly work, but it's * an ugly hack. */ top_widget = XtAppCreateShell("drawingArea", "Wrapper", applicationShellWidgetClass, xtbin->xtdisplay, NULL, 0); xtbin->xtwidget = top_widget; /* set size of Xt window */ n = 0; XtSetArg(args[n], XtNheight, xtbin->height);n++; XtSetArg(args[n], XtNwidth, xtbin->width);n++; XtSetValues(top_widget, args, n); embedded = XtVaCreateWidget("form", compositeWidgetClass, top_widget, NULL); n = 0; XtSetArg(args[n], XtNheight, xtbin->height);n++; XtSetArg(args[n], XtNwidth, xtbin->width);n++; XtSetArg(args[n], XtNvisual, GDK_VISUAL_XVISUAL(gdk_window_get_visual( xtbin->parent_window )) ); n++; XtSetArg(args[n], XtNdepth, gdk_window_get_visual( xtbin->parent_window )->depth ); n++; XtSetArg(args[n], XtNcolormap, GDK_COLORMAP_XCOLORMAP(gdk_window_get_colormap( xtbin->parent_window)) ); n++; XtSetValues(embedded, args, n); /* Ok, here is the dirty little secret on how I am */ /* switching the widget's XWindow to the GdkWindow's XWindow. */ /* I should be ashamed of myself! */ gtk_object_set_data(GTK_OBJECT(widget), "oldwindow", GUINT_TO_POINTER(top_widget->core.window)); /* save it off so we can get it during destroy */ top_widget->core.window = GDK_WINDOW_XWINDOW(widget->window); /* this little trick seems to finish initializing the widget */ #if XlibSpecificationRelease >= 6 XtRegisterDrawable(xtbin->xtdisplay, GDK_WINDOW_XWINDOW(widget->window), top_widget); #else _XtRegisterWindow( GDK_WINDOW_XWINDOW(widget->window), top_widget); #endif XtRealizeWidget(embedded); #ifdef DEBUG_XTBIN printf("embedded window = %li\n", XtWindow(embedded)); #endif XtManageChild(embedded); /* now fill out the xtbin info */ xtbin->xtwindow = XtWindow(embedded); /* listen to all Xt events */ XSelectInput(xtbin->xtdisplay, XtWindow(top_widget), 0x0FFFFF); XSelectInput(xtbin->xtdisplay, XtWindow(embedded), 0x0FFFFF); XFlush(xtbin->xtdisplay); } GtkWidget* gtk_xtbin_new (GdkWindow *parent_window, String * f) { static Display *xtdisplay = NULL; GtkXtBin *xtbin; assert(parent_window != NULL); xtbin = gtk_type_new (GTK_TYPE_XTBIN); if (!xtbin) return (GtkWidget*)NULL; /* Initialize the Xt toolkit */ if (!xt_is_initialized) { char *mArgv[1]; int mArgc = 0; XtAppContext app_context; #ifdef DEBUG_XTBIN printf("starting up Xt stuff\n"); #endif /* * Initialize Xt stuff */ 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) { /* If XtOpenDisplay failed, we can't go any further. * Bail out. */ #ifdef DEBUG_XTBIN printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n"); #endif gtk_type_free (GTK_TYPE_XTBIN, xtbin); return (GtkWidget *)NULL; } xt_is_initialized = TRUE; } /* 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 */ g_source_add (GDK_PRIORITY_EVENTS, TRUE, &xt_event_funcs, NULL, xtdisplay, (GDestroyNotify)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_add_poll (&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++; xtbin->xtdisplay = xtdisplay; xtbin->parent_window = parent_window; if (f) fallback = f; 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); Widget xtwidget = XtWindowToWidget(xtbin->xtdisplay, xtbin->xtwindow); XtSetArg(args[0], XtNheight, height); XtSetArg(args[1], XtNwidth, width); XtSetValues(XtParent(xtwidget), args, 2); } static void gtk_xtbin_shutdown (GtkObject *object) { GtkXtBin *xtbin; GtkWidget *widget; #ifdef DEBUG_XTBIN printf("gtk_xtbin_shutdown()\n"); #endif /* gtk_object_destroy() will already hold a refcount on object */ xtbin = GTK_XTBIN(object); widget = GTK_WIDGET(object); if (widget->parent) gtk_container_remove (GTK_CONTAINER (widget->parent), widget); GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE); if (GTK_WIDGET_REALIZED (widget)) { #if XlibSpecificationRelease >= 6 XtUnregisterDrawable(xtbin->xtdisplay, GDK_WINDOW_XWINDOW(GTK_WIDGET(object)->window)); #else _XtUnregisterWindow(GDK_WINDOW_XWINDOW(GTK_WIDGET(object)->window), XtWindowToWidget(xtbin->xtdisplay, GDK_WINDOW_XWINDOW(GTK_WIDGET(object)->window))); #endif /* flush the queue before we returning origin top_widget->core.window or we can get X error since the window is gone */ XSync(xtbin->xtdisplay, False); xtbin->xtwidget->core.window = GPOINTER_TO_UINT(gtk_object_get_data(object, "oldwindow")); XtUnrealizeWidget(xtbin->xtwidget); gtk_widget_unrealize (widget); } gtk_object_remove_data(object, "oldwindow"); GTK_OBJECT_CLASS(parent_class)->shutdown (object); } 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); XtDestroyWidget(xtbin->xtwidget); 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) { XtAppContext ac; #ifdef DEBUG_XTBIN printf("removing the Xt connection from the main loop\n"); #endif g_main_remove_poll(&xt_event_poll_fd); g_source_remove_by_user_data(xtbin->xtdisplay); gtk_timeout_remove(xt_polling_timer_id); xt_polling_timer_id = 0; } GTK_OBJECT_CLASS(parent_class)->destroy(object); } static void gtk_xtbin_show (GtkWidget *widget) { g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WIDGET (widget)); #ifdef DEBUG_XTBIN printf("gtk_xtbin_show()\n"); #endif if (!GTK_WIDGET_VISIBLE (widget)) { GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE); if (!GTK_WIDGET_MAPPED(widget)) gtk_widget_map (widget); } }