1 /*
2 * Copyright (c) 2002 Anders Carlsson <andersca@gnu.org>
3 * Copyright (c) 2003-2004 Benedikt Meurer <benny@xfce.org>
4 * Copyright (c) 2003-2004 Olivier Fourdan <fourdan@xfce.org>
5 * Copyright (c) 2003-2006 Vincent Untz
6 * Copyright (c) 2007-2010 Nick Schermer <nick@xfce.org>
7 *
8 * This library is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #ifdef HAVE_STRING_H
28 #include <string.h>
29 #endif
30
31 #include <X11/Xlib.h>
32 #include <X11/Xatom.h>
33
34 #include <gdk/gdk.h>
35 #include <gdk/gdkx.h>
36 #include <gtk/gtk.h>
37
38 #include <common/panel-private.h>
39 #include <common/panel-debug.h>
40
41 #include <libxfce4panel/libxfce4panel.h>
42 #include <libxfce4util/libxfce4util.h>
43
44 #include "systray-manager.h"
45 #include "systray-socket.h"
46 #include "systray-marshal.h"
47
48
49
50 #define XFCE_SYSTRAY_MANAGER_REQUEST_DOCK 0
51 #define XFCE_SYSTRAY_MANAGER_BEGIN_MESSAGE 1
52 #define XFCE_SYSTRAY_MANAGER_CANCEL_MESSAGE 2
53
54 #define XFCE_SYSTRAY_MANAGER_ORIENTATION_HORIZONTAL 0
55 #define XFCE_SYSTRAY_MANAGER_ORIENTATION_VERTICAL 1
56
57
58
59 static void systray_manager_finalize (GObject *object);
60 static void systray_manager_remove_socket (gpointer key,
61 gpointer value,
62 gpointer user_data);
63 static GdkFilterReturn systray_manager_window_filter (GdkXEvent *xev,
64 GdkEvent *event,
65 gpointer user_data);
66 static GdkFilterReturn systray_manager_handle_client_message_opcode (GdkXEvent *xevent,
67 GdkEvent *event,
68 gpointer user_data);
69 static GdkFilterReturn systray_manager_handle_client_message_message_data (GdkXEvent *xevent,
70 GdkEvent *event,
71 gpointer user_data);
72 static void systray_manager_handle_begin_message (SystrayManager *manager,
73 XClientMessageEvent *xevent);
74 static void systray_manager_handle_cancel_message (SystrayManager *manager,
75 XClientMessageEvent *xevent);
76 static void systray_manager_handle_dock_request (SystrayManager *manager,
77 XClientMessageEvent *xevent);
78 static gboolean systray_manager_handle_undock_request (GtkSocket *socket,
79 gpointer user_data);
80 static void systray_manager_set_visual (SystrayManager *manager);
81 static void systray_manager_set_colors_property (SystrayManager *manager);
82 static void systray_manager_message_free (SystrayMessage *message);
83 static void systray_manager_message_remove_from_list (SystrayManager *manager,
84 XClientMessageEvent *xevent);
85
86
87
88 enum
89 {
90 ICON_ADDED,
91 ICON_REMOVED,
92 MESSAGE_SENT,
93 MESSAGE_CANCELLED,
94 LOST_SELECTION,
95 LAST_SIGNAL
96 };
97
98 struct _SystrayManagerClass
99 {
100 GObjectClass __parent__;
101 };
102
103 struct _SystrayManager
104 {
105 GObject __parent__;
106
107 /* invisible window */
108 GtkWidget *invisible;
109
110 /* list of client sockets */
111 GHashTable *sockets;
112
113 /* symbolic colors */
114 GdkColor fg;
115 GdkColor error;
116 GdkColor warning;
117 GdkColor success;
118
119 /* orientation of the tray */
120 GtkOrientation orientation;
121
122 /* list of pending messages */
123 GSList *messages;
124
125 /* _net_system_tray_opcode atom */
126 Atom opcode_atom;
127
128 /* _net_system_tray_message_data atom */
129 Atom data_atom;
130
131 /* _net_system_tray_s%d atom */
132 GdkAtom selection_atom;
133 };
134
135 struct _SystrayMessage
136 {
137 /* message string */
138 gchar *string;
139
140 /* message id */
141 glong id;
142
143 /* x11 window */
144 Window window;
145
146 /* numb3rs */
147 glong length;
148 glong remaining_length;
149 glong timeout;
150 };
151
152
153
154 static guint systray_manager_signals[LAST_SIGNAL];
155
156
157
XFCE_PANEL_DEFINE_TYPE(SystrayManager,systray_manager,G_TYPE_OBJECT)158 XFCE_PANEL_DEFINE_TYPE (SystrayManager, systray_manager, G_TYPE_OBJECT)
159
160
161
162 static void
163 systray_manager_class_init (SystrayManagerClass *klass)
164 {
165 GObjectClass *gobject_class;
166
167 gobject_class = G_OBJECT_CLASS (klass);
168 gobject_class->finalize = systray_manager_finalize;
169
170 systray_manager_signals[ICON_ADDED] =
171 g_signal_new (g_intern_static_string ("icon-added"),
172 G_OBJECT_CLASS_TYPE (klass),
173 G_SIGNAL_RUN_LAST,
174 0, NULL, NULL,
175 g_cclosure_marshal_VOID__OBJECT,
176 G_TYPE_NONE, 1,
177 GTK_TYPE_SOCKET);
178
179 systray_manager_signals[ICON_REMOVED] =
180 g_signal_new (g_intern_static_string ("icon-removed"),
181 G_OBJECT_CLASS_TYPE (klass),
182 G_SIGNAL_RUN_LAST,
183 0, NULL, NULL,
184 g_cclosure_marshal_VOID__OBJECT,
185 G_TYPE_NONE, 1,
186 GTK_TYPE_SOCKET);
187
188 systray_manager_signals[MESSAGE_SENT] =
189 g_signal_new (g_intern_static_string ("message-sent"),
190 G_OBJECT_CLASS_TYPE (klass),
191 G_SIGNAL_RUN_LAST,
192 0, NULL, NULL,
193 _systray_marshal_VOID__OBJECT_STRING_LONG_LONG,
194 G_TYPE_NONE, 4,
195 GTK_TYPE_SOCKET,
196 G_TYPE_STRING,
197 G_TYPE_LONG,
198 G_TYPE_LONG);
199
200 systray_manager_signals[MESSAGE_CANCELLED] =
201 g_signal_new (g_intern_static_string ("message-cancelled"),
202 G_OBJECT_CLASS_TYPE (klass),
203 G_SIGNAL_RUN_LAST,
204 0, NULL, NULL,
205 _systray_marshal_VOID__OBJECT_LONG,
206 G_TYPE_NONE, 2,
207 GTK_TYPE_SOCKET,
208 G_TYPE_LONG);
209
210 systray_manager_signals[LOST_SELECTION] =
211 g_signal_new (g_intern_static_string ("lost-selection"),
212 G_OBJECT_CLASS_TYPE (klass),
213 G_SIGNAL_RUN_LAST,
214 0, NULL, NULL,
215 g_cclosure_marshal_VOID__VOID,
216 G_TYPE_NONE, 0);
217 }
218
219
220
221 static void
systray_manager_init(SystrayManager * manager)222 systray_manager_init (SystrayManager *manager)
223 {
224 manager->invisible = NULL;
225 manager->orientation = GTK_ORIENTATION_HORIZONTAL;
226 manager->messages = NULL;
227 manager->sockets = g_hash_table_new (NULL, NULL);
228
229 /* initialize symbolic colors */
230 manager->fg.red = 0.0;
231 manager->fg.green = 0.0;
232 manager->fg.blue = 0.0;
233
234 manager->error.red = 1.0;
235 manager->error.green = 0.0;
236 manager->error.blue = 0.0;
237
238 manager->warning.red = 1.0;
239 manager->warning.green = 1.0;
240 manager->warning.blue = 0.0;
241
242 manager->success.red = 0.0;
243 manager->success.green = 1.0;
244 manager->success.blue = 0.0;
245 }
246
247
248
249 GQuark
systray_manager_error_quark(void)250 systray_manager_error_quark (void)
251 {
252 static GQuark q = 0;
253
254 if (q == 0)
255 q = g_quark_from_static_string ("systray-manager-error-quark");
256
257 return q;
258 }
259
260
261
262 static void
systray_manager_finalize(GObject * object)263 systray_manager_finalize (GObject *object)
264 {
265 SystrayManager *manager = XFCE_SYSTRAY_MANAGER (object);
266
267 panel_return_if_fail (manager->invisible == NULL);
268
269 /* destroy the hash table */
270 g_hash_table_destroy (manager->sockets);
271
272 if (manager->messages)
273 {
274 /* cleanup all pending messages */
275 g_slist_foreach (manager->messages,
276 (GFunc) (void (*)(void)) systray_manager_message_free, NULL);
277
278 /* free the list */
279 g_slist_free (manager->messages);
280 }
281
282 G_OBJECT_CLASS (systray_manager_parent_class)->finalize (object);
283 }
284
285
286
287 SystrayManager *
systray_manager_new(void)288 systray_manager_new (void)
289 {
290 return g_object_new (XFCE_TYPE_SYSTRAY_MANAGER, NULL);
291 }
292
293
294
295 #if 0
296 gboolean
297 systray_manager_check_running (GdkScreen *screen)
298 {
299 gchar *selection_name;
300 GdkDisplay *display;
301 Atom selection_atom;
302
303 panel_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
304
305 /* get the display */
306 display = gdk_screen_get_display (screen);
307
308 /* create the selection atom name */
309 selection_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
310 gdk_screen_get_number (screen));
311
312 /* get the atom */
313 selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
314 selection_name);
315
316 g_free (selection_name);
317
318 /* return result */
319 return (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), selection_atom) != None);
320 }
321 #endif
322
323
324
325 gboolean
systray_manager_register(SystrayManager * manager,GdkScreen * screen,GError ** error)326 systray_manager_register (SystrayManager *manager,
327 GdkScreen *screen,
328 GError **error)
329 {
330 GdkDisplay *display;
331 gchar *selection_name;
332 gboolean succeed;
333 gint screen_number;
334 GtkWidget *invisible;
335 guint32 timestamp;
336 GdkAtom opcode_atom;
337 GdkAtom data_atom;
338 XClientMessageEvent xevent;
339 Window root_window;
340
341 panel_return_val_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager), FALSE);
342 panel_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
343 panel_return_val_if_fail (error == NULL || *error == NULL, FALSE);
344
345 /* create invisible window */
346 invisible = gtk_invisible_new_for_screen (screen);
347 gtk_widget_realize (invisible);
348
349 /* let the invisible window monitor property and configuration changes */
350 gtk_widget_add_events (invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
351
352 /* get the screen number */
353 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
354 screen_number = gdk_screen_get_number (screen);
355 G_GNUC_END_IGNORE_DEPRECATIONS
356
357 /* create the selection atom name */
358 selection_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", screen_number);
359
360 /* get the selection atom */
361 manager->selection_atom = gdk_atom_intern (selection_name, FALSE);
362
363 g_free (selection_name);
364
365 /* get the display */
366 display = gdk_screen_get_display (screen);
367
368 /* set the invisible window and take a reference */
369 manager->invisible = GTK_WIDGET (g_object_ref (G_OBJECT (invisible)));
370
371 /* set the visual property for transparent tray icons */
372 systray_manager_set_visual (manager);
373
374 /* set the property for symbolic color support */
375 systray_manager_set_colors_property (manager);
376
377 /* get the current x server time stamp */
378 timestamp = gdk_x11_get_server_time (gtk_widget_get_window (GTK_WIDGET (invisible)));
379
380 /* try to become the selection owner of this display */
381 succeed = gdk_selection_owner_set_for_display (display,
382 gtk_widget_get_window (GTK_WIDGET (invisible)),
383 manager->selection_atom,
384 timestamp, TRUE);
385
386 if (G_LIKELY (succeed))
387 {
388 /* get the root window */
389 root_window = RootWindowOfScreen (GDK_SCREEN_XSCREEN (screen));
390
391 /* send a message to x11 that we're going to handle this display */
392 xevent.type = ClientMessage;
393 xevent.window = root_window;
394 xevent.message_type = gdk_x11_get_xatom_by_name_for_display (display, "MANAGER");
395 xevent.format = 32;
396 xevent.data.l[0] = timestamp;
397 xevent.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
398 manager->selection_atom);
399 xevent.data.l[2] = GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (invisible)));
400 xevent.data.l[3] = 0;
401 xevent.data.l[4] = 0;
402
403 /* send the message */
404 XSendEvent (GDK_DISPLAY_XDISPLAY (display), root_window,
405 False, StructureNotifyMask, (XEvent *)&xevent);
406
407 /* system_tray_request_dock, system_tray_begin_message, system_tray_cancel_message and selectionclear */
408 gdk_window_add_filter (gtk_widget_get_window (GTK_WIDGET (invisible)),
409 systray_manager_window_filter, manager);
410
411 /* get the opcode atom (for both gdk and x11) */
412 opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
413 manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display, opcode_atom);
414
415 data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA", FALSE);
416 manager->data_atom = gdk_x11_atom_to_xatom_for_display (display, data_atom);
417
418 panel_debug (PANEL_DEBUG_SYSTRAY, "registered manager on screen %d", screen_number);
419 }
420 else
421 {
422 /* release the invisible */
423 g_object_unref (G_OBJECT (manager->invisible));
424 manager->invisible = NULL;
425
426 /* desktroy the invisible window */
427 gtk_widget_destroy (invisible);
428
429 /* set an error */
430 g_set_error (error, XFCE_SYSTRAY_MANAGER_ERROR,
431 XFCE_SYSTRAY_MANAGER_ERROR_SELECTION_FAILED,
432 _("Failed to acquire manager selection for screen %d"),
433 screen_number);
434 }
435
436 return succeed;
437 }
438
439
440
441 static void
systray_manager_remove_socket(gpointer key,gpointer value,gpointer user_data)442 systray_manager_remove_socket (gpointer key,
443 gpointer value,
444 gpointer user_data)
445 {
446 SystrayManager *manager = XFCE_SYSTRAY_MANAGER (user_data);
447 GtkSocket *socket = GTK_SOCKET (value);
448
449 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
450 panel_return_if_fail (GTK_IS_SOCKET (socket));
451
452 /* properly undock from the tray */
453 g_signal_emit (manager, systray_manager_signals[ICON_REMOVED], 0, socket);
454 }
455
456
457
458 void
systray_manager_unregister(SystrayManager * manager)459 systray_manager_unregister (SystrayManager *manager)
460 {
461 GdkDisplay *display;
462 GtkWidget *invisible = manager->invisible;
463 GdkWindow *owner;
464
465 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
466
467 /* leave when there is no invisible window */
468 if (G_UNLIKELY (invisible == NULL))
469 return;
470
471 panel_return_if_fail (GTK_IS_INVISIBLE (invisible));
472 panel_return_if_fail (gtk_widget_get_realized (invisible));
473 panel_return_if_fail (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (invisible))));
474
475 /* get the display of the invisible window */
476 display = gtk_widget_get_display (invisible);
477
478 /* remove our handling of the selection if we're the owner */
479 owner = gdk_selection_owner_get_for_display (display, manager->selection_atom);
480 if (owner == gtk_widget_get_window (GTK_WIDGET (invisible)))
481 {
482 gdk_selection_owner_set_for_display (display, NULL,
483 manager->selection_atom,
484 gdk_x11_get_server_time (gtk_widget_get_window (GTK_WIDGET (invisible))),
485 TRUE);
486 }
487
488 /* remove window filter */
489 gdk_window_remove_filter (gtk_widget_get_window (GTK_WIDGET (invisible)),
490 systray_manager_window_filter, manager);
491
492 /* remove all sockets from the hash table */
493 g_hash_table_foreach (manager->sockets,
494 systray_manager_remove_socket, manager);
495
496 /* destroy and unref the invisible window */
497 manager->invisible = NULL;
498 gtk_widget_destroy (invisible);
499 g_object_unref (G_OBJECT (invisible));
500
501 panel_debug (PANEL_DEBUG_SYSTRAY, "unregistered manager");
502 }
503
504
505
506 static GdkFilterReturn
systray_manager_window_filter(GdkXEvent * xev,GdkEvent * event,gpointer user_data)507 systray_manager_window_filter (GdkXEvent *xev,
508 GdkEvent *event,
509 gpointer user_data)
510 {
511 XEvent *xevent = (XEvent *)xev;
512 SystrayManager *manager = XFCE_SYSTRAY_MANAGER (user_data);
513
514 panel_return_val_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager), GDK_FILTER_CONTINUE);
515
516 if (xevent->type == ClientMessage)
517 {
518 if (xevent->xclient.message_type == manager->opcode_atom)
519 return systray_manager_handle_client_message_opcode (xevent, event, user_data);
520
521 if (xevent->xclient.message_type == manager->data_atom)
522 return systray_manager_handle_client_message_message_data (xevent, event, user_data);
523 }
524 else if (xevent->type == SelectionClear)
525 {
526 /* emit the signal */
527 g_signal_emit (manager, systray_manager_signals[LOST_SELECTION], 0);
528
529 /* unregister the manager */
530 systray_manager_unregister (manager);
531 }
532
533 return GDK_FILTER_CONTINUE;
534 }
535
536
537
538 static GdkFilterReturn
systray_manager_handle_client_message_opcode(GdkXEvent * xevent,GdkEvent * event,gpointer user_data)539 systray_manager_handle_client_message_opcode (GdkXEvent *xevent,
540 GdkEvent *event,
541 gpointer user_data)
542 {
543 XClientMessageEvent *xev;
544 SystrayManager *manager = XFCE_SYSTRAY_MANAGER (user_data);
545
546 panel_return_val_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager), GDK_FILTER_REMOVE);
547
548 /* cast to x11 event */
549 xev = (XClientMessageEvent *) xevent;
550
551 switch (xev->data.l[1])
552 {
553 case XFCE_SYSTRAY_MANAGER_REQUEST_DOCK:
554 systray_manager_handle_dock_request (manager, xev);
555 return GDK_FILTER_REMOVE;
556
557 case XFCE_SYSTRAY_MANAGER_BEGIN_MESSAGE:
558 systray_manager_handle_begin_message (manager, xev);
559 return GDK_FILTER_REMOVE;
560
561 case XFCE_SYSTRAY_MANAGER_CANCEL_MESSAGE:
562 systray_manager_handle_cancel_message (manager, xev);
563 return GDK_FILTER_REMOVE;
564
565 default:
566 break;
567 }
568
569 return GDK_FILTER_CONTINUE;
570 }
571
572
573
574 static GdkFilterReturn
systray_manager_handle_client_message_message_data(GdkXEvent * xevent,GdkEvent * event,gpointer user_data)575 systray_manager_handle_client_message_message_data (GdkXEvent *xevent,
576 GdkEvent *event,
577 gpointer user_data)
578 {
579 XClientMessageEvent *xev = xevent;
580 SystrayManager *manager = XFCE_SYSTRAY_MANAGER (user_data);
581 GSList *li;
582 SystrayMessage *message;
583 glong length;
584 GtkSocket *socket;
585
586 panel_return_val_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager), GDK_FILTER_REMOVE);
587
588 /* try to find the pending message in the list */
589 for (li = manager->messages; li != NULL; li = li->next)
590 {
591 message = li->data;
592
593 if (xev->window == message->window)
594 {
595 /* copy the data of this message */
596 length = MIN (message->remaining_length, 20);
597 memcpy ((message->string + message->length - message->remaining_length), &xev->data, length);
598 message->remaining_length -= length;
599
600 /* check if we have the complete message */
601 if (message->remaining_length == 0)
602 {
603 /* try to get the socket from the known tray icons */
604 socket = g_hash_table_lookup (manager->sockets, GUINT_TO_POINTER (message->window));
605
606 if (G_LIKELY (socket))
607 {
608 /* known socket, send the signal */
609 g_signal_emit (manager, systray_manager_signals[MESSAGE_SENT], 0,
610 socket, message->string, message->id, message->timeout);
611 }
612
613 /* delete the message from the list */
614 manager->messages = g_slist_delete_link (manager->messages, li);
615
616 /* free the message */
617 systray_manager_message_free (message);
618 }
619
620 /* stop searching */
621 break;
622 }
623 }
624
625 return GDK_FILTER_REMOVE;
626 }
627
628
629
630 static void
systray_manager_handle_begin_message(SystrayManager * manager,XClientMessageEvent * xevent)631 systray_manager_handle_begin_message (SystrayManager *manager,
632 XClientMessageEvent *xevent)
633 {
634 GtkSocket *socket;
635 SystrayMessage *message;
636 glong length, timeout, id;
637
638 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
639
640 /* try to find the window in the list of known tray icons */
641 socket = g_hash_table_lookup (manager->sockets, GUINT_TO_POINTER (xevent->window));
642
643 /* unkown tray icon: ignore the message */
644 if (G_UNLIKELY (socket == NULL))
645 return;
646
647 /* remove the same message from the list */
648 systray_manager_message_remove_from_list (manager, xevent);
649
650 /* get some message information */
651 timeout = xevent->data.l[2];
652 length = xevent->data.l[3];
653 id = xevent->data.l[4];
654
655 if (length == 0)
656 {
657 /* directly emit empty messages */
658 g_signal_emit (manager, systray_manager_signals[MESSAGE_SENT], 0,
659 socket, "", id, timeout);
660 }
661 else
662 {
663 /* create new structure */
664 message = g_slice_new0 (SystrayMessage);
665
666 /* set message data */
667 message->window = xevent->window;
668 message->timeout = timeout;
669 message->length = length;
670 message->id = id;
671 message->remaining_length = length;
672 message->string = g_malloc (length + 1);
673 message->string[length] = '\0';
674
675 /* add this message to the list of pending messages */
676 manager->messages = g_slist_prepend (manager->messages, message);
677 }
678 }
679
680
681
682 static void
systray_manager_handle_cancel_message(SystrayManager * manager,XClientMessageEvent * xevent)683 systray_manager_handle_cancel_message (SystrayManager *manager,
684 XClientMessageEvent *xevent)
685 {
686 GtkSocket *socket;
687 Window window = xevent->data.l[2];
688
689 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
690
691 /* remove the same message from the list */
692 systray_manager_message_remove_from_list (manager, xevent);
693
694 /* try to find the window in the list of known tray icons */
695 socket = g_hash_table_lookup (manager->sockets, GUINT_TO_POINTER (xevent->window));
696
697 /* emit the cancelled signal */
698 if (G_LIKELY (socket != NULL))
699 g_signal_emit (manager, systray_manager_signals[MESSAGE_CANCELLED],
700 0, socket, window);
701 }
702
703
704
705 static void
systray_manager_handle_dock_request(SystrayManager * manager,XClientMessageEvent * xevent)706 systray_manager_handle_dock_request (SystrayManager *manager,
707 XClientMessageEvent *xevent)
708 {
709 GtkWidget *socket;
710 GdkScreen *screen;
711 Window window = xevent->data.l[2];
712
713 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
714 panel_return_if_fail (GTK_IS_INVISIBLE (manager->invisible));
715
716 /* check if we already have this window */
717 if (g_hash_table_lookup (manager->sockets, GUINT_TO_POINTER (window)) != NULL)
718 return;
719
720 /* create the socket */
721 screen = gtk_widget_get_screen (manager->invisible);
722 socket = systray_socket_new (screen, window);
723 if (G_UNLIKELY (socket == NULL))
724 return;
725
726 /* add the icon to the tray */
727 g_signal_emit (manager, systray_manager_signals[ICON_ADDED], 0, socket);
728
729 /* check if the widget has been attached. if the widget has no
730 toplevel window, we cannot set the socket id. */
731 if (G_LIKELY (GTK_IS_WINDOW (gtk_widget_get_toplevel (socket))))
732 {
733 /* signal to monitor if the client is removed from the socket */
734 g_signal_connect (G_OBJECT (socket), "plug-removed",
735 G_CALLBACK (systray_manager_handle_undock_request), manager);
736
737 /* register the xembed client window id for this socket */
738 gtk_socket_add_id (GTK_SOCKET (socket), window);
739
740 /* add the socket to the list of known sockets */
741 g_hash_table_insert (manager->sockets, GUINT_TO_POINTER (window), socket);
742 }
743 else
744 {
745 /* warning */
746 g_warning ("No parent window set, destroying socket");
747
748 /* not attached successfully, destroy it */
749 gtk_widget_destroy (socket);
750 }
751 }
752
753
754
755 static gboolean
systray_manager_handle_undock_request(GtkSocket * socket,gpointer user_data)756 systray_manager_handle_undock_request (GtkSocket *socket,
757 gpointer user_data)
758 {
759 SystrayManager *manager = XFCE_SYSTRAY_MANAGER (user_data);
760 Window *window;
761
762 panel_return_val_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager), FALSE);
763
764 /* remove the socket from the list */
765 window = systray_socket_get_window (XFCE_SYSTRAY_SOCKET (socket));
766 g_hash_table_remove (manager->sockets, GUINT_TO_POINTER (*window));
767
768 /* emit signal that the socket will be removed */
769 g_signal_emit (manager, systray_manager_signals[ICON_REMOVED], 0, socket);
770
771 /* destroy the socket */
772 return FALSE;
773 }
774
775
776
777 static void
systray_manager_set_visual(SystrayManager * manager)778 systray_manager_set_visual (SystrayManager *manager)
779 {
780 GdkDisplay *display;
781 GdkVisual *visual;
782 Visual *xvisual;
783 Atom visual_atom;
784 gulong data[1];
785 GdkScreen *screen;
786
787 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
788 panel_return_if_fail (GTK_IS_INVISIBLE (manager->invisible));
789 panel_return_if_fail (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (manager->invisible))));
790
791 /* get invisible display and screen */
792 display = gtk_widget_get_display (manager->invisible);
793 screen = gtk_invisible_get_screen (GTK_INVISIBLE (manager->invisible));
794
795 /* get the xatom for the visual property */
796 visual_atom = gdk_x11_get_xatom_by_name_for_display (display,
797 "_NET_SYSTEM_TRAY_VISUAL");
798
799 visual = gdk_screen_get_rgba_visual (screen);
800 panel_debug (PANEL_DEBUG_SYSTRAY, "rgba visual is %p", visual);
801 if (visual != NULL)
802 {
803 /* use the rgba visual */
804 xvisual = GDK_VISUAL_XVISUAL (visual);
805 }
806 else
807 {
808 /* use the default visual for the screen */
809 xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (screen));
810 }
811
812 data[0] = XVisualIDFromVisual (xvisual);
813 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
814 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (manager->invisible))),
815 visual_atom,
816 XA_VISUALID, 32,
817 PropModeReplace,
818 (guchar *) &data, 1);
819 }
820
821
822
823 void
systray_manager_set_colors(SystrayManager * manager,GdkColor * fg,GdkColor * error,GdkColor * warning,GdkColor * success)824 systray_manager_set_colors (SystrayManager *manager,
825 GdkColor *fg,
826 GdkColor *error,
827 GdkColor *warning,
828 GdkColor *success)
829 {
830 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
831
832 manager->fg = *fg;
833 manager->error = *error;
834 manager->warning = *warning;
835 manager->success = *success;
836
837 systray_manager_set_colors_property (manager);
838 }
839
840
841
842 static void
systray_manager_set_colors_property(SystrayManager * manager)843 systray_manager_set_colors_property (SystrayManager *manager)
844 {
845 GdkWindow *window;
846 GdkDisplay *display;
847 Atom atom;
848 gulong data[12];
849
850 g_return_if_fail (manager->invisible != NULL);
851 window = gtk_widget_get_window (manager->invisible);
852 g_return_if_fail (window != NULL);
853
854 display = gtk_widget_get_display (manager->invisible);
855 atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_SYSTEM_TRAY_COLORS");
856
857 data[0] = manager->fg.red;
858 data[1] = manager->fg.green;
859 data[2] = manager->fg.blue;
860 data[3] = manager->error.red;
861 data[4] = manager->error.green;
862 data[5] = manager->error.blue;
863 data[6] = manager->warning.red;
864 data[7] = manager->warning.green;
865 data[8] = manager->warning.blue;
866 data[9] = manager->success.red;
867 data[10] = manager->success.green;
868 data[11] = manager->success.blue;
869
870 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
871 GDK_WINDOW_XID (window),
872 atom,
873 XA_CARDINAL, 32,
874 PropModeReplace,
875 (guchar *) &data, 12);
876 }
877
878
879
880 void
systray_manager_set_orientation(SystrayManager * manager,GtkOrientation orientation)881 systray_manager_set_orientation (SystrayManager *manager,
882 GtkOrientation orientation)
883 {
884 GdkDisplay *display;
885 Atom orientation_atom;
886 gulong data[1];
887
888 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
889 panel_return_if_fail (GTK_IS_INVISIBLE (manager->invisible));
890 panel_return_if_fail (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (manager->invisible))));
891
892 /* set the new orientation */
893 manager->orientation = orientation;
894
895 /* get invisible display */
896 display = gtk_widget_get_display (manager->invisible);
897
898 /* get the xatom for the orientation property */
899 orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
900 "_NET_SYSTEM_TRAY_ORIENTATION");
901
902 /* set the data we're going to send to x */
903 data[0] = (manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
904 XFCE_SYSTRAY_MANAGER_ORIENTATION_HORIZONTAL
905 : XFCE_SYSTRAY_MANAGER_ORIENTATION_VERTICAL);
906
907 /* change the x property */
908 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
909 GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (manager->invisible))),
910 orientation_atom,
911 XA_CARDINAL, 32,
912 PropModeReplace,
913 (guchar *) &data, 1);
914 }
915
916
917
918 /**
919 * tray messages
920 **/
921 static void
systray_manager_message_free(SystrayMessage * message)922 systray_manager_message_free (SystrayMessage *message)
923 {
924 g_free (message->string);
925 g_slice_free (SystrayMessage, message);
926 }
927
928
929
930 static void
systray_manager_message_remove_from_list(SystrayManager * manager,XClientMessageEvent * xevent)931 systray_manager_message_remove_from_list (SystrayManager *manager,
932 XClientMessageEvent *xevent)
933 {
934 GSList *li;
935 SystrayMessage *message;
936
937 panel_return_if_fail (XFCE_IS_SYSTRAY_MANAGER (manager));
938
939 /* seach for the same message in the list of pending messages */
940 for (li = manager->messages; li != NULL; li = li->next)
941 {
942 message = li->data;
943
944 /* check if this is the same message */
945 if (xevent->window == message->window && xevent->data.l[4] == message->id)
946 {
947 /* delete the message from the list */
948 manager->messages = g_slist_delete_link (manager->messages, li);
949
950 /* free the message */
951 systray_manager_message_free (message);
952
953 break;
954 }
955 }
956 }
957