source: proiecte/PPPP/gdm/gui/simple-greeter/libnotificationarea/na-tray-manager.c @ 134

Last change on this file since 134 was 134, checked in by (none), 14 years ago

gdm sources with the modifications for webcam

File size: 24.9 KB
Line 
1/* na-tray-manager.c
2 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3 * Copyright (C) 2003-2006 Vincent Untz
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Used to be: eggtraymanager.c
21 */
22
23#include <config.h>
24#include <string.h>
25#include <libintl.h>
26
27#include "na-tray-manager.h"
28
29#include <gdkconfig.h>
30#include <glib/gi18n.h>
31#if defined (GDK_WINDOWING_X11)
32#include <gdk/gdkx.h>
33#include <X11/Xatom.h>
34#elif defined (GDK_WINDOWING_WIN32)
35#include <gdk/gdkwin32.h>
36#endif
37#include <gtk/gtk.h>
38
39#include "na-marshal.h"
40
41/* Signals */
42enum
43{
44  TRAY_ICON_ADDED,
45  TRAY_ICON_REMOVED,
46  MESSAGE_SENT,
47  MESSAGE_CANCELLED,
48  LOST_SELECTION,
49  LAST_SIGNAL
50};
51
52enum {
53  PROP_0,
54  PROP_ORIENTATION
55};
56
57typedef struct
58{
59  long id, len;
60  long remaining_len;
61 
62  long timeout;
63  char *str;
64#ifdef GDK_WINDOWING_X11
65  Window window;
66#endif
67} PendingMessage;
68
69static guint manager_signals[LAST_SIGNAL];
70
71#define SYSTEM_TRAY_REQUEST_DOCK    0
72#define SYSTEM_TRAY_BEGIN_MESSAGE   1
73#define SYSTEM_TRAY_CANCEL_MESSAGE  2
74
75#define SYSTEM_TRAY_ORIENTATION_HORZ 0
76#define SYSTEM_TRAY_ORIENTATION_VERT 1
77
78#ifdef GDK_WINDOWING_X11
79static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen);
80#endif
81
82static void na_tray_manager_finalize     (GObject      *object);
83static void na_tray_manager_set_property (GObject      *object,
84                                          guint         prop_id,
85                                          const GValue *value,
86                                          GParamSpec   *pspec);
87static void na_tray_manager_get_property (GObject      *object,
88                                          guint         prop_id,
89                                          GValue       *value,
90                                          GParamSpec   *pspec);
91
92static void na_tray_manager_unmanage (NaTrayManager *manager);
93
94G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
95
96static void
97na_tray_manager_init (NaTrayManager *manager)
98{
99  manager->invisible = NULL;
100  manager->socket_table = g_hash_table_new (NULL, NULL);
101}
102
103static void
104na_tray_manager_class_init (NaTrayManagerClass *klass)
105{
106  GObjectClass *gobject_class;
107 
108  gobject_class = (GObjectClass *)klass;
109
110  gobject_class->finalize = na_tray_manager_finalize;
111  gobject_class->set_property = na_tray_manager_set_property;
112  gobject_class->get_property = na_tray_manager_get_property;
113
114  g_object_class_install_property (gobject_class,
115                                   PROP_ORIENTATION,
116                                   g_param_spec_enum ("orientation",
117                                                      "orientation",
118                                                      "orientation",
119                                                      GTK_TYPE_ORIENTATION,
120                                                      GTK_ORIENTATION_HORIZONTAL,
121                                                      G_PARAM_READWRITE |
122                                                      G_PARAM_CONSTRUCT |
123                                                      G_PARAM_STATIC_NAME |
124                                                      G_PARAM_STATIC_NICK |
125                                                      G_PARAM_STATIC_BLURB));
126 
127  manager_signals[TRAY_ICON_ADDED] =
128    g_signal_new ("tray_icon_added",
129                  G_OBJECT_CLASS_TYPE (klass),
130                  G_SIGNAL_RUN_LAST,
131                  G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
132                  NULL, NULL,
133                  g_cclosure_marshal_VOID__OBJECT,
134                  G_TYPE_NONE, 1,
135                  GTK_TYPE_SOCKET);
136
137  manager_signals[TRAY_ICON_REMOVED] =
138    g_signal_new ("tray_icon_removed",
139                  G_OBJECT_CLASS_TYPE (klass),
140                  G_SIGNAL_RUN_LAST,
141                  G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
142                  NULL, NULL,
143                  g_cclosure_marshal_VOID__OBJECT,
144                  G_TYPE_NONE, 1,
145                  GTK_TYPE_SOCKET);
146  manager_signals[MESSAGE_SENT] =
147    g_signal_new ("message_sent",
148                  G_OBJECT_CLASS_TYPE (klass),
149                  G_SIGNAL_RUN_LAST,
150                  G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
151                  NULL, NULL,
152                  _na_marshal_VOID__OBJECT_STRING_LONG_LONG,
153                  G_TYPE_NONE, 4,
154                  GTK_TYPE_SOCKET,
155                  G_TYPE_STRING,
156                  G_TYPE_LONG,
157                  G_TYPE_LONG);
158  manager_signals[MESSAGE_CANCELLED] =
159    g_signal_new ("message_cancelled",
160                  G_OBJECT_CLASS_TYPE (klass),
161                  G_SIGNAL_RUN_LAST,
162                  G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
163                  NULL, NULL,
164                  _na_marshal_VOID__OBJECT_LONG,
165                  G_TYPE_NONE, 2,
166                  GTK_TYPE_SOCKET,
167                  G_TYPE_LONG);
168  manager_signals[LOST_SELECTION] =
169    g_signal_new ("lost_selection",
170                  G_OBJECT_CLASS_TYPE (klass),
171                  G_SIGNAL_RUN_LAST,
172                  G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
173                  NULL, NULL,
174                  g_cclosure_marshal_VOID__VOID,
175                  G_TYPE_NONE, 0);
176
177#if defined (GDK_WINDOWING_X11)
178  /* Nothing */
179#elif defined (GDK_WINDOWING_WIN32)
180  g_warning ("Port NaTrayManager to Win32");
181#else
182  g_warning ("Port NaTrayManager to this GTK+ backend");
183#endif
184}
185
186static void
187na_tray_manager_finalize (GObject *object)
188{
189  NaTrayManager *manager;
190 
191  manager = NA_TRAY_MANAGER (object);
192
193  na_tray_manager_unmanage (manager);
194
195  g_list_free (manager->messages);
196  g_hash_table_destroy (manager->socket_table);
197 
198  G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
199}
200
201static void
202na_tray_manager_set_property (GObject      *object,
203                              guint         prop_id,
204                              const GValue *value,
205                              GParamSpec   *pspec)
206{
207  NaTrayManager *manager = NA_TRAY_MANAGER (object);
208
209  switch (prop_id)
210    {
211    case PROP_ORIENTATION:
212      na_tray_manager_set_orientation (manager, g_value_get_enum (value));
213      break;
214    default:
215      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216      break;
217    }
218}
219
220static void
221na_tray_manager_get_property (GObject    *object,
222                              guint       prop_id,
223                              GValue     *value,
224                              GParamSpec *pspec)
225{
226  NaTrayManager *manager = NA_TRAY_MANAGER (object);
227
228  switch (prop_id)
229    {
230    case PROP_ORIENTATION:
231      g_value_set_enum (value, manager->orientation);
232      break;
233    default:
234      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235      break;
236    }
237}
238
239NaTrayManager *
240na_tray_manager_new (void)
241{
242  NaTrayManager *manager;
243
244  manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL);
245
246  return manager;
247}
248
249#ifdef GDK_WINDOWING_X11
250
251static gboolean
252na_tray_manager_plug_removed (GtkSocket       *socket,
253                              NaTrayManager   *manager)
254{
255  Window *window;
256
257  window = g_object_get_data (G_OBJECT (socket), "na-tray-child-window");
258
259  g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window));
260  g_object_set_data (G_OBJECT (socket), "na-tray-child-window",
261                     NULL);
262 
263  g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket);
264
265  /* This destroys the socket. */
266  return FALSE;
267}
268
269static void
270na_tray_manager_make_socket_transparent (GtkWidget *widget,
271                                         gpointer   user_data)
272{
273  if (GTK_WIDGET_NO_WINDOW (widget))
274    return;
275
276  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
277}
278
279static gboolean
280na_tray_manager_socket_exposed (GtkWidget      *widget,
281                                GdkEventExpose *event,
282                                gpointer        user_data)
283{
284  gdk_window_clear_area (widget->window,
285                         event->area.x, event->area.y,
286                         event->area.width, event->area.height);
287  return FALSE;
288}
289
290static void
291na_tray_manager_socket_style_set (GtkWidget *widget,
292                                  GtkStyle  *previous_style,
293                                  gpointer   user_data)
294{
295  if (widget->window == NULL)
296    return;
297
298  na_tray_manager_make_socket_transparent (widget, user_data);
299}
300
301static void
302na_tray_manager_handle_dock_request (NaTrayManager       *manager,
303                                     XClientMessageEvent *xevent)
304{
305  GtkWidget *socket;
306  Window *window;
307  GtkRequisition req;
308
309  if (g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->data.l[2])))
310    {
311      /* We already got this notification earlier, ignore this one */
312      return;
313    }
314 
315  socket = gtk_socket_new ();
316
317  gtk_widget_set_app_paintable (socket, TRUE);
318  //FIXME: need to find a theme where this (and expose event) is needed
319  gtk_widget_set_double_buffered (socket, FALSE);
320  g_signal_connect (socket, "realize",
321                    G_CALLBACK (na_tray_manager_make_socket_transparent), NULL);
322  g_signal_connect (socket, "expose_event",
323                    G_CALLBACK (na_tray_manager_socket_exposed), NULL);
324  g_signal_connect_after (socket, "style_set",
325                          G_CALLBACK (na_tray_manager_socket_style_set), NULL);
326 
327  /* We need to set the child window here
328   * so that the client can call _get functions
329   * in the signal handler
330   */
331  window = g_new (Window, 1);
332  *window = xevent->data.l[2];
333     
334  g_object_set_data_full (G_OBJECT (socket),
335                          "na-tray-child-window",
336                          window, g_free);
337  g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
338                 socket);
339
340  /* Add the socket only if it's been attached */
341  if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))))
342    {
343      g_signal_connect (socket, "plug_removed",
344                        G_CALLBACK (na_tray_manager_plug_removed), manager);
345     
346      gtk_socket_add_id (GTK_SOCKET (socket), *window);
347
348      g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (*window), socket);
349
350      /*
351       * Make sure the icons have a meaningfull size ...
352       */ 
353      req.width = req.height = 1;
354      gtk_widget_size_request (socket, &req);
355      /*
356      if ((req.width < 16) || (req.height < 16))
357      {
358          gint nw = MAX (24, req.width);
359          gint nh = MAX (24, req.height);
360          g_warning (_("tray icon has requested a size of (%i x %i), resizing to (%i x %i)"),
361                      req.width, req.height, nw, nh);
362          gtk_widget_set_size_request(icon, nw,  nh);
363      }
364      */
365      gtk_widget_show(socket);
366    }
367  else
368    gtk_widget_destroy (socket);
369}
370
371static void
372pending_message_free (PendingMessage *message)
373{
374  g_free (message->str);
375  g_free (message);
376}
377
378static GdkFilterReturn
379na_tray_manager_handle_client_message_message_data (GdkXEvent *xev,
380                                                    GdkEvent  *event,
381                                                    gpointer   data)
382{
383  XClientMessageEvent *xevent;
384  NaTrayManager       *manager;
385  GList               *p;
386  int                  len;
387 
388  xevent  = (XClientMessageEvent *) xev;
389  manager = data;
390
391  /* Try to see if we can find the pending message in the list */
392  for (p = manager->messages; p; p = p->next)
393    {
394      PendingMessage *msg = p->data;
395
396      if (xevent->window == msg->window)
397        {
398          /* Append the message */
399          len = MIN (msg->remaining_len, 20);
400
401          memcpy ((msg->str + msg->len - msg->remaining_len),
402                  &xevent->data, len);
403          msg->remaining_len -= len;
404
405          if (msg->remaining_len == 0)
406            {
407              GtkSocket *socket;
408
409              socket = g_hash_table_lookup (manager->socket_table,
410                                            GINT_TO_POINTER (msg->window));
411
412              if (socket)
413                  g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
414                                 socket, msg->str, msg->id, msg->timeout);
415
416              pending_message_free (msg);
417              manager->messages = g_list_remove_link (manager->messages, p);
418              g_list_free_1 (p);
419            }
420
421          break;
422        }
423    }
424
425  return GDK_FILTER_REMOVE;
426}
427
428static void
429na_tray_manager_handle_begin_message (NaTrayManager       *manager,
430                                      XClientMessageEvent *xevent)
431{
432  GtkSocket      *socket;
433  GList          *p;
434  PendingMessage *msg;
435  long            timeout;
436  long            len;
437  long            id;
438
439  socket = g_hash_table_lookup (manager->socket_table,
440                                GINT_TO_POINTER (xevent->window));
441  /* we don't know about this tray icon, so ignore the message */
442  if (!socket)
443    return;
444
445  /* Check if the same message is already in the queue and remove it if so */
446  for (p = manager->messages; p; p = p->next)
447    {
448      PendingMessage *message = p->data;
449
450      if (xevent->window == message->window &&
451          xevent->data.l[4] == message->id)
452        {
453          /* Hmm, we found it, now remove it */
454          pending_message_free (message);
455          manager->messages = g_list_remove_link (manager->messages, p);
456          g_list_free_1 (p);
457          break;
458        }
459    }
460
461  timeout = xevent->data.l[2];
462  len     = xevent->data.l[3];
463  id      = xevent->data.l[4];
464
465  if (len == 0)
466    {
467      g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
468                     socket, "", id, timeout);
469    }
470  else
471    {
472      /* Now add the new message to the queue */
473      msg = g_new0 (PendingMessage, 1);
474      msg->window = xevent->window;
475      msg->timeout = timeout;
476      msg->len = len;
477      msg->id = id;
478      msg->remaining_len = msg->len;
479      msg->str = g_malloc (msg->len + 1);
480      msg->str[msg->len] = '\0';
481      manager->messages = g_list_prepend (manager->messages, msg);
482    }
483}
484
485static void
486na_tray_manager_handle_cancel_message (NaTrayManager       *manager,
487                                       XClientMessageEvent *xevent)
488{
489  GList     *p;
490  GtkSocket *socket;
491 
492  /* Check if the message is in the queue and remove it if so */
493  for (p = manager->messages; p; p = p->next)
494    {
495      PendingMessage *msg = p->data;
496
497      if (xevent->window == msg->window &&
498          xevent->data.l[4] == msg->id)
499        {
500          pending_message_free (msg);
501          manager->messages = g_list_remove_link (manager->messages, p);
502          g_list_free_1 (p);
503          break;
504        }
505    }
506
507  socket = g_hash_table_lookup (manager->socket_table,
508                                GINT_TO_POINTER (xevent->window));
509 
510  if (socket)
511    {
512      g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
513                     socket, xevent->data.l[2]);
514    }
515}
516
517static GdkFilterReturn
518na_tray_manager_handle_client_message_opcode (GdkXEvent *xev,
519                                              GdkEvent  *event,
520                                              gpointer   data)
521{
522  XClientMessageEvent *xevent;
523  NaTrayManager       *manager;
524
525  xevent  = (XClientMessageEvent *) xev;
526  manager = data;
527
528  switch (xevent->data.l[1])
529    {
530    case SYSTEM_TRAY_REQUEST_DOCK:
531      /* Ignore this one since we don't know on which window this was received
532       * and so we can't know for which screen this is. It will be handled
533       * in na_tray_manager_window_filter() since we also receive it there */
534      break;
535
536    case SYSTEM_TRAY_BEGIN_MESSAGE:
537      na_tray_manager_handle_begin_message (manager, xevent);
538      return GDK_FILTER_REMOVE;
539
540    case SYSTEM_TRAY_CANCEL_MESSAGE:
541      na_tray_manager_handle_cancel_message (manager, xevent);
542      return GDK_FILTER_REMOVE;
543    default:
544      break;
545    }
546
547  return GDK_FILTER_CONTINUE;
548}
549
550static GdkFilterReturn
551na_tray_manager_window_filter (GdkXEvent *xev,
552                               GdkEvent  *event,
553                               gpointer   data)
554{
555  XEvent        *xevent = (GdkXEvent *)xev;
556  NaTrayManager *manager = data;
557
558  if (xevent->type == ClientMessage)
559    {
560      /* We handle this client message here. See comment in
561       * na_tray_manager_handle_client_message_opcode() for details */
562      if (xevent->xclient.message_type == manager->opcode_atom &&
563          xevent->xclient.data.l[1]    == SYSTEM_TRAY_REQUEST_DOCK)
564        {
565          na_tray_manager_handle_dock_request (manager,
566                                               (XClientMessageEvent *) xevent);
567          return GDK_FILTER_REMOVE;
568        }
569    }
570  else if (xevent->type == SelectionClear)
571    {
572      g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
573      na_tray_manager_unmanage (manager);
574    }
575
576  return GDK_FILTER_CONTINUE;
577}
578
579#if 0
580//FIXME investigate why this doesn't work
581static gboolean
582na_tray_manager_selection_clear_event (GtkWidget         *widget,
583                                       GdkEventSelection *event,
584                                       NaTrayManager     *manager)
585{
586  g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
587  na_tray_manager_unmanage (manager);
588
589  return FALSE;
590}
591#endif
592#endif 
593
594static void
595na_tray_manager_unmanage (NaTrayManager *manager)
596{
597#ifdef GDK_WINDOWING_X11
598  GdkDisplay *display;
599  guint32     timestamp;
600  GtkWidget  *invisible;
601
602  if (manager->invisible == NULL)
603    return;
604
605  invisible = manager->invisible;
606  g_assert (GTK_IS_INVISIBLE (invisible));
607  g_assert (GTK_WIDGET_REALIZED (invisible));
608  g_assert (GDK_IS_WINDOW (invisible->window));
609 
610  display = gtk_widget_get_display (invisible);
611 
612  if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
613      invisible->window)
614    {
615      timestamp = gdk_x11_get_server_time (invisible->window);     
616      gdk_selection_owner_set_for_display (display,
617                                           NULL,
618                                           manager->selection_atom,
619                                           timestamp,
620                                           TRUE);
621    }
622
623  //FIXME: we should also use gdk_remove_client_message_filter when it's
624  //available
625  // See bug #351254
626  gdk_window_remove_filter (invisible->window,
627                            na_tray_manager_window_filter, manager); 
628
629  manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */
630  gtk_widget_destroy (invisible);
631  g_object_unref (G_OBJECT (invisible));
632#endif
633}
634
635static void
636na_tray_manager_set_orientation_property (NaTrayManager *manager)
637{
638#ifdef GDK_WINDOWING_X11
639  GdkDisplay *display;
640  Atom        orientation_atom;
641  gulong      data[1];
642
643  if (!manager->invisible || !manager->invisible->window)
644    return;
645
646  display = gtk_widget_get_display (manager->invisible);
647  orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
648                                                            "_NET_SYSTEM_TRAY_ORIENTATION");
649
650  data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
651                SYSTEM_TRAY_ORIENTATION_HORZ :
652                SYSTEM_TRAY_ORIENTATION_VERT;
653
654  XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
655                   GDK_WINDOW_XWINDOW (manager->invisible->window),
656                   orientation_atom,
657                   XA_CARDINAL, 32,
658                   PropModeReplace,
659                   (guchar *) &data, 1);
660#endif
661}
662
663#ifdef GDK_WINDOWING_X11
664
665static gboolean
666na_tray_manager_manage_screen_x11 (NaTrayManager *manager,
667                                   GdkScreen     *screen)
668{
669  GdkDisplay *display;
670  Screen     *xscreen;
671  GtkWidget  *invisible;
672  char       *selection_atom_name;
673  guint32     timestamp;
674 
675  g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE);
676  g_return_val_if_fail (manager->screen == NULL, FALSE);
677
678  /* If there's already a manager running on the screen
679   * we can't create another one.
680   */
681#if 0
682  if (na_tray_manager_check_running_screen_x11 (screen))
683    return FALSE;
684#endif
685  display = gdk_screen_get_display (screen);
686  xscreen = GDK_SCREEN_XSCREEN (screen);
687 
688  invisible = gtk_invisible_new_for_screen (screen);
689  gtk_widget_realize (invisible);
690 
691  gtk_widget_add_events (invisible,
692                         GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
693
694  selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
695                                         gdk_screen_get_number (screen));
696  manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
697  g_free (selection_atom_name);
698
699  na_tray_manager_set_orientation_property (manager);
700 
701  timestamp = gdk_x11_get_server_time (invisible->window);
702
703  /* Check if we could set the selection owner successfully */
704  if (gdk_selection_owner_set_for_display (display,
705                                           invisible->window,
706                                           manager->selection_atom,
707                                           timestamp,
708                                           TRUE))
709    {
710      XClientMessageEvent xev;
711      GdkAtom             opcode_atom;
712      GdkAtom             message_data_atom;
713
714      xev.type = ClientMessage;
715      xev.window = RootWindowOfScreen (xscreen);
716      xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
717                                                                "MANAGER");
718
719      xev.format = 32;
720      xev.data.l[0] = timestamp;
721      xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
722                                                         manager->selection_atom);
723      xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window);
724      xev.data.l[3] = 0;        /* manager specific data */
725      xev.data.l[4] = 0;        /* manager specific data */
726
727      XSendEvent (GDK_DISPLAY_XDISPLAY (display),
728                  RootWindowOfScreen (xscreen),
729                  False, StructureNotifyMask, (XEvent *)&xev);
730
731      manager->invisible = invisible;
732      g_object_ref (G_OBJECT (manager->invisible));
733     
734      opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
735      manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
736                                                                opcode_atom);
737
738      message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
739                                           FALSE);
740
741      /* Add a window filter */
742#if 0
743      /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
744      g_signal_connect (invisible, "selection-clear-event",
745                        G_CALLBACK (na_tray_manager_selection_clear_event),
746                        manager);
747#endif
748      /* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */
749      gdk_window_add_filter (invisible->window,
750                             na_tray_manager_window_filter, manager);
751      /* This is for SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE */
752      gdk_display_add_client_message_filter (display, opcode_atom,
753                                             na_tray_manager_handle_client_message_opcode,
754                                             manager);
755      /* This is for _NET_SYSTEM_TRAY_MESSAGE_DATA */
756      gdk_display_add_client_message_filter (display, message_data_atom,
757                                             na_tray_manager_handle_client_message_message_data,
758                                             manager);
759      return TRUE;
760    }
761  else
762    {
763      gtk_widget_destroy (invisible);
764 
765      return FALSE;
766    }
767}
768
769#endif
770
771gboolean
772na_tray_manager_manage_screen (NaTrayManager *manager,
773                               GdkScreen     *screen)
774{
775  g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
776  g_return_val_if_fail (manager->screen == NULL, FALSE);
777
778#ifdef GDK_WINDOWING_X11
779  return na_tray_manager_manage_screen_x11 (manager, screen);
780#else
781  return FALSE;
782#endif
783}
784
785#ifdef GDK_WINDOWING_X11
786
787static gboolean
788na_tray_manager_check_running_screen_x11 (GdkScreen *screen)
789{
790  GdkDisplay *display;
791  Atom        selection_atom;
792  char       *selection_atom_name;
793
794  display = gdk_screen_get_display (screen);
795  selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
796                                         gdk_screen_get_number (screen));
797  selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
798                                                          selection_atom_name);
799  g_free (selection_atom_name);
800
801  if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
802                          selection_atom) != None)
803    return TRUE;
804  else
805    return FALSE;
806}
807
808#endif
809
810gboolean
811na_tray_manager_check_running (GdkScreen *screen)
812{
813  g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
814
815#ifdef GDK_WINDOWING_X11
816  return na_tray_manager_check_running_screen_x11 (screen);
817#else
818  return FALSE;
819#endif
820}
821
822char *
823na_tray_manager_get_child_title (NaTrayManager      *manager,
824                                 NaTrayManagerChild *child)
825{
826  char *retval = NULL;
827#ifdef GDK_WINDOWING_X11
828  GdkDisplay *display;
829  Window *child_window;
830  Atom utf8_string, atom, type;
831  int result;
832  int format;
833  gulong nitems;
834  gulong bytes_after;
835  gchar *val;
836
837  g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), NULL);
838  g_return_val_if_fail (GTK_IS_SOCKET (child), NULL);
839 
840  display = gdk_screen_get_display (manager->screen);
841
842  child_window = g_object_get_data (G_OBJECT (child),
843                                    "na-tray-child-window");
844
845  utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
846  atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
847
848  gdk_error_trap_push ();
849
850  result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
851                               *child_window,
852                               atom,
853                               0, G_MAXLONG,
854                               False, utf8_string,
855                               &type, &format, &nitems,
856                               &bytes_after, (guchar **)&val);
857 
858  if (gdk_error_trap_pop () || result != Success)
859    return NULL;
860
861  if (type != utf8_string ||
862      format != 8 ||
863      nitems == 0)
864    {
865      if (val)
866        XFree (val);
867      return NULL;
868    }
869
870  if (!g_utf8_validate (val, nitems, NULL))
871    {
872      XFree (val);
873      return NULL;
874    }
875
876  retval = g_strndup (val, nitems);
877
878  XFree (val);
879#endif
880  return retval;
881}
882
883void
884na_tray_manager_set_orientation (NaTrayManager  *manager,
885                                 GtkOrientation  orientation)
886{
887  g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
888
889  if (manager->orientation != orientation)
890    {
891      manager->orientation = orientation;
892
893      na_tray_manager_set_orientation_property (manager);
894
895      g_object_notify (G_OBJECT (manager), "orientation");
896    }
897}
898
899GtkOrientation
900na_tray_manager_get_orientation (NaTrayManager *manager)
901{
902  g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
903
904  return manager->orientation;
905}
Note: See TracBrowser for help on using the repository browser.