1 /*
2 * Copyright (c) 2002 Anders Carlsson <andersca@gnu.org>
3 * Copyright (c) 2003-2006 Vincent Untz
4 * Copyright (c) 2008 Red Hat, Inc.
5 * Copyright (c) 2009-2010 Nick Schermer <nick@xfce.org>
6 *
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #endif
29
30 #include <X11/Xlib.h>
31 #include <X11/Xatom.h>
32
33 #include <gdk/gdk.h>
34 #include <gdk/gdkx.h>
35 #include <gtk/gtk.h>
36
37 #include <libxfce4panel/libxfce4panel.h>
38
39 #include <common/panel-private.h>
40 #include <common/panel-debug.h>
41
42 #include "systray-socket.h"
43
44
45
46 struct _SystraySocketClass
47 {
48 GtkSocketClass __parent__;
49 };
50
51 struct _SystraySocket
52 {
53 GtkSocket __parent__;
54
55 /* plug window */
56 Window window;
57
58 gchar *name;
59
60 guint is_composited : 1;
61 guint parent_relative_bg : 1;
62 guint hidden : 1;
63 };
64
65
66
67 static void systray_socket_finalize (GObject *object);
68 static void systray_socket_realize (GtkWidget *widget);
69 static void systray_socket_size_allocate (GtkWidget *widget,
70 GtkAllocation *allocation);
71 static gboolean systray_socket_draw (GtkWidget *widget,
72 cairo_t *cr);
73 static void systray_socket_style_set (GtkWidget *widget,
74 GtkStyle *previous_style);
75
76
77
XFCE_PANEL_DEFINE_TYPE(SystraySocket,systray_socket,GTK_TYPE_SOCKET)78 XFCE_PANEL_DEFINE_TYPE (SystraySocket, systray_socket, GTK_TYPE_SOCKET)
79
80
81
82 static void
83 systray_socket_class_init (SystraySocketClass *klass)
84 {
85 GtkWidgetClass *gtkwidget_class;
86 GObjectClass *gobject_class;
87
88 gobject_class = G_OBJECT_CLASS (klass);
89 gobject_class->finalize = systray_socket_finalize;
90
91 gtkwidget_class = GTK_WIDGET_CLASS (klass);
92 gtkwidget_class->realize = systray_socket_realize;
93 gtkwidget_class->size_allocate = systray_socket_size_allocate;
94 gtkwidget_class->draw = systray_socket_draw;
95 gtkwidget_class->style_set = systray_socket_style_set;
96 }
97
98
99
100 static void
systray_socket_init(SystraySocket * socket)101 systray_socket_init (SystraySocket *socket)
102 {
103 socket->hidden = FALSE;
104 socket->name = NULL;
105 }
106
107
108
109 static void
systray_socket_finalize(GObject * object)110 systray_socket_finalize (GObject *object)
111 {
112 SystraySocket *socket = XFCE_SYSTRAY_SOCKET (object);
113
114 g_free (socket->name);
115
116 G_OBJECT_CLASS (systray_socket_parent_class)->finalize (object);
117 }
118
119
120
121 static void
systray_socket_realize(GtkWidget * widget)122 systray_socket_realize (GtkWidget *widget)
123 {
124 SystraySocket *socket = XFCE_SYSTRAY_SOCKET (widget);
125 GdkRGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
126 GdkWindow *window;
127
128 GTK_WIDGET_CLASS (systray_socket_parent_class)->realize (widget);
129
130 window = gtk_widget_get_window (widget);
131
132 if (socket->is_composited)
133 {
134 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
135 gdk_window_set_background_rgba (window, &transparent);
136 gdk_window_set_composited (window, TRUE);
137 G_GNUC_END_IGNORE_DEPRECATIONS
138
139 socket->parent_relative_bg = FALSE;
140 }
141 else if (gtk_widget_get_visual (widget) ==
142 gdk_window_get_visual (gdk_window_get_parent (window)))
143 {
144 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
145 gdk_window_set_background_pattern (window, NULL);
146 G_GNUC_END_IGNORE_DEPRECATIONS
147
148 socket->parent_relative_bg = TRUE;
149 }
150 else
151 {
152 socket->parent_relative_bg = FALSE;
153 }
154
155 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
156 gdk_window_set_composited (window, socket->is_composited);
157 G_GNUC_END_IGNORE_DEPRECATIONS
158
159 gtk_widget_set_app_paintable (widget,
160 socket->parent_relative_bg || socket->is_composited);
161
162 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
163 gtk_widget_set_double_buffered (widget, socket->parent_relative_bg);
164 G_GNUC_END_IGNORE_DEPRECATIONS
165
166 panel_debug_filtered (PANEL_DEBUG_SYSTRAY,
167 "socket %s[%p] (composited=%s, relative-bg=%s",
168 systray_socket_get_name (socket), socket,
169 PANEL_DEBUG_BOOL (socket->is_composited),
170 PANEL_DEBUG_BOOL (socket->parent_relative_bg));
171 }
172
173
174
175 static void
systray_socket_size_allocate(GtkWidget * widget,GtkAllocation * allocation)176 systray_socket_size_allocate (GtkWidget *widget,
177 GtkAllocation *allocation)
178 {
179 SystraySocket *socket = XFCE_SYSTRAY_SOCKET (widget);
180 GtkAllocation widget_allocation;
181 gboolean moved;
182 gboolean resized;
183
184 gtk_widget_get_allocation (widget, &widget_allocation);
185 moved = allocation->x != widget_allocation.x
186 || allocation->y != widget_allocation.y;
187 resized = allocation->width != widget_allocation.width
188 ||allocation->height != widget_allocation.height;
189
190 if ((moved || resized)
191 && gtk_widget_get_mapped (widget))
192 {
193 if (socket->is_composited)
194 gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
195 &widget_allocation, FALSE);
196 }
197
198 GTK_WIDGET_CLASS (systray_socket_parent_class)->size_allocate (widget, allocation);
199
200 if ((moved || resized)
201 && gtk_widget_get_mapped (widget))
202 {
203 if (socket->is_composited)
204 gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
205 &widget_allocation, FALSE);
206 else if (moved && socket->parent_relative_bg)
207 systray_socket_force_redraw (socket);
208 }
209 }
210
211
212
213 static gboolean
systray_socket_draw(GtkWidget * widget,cairo_t * cr)214 systray_socket_draw (GtkWidget *widget,
215 cairo_t *cr)
216 {
217 SystraySocket *socket = XFCE_SYSTRAY_SOCKET (widget);
218
219 if (socket->is_composited)
220 {
221 /* clear to transparent */
222 cairo_set_source_rgba (cr, 0, 0, 0, 0);
223 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
224 cairo_fill (cr);
225 }
226 else if (socket->parent_relative_bg)
227 {
228 /* clear to parent-relative pixmap */
229 cairo_set_source_rgb (cr, 0, 0, 0);
230 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
231 cairo_fill (cr);
232 }
233
234 return FALSE;
235 }
236
237
238
239 static void
systray_socket_style_set(GtkWidget * widget,GtkStyle * previous_style)240 systray_socket_style_set (GtkWidget *widget,
241 GtkStyle *previous_style)
242 {
243 }
244
245
246
247 GtkWidget *
systray_socket_new(GdkScreen * screen,Window window)248 systray_socket_new (GdkScreen *screen,
249 Window window)
250 {
251 SystraySocket *socket;
252 GdkDisplay *display;
253 XWindowAttributes attr;
254 gint result;
255 GdkVisual *visual;
256 gint red_prec, green_prec, blue_prec;
257 gboolean supports_composite = FALSE;
258
259 panel_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
260
261 /* get the window attributes */
262 display = gdk_screen_get_display (screen);
263 gdk_x11_display_error_trap_push (display);
264 result = XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (display),
265 window, &attr);
266
267 /* leave on an error or if the window does not exist */
268 if (gdk_x11_display_error_trap_pop (display) != 0 || result == 0)
269 return NULL;
270
271 /* get the windows visual */
272 visual = gdk_x11_screen_lookup_visual (screen, attr.visual->visualid);
273 panel_return_val_if_fail (visual == NULL || GDK_IS_VISUAL (visual), NULL);
274 if (G_UNLIKELY (visual == NULL))
275 return NULL;
276
277 /* create a new socket */
278 socket = g_object_new (XFCE_TYPE_SYSTRAY_SOCKET, NULL);
279 socket->window = window;
280 socket->is_composited = FALSE;
281 gtk_widget_set_visual (GTK_WIDGET (socket), visual);
282
283 /* check if there is an alpha channel in the visual */
284 gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
285 gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
286 gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
287 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
288 supports_composite = gdk_display_supports_composite (gdk_screen_get_display (screen));
289 G_GNUC_END_IGNORE_DEPRECATIONS
290 if (red_prec + blue_prec + green_prec < gdk_visual_get_depth (visual)
291 && supports_composite)
292 socket->is_composited = TRUE;
293
294 return GTK_WIDGET (socket);
295 }
296
297
298
299 void
systray_socket_force_redraw(SystraySocket * socket)300 systray_socket_force_redraw (SystraySocket *socket)
301 {
302 GtkWidget *widget = GTK_WIDGET (socket);
303 XEvent xev;
304 GdkDisplay *display;
305 GtkAllocation allocation;
306
307 panel_return_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket));
308
309 if (gtk_widget_get_mapped (widget) && socket->parent_relative_bg)
310 {
311 display = gtk_widget_get_display (widget);
312
313 gtk_widget_get_allocation (widget, &allocation);
314
315 xev.xexpose.type = Expose;
316 xev.xexpose.window = GDK_WINDOW_XID (gtk_socket_get_plug_window (GTK_SOCKET (socket)));
317 xev.xexpose.x = 0;
318 xev.xexpose.y = 0;
319 xev.xexpose.width = allocation.width;
320 xev.xexpose.height = allocation.height;
321 xev.xexpose.count = 0;
322
323 gdk_x11_display_error_trap_push (display);
324 XSendEvent (GDK_DISPLAY_XDISPLAY (display),
325 xev.xexpose.window,
326 False, ExposureMask,
327 &xev);
328 /* We have to sync to reliably catch errors from the XSendEvent(),
329 * since that is asynchronous.
330 */
331 XSync (GDK_DISPLAY_XDISPLAY (display), False);
332 gdk_x11_display_error_trap_pop_ignored (display);
333 }
334 }
335
336
337
338 gboolean
systray_socket_is_composited(SystraySocket * socket)339 systray_socket_is_composited (SystraySocket *socket)
340 {
341 panel_return_val_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket), FALSE);
342
343 return socket->is_composited;
344 }
345
346
347
348 static gchar *
systray_socket_get_name_prop(SystraySocket * socket,const gchar * prop_name,const gchar * type_name)349 systray_socket_get_name_prop (SystraySocket *socket,
350 const gchar *prop_name,
351 const gchar *type_name)
352 {
353 GdkDisplay *display;
354 Atom req_type, type;
355 gint result;
356 gchar *val;
357 gint format;
358 gulong nitems;
359 gulong bytes_after;
360 gchar *name = NULL;
361
362 panel_return_val_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket), NULL);
363 panel_return_val_if_fail (type_name != NULL && prop_name != NULL, NULL);
364
365 display = gtk_widget_get_display (GTK_WIDGET (socket));
366
367 req_type = gdk_x11_get_xatom_by_name_for_display (display, type_name);
368
369 gdk_x11_display_error_trap_push (display);
370
371 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
372 socket->window,
373 gdk_x11_get_xatom_by_name_for_display (display, prop_name),
374 0, G_MAXLONG, False,
375 req_type,
376 &type, &format, &nitems,
377 &bytes_after,
378 (guchar **) &val);
379
380 /* check if everything went fine */
381 if (gdk_x11_display_error_trap_pop (display) != 0
382 || result != Success
383 || val == NULL)
384 return NULL;
385
386 /* check the returned data */
387 if (type == req_type
388 && format == 8
389 && nitems > 0
390 && g_utf8_validate (val, nitems, NULL))
391 {
392 /* lowercase the result */
393 name = g_utf8_strdown (val, nitems);
394 }
395
396 XFree (val);
397
398 return name;
399 }
400
401
402
403 const gchar *
systray_socket_get_name(SystraySocket * socket)404 systray_socket_get_name (SystraySocket *socket)
405 {
406 panel_return_val_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket), NULL);
407
408 if (G_LIKELY (socket->name != NULL))
409 return socket->name;
410
411 /* try _NET_WM_NAME first, for gtk icon implementations, fall back to
412 * WM_NAME for qt icons */
413 socket->name = systray_socket_get_name_prop (socket, "_NET_WM_NAME", "UTF8_STRING");
414 if (G_UNLIKELY (socket->name == NULL))
415 socket->name = systray_socket_get_name_prop (socket, "WM_NAME", "STRING");
416
417 return socket->name;
418 }
419
420
421
422 Window *
systray_socket_get_window(SystraySocket * socket)423 systray_socket_get_window (SystraySocket *socket)
424 {
425 panel_return_val_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket), NULL);
426
427 return &socket->window;
428 }
429
430
431
432 gboolean
systray_socket_get_hidden(SystraySocket * socket)433 systray_socket_get_hidden (SystraySocket *socket)
434 {
435 panel_return_val_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket), FALSE);
436
437 return socket->hidden;
438 }
439
440
441
442 void
systray_socket_set_hidden(SystraySocket * socket,gboolean hidden)443 systray_socket_set_hidden (SystraySocket *socket,
444 gboolean hidden)
445 {
446 panel_return_if_fail (XFCE_IS_SYSTRAY_SOCKET (socket));
447
448 socket->hidden = hidden;
449 }
450