gtk+ r21531 - in trunk: . gtk



Author: matthiasc
Date: Sat Sep 27 04:27:53 2008
New Revision: 21531
URL: http://svn.gnome.org/viewvc/gtk+?rev=21531&view=rev

Log:
        Bug 552959 â GtkTrayIcon: _NET_SYSTEM_TRAY_VISUAL and real
        transparency

        * gtk/gtktrayicon-x11.c: Add support for the _BET_SYSTEM_TRAY_VISUAL
        property described in
        http://lists.freedesktop.org/archives/xdg/2008-September/009919.html
        If _NET_SYSTEM_TRAY_VISUAL is a visual with an alpha channel, the
        parent-relative-background hack is skipped and we draw with a real
        transparent background.

        * gtk/gtkrc.c: Remove the default GtkTrayIcon style, since the
        parent-relative background is now set when realizing the tray
        icon.

        Patch by Owen Taylor



Modified:
   trunk/ChangeLog
   trunk/gtk/gtkrc.c
   trunk/gtk/gtktrayicon-x11.c

Modified: trunk/gtk/gtkrc.c
==============================================================================
--- trunk/gtk/gtkrc.c	(original)
+++ trunk/gtk/gtkrc.c	Sat Sep 27 04:27:53 2008
@@ -876,18 +876,12 @@
 		       "  text[PRELIGHT] = \"#ffffff\"\n"
 		       "}\n"
 		       "\n"
-                       /* Make transparent tray icons work */
-		       "style \"gtk-default-tray-icon-style\" {\n"
-		       "  bg_pixmap[NORMAL] = \"<parent>\"\n"
-		       "}\n"
-		       "\n"
                        /* Work around clipping of accelerator underlines */
                        "style \"gtk-default-label-style\" {\n"
                        "  GtkWidget::draw-border = {0,0,0,1}\n"
                        "}\n"
                        "\n"    
 		       "class \"GtkProgressBar\" style : gtk \"gtk-default-progress-bar-style\"\n"
-		       "class \"GtkTrayIcon\" style : gtk \"gtk-default-tray-icon-style\"\n"
 		       "widget \"gtk-tooltip*\" style : gtk \"gtk-default-tooltips-style\"\n"
 		       "widget_class \"*<GtkMenuItem>*\" style : gtk \"gtk-default-menu-item-style\"\n"
 		       "widget_class \"*<GtkMenuBar>*<GtkMenuItem>\" style : gtk \"gtk-default-menu-bar-item-style\"\n"

Modified: trunk/gtk/gtktrayicon-x11.c
==============================================================================
--- trunk/gtk/gtktrayicon-x11.c	(original)
+++ trunk/gtk/gtktrayicon-x11.c	Sat Sep 27 04:27:53 2008
@@ -54,26 +54,39 @@
   Atom manager_atom;
   Atom system_tray_opcode_atom;
   Atom orientation_atom;
+  Atom visual_atom;
   Window manager_window;
+  GdkVisual *manager_visual;
+  gboolean manager_visual_rgba;
 
   GtkOrientation orientation;
 };
-         
+
+static void gtk_tray_icon_constructed   (GObject     *object);
+static void gtk_tray_icon_dispose       (GObject     *object);
+
 static void gtk_tray_icon_get_property  (GObject     *object,
 				 	 guint        prop_id,
 					 GValue      *value,
 					 GParamSpec  *pspec);
 
 static void     gtk_tray_icon_realize   (GtkWidget   *widget);
-static void     gtk_tray_icon_unrealize (GtkWidget   *widget);
+static void     gtk_tray_icon_style_set (GtkWidget   *widget,
+					 GtkStyle    *previous_style);
 static gboolean gtk_tray_icon_delete    (GtkWidget   *widget,
 					 GdkEventAny *event);
 static gboolean gtk_tray_icon_expose    (GtkWidget      *widget, 
 					 GdkEventExpose *event);
 
+static void gtk_tray_icon_clear_manager_window     (GtkTrayIcon *icon);
 static void gtk_tray_icon_update_manager_window    (GtkTrayIcon *icon);
 static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
 
+static GdkFilterReturn gtk_tray_icon_manager_filter (GdkXEvent *xevent,
+						     GdkEvent  *event,
+						     gpointer   user_data);
+
+
 G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG)
 
 static void
@@ -83,9 +96,11 @@
   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
 
   gobject_class->get_property = gtk_tray_icon_get_property;
+  gobject_class->constructed = gtk_tray_icon_constructed;
+  gobject_class->dispose = gtk_tray_icon_dispose;
 
-  widget_class->realize   = gtk_tray_icon_realize;
-  widget_class->unrealize = gtk_tray_icon_unrealize;
+  widget_class->realize = gtk_tray_icon_realize;
+  widget_class->style_set = gtk_tray_icon_style_set;
   widget_class->delete_event = gtk_tray_icon_delete;
   widget_class->expose_event = gtk_tray_icon_expose;
 
@@ -111,11 +126,80 @@
   icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
 
   gtk_widget_set_app_paintable (GTK_WIDGET (icon), TRUE);
-  gtk_widget_set_double_buffered (GTK_WIDGET (icon), FALSE);
   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
 }
 
 static void
+gtk_tray_icon_constructed (GObject *object)
+{
+  /* Do setup that depends on the screen; screen has been set at this point */
+
+  GtkTrayIcon *icon = GTK_TRAY_ICON (object);
+  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (object));
+  GdkWindow *root_window = gdk_screen_get_root_window (screen);
+  GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (object));
+  Display *xdisplay = gdk_x11_display_get_xdisplay (display);
+  char buffer[256];
+  
+  g_snprintf (buffer, sizeof (buffer),
+	      "_NET_SYSTEM_TRAY_S%d",
+	      gdk_screen_get_number (screen));
+
+  icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
+  
+  icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
+  
+  icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
+						     "_NET_SYSTEM_TRAY_OPCODE",
+						     False);
+
+  icon->priv->orientation_atom = XInternAtom (xdisplay,
+					      "_NET_SYSTEM_TRAY_ORIENTATION",
+					      False);
+
+  icon->priv->visual_atom = XInternAtom (xdisplay,
+					 "_NET_SYSTEM_TRAY_VISUAL",
+					 False);
+
+  /* Add a root window filter so that we get changes on MANAGER */
+  gdk_window_add_filter (root_window,
+			 gtk_tray_icon_manager_filter, icon);
+
+  gtk_tray_icon_update_manager_window (icon);
+}
+
+static void
+gtk_tray_icon_clear_manager_window (GtkTrayIcon *icon)
+{
+  GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (icon));
+
+  if (icon->priv->manager_window != None)
+    {
+      GdkWindow *gdkwin;
+
+      gdkwin = gdk_window_lookup_for_display (display,
+                                              icon->priv->manager_window);
+
+      gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+
+      icon->priv->manager_window = None;
+      icon->priv->manager_visual = NULL;
+    }
+}
+
+static void
+gtk_tray_icon_dispose (GObject *object)
+{
+  GtkTrayIcon *icon = GTK_TRAY_ICON (object);
+  GtkWidget *widget = GTK_WIDGET (object);
+  GdkWindow *root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+  gtk_tray_icon_clear_manager_window (icon);
+
+  gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
+}
+
+static void
 gtk_tray_icon_get_property (GObject    *object,
 			    guint       prop_id,
 			    GValue     *value,
@@ -138,12 +222,27 @@
 gtk_tray_icon_expose (GtkWidget      *widget, 
 		      GdkEventExpose *event)
 {
+  GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
   GtkWidget *focus_child;
   gint border_width, x, y, width, height;
   gboolean retval = FALSE;
 
-  gdk_window_clear_area (widget->window, event->area.x, event->area.y,
-			 event->area.width, event->area.height);
+  if (icon->priv->manager_visual_rgba)
+    {
+      /* Clear to transparent */
+      cairo_t *cr = gdk_cairo_create (widget->window);
+      cairo_set_source_rgba (cr, 0, 0, 0, 0);
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      gdk_cairo_region (cr, event->region);
+      cairo_fill (cr);
+      cairo_destroy (cr);
+    }
+  else
+    {
+      /* Clear to parent-relative pixmap */
+      gdk_window_clear_area (widget->window, event->area.x, event->area.y,
+			     event->area.width, event->area.height);
+    }
 
   if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event)
     retval = GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event (widget, event);
@@ -171,7 +270,10 @@
 static void
 gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
 {
-  Display *xdisplay;
+  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
   Atom type;
   int format;
   union {
@@ -184,8 +286,6 @@
 
   g_assert (icon->priv->manager_window != None);
   
-  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
-
   gdk_error_trap_push ();
   type = None;
   result = XGetWindowProperty (xdisplay,
@@ -200,7 +300,7 @@
   if (error || result != Success)
     return;
 
-  if (type == XA_CARDINAL)
+  if (type == XA_CARDINAL && nitems == 1 && format == 32)
     {
       GtkOrientation orientation;
 
@@ -216,7 +316,59 @@
 	}
     }
 
-  if (prop.prop)
+  if (type != None)
+    XFree (prop.prop);
+}
+
+void
+gtk_tray_icon_get_visual_property (GtkTrayIcon *icon)
+{
+  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+  Atom type;
+  int format;
+  union {
+	gulong *prop;
+	guchar *prop_ch;
+  } prop = { NULL };
+  gulong nitems;
+  gulong bytes_after;
+  int error, result;
+  GdkVisual *visual;
+
+  g_assert (icon->priv->manager_window != None);
+
+  gdk_error_trap_push ();
+  type = None;
+  result = XGetWindowProperty (xdisplay,
+			       icon->priv->manager_window,
+			       icon->priv->visual_atom,
+			       0, G_MAXLONG, FALSE,
+			       XA_VISUALID,
+			       &type, &format, &nitems,
+			       &bytes_after, &(prop.prop_ch));
+  error = gdk_error_trap_pop ();
+
+  visual = NULL;
+
+  if (!error && result == Success &&
+      type == XA_VISUALID && nitems == 1 && format == 32)
+    {
+      VisualID visual_id = prop.prop[0];
+      visual = gdk_x11_screen_lookup_visual (screen, visual_id);
+    }
+
+  icon->priv->manager_visual = visual;
+  icon->priv->manager_visual_rgba = visual != NULL &&
+    (visual->red_prec + visual->blue_prec + visual->green_prec < visual->depth);
+
+  /* For the background-relative hack we use when we aren't using a real RGBA
+   * visual, we can't be double-buffered */
+  gtk_widget_set_double_buffered (GTK_WIDGET (icon), icon->priv->manager_visual_rgba);
+
+  if (type != None)
     XFree (prop.prop);
 }
 
@@ -263,34 +415,6 @@
 }
 
 static void
-gtk_tray_icon_unrealize (GtkWidget *widget)
-{
-  GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
-  GdkWindow *root_window;
-
-  GTK_NOTE (PLUGSOCKET,
-	    g_print ("GtkStatusIcon %p: unrealizing\n", icon));
-
-  if (icon->priv->manager_window != None)
-    {
-      GdkWindow *gdkwin;
-
-      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
-                                              icon->priv->manager_window);
-      
-      gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
-
-      icon->priv->manager_window = None;
-    }
-
-  root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
-
-  gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
-
-  GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize (widget);
-}
-
-static void
 gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
 				    long         message,
 				    Window       window,
@@ -338,9 +462,10 @@
 static void
 gtk_tray_icon_update_manager_window (GtkTrayIcon *icon)
 {
-  Display *xdisplay;
-
-  g_return_if_fail (GTK_WIDGET_REALIZED (icon));
+  GtkWidget *widget = GTK_WIDGET (icon);
+  GdkScreen *screen = gtk_widget_get_screen (widget);
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
 
   GTK_NOTE (PLUGSOCKET,
 	    g_print ("GtkStatusIcon %p: updating tray icon manager window, current manager window: %lx\n",
@@ -352,8 +477,6 @@
   GTK_NOTE (PLUGSOCKET,
 	    g_print ("GtkStatusIcon %p: trying to find manager window\n", icon));
 
-  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
-  
   XGrabServer (xdisplay);
   
   icon->priv->manager_window = XGetSelectionOwner (xdisplay,
@@ -374,14 +497,33 @@
 		g_print ("GtkStatusIcon %p: is being managed by window %lx\n",
 				icon, (gulong) icon->priv->manager_window));
 
-      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+      gdkwin = gdk_window_lookup_for_display (display,
 					      icon->priv->manager_window);
       
       gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
 
-      gtk_tray_icon_send_dock_request (icon);
-
       gtk_tray_icon_get_orientation_property (icon);
+      gtk_tray_icon_get_visual_property (icon);
+
+      if (GTK_WIDGET_REALIZED (icon))
+	{
+	  if ((icon->priv->manager_visual == NULL &&
+	       gtk_widget_get_visual (widget) == gdk_screen_get_system_visual (screen)) ||
+	      (icon->priv->manager_visual == gtk_widget_get_visual (widget)))
+	    {
+	      /* Already have the right visual, can just dock
+	       */
+	      gtk_tray_icon_send_dock_request (icon);
+	    }
+	  else
+	    {
+	      /* Need to re-realize the widget to get the right visual
+	       */
+	      gtk_widget_hide (widget);
+	      gtk_widget_unrealize (widget);
+	      gtk_widget_show (widget);
+	    }
+	}
     }
   else
     GTK_NOTE (PLUGSOCKET,
@@ -391,21 +533,15 @@
 static void
 gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
 {
-  GtkWidget *widget = GTK_WIDGET (icon);
-
-  g_return_if_fail (GTK_WIDGET_REALIZED (icon));
   g_return_if_fail (icon->priv->manager_window != None);
 
   GTK_NOTE (PLUGSOCKET,
 	    g_print ("GtkStatusIcon %p: tray manager window destroyed\n", icon));
 
-  gtk_widget_hide (widget);
-  gtk_widget_unrealize (widget);
-
-  gtk_widget_show (widget);
+  gtk_tray_icon_clear_manager_window (icon);
 }
 
-static gboolean 
+static gboolean
 gtk_tray_icon_delete (GtkWidget   *widget,
 		      GdkEventAny *event)
 {
@@ -413,27 +549,74 @@
 
   GTK_NOTE (PLUGSOCKET,
 	    g_print ("GtkStatusIcon %p: delete notify, tray manager window %lx\n",
-	    	     icon, (gulong) icon->priv->manager_window));
+		     icon, (gulong) icon->priv->manager_window));
 
+  /* A bug in X server versions up to x.org 1.5.0 means that:
+   * XFixesChangeSaveSet(...., SaveSetRoot, SaveSetUnmap) doesn't work properly
+   * and we'll left mapped in a separate toplevel window if the tray is destroyed.
+   * For simplicity just get rid of our X window and start over.
+   */
   gtk_widget_hide (widget);
   gtk_widget_unrealize (widget);
-
   gtk_widget_show (widget);
 
+  /* Handled it, don't destroy the tray icon */
   return TRUE;
 }
 
 static void
+gtk_tray_icon_set_colormap (GtkTrayIcon *icon)
+{
+  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (icon));
+  GdkColormap *colormap;
+  GdkVisual *visual = icon->priv->manager_visual;
+  gboolean new_colormap = FALSE;
+
+  /* To avoid uncertainty about colormaps, _NET_SYSTEM_TRAY_VISUAL is supposed
+   * to be either the screen default visual or a TrueColor visual; ignore it
+   * if it is something else
+   */
+  if (visual && visual->type != GDK_VISUAL_TRUE_COLOR)
+    visual = NULL;
+
+  if (visual == NULL || visual == gdk_screen_get_system_visual (screen))
+    colormap = gdk_screen_get_system_colormap (screen);
+  else if (visual == gdk_screen_get_rgb_visual (screen))
+    colormap = gdk_screen_get_rgb_colormap (screen);
+  else if (visual == gdk_screen_get_rgba_visual (screen))
+    colormap = gdk_screen_get_rgba_colormap (screen);
+  else
+    {
+      colormap = gdk_colormap_new (visual, FALSE);
+      new_colormap = TRUE;
+    }
+
+  gtk_widget_set_colormap (GTK_WIDGET (icon), colormap);
+
+  if (new_colormap)
+    g_object_unref (colormap);
+}
+
+static void
 gtk_tray_icon_realize (GtkWidget *widget)
 {
   GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
-  GdkScreen *screen;
-  GdkDisplay *display;
-  Display *xdisplay;
-  char buffer[256];
-  GdkWindow *root_window;
+
+  /* Set our colormap before realizing */
+  gtk_tray_icon_set_colormap (icon);
 
   GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
+  if (icon->priv->manager_visual_rgba)
+    {
+      /* Set a transparent background */
+      GdkColor transparent = { 0, 0, 0, 0 }; /* Only pixel=0 matters */
+      gdk_window_set_background (widget->window, &transparent);
+    }
+  else
+    {
+      /* Set a parent-relative background pixmap */
+      gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
+    }
 
   GTK_NOTE (PLUGSOCKET,
 	    g_print ("GtkStatusIcon %p: realized, window: %lx, socket window: %lx\n",
@@ -442,34 +625,18 @@
 		     GTK_PLUG (icon)->socket_window ?
 			     (gulong) GDK_WINDOW_XWINDOW (GTK_PLUG (icon)->socket_window) : 0UL));
 
-  screen = gtk_widget_get_screen (widget);
-  display = gdk_screen_get_display (screen);
-  xdisplay = gdk_x11_display_get_xdisplay (display);
-
-  /* Now see if there's a manager window around */
-  g_snprintf (buffer, sizeof (buffer),
-	      "_NET_SYSTEM_TRAY_S%d",
-	      gdk_screen_get_number (screen));
-
-  icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
-  
-  icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
-  
-  icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
-						     "_NET_SYSTEM_TRAY_OPCODE",
-						     False);
-
-  icon->priv->orientation_atom = XInternAtom (xdisplay,
-					      "_NET_SYSTEM_TRAY_ORIENTATION",
-					      False);
-
-  gtk_tray_icon_update_manager_window (icon);
+  if (icon->priv->manager_window != None)
+    gtk_tray_icon_send_dock_request (icon);
+}
 
-  root_window = gdk_screen_get_root_window (screen);
-  
-  /* Add a root window filter so that we get changes on MANAGER */
-  gdk_window_add_filter (root_window,
-			 gtk_tray_icon_manager_filter, icon);
+static void
+gtk_tray_icon_style_set (GtkWidget   *widget,
+			 GtkStyle    *previous_style)
+{
+  /* The default handler resets the background according to the style. We either
+   * use a transparent background or a parent-relative background and ignore the
+   * style background. So, just don't chain up.
+   */
 }
 
 guint



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]