~ubuntu-branches/ubuntu/natty/xfce4-panel/natty

« back to all changes in this revision

Viewing changes to plugins/systray/systray-manager.c

  • Committer: Bazaar Package Importer
  • Author(s): Lionel Le Folgoc
  • Date: 2010-12-04 15:45:53 UTC
  • mfrom: (1.1.25 upstream)
  • Revision ID: james.westby@ubuntu.com-20101204154553-c1k0n2p2j83chld0
Tags: 4.7.5-0ubuntu1
Upload to natty (pkg-xfce svn r4611).

Show diffs side-by-side

added added

removed removed

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