From 783c7932f38e5690b8b5f98313f301b0f18b09c3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 27 Sep 2008 04:27:53 +0000 Subject: [PATCH] =?utf8?q?=20=20=20=20=20=20=20=20Bug=20552959=20=E2=80=93?= =?utf8?q?=20GtkTrayIcon:=20=5FNET=5FSYSTEM=5FTRAY=5FVISUAL=20and=20real?= =?utf8?q?=20=20=20=20=20=20=20=20=20transparency?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * 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 svn path=/trunk/; revision=21531 --- ChangeLog | 18 +++ gtk/gtkrc.c | 6 - gtk/gtktrayicon-x11.c | 347 +++++++++++++++++++++++++++++++----------- 3 files changed, 275 insertions(+), 96 deletions(-) diff --git a/ChangeLog b/ChangeLog index a84cb04cb..25e24c447 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2008-09-26 Matthias Clasen + + 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 + 2008-09-26 Matthias Clasen Bug 552956 – Should check composite extension version diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c index 9a56e708c..adf3c925e 100644 --- a/gtk/gtkrc.c +++ b/gtk/gtkrc.c @@ -876,18 +876,12 @@ _gtk_rc_init (void) " text[PRELIGHT] = \"#ffffff\"\n" "}\n" "\n" - /* Make transparent tray icons work */ - "style \"gtk-default-tray-icon-style\" {\n" - " bg_pixmap[NORMAL] = \"\"\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 \"**\" style : gtk \"gtk-default-menu-item-style\"\n" "widget_class \"**\" style : gtk \"gtk-default-menu-bar-item-style\"\n" diff --git a/gtk/gtktrayicon-x11.c b/gtk/gtktrayicon-x11.c index d53baad7d..05b1c946d 100644 --- a/gtk/gtktrayicon-x11.c +++ b/gtk/gtktrayicon-x11.c @@ -54,26 +54,39 @@ struct _GtkTrayIconPrivate 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 @@ gtk_tray_icon_class_init (GtkTrayIconClass *class) 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,10 +126,79 @@ gtk_tray_icon_init (GtkTrayIcon *icon) 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, @@ -138,12 +222,27 @@ static gboolean 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 @@ gtk_tray_icon_expose (GtkWidget *widget, 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 @@ gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon) 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 @@ gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon) if (error || result != Success) return; - if (type == XA_CARDINAL) + if (type == XA_CARDINAL && nitems == 1 && format == 32) { GtkOrientation orientation; @@ -216,7 +316,59 @@ gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon) } } - 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); } @@ -262,34 +414,6 @@ gtk_tray_icon_manager_filter (GdkXEvent *xevent, return GDK_FILTER_CONTINUE; } -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, @@ -338,9 +462,10 @@ gtk_tray_icon_send_dock_request (GtkTrayIcon *icon) 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_tray_icon_update_manager_window (GtkTrayIcon *icon) 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 @@ gtk_tray_icon_update_manager_window (GtkTrayIcon *icon) 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 @@ gtk_tray_icon_update_manager_window (GtkTrayIcon *icon) 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_tray_icon_delete (GtkWidget *widget, 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_tray_icon_realize (GtkWidget *widget) 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 -- 2.43.2