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