Back to index

lightning-sunbird  0.9+nobinonly
gtkxtbin.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * vim:expandtab:shiftwidth=2:tabstop=2: */
00003  
00004 /* ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is the GtkXtBin Widget Implementation.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Intel Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2000
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039  
00040 /*
00041  * The GtkXtBin widget allows for Xt toolkit code to be used
00042  * inside a GTK application.  
00043  */
00044 
00045 #include "gtkxtbin.h"
00046 #include <gtk/gtkcontainer.h>
00047 #include <gtk/gtkmain.h>
00048 #include <gdk/gdkx.h>
00049 #include <glib.h>
00050 #include <assert.h>
00051 #include <sys/time.h>
00052 #include <sys/types.h>
00053 #include <stdio.h>
00054 #include <stdlib.h>
00055 #include <unistd.h>
00056 
00057 /* Xlib/Xt stuff */
00058 #include <X11/Xlib.h>
00059 #include <X11/Xutil.h>
00060 #include <X11/Shell.h>
00061 #include <X11/Intrinsic.h>
00062 #include <X11/StringDefs.h>
00063 
00064 /* uncomment this if you want debugging information about widget
00065    creation and destruction */
00066 /* #define DEBUG_XTBIN */
00067 
00068 #define XTBIN_MAX_EVENTS 30
00069 
00070 static void            gtk_xtbin_class_init (GtkXtBinClass *klass);
00071 static void            gtk_xtbin_init       (GtkXtBin      *xtbin);
00072 static void            gtk_xtbin_realize    (GtkWidget      *widget);
00073 static void            gtk_xtbin_destroy    (GtkObject      *object);
00074 static void            gtk_xtbin_shutdown   (GtkObject      *object);
00075 static void            gtk_xtbin_show       (GtkWidget *widget);
00076 
00077 static GtkWidgetClass *parent_class = NULL;
00078 
00079 static String          *fallback = NULL;
00080 static gboolean         xt_is_initialized = FALSE;
00081 static gint             num_widgets = 0;
00082 
00083 static GPollFD          xt_event_poll_fd;
00084 
00085 static gboolean
00086 xt_event_prepare (gpointer  source_data,
00087                    GTimeVal *current_time,
00088                    gint     *timeout,
00089                    gpointer  user_data)
00090 {   
00091   int mask;
00092 
00093   GDK_THREADS_ENTER();
00094   mask = XPending((Display *)user_data);
00095   GDK_THREADS_LEAVE();
00096 
00097   return (gboolean)mask;
00098 }
00099 
00100 static gboolean
00101 xt_event_check (gpointer  source_data,
00102                  GTimeVal *current_time,
00103                  gpointer  user_data)
00104 {
00105   GDK_THREADS_ENTER ();
00106 
00107   if (xt_event_poll_fd.revents & G_IO_IN) {
00108     int mask;
00109 
00110     mask = XPending((Display *)user_data);
00111     
00112     GDK_THREADS_LEAVE ();
00113 
00114     return (gboolean)mask;
00115   }
00116 
00117   GDK_THREADS_LEAVE ();
00118   return FALSE;
00119 }   
00120 
00121 static gboolean
00122 xt_event_dispatch (gpointer  source_data,
00123                     GTimeVal *current_time,
00124                     gpointer  user_data)
00125 {
00126   XEvent event;
00127   Display * display;
00128   XtAppContext ac;
00129   int i = 0;
00130 
00131   display = (Display *)user_data;
00132   ac = XtDisplayToApplicationContext(display);
00133 
00134   GDK_THREADS_ENTER ();
00135 
00136   /* Process only real X traffic here.  We only look for data on the
00137    * pipe, limit it to XTBIN_MAX_EVENTS and only call
00138    * XtAppProcessEvent so that it will look for X events.  There's no
00139    * timer processing here since we already have a timer callback that
00140    * does it.  */
00141   for (i=0; i < XTBIN_MAX_EVENTS && XPending(display); i++) {
00142     XtAppProcessEvent(ac, XtIMXEvent);
00143   }
00144 
00145   GDK_THREADS_LEAVE ();
00146 
00147   return TRUE;  
00148 }
00149 
00150 static GSourceFuncs xt_event_funcs = {
00151   xt_event_prepare,
00152   xt_event_check,
00153   xt_event_dispatch,
00154   (GDestroyNotify)g_free
00155 };
00156 
00157 static gint             xt_polling_timer_id = 0;
00158 
00159 static gboolean
00160 xt_event_polling_timer_callback(gpointer user_data)
00161 {
00162   Display * display;
00163   XtAppContext ac;
00164   int eventsToProcess = 20;
00165 
00166   display = (Display *)user_data;
00167   ac = XtDisplayToApplicationContext(display);
00168 
00169   /* We need to process many Xt events here. If we just process
00170      one event we might starve one or more Xt consumers. On the other hand
00171      this could hang the whole app if Xt events come pouring in. So process
00172      up to 20 Xt events right now and save the rest for later. This is a hack,
00173      but it oughta work. We *really* should have out of process plugins.
00174   */
00175   while (eventsToProcess-- && XtAppPending(ac))
00176     XtAppProcessEvent(ac, XtIMAll);
00177 
00178   /* restart the timer */
00179   return TRUE;
00180 }
00181 
00182 GtkType
00183 gtk_xtbin_get_type (void)
00184 {
00185   static GtkType xtbin_type = 0;
00186 
00187   if (!xtbin_type)
00188     {
00189       static const GtkTypeInfo xtbin_info =
00190       {
00191         "GtkXtBin",
00192         sizeof (GtkXtBin),
00193         sizeof (GtkXtBinClass),
00194         (GtkClassInitFunc) gtk_xtbin_class_init,
00195         (GtkObjectInitFunc) gtk_xtbin_init,
00196         /* reserved_1 */ NULL,
00197         /* reserved_2 */ NULL,
00198         (GtkClassInitFunc) NULL
00199       };
00200 
00201       xtbin_type = gtk_type_unique (GTK_TYPE_WIDGET, &xtbin_info);
00202     }
00203 
00204   return xtbin_type;
00205 }
00206 
00207 static void
00208 gtk_xtbin_class_init (GtkXtBinClass *klass)
00209 {
00210   GtkWidgetClass *widget_class;
00211   GtkObjectClass *object_class;
00212 
00213   parent_class = gtk_type_class (gtk_widget_get_type ());
00214 
00215   widget_class = GTK_WIDGET_CLASS (klass);
00216   widget_class->realize = gtk_xtbin_realize;
00217   widget_class->show = gtk_xtbin_show;
00218 
00219   object_class = GTK_OBJECT_CLASS (klass);
00220   object_class->shutdown= gtk_xtbin_shutdown;
00221   object_class->destroy = gtk_xtbin_destroy;
00222 }
00223 
00224 static void
00225 gtk_xtbin_init (GtkXtBin *xtbin)
00226 {
00227   xtbin->xtdisplay = NULL;
00228   xtbin->xtwidget = NULL;
00229   xtbin->parent_window = NULL;
00230   xtbin->xtwindow = 0;
00231   xtbin->x = 0;
00232   xtbin->y = 0;
00233 }
00234 
00235 static void
00236 gtk_xtbin_realize (GtkWidget *widget)
00237 {
00238   GdkWindowAttr attributes;
00239   gint          attributes_mask, n;
00240   GtkXtBin     *xtbin;
00241   Arg           args[5];
00242   gint          width, height;
00243   Widget        top_widget;
00244   Window        win;
00245   Widget        embedded;
00246 
00247 #ifdef DEBUG_XTBIN
00248   printf("gtk_xtbin_realize()\n");
00249 #endif
00250 
00251   g_return_if_fail (GTK_IS_XTBIN (widget));
00252 
00253   gdk_flush();
00254   xtbin = GTK_XTBIN (widget);
00255 
00256   if (widget->allocation.x == -1 &&
00257       widget->allocation.y == -1 &&
00258       widget->allocation.width == 1 &&
00259       widget->allocation.height == 1)
00260   {
00261     GtkRequisition requisition;
00262     GtkAllocation allocation = { 0, 0, 200, 200 };
00263 
00264     gtk_widget_size_request (widget, &requisition);
00265     if (requisition.width || requisition.height)
00266     {
00267       /* non-empty window */
00268       allocation.width = requisition.width;
00269       allocation.height = requisition.height;
00270     }
00271     gtk_widget_size_allocate (widget, &allocation);
00272   }
00273 
00274   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
00275 
00276   attributes.window_type  = GDK_WINDOW_CHILD;
00277   attributes.x            = xtbin->x;
00278   attributes.y            = xtbin->y;
00279   attributes.width        = widget->allocation.width;
00280   attributes.height       = widget->allocation.height;
00281   attributes.wclass       = GDK_INPUT_OUTPUT;
00282   attributes.visual       = gdk_window_get_visual (xtbin->parent_window);
00283   attributes.colormap     = gdk_window_get_colormap (xtbin->parent_window);
00284   attributes.event_mask   = gdk_window_get_events (xtbin->parent_window);
00285   attributes.event_mask  |= GDK_EXPOSURE_MASK;
00286   attributes_mask         = GDK_WA_X      | GDK_WA_Y | 
00287                             GDK_WA_VISUAL | GDK_WA_COLORMAP;
00288 
00289   xtbin->width = attributes.width;
00290   xtbin->height = attributes.height;
00291 
00292   widget->window = gdk_window_new (xtbin->parent_window,
00293                                    &attributes, attributes_mask);
00294 
00295   /* Turn off any event catching for this window by */
00296   /* the Gtk/Gdk event loop... otherwise some strange */
00297   /* things happen */
00298   XSelectInput(GDK_WINDOW_XDISPLAY(widget->window), 
00299                GDK_WINDOW_XWINDOW(widget->window),
00300                0);
00301 
00302   gdk_window_set_user_data (widget->window, xtbin);
00303 
00304   widget->style = gtk_style_attach (widget->style, widget->window);
00305   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
00306 
00307   /* ensure all the outgoing events are flushed */
00308   /* before we try this crazy dual toolkit stuff */
00309   gdk_flush();
00310 
00311 
00312   /***
00313    * I'm sure there is a better way, but for now I'm 
00314    * just creating a new application shell (since it doesn't
00315    * need a parent widget,) and then swapping out the XWindow
00316    * from under it.  This seems to mostly work, but it's
00317    * an ugly hack.
00318    */
00319 
00320   top_widget = XtAppCreateShell("drawingArea", "Wrapper", 
00321                                 applicationShellWidgetClass, xtbin->xtdisplay, 
00322                                 NULL, 0);
00323 
00324   xtbin->xtwidget = top_widget;
00325 
00326   /* set size of Xt window */
00327   n = 0;
00328   XtSetArg(args[n], XtNheight, xtbin->height);n++;
00329   XtSetArg(args[n], XtNwidth,  xtbin->width);n++;
00330   XtSetValues(top_widget, args, n);
00331 
00332   embedded = XtVaCreateWidget("form", compositeWidgetClass, top_widget, NULL);
00333 
00334   n = 0;
00335   XtSetArg(args[n], XtNheight, xtbin->height);n++;
00336   XtSetArg(args[n], XtNwidth,  xtbin->width);n++;
00337   XtSetArg(args[n], XtNvisual, GDK_VISUAL_XVISUAL(gdk_window_get_visual( xtbin->parent_window )) ); n++;
00338   XtSetArg(args[n], XtNdepth, gdk_window_get_visual( xtbin->parent_window )->depth ); n++;
00339   XtSetArg(args[n], XtNcolormap, GDK_COLORMAP_XCOLORMAP(gdk_window_get_colormap( xtbin->parent_window)) ); n++;
00340   XtSetValues(embedded, args, n);
00341 
00342   /* Ok, here is the dirty little secret on how I am */
00343   /* switching the widget's XWindow to the GdkWindow's XWindow. */
00344   /* I should be ashamed of myself! */
00345   gtk_object_set_data(GTK_OBJECT(widget), "oldwindow",
00346                       GUINT_TO_POINTER(top_widget->core.window)); /* save it off so we can get it during destroy */
00347 
00348   top_widget->core.window = GDK_WINDOW_XWINDOW(widget->window);
00349   
00350 
00351   /* this little trick seems to finish initializing the widget */
00352 #if XlibSpecificationRelease >= 6
00353   XtRegisterDrawable(xtbin->xtdisplay, 
00354                      GDK_WINDOW_XWINDOW(widget->window),
00355                      top_widget);
00356 #else
00357   _XtRegisterWindow( GDK_WINDOW_XWINDOW(widget->window),
00358                      top_widget);
00359 #endif
00360   
00361   XtRealizeWidget(embedded);
00362 #ifdef DEBUG_XTBIN
00363   printf("embedded window = %li\n", XtWindow(embedded));
00364 #endif
00365   XtManageChild(embedded);
00366 
00367   /* now fill out the xtbin info */
00368   xtbin->xtwindow  = XtWindow(embedded);
00369 
00370   /* listen to all Xt events */
00371   XSelectInput(xtbin->xtdisplay, 
00372                XtWindow(top_widget), 
00373                0x0FFFFF);
00374   XSelectInput(xtbin->xtdisplay, 
00375                XtWindow(embedded), 
00376                0x0FFFFF);
00377   XFlush(xtbin->xtdisplay);
00378 }
00379 
00380 GtkWidget*
00381 gtk_xtbin_new (GdkWindow *parent_window, String * f)
00382 {
00383   static Display *xtdisplay = NULL;
00384 
00385   GtkXtBin *xtbin;
00386   assert(parent_window != NULL);
00387 
00388   xtbin = gtk_type_new (GTK_TYPE_XTBIN);
00389 
00390   if (!xtbin)
00391     return (GtkWidget*)NULL;
00392 
00393   /* Initialize the Xt toolkit */
00394   if (!xt_is_initialized) {
00395     char         *mArgv[1];
00396     int           mArgc = 0;
00397     XtAppContext  app_context;
00398 
00399 #ifdef DEBUG_XTBIN
00400     printf("starting up Xt stuff\n");
00401 #endif
00402     /*
00403      * Initialize Xt stuff
00404      */
00405 
00406     XtToolkitInitialize();
00407     app_context = XtCreateApplicationContext();
00408     if (fallback)
00409       XtAppSetFallbackResources(app_context, fallback);
00410   
00411     xtdisplay = XtOpenDisplay(app_context, gdk_get_display(), NULL, 
00412                               "Wrapper", NULL, 0, &mArgc, mArgv);
00413 
00414     if (!xtdisplay) {
00415       /* If XtOpenDisplay failed, we can't go any further.
00416        *  Bail out.
00417        */
00418 #ifdef DEBUG_XTBIN
00419       printf("gtk_xtbin_init: XtOpenDisplay() returned NULL.\n");
00420 #endif
00421 
00422       gtk_type_free (GTK_TYPE_XTBIN, xtbin);
00423       return (GtkWidget *)NULL;
00424     }
00425 
00426     xt_is_initialized = TRUE;
00427   }
00428 
00429   /* If this is the first running widget, hook this display into the
00430      mainloop */
00431   if (0 == num_widgets) {
00432     int           cnumber;
00433     /*
00434      * hook Xt event loop into the glib event loop.
00435      */
00436 
00437     /* the assumption is that gtk_init has already been called */
00438     g_source_add (GDK_PRIORITY_EVENTS, TRUE, 
00439                   &xt_event_funcs, NULL, xtdisplay, (GDestroyNotify)NULL);
00440     
00441 #ifdef VMS
00442     cnumber = XConnectionNumber(xtdisplay);
00443 #else
00444     cnumber = ConnectionNumber(xtdisplay);
00445 #endif
00446     xt_event_poll_fd.fd = cnumber;
00447     xt_event_poll_fd.events = G_IO_IN; 
00448     xt_event_poll_fd.revents = 0;    /* hmm... is this correct? */
00449 
00450     g_main_add_poll (&xt_event_poll_fd, G_PRIORITY_LOW);
00451 
00452     /* add a timer so that we can poll and process Xt timers */
00453     xt_polling_timer_id =
00454       gtk_timeout_add(25,
00455                       (GtkFunction)xt_event_polling_timer_callback,
00456                       xtdisplay);
00457   }
00458 
00459   /* Bump up our usage count */
00460   num_widgets++;
00461 
00462   xtbin->xtdisplay = xtdisplay;
00463   xtbin->parent_window = parent_window;
00464   if (f)
00465     fallback = f;
00466 
00467   return GTK_WIDGET (xtbin);
00468 }
00469 
00470 void
00471 gtk_xtbin_set_position (GtkXtBin *xtbin,
00472                         gint       x,
00473                         gint       y)
00474 {
00475   xtbin->x = x;
00476   xtbin->y = y;
00477 
00478   if (GTK_WIDGET_REALIZED (xtbin))
00479     gdk_window_move (GTK_WIDGET (xtbin)->window, x, y);
00480 }
00481 
00482 void
00483 gtk_xtbin_resize (GtkWidget *widget,
00484                   gint       width,
00485                   gint       height)
00486 {
00487   Arg args[2];
00488   GtkXtBin *xtbin = GTK_XTBIN (widget);
00489   Widget xtwidget = XtWindowToWidget(xtbin->xtdisplay, xtbin->xtwindow);
00490 
00491   XtSetArg(args[0], XtNheight, height);
00492   XtSetArg(args[1], XtNwidth,  width);
00493   XtSetValues(XtParent(xtwidget), args, 2);
00494 }
00495 
00496 static void
00497 gtk_xtbin_shutdown (GtkObject *object)
00498 {
00499   GtkXtBin *xtbin;
00500   GtkWidget *widget;
00501 
00502 #ifdef DEBUG_XTBIN
00503   printf("gtk_xtbin_shutdown()\n");
00504 #endif
00505 
00506   /* gtk_object_destroy() will already hold a refcount on object
00507    */
00508   xtbin = GTK_XTBIN(object);
00509   widget = GTK_WIDGET(object);
00510 
00511   if (widget->parent)
00512     gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
00513 
00514   GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE);
00515   if (GTK_WIDGET_REALIZED (widget)) {
00516 #if XlibSpecificationRelease >= 6
00517     XtUnregisterDrawable(xtbin->xtdisplay,
00518                          GDK_WINDOW_XWINDOW(GTK_WIDGET(object)->window));
00519 #else
00520     _XtUnregisterWindow(GDK_WINDOW_XWINDOW(GTK_WIDGET(object)->window),
00521                         XtWindowToWidget(xtbin->xtdisplay,
00522                         GDK_WINDOW_XWINDOW(GTK_WIDGET(object)->window)));
00523 #endif
00524 
00525     /* flush the queue before we returning origin top_widget->core.window
00526        or we can get X error since the window is gone */
00527     XSync(xtbin->xtdisplay, False);
00528 
00529     xtbin->xtwidget->core.window = GPOINTER_TO_UINT(gtk_object_get_data(object, "oldwindow"));
00530     XtUnrealizeWidget(xtbin->xtwidget);
00531 
00532     gtk_widget_unrealize (widget);
00533   }
00534 
00535 
00536   gtk_object_remove_data(object, "oldwindow");
00537 
00538   GTK_OBJECT_CLASS(parent_class)->shutdown (object);
00539 }
00540 
00541 
00542 static void
00543 gtk_xtbin_destroy (GtkObject *object)
00544 {
00545   GtkXtBin *xtbin;
00546 
00547 #ifdef DEBUG_XTBIN
00548   printf("gtk_xtbin_destroy()\n");
00549 #endif
00550 
00551   g_return_if_fail (object != NULL);
00552   g_return_if_fail (GTK_IS_XTBIN (object));
00553 
00554   xtbin = GTK_XTBIN (object);
00555 
00556   XtDestroyWidget(xtbin->xtwidget);
00557 
00558   num_widgets--; /* reduce our usage count */
00559 
00560   /* If this is the last running widget, remove the Xt display
00561      connection from the mainloop */
00562   if (0 == num_widgets) {
00563     XtAppContext  ac;
00564 
00565 #ifdef DEBUG_XTBIN
00566     printf("removing the Xt connection from the main loop\n");
00567 #endif
00568 
00569     g_main_remove_poll(&xt_event_poll_fd);
00570     g_source_remove_by_user_data(xtbin->xtdisplay);
00571 
00572     gtk_timeout_remove(xt_polling_timer_id);
00573     xt_polling_timer_id = 0;
00574 
00575   }
00576 
00577   GTK_OBJECT_CLASS(parent_class)->destroy(object);
00578 }
00579 
00580 static void
00581 gtk_xtbin_show (GtkWidget *widget)
00582 {
00583   g_return_if_fail (widget != NULL);
00584   g_return_if_fail (GTK_IS_WIDGET (widget));
00585 
00586 #ifdef DEBUG_XTBIN
00587   printf("gtk_xtbin_show()\n");
00588 #endif
00589 
00590   if (!GTK_WIDGET_VISIBLE (widget))
00591   {
00592     GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
00593 
00594     if (!GTK_WIDGET_MAPPED(widget))
00595       gtk_widget_map (widget);
00596   }
00597 }