Statistics
| Revision:

root / src / eggtrayicon.c @ 3064

History | View | Annotate | Download (14.9 kB)

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