~dylanmccall/ubuntu/oneiric/network-manager-applet/lp852961-disable-autostart-for-gnome-shell

« back to all changes in this revision

Viewing changes to src/eggtrayicon.c

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Mercatante
  • Date: 2007-06-15 12:46:22 UTC
  • Revision ID: james.westby@ubuntu.com-20070615124622-01cyrnf0uxxun4lz
Tags: upstream-0.6.5
ImportĀ upstreamĀ versionĀ 0.6.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/* eggtrayicon.c
 
3
 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
 
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
 
 
21
#include <config.h>
 
22
#include <string.h>
 
23
#include <glib/gi18n.h>
 
24
 
 
25
#include "eggtrayicon.h"
 
26
 
 
27
#include <gdkconfig.h>
 
28
#if defined (GDK_WINDOWING_X11)
 
29
#include <gdk/gdkx.h>
 
30
#include <X11/Xatom.h>
 
31
#elif defined (GDK_WINDOWING_WIN32)
 
32
#include <gdk/gdkwin32.h>
 
33
#endif
 
34
 
 
35
#ifndef EGG_COMPILATION
 
36
#ifndef _
 
37
#define _(x) dgettext (GETTEXT_PACKAGE, x)
 
38
#define N_(x) x
 
39
#endif
 
40
#else
 
41
#define _(x) x
 
42
#define N_(x) x
 
43
#endif
 
44
 
 
45
#define SYSTEM_TRAY_REQUEST_DOCK    0
 
46
#define SYSTEM_TRAY_BEGIN_MESSAGE   1
 
47
#define SYSTEM_TRAY_CANCEL_MESSAGE  2
 
48
 
 
49
#define SYSTEM_TRAY_ORIENTATION_HORZ 0
 
50
#define SYSTEM_TRAY_ORIENTATION_VERT 1
 
51
 
 
52
enum {
 
53
  PROP_0,
 
54
  PROP_ORIENTATION
 
55
};
 
56
         
 
57
static GtkPlugClass *parent_class = NULL;
 
58
 
 
59
static void egg_tray_icon_init (EggTrayIcon *icon);
 
60
static void egg_tray_icon_class_init (EggTrayIconClass *klass);
 
61
 
 
62
static void egg_tray_icon_get_property (GObject    *object,
 
63
                                        guint       prop_id,
 
64
                                        GValue     *value,
 
65
                                        GParamSpec *pspec);
 
66
 
 
67
static void egg_tray_icon_realize   (GtkWidget *widget);
 
68
static void egg_tray_icon_unrealize (GtkWidget *widget);
 
69
 
 
70
static void egg_tray_icon_add (GtkContainer *container,
 
71
                               GtkWidget    *widget);
 
72
 
 
73
#ifdef GDK_WINDOWING_X11
 
74
static void egg_tray_icon_update_manager_window    (EggTrayIcon *icon,
 
75
                                                    gboolean     dock_if_realized);
 
76
static void egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon);
 
77
#endif
 
78
 
 
79
GType
 
80
egg_tray_icon_get_type (void)
 
81
{
 
82
  static GType our_type = 0;
 
83
 
 
84
  if (our_type == 0)
 
85
    {
 
86
      static const GTypeInfo our_info =
 
87
      {
 
88
        sizeof (EggTrayIconClass),
 
89
        (GBaseInitFunc) NULL,
 
90
        (GBaseFinalizeFunc) NULL,
 
91
        (GClassInitFunc) egg_tray_icon_class_init,
 
92
        NULL, /* class_finalize */
 
93
        NULL, /* class_data */
 
94
        sizeof (EggTrayIcon),
 
95
        0,    /* n_preallocs */
 
96
        (GInstanceInitFunc) egg_tray_icon_init,
 
97
        NULL  /* value_table */
 
98
      };
 
99
 
 
100
      our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
 
101
    }
 
102
 
 
103
  return our_type;
 
104
}
 
105
 
 
106
static void
 
107
egg_tray_icon_init (EggTrayIcon *icon)
 
108
{
 
109
  icon->stamp = 1;
 
110
  icon->orientation = GTK_ORIENTATION_HORIZONTAL;
 
111
  
 
112
  gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
 
113
}
 
114
 
 
115
static void
 
116
egg_tray_icon_class_init (EggTrayIconClass *klass)
 
117
{
 
118
  GObjectClass *gobject_class = (GObjectClass *)klass;
 
119
  GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
 
120
  GtkContainerClass *container_class = (GtkContainerClass *)klass;
 
121
 
 
122
  parent_class = g_type_class_peek_parent (klass);
 
123
 
 
124
  gobject_class->get_property = egg_tray_icon_get_property;
 
125
 
 
126
  widget_class->realize   = egg_tray_icon_realize;
 
127
  widget_class->unrealize = egg_tray_icon_unrealize;
 
128
 
 
129
  container_class->add = egg_tray_icon_add;
 
130
 
 
131
  g_object_class_install_property (gobject_class,
 
132
                                   PROP_ORIENTATION,
 
133
                                   g_param_spec_enum ("orientation",
 
134
                                                      _("Orientation"),
 
135
                                                      _("The orientation of the tray."),
 
136
                                                      GTK_TYPE_ORIENTATION,
 
137
                                                      GTK_ORIENTATION_HORIZONTAL,
 
138
                                                      G_PARAM_READABLE));
 
139
 
 
140
#if defined (GDK_WINDOWING_X11)
 
141
  /* Nothing */
 
142
#elif defined (GDK_WINDOWING_WIN32)
 
143
  g_warning ("Port eggtrayicon to Win32");
 
144
#else
 
145
  g_warning ("Port eggtrayicon to this GTK+ backend");
 
146
#endif
 
147
}
 
148
 
 
149
static void
 
150
egg_tray_icon_get_property (GObject    *object,
 
151
                            guint       prop_id,
 
152
                            GValue     *value,
 
153
                            GParamSpec *pspec)
 
154
{
 
155
  EggTrayIcon *icon = EGG_TRAY_ICON (object);
 
156
 
 
157
  switch (prop_id)
 
158
    {
 
159
    case PROP_ORIENTATION:
 
160
      g_value_set_enum (value, icon->orientation);
 
161
      break;
 
162
    default:
 
163
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
164
      break;
 
165
    }
 
166
}
 
167
 
 
168
#ifdef GDK_WINDOWING_X11
 
169
 
 
170
static void
 
171
egg_tray_icon_get_orientation_property (EggTrayIcon *icon)
 
172
{
 
173
  Display *xdisplay;
 
174
  Atom type;
 
175
  int format;
 
176
  union {
 
177
        gulong *prop;
 
178
        guchar *prop_ch;
 
179
  } prop = { NULL };
 
180
  gulong nitems;
 
181
  gulong bytes_after;
 
182
  int error, result;
 
183
 
 
184
  g_assert (icon->manager_window != None);
 
185
  
 
186
  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
 
187
 
 
188
  gdk_error_trap_push ();
 
189
  type = None;
 
190
  result = XGetWindowProperty (xdisplay,
 
191
                               icon->manager_window,
 
192
                               icon->orientation_atom,
 
193
                               0, G_MAXLONG, FALSE,
 
194
                               XA_CARDINAL,
 
195
                               &type, &format, &nitems,
 
196
                               &bytes_after, &(prop.prop_ch));
 
197
  error = gdk_error_trap_pop ();
 
198
 
 
199
  if (error || result != Success)
 
200
    return;
 
201
 
 
202
  if (type == XA_CARDINAL)
 
203
    {
 
204
      GtkOrientation orientation;
 
205
 
 
206
      orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
 
207
                                        GTK_ORIENTATION_HORIZONTAL :
 
208
                                        GTK_ORIENTATION_VERTICAL;
 
209
 
 
210
      if (icon->orientation != orientation)
 
211
        {
 
212
          icon->orientation = orientation;
 
213
 
 
214
          g_object_notify (G_OBJECT (icon), "orientation");
 
215
        }
 
216
    }
 
217
 
 
218
  if (prop.prop)
 
219
    XFree (prop.prop);
 
220
}
 
221
 
 
222
static GdkFilterReturn
 
223
egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
 
224
{
 
225
  EggTrayIcon *icon = user_data;
 
226
  XEvent *xev = (XEvent *)xevent;
 
227
 
 
228
  if (xev->xany.type == ClientMessage &&
 
229
      xev->xclient.message_type == icon->manager_atom &&
 
230
      xev->xclient.data.l[1] == icon->selection_atom)
 
231
    {
 
232
      egg_tray_icon_update_manager_window (icon, TRUE);
 
233
    }
 
234
  else if (xev->xany.window == icon->manager_window)
 
235
    {
 
236
      if (xev->xany.type == PropertyNotify &&
 
237
          xev->xproperty.atom == icon->orientation_atom)
 
238
        {
 
239
          egg_tray_icon_get_orientation_property (icon);
 
240
        }
 
241
      if (xev->xany.type == DestroyNotify)
 
242
        {
 
243
          egg_tray_icon_manager_window_destroyed (icon);
 
244
        }
 
245
    }
 
246
  return GDK_FILTER_CONTINUE;
 
247
}
 
248
 
 
249
#endif  
 
250
 
 
251
static void
 
252
egg_tray_icon_unrealize (GtkWidget *widget)
 
253
{
 
254
#ifdef GDK_WINDOWING_X11
 
255
  EggTrayIcon *icon = EGG_TRAY_ICON (widget);
 
256
  GdkWindow *root_window;
 
257
 
 
258
  if (icon->manager_window != None)
 
259
    {
 
260
      GdkWindow *gdkwin;
 
261
 
 
262
      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
 
263
                                              icon->manager_window);
 
264
 
 
265
      gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
 
266
    }
 
267
 
 
268
  root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
 
269
 
 
270
  gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
 
271
 
 
272
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
 
273
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
 
274
#endif
 
275
}
 
276
 
 
277
#ifdef GDK_WINDOWING_X11
 
278
 
 
279
static void
 
280
egg_tray_icon_send_manager_message (EggTrayIcon *icon,
 
281
                                    long         message,
 
282
                                    Window       window,
 
283
                                    long         data1,
 
284
                                    long         data2,
 
285
                                    long         data3)
 
286
{
 
287
  XClientMessageEvent ev;
 
288
  Display *display;
 
289
  
 
290
  ev.type = ClientMessage;
 
291
  ev.window = window;
 
292
  ev.message_type = icon->system_tray_opcode_atom;
 
293
  ev.format = 32;
 
294
  ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
 
295
  ev.data.l[1] = message;
 
296
  ev.data.l[2] = data1;
 
297
  ev.data.l[3] = data2;
 
298
  ev.data.l[4] = data3;
 
299
 
 
300
  display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
 
301
  
 
302
  gdk_error_trap_push ();
 
303
  XSendEvent (display,
 
304
              icon->manager_window, False, NoEventMask, (XEvent *)&ev);
 
305
  XSync (display, False);
 
306
  gdk_error_trap_pop ();
 
307
}
 
308
 
 
309
static void
 
310
egg_tray_icon_send_dock_request (EggTrayIcon *icon)
 
311
{
 
312
  egg_tray_icon_send_manager_message (icon,
 
313
                                      SYSTEM_TRAY_REQUEST_DOCK,
 
314
                                      icon->manager_window,
 
315
                                      gtk_plug_get_id (GTK_PLUG (icon)),
 
316
                                      0, 0);
 
317
}
 
318
 
 
319
static void
 
320
egg_tray_icon_update_manager_window (EggTrayIcon *icon,
 
321
                                     gboolean     dock_if_realized)
 
322
{
 
323
  Display *xdisplay;
 
324
  
 
325
  if (icon->manager_window != None)
 
326
    return;
 
327
 
 
328
  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
 
329
  
 
330
  XGrabServer (xdisplay);
 
331
  
 
332
  icon->manager_window = XGetSelectionOwner (xdisplay,
 
333
                                             icon->selection_atom);
 
334
 
 
335
  if (icon->manager_window != None)
 
336
    XSelectInput (xdisplay,
 
337
                  icon->manager_window, StructureNotifyMask|PropertyChangeMask);
 
338
 
 
339
  XUngrabServer (xdisplay);
 
340
  XFlush (xdisplay);
 
341
  
 
342
  if (icon->manager_window != None)
 
343
    {
 
344
      GdkWindow *gdkwin;
 
345
 
 
346
      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
 
347
                                              icon->manager_window);
 
348
      
 
349
      gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
 
350
 
 
351
      if (dock_if_realized && GTK_WIDGET_REALIZED (icon))
 
352
        egg_tray_icon_send_dock_request (icon);
 
353
 
 
354
      egg_tray_icon_get_orientation_property (icon);
 
355
    }
 
356
}
 
357
 
 
358
static void
 
359
egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon)
 
360
{
 
361
  GdkWindow *gdkwin;
 
362
  
 
363
  g_return_if_fail (icon->manager_window != None);
 
364
 
 
365
  gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
 
366
                                          icon->manager_window);
 
367
      
 
368
  gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
 
369
 
 
370
  icon->manager_window = None;
 
371
 
 
372
  egg_tray_icon_update_manager_window (icon, TRUE);
 
373
}
 
374
 
 
375
#endif
 
376
 
 
377
static gboolean
 
378
transparent_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
 
379
{
 
380
  gdk_window_clear_area (widget->window, event->area.x, event->area.y,
 
381
                         event->area.width, event->area.height);
 
382
  return FALSE;
 
383
}
 
384
 
 
385
static void
 
386
make_transparent_again (GtkWidget *widget, GtkStyle *previous_style,
 
387
                        gpointer user_data)
 
388
{
 
389
  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
 
390
}
 
391
 
 
392
static void
 
393
make_transparent (GtkWidget *widget, gpointer user_data)
 
394
{
 
395
  if (GTK_WIDGET_NO_WINDOW (widget) || GTK_WIDGET_APP_PAINTABLE (widget))
 
396
    return;
 
397
 
 
398
  gtk_widget_set_app_paintable (widget, TRUE);
 
399
  gtk_widget_set_double_buffered (widget, FALSE);
 
400
  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
 
401
  g_signal_connect (widget, "expose_event",
 
402
                    G_CALLBACK (transparent_expose_event), NULL);
 
403
  g_signal_connect_after (widget, "style_set",
 
404
                          G_CALLBACK (make_transparent_again), NULL);
 
405
}       
 
406
 
 
407
static void
 
408
egg_tray_icon_realize (GtkWidget *widget)
 
409
{
 
410
#ifdef GDK_WINDOWING_X11
 
411
  EggTrayIcon *icon = EGG_TRAY_ICON (widget);
 
412
  GdkScreen *screen;
 
413
  GdkDisplay *display;
 
414
  Display *xdisplay;
 
415
  char buffer[256];
 
416
  GdkWindow *root_window;
 
417
 
 
418
  if (GTK_WIDGET_CLASS (parent_class)->realize)
 
419
    GTK_WIDGET_CLASS (parent_class)->realize (widget);
 
420
 
 
421
  make_transparent (widget, NULL);
 
422
 
 
423
  screen = gtk_widget_get_screen (widget);
 
424
  display = gdk_screen_get_display (screen);
 
425
  xdisplay = gdk_x11_display_get_xdisplay (display);
 
426
 
 
427
  /* Now see if there's a manager window around */
 
428
  g_snprintf (buffer, sizeof (buffer),
 
429
              "_NET_SYSTEM_TRAY_S%d",
 
430
              gdk_screen_get_number (screen));
 
431
 
 
432
  icon->selection_atom = XInternAtom (xdisplay, buffer, False);
 
433
  
 
434
  icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
 
435
  
 
436
  icon->system_tray_opcode_atom = XInternAtom (xdisplay,
 
437
                                                   "_NET_SYSTEM_TRAY_OPCODE",
 
438
                                                   False);
 
439
 
 
440
  icon->orientation_atom = XInternAtom (xdisplay,
 
441
                                        "_NET_SYSTEM_TRAY_ORIENTATION",
 
442
                                        False);
 
443
 
 
444
  egg_tray_icon_update_manager_window (icon, FALSE);
 
445
  egg_tray_icon_send_dock_request (icon);
 
446
 
 
447
  root_window = gdk_screen_get_root_window (screen);
 
448
  
 
449
  /* Add a root window filter so that we get changes on MANAGER */
 
450
  gdk_window_add_filter (root_window,
 
451
                         egg_tray_icon_manager_filter, icon);
 
452
#endif
 
453
}
 
454
 
 
455
static void
 
456
egg_tray_icon_add (GtkContainer *container, GtkWidget *widget)
 
457
{
 
458
  g_signal_connect (widget, "realize",
 
459
                    G_CALLBACK (make_transparent), NULL);
 
460
  GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
 
461
}
 
462
 
 
463
EggTrayIcon *
 
464
egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
 
465
{
 
466
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
 
467
 
 
468
  return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL);
 
469
}
 
470
 
 
471
EggTrayIcon*
 
472
egg_tray_icon_new (const gchar *name)
 
473
{
 
474
  return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL);
 
475
}
 
476
 
 
477
guint
 
478
egg_tray_icon_send_message (EggTrayIcon *icon,
 
479
                            gint         timeout,
 
480
                            const gchar *message,
 
481
                            gint         len)
 
482
{
 
483
  guint stamp;
 
484
  
 
485
  g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
 
486
  g_return_val_if_fail (timeout >= 0, 0);
 
487
  g_return_val_if_fail (message != NULL, 0);
 
488
                     
 
489
#ifdef GDK_WINDOWING_X11
 
490
  if (icon->manager_window == None)
 
491
    return 0;
 
492
#endif
 
493
 
 
494
  if (len < 0)
 
495
    len = strlen (message);
 
496
 
 
497
  stamp = icon->stamp++;
 
498
  
 
499
#ifdef GDK_WINDOWING_X11
 
500
  /* Get ready to send the message */
 
501
  egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
 
502
                                      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
 
503
                                      timeout, len, stamp);
 
504
 
 
505
  /* Now to send the actual message */
 
506
  gdk_error_trap_push ();
 
507
  while (len > 0)
 
508
    {
 
509
      XClientMessageEvent ev;
 
510
      Display *xdisplay;
 
511
 
 
512
      xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
 
513
      
 
514
      ev.type = ClientMessage;
 
515
      ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
 
516
      ev.format = 8;
 
517
      ev.message_type = XInternAtom (xdisplay,
 
518
                                     "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
 
519
      if (len > 20)
 
520
        {
 
521
          memcpy (&ev.data, message, 20);
 
522
          len -= 20;
 
523
          message += 20;
 
524
        }
 
525
      else
 
526
        {
 
527
          memcpy (&ev.data, message, len);
 
528
          len = 0;
 
529
        }
 
530
 
 
531
      XSendEvent (xdisplay,
 
532
                  icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
 
533
      XSync (xdisplay, False);
 
534
    }
 
535
  gdk_error_trap_pop ();
 
536
#endif
 
537
 
 
538
  return stamp;
 
539
}
 
540
 
 
541
void
 
542
egg_tray_icon_cancel_message (EggTrayIcon *icon,
 
543
                              guint        id)
 
544
{
 
545
  g_return_if_fail (EGG_IS_TRAY_ICON (icon));
 
546
  g_return_if_fail (id > 0);
 
547
#ifdef GDK_WINDOWING_X11  
 
548
  egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
 
549
                                      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
 
550
                                      id, 0, 0);
 
551
#endif
 
552
}
 
553
 
 
554
GtkOrientation
 
555
egg_tray_icon_get_orientation (EggTrayIcon *icon)
 
556
{
 
557
  g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
 
558
 
 
559
  return icon->orientation;
 
560
}