2
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3
* Copyright (C) 2003-2006 Vincent Untz
4
* Copyright (C) 2008 Red Hat, Inc.
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the
18
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19
* Boston, MA 02111-1307, USA.
24
#include "na-tray-child.h"
28
#include <X11/Xatom.h>
30
G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET)
33
na_tray_child_finalize (GObject *object)
35
G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object);
39
na_tray_child_realize (GtkWidget *widget)
41
NaTrayChild *child = NA_TRAY_CHILD (widget);
42
GdkVisual *visual = gtk_widget_get_visual (widget);
45
GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
47
window = gtk_widget_get_window (widget);
51
/* We have real transparency with an ARGB visual and the Composite
54
/* Set a transparent background */
55
cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0);
56
gdk_window_set_background_pattern (window, transparent);
57
gdk_window_set_composited (window, TRUE);
58
cairo_pattern_destroy (transparent);
60
child->parent_relative_bg = FALSE;
62
else if (visual == gdk_window_get_visual (gdk_window_get_parent (window)))
64
/* Otherwise, if the visual matches the visual of the parent window, we
65
* can use a parent-relative background and fake transparency. */
66
gdk_window_set_background_pattern (window, NULL);
68
child->parent_relative_bg = TRUE;
72
/* Nothing to do; the icon will sit on top of an ugly gray box */
73
child->parent_relative_bg = FALSE;
76
gdk_window_set_composited (window, child->composited);
78
gtk_widget_set_app_paintable (GTK_WIDGET (child),
79
child->parent_relative_bg || child->has_alpha);
81
/* Double-buffering will interfere with the parent-relative-background fake
82
* transparency, since the double-buffer code doesn't know how to fill in the
83
* background of the double-buffer correctly.
85
gtk_widget_set_double_buffered (GTK_WIDGET (child),
86
child->parent_relative_bg);
90
na_tray_child_style_set (GtkWidget *widget,
91
GtkStyle *previous_style)
93
/* The default handler resets the background according to the new style.
94
* We either use a transparent background or a parent-relative background
95
* and ignore the style background. So, just don't chain up.
100
/* This is adapted from code that was commented out in na-tray-manager.c; the
101
* code in na-tray-manager.c wouldn't have worked reliably, this will. So maybe
102
* it can be reenabled. On other hand, things seem to be working fine without
105
* If reenabling, you need to hook it up in na_tray_child_class_init().
108
na_tray_child_size_request (GtkWidget *widget,
109
GtkRequisition *request)
111
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_request (widget, request);
114
* Make sure the icons have a meaningful size ..
116
if ((request->width < 16) || (request->height < 16))
118
gint nw = MAX (24, request->width);
119
gint nh = MAX (24, request->height);
120
g_warning ("Tray icon has requested a size of (%ix%i), resizing to (%ix%i)",
121
req.width, req.height, nw, nh);
123
request->height = nh;
129
na_tray_child_size_allocate (GtkWidget *widget,
130
GtkAllocation *allocation)
132
NaTrayChild *child = NA_TRAY_CHILD (widget);
133
GtkAllocation widget_allocation;
134
gboolean moved, resized;
136
gtk_widget_get_allocation (widget, &widget_allocation);
138
moved = (allocation->x != widget_allocation.x ||
139
allocation->y != widget_allocation.y);
140
resized = (allocation->width != widget_allocation.width ||
141
allocation->height != widget_allocation.height);
143
/* When we are allocating the widget while mapped we need special handling
144
* for both real and fake transparency.
146
* Real transparency: we need to invalidate and trigger a redraw of the old
147
* and new areas. (GDK really should handle this for us, but doesn't as of
150
* Fake transparency: if the widget moved, we need to force the contents to
151
* be redrawn with the new offset for the parent-relative background.
153
if ((moved || resized) && gtk_widget_get_mapped (widget))
155
if (na_tray_child_has_alpha (child))
156
gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
157
&widget_allocation, FALSE);
160
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget,
163
if ((moved || resized) && gtk_widget_get_mapped (widget))
165
if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget)))
166
gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
167
&widget_allocation, FALSE);
168
else if (moved && child->parent_relative_bg)
169
na_tray_child_force_redraw (child);
173
/* The plug window should completely occupy the area of the child, so we won't
174
* get a draw event. But in case we do (the plug unmaps itself, say), this
175
* draw handler draws with real or fake transparency.
178
na_tray_child_draw (GtkWidget *widget,
181
NaTrayChild *child = NA_TRAY_CHILD (widget);
183
if (na_tray_child_has_alpha (child))
185
/* Clear to transparent */
186
cairo_set_source_rgba (cr, 0, 0, 0, 0);
187
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
190
else if (child->parent_relative_bg)
193
cairo_surface_t *target;
194
GdkRectangle clip_rect;
196
window = gtk_widget_get_window (widget);
197
target = cairo_get_group_target (cr);
199
gdk_cairo_get_clip_rectangle (cr, &clip_rect);
201
/* Clear to parent-relative pixmap
202
* We need to use direct X access here because GDK doesn't know about
203
* the parent relative pixmap. */
204
cairo_surface_flush (target);
206
XClearArea (GDK_WINDOW_XDISPLAY (window),
207
GDK_WINDOW_XID (window),
208
clip_rect.x, clip_rect.y,
209
clip_rect.width, clip_rect.height,
211
cairo_surface_mark_dirty_rectangle (target,
212
clip_rect.x, clip_rect.y,
213
clip_rect.width, clip_rect.height);
220
na_tray_child_init (NaTrayChild *child)
226
na_tray_child_get_preferred_width (GtkWidget* self,
230
*minimum_width = *natural_width = 24;
233
na_tray_child_get_preferred_height (GtkWidget* self,
234
gint* minimum_height,
235
gint* natural_height)
237
*minimum_height = *natural_height = 24;
241
na_tray_child_class_init (NaTrayChildClass *klass)
243
GObjectClass *gobject_class;
244
GtkWidgetClass *widget_class;
246
gobject_class = (GObjectClass *)klass;
247
widget_class = (GtkWidgetClass *)klass;
249
gobject_class->finalize = na_tray_child_finalize;
250
widget_class->style_set = na_tray_child_style_set;
251
widget_class->realize = na_tray_child_realize;
252
widget_class->size_allocate = na_tray_child_size_allocate;
253
widget_class->draw = na_tray_child_draw;
254
// we force the size of the icons, because some programs (like xchat) don't respect the _NET_SYSTEM_TRAY_ICON_SIZE property.
255
widget_class->get_preferred_width = na_tray_child_get_preferred_width;
256
widget_class->get_preferred_height = na_tray_child_get_preferred_height;
260
na_tray_child_new (GdkScreen *screen,
263
XWindowAttributes window_attributes;
267
gboolean visual_has_alpha;
268
int red_prec, green_prec, blue_prec, depth;
271
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
272
g_return_val_if_fail (icon_window != None, NULL);
274
xdisplay = GDK_SCREEN_XDISPLAY (screen);
276
/* We need to determine the visual of the window we are embedding and create
277
* the socket in the same visual.
280
gdk_error_trap_push ();
281
result = XGetWindowAttributes (xdisplay, icon_window,
283
gdk_error_trap_pop_ignored ();
285
if (!result) /* Window already gone */
288
visual = gdk_x11_screen_lookup_visual (screen,
289
window_attributes.visual->visualid);
290
if (!visual) /* Icon window is on another screen? */
293
child = g_object_new (NA_TYPE_TRAY_CHILD, NULL);
294
child->icon_window = icon_window;
296
gtk_widget_set_visual (GTK_WIDGET (child), visual);
298
/* We have alpha if the visual has something other than red, green,
300
gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
301
gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
302
gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
303
depth = gdk_visual_get_depth (visual);
305
visual_has_alpha = red_prec + blue_prec + green_prec < depth;
306
child->has_alpha = (visual_has_alpha &&
307
gdk_display_supports_composite (gdk_screen_get_display (screen)));
309
child->composited = child->has_alpha;
311
return GTK_WIDGET (child);
315
na_tray_child_get_title (NaTrayChild *child)
319
Atom utf8_string, atom, type;
326
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL);
328
display = gtk_widget_get_display (GTK_WIDGET (child));
330
utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
331
atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
333
gdk_error_trap_push ();
335
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
340
&type, &format, &nitems,
341
&bytes_after, (guchar **)&val);
343
if (gdk_error_trap_pop () || result != Success)
346
if (type != utf8_string ||
355
if (!g_utf8_validate (val, nitems, NULL))
361
retval = g_strndup (val, nitems);
369
* na_tray_child_has_alpha;
370
* @child: a #NaTrayChild
372
* Checks if the child has an ARGB visual and real alpha transparence.
373
* (as opposed to faked alpha transparency with an parent-relative
376
* Return value: %TRUE if the child has an alpha transparency
379
na_tray_child_has_alpha (NaTrayChild *child)
381
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
383
return child->has_alpha;
387
* na_tray_child_set_composited;
388
* @child: a #NaTrayChild
389
* @composited: %TRUE if the child's window should be redirected
391
* Sets whether the #GdkWindow of the child should be set redirected
392
* using gdk_window_set_composited(). By default this is based off of
393
* na_tray_child_has_alpha(), but it may be useful to override it in
394
* certain circumstances; for example, if the #NaTrayChild is added
395
* to a parent window and that parent window is composited against the
399
na_tray_child_set_composited (NaTrayChild *child,
402
g_return_if_fail (NA_IS_TRAY_CHILD (child));
404
if (child->composited == composited)
407
child->composited = composited;
408
if (gtk_widget_get_realized (GTK_WIDGET (child)))
409
gdk_window_set_composited (gtk_widget_get_window (GTK_WIDGET (child)),
413
/* If we are faking transparency with a window-relative background, force a
414
* redraw of the icon. This should be called if the background changes or if
415
* the child is shifted with respect to the background.
418
na_tray_child_force_redraw (NaTrayChild *child)
420
GtkWidget *widget = GTK_WIDGET (child);
422
if (gtk_widget_get_mapped (widget) && child->parent_relative_bg)
425
/* Sending an ExposeEvent might cause redraw problems if the
426
* icon is expecting the server to clear-to-background before
427
* the redraw. It should be ok for GtkStatusIcon or EggTrayIcon.
429
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget));
431
GdkWindow *plug_window;
432
GtkAllocation allocation;
434
plug_window = gtk_socket_get_plug_window (GTK_SOCKET (child));
435
gtk_widget_get_allocation (widget, &allocation);
437
xev.xexpose.type = Expose;
438
xev.xexpose.window = GDK_WINDOW_XID (plug_window);
441
xev.xexpose.width = allocation.width;
442
xev.xexpose.height = allocation.height;
443
xev.xexpose.count = 0;
445
gdk_error_trap_push ();
446
XSendEvent (xdisplay,
450
gdk_error_trap_pop_ignored ();
452
/* Hiding and showing is the safe way to do it, but can result in more
455
gdk_window_hide (widget->window);
456
gdk_window_show (widget->window);
461
/* from libwnck/xutils.c, comes as LGPLv2+ */
463
latin1_to_utf8 (const char *latin1)
468
str = g_string_new (NULL);
473
g_string_append_unichar (str, (gunichar) *p);
477
return g_string_free (str, FALSE);
480
/* derived from libwnck/xutils.c, comes as LGPLv2+ */
482
_get_wmclass (Display *xdisplay,
492
gdk_error_trap_push ();
493
XGetClassHint (xdisplay, xwindow, &ch);
494
gdk_error_trap_pop_ignored ();
505
*res_name = latin1_to_utf8 (ch.res_name);
513
*res_class = latin1_to_utf8 (ch.res_class);
515
XFree (ch.res_class);
520
* na_tray_child_get_wm_class;
521
* @child: a #NaTrayChild
522
* @res_name: return location for a string containing the application name of
524
* @res_class: return location for a string containing the application class of
527
* Fetches the resource associated with @child.
530
na_tray_child_get_wm_class (NaTrayChild *child,
536
g_return_if_fail (NA_IS_TRAY_CHILD (child));
538
display = gtk_widget_get_display (GTK_WIDGET (child));
540
_get_wmclass (GDK_DISPLAY_XDISPLAY (display),