~cairo-dock-team/cairo-dock-plug-ins/plug-ins

« back to all changes in this revision

Viewing changes to systray/src/gtk3/na-tray-child.c

  • Committer: Matthieu Baerts
  • Date: 2014-10-19 00:26:10 UTC
  • Revision ID: matttbe@gmail.com-20141019002610-ulf26s9b4c4rw10r
We just switched from BZR to Git.
Follow us on Github: https://github.com/Cairo-Dock

Note: we will only use Github to manage our source code and all pull requests.
Please continue to report your bugs/ideas/messages on our forum or Launchpad! 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* na-tray-child.c
2
 
 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
3
 
 * Copyright (C) 2003-2006 Vincent Untz
4
 
 * Copyright (C) 2008 Red Hat, Inc.
5
 
 *
6
 
 * This library is free software; you can redistribute it and/or
7
 
 * modify it under the terms of the GNU Lesser General Public
8
 
 * License as published by the Free Software Foundation; either
9
 
 * version 2 of the License, or (at your option) any later version.
10
 
 *
11
 
 * This library is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
 * Lesser General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU Lesser General Public
17
 
 * License along with this library; if not, write to the
18
 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19
 
 * Boston, MA 02111-1307, USA.
20
 
 */
21
 
 
22
 
#include <string.h>
23
 
 
24
 
#include "na-tray-child.h"
25
 
 
26
 
#include <gdk/gdk.h>
27
 
#include <gdk/gdkx.h>
28
 
#include <X11/Xatom.h>
29
 
 
30
 
G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET)
31
 
 
32
 
static void
33
 
na_tray_child_finalize (GObject *object)
34
 
{
35
 
  G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object);
36
 
}
37
 
 
38
 
static void
39
 
na_tray_child_realize (GtkWidget *widget)
40
 
{
41
 
  NaTrayChild *child = NA_TRAY_CHILD (widget);
42
 
  GdkVisual *visual = gtk_widget_get_visual (widget);
43
 
  GdkWindow *window;
44
 
 
45
 
  GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
46
 
 
47
 
  window = gtk_widget_get_window (widget);
48
 
 
49
 
  if (child->has_alpha)
50
 
    {
51
 
      /* We have real transparency with an ARGB visual and the Composite
52
 
       * extension. */
53
 
 
54
 
      /* Set a transparent background */
55
 
      cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0);
56
 
      gdk_window_set_background_pattern (window, transparent);
57
 
      gdk_window_set_composited (window, TRUE);
58
 
      cairo_pattern_destroy (transparent);
59
 
 
60
 
      child->parent_relative_bg = FALSE;
61
 
    }
62
 
  else if (visual == gdk_window_get_visual (gdk_window_get_parent (window)))
63
 
    {
64
 
      /* Otherwise, if the visual matches the visual of the parent window, we
65
 
       * can use a parent-relative background and fake transparency. */
66
 
      gdk_window_set_background_pattern (window, NULL);
67
 
 
68
 
      child->parent_relative_bg = TRUE;
69
 
    }
70
 
  else
71
 
    {
72
 
      /* Nothing to do; the icon will sit on top of an ugly gray box */
73
 
      child->parent_relative_bg = FALSE;
74
 
    }
75
 
 
76
 
  gdk_window_set_composited (window, child->composited);
77
 
 
78
 
  gtk_widget_set_app_paintable (GTK_WIDGET (child),
79
 
                                child->parent_relative_bg || child->has_alpha);
80
 
 
81
 
  /* Double-buffering will interfere with the parent-relative-background fake
82
 
   * transparency, since the double-buffer code doesn't know how to fill in the
83
 
   * background of the double-buffer correctly.
84
 
   */
85
 
  gtk_widget_set_double_buffered (GTK_WIDGET (child),
86
 
                                  child->parent_relative_bg);
87
 
}
88
 
 
89
 
static void
90
 
na_tray_child_style_set (GtkWidget *widget,
91
 
                         GtkStyle  *previous_style)
92
 
{
93
 
  /* The default handler resets the background according to the new style.
94
 
   * We either use a transparent background or a parent-relative background
95
 
   * and ignore the style background. So, just don't chain up.
96
 
   */
97
 
}
98
 
 
99
 
#if 0
100
 
/* This is adapted from code that was commented out in na-tray-manager.c; the
101
 
 * code in na-tray-manager.c wouldn't have worked reliably, this will. So maybe
102
 
 * it can be reenabled. On other hand, things seem to be working fine without
103
 
 * it.
104
 
 *
105
 
 * If reenabling, you need to hook it up in na_tray_child_class_init().
106
 
 */
107
 
static void
108
 
na_tray_child_size_request (GtkWidget      *widget,
109
 
                            GtkRequisition *request)
110
 
{
111
 
  GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_request (widget, request);
112
 
 
113
 
  /*
114
 
   * Make sure the icons have a meaningful size ..
115
 
   */ 
116
 
  if ((request->width < 16) || (request->height < 16))
117
 
    {
118
 
      gint nw = MAX (24, request->width);
119
 
      gint nh = MAX (24, request->height);
120
 
      g_warning ("Tray icon has requested a size of (%ix%i), resizing to (%ix%i)", 
121
 
                 req.width, req.height, nw, nh);
122
 
      request->width = nw;
123
 
      request->height = nh;
124
 
    }
125
 
}
126
 
#endif
127
 
 
128
 
static void
129
 
na_tray_child_size_allocate (GtkWidget      *widget,
130
 
                             GtkAllocation  *allocation)
131
 
{
132
 
  NaTrayChild *child = NA_TRAY_CHILD (widget);
133
 
  GtkAllocation widget_allocation;
134
 
  gboolean moved, resized;
135
 
 
136
 
  gtk_widget_get_allocation (widget, &widget_allocation);
137
 
 
138
 
  moved = (allocation->x != widget_allocation.x ||
139
 
           allocation->y != widget_allocation.y);
140
 
  resized = (allocation->width != widget_allocation.width ||
141
 
             allocation->height != widget_allocation.height);
142
 
 
143
 
  /* When we are allocating the widget while mapped we need special handling
144
 
   * for both real and fake transparency.
145
 
   *
146
 
   * Real transparency: we need to invalidate and trigger a redraw of the old
147
 
   *   and new areas. (GDK really should handle this for us, but doesn't as of
148
 
   *   GTK+-2.14)
149
 
   *
150
 
   * Fake transparency: if the widget moved, we need to force the contents to
151
 
   *   be redrawn with the new offset for the parent-relative background.
152
 
   */
153
 
  if ((moved || resized) && gtk_widget_get_mapped (widget))
154
 
    {
155
 
      if (na_tray_child_has_alpha (child))
156
 
        gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
157
 
                                    &widget_allocation, FALSE);
158
 
    }
159
 
 
160
 
  GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget,
161
 
                                                                allocation);
162
 
 
163
 
  if ((moved || resized) && gtk_widget_get_mapped (widget))
164
 
    {
165
 
      if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget)))
166
 
        gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
167
 
                                    &widget_allocation, FALSE);
168
 
      else if (moved && child->parent_relative_bg)
169
 
        na_tray_child_force_redraw (child);
170
 
    }
171
 
}
172
 
 
173
 
/* The plug window should completely occupy the area of the child, so we won't
174
 
 * get a draw event. But in case we do (the plug unmaps itself, say), this
175
 
 * draw handler draws with real or fake transparency.
176
 
 */
177
 
static gboolean
178
 
na_tray_child_draw (GtkWidget *widget,
179
 
                    cairo_t   *cr)
180
 
{
181
 
  NaTrayChild *child = NA_TRAY_CHILD (widget);
182
 
 
183
 
  if (na_tray_child_has_alpha (child))
184
 
    {
185
 
      /* Clear to transparent */
186
 
      cairo_set_source_rgba (cr, 0, 0, 0, 0);
187
 
      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
188
 
      cairo_paint (cr);
189
 
    }
190
 
  else if (child->parent_relative_bg)
191
 
    {
192
 
      GdkWindow *window;
193
 
      cairo_surface_t *target;
194
 
      GdkRectangle clip_rect;
195
 
 
196
 
      window = gtk_widget_get_window (widget);
197
 
      target = cairo_get_group_target (cr);
198
 
 
199
 
      gdk_cairo_get_clip_rectangle (cr, &clip_rect);
200
 
 
201
 
      /* Clear to parent-relative pixmap
202
 
       * We need to use direct X access here because GDK doesn't know about
203
 
       * the parent relative pixmap. */
204
 
      cairo_surface_flush (target);
205
 
 
206
 
      XClearArea (GDK_WINDOW_XDISPLAY (window),
207
 
                  GDK_WINDOW_XID (window),
208
 
                  clip_rect.x, clip_rect.y,
209
 
                  clip_rect.width, clip_rect.height,
210
 
                  False);
211
 
      cairo_surface_mark_dirty_rectangle (target,
212
 
                                          clip_rect.x, clip_rect.y,
213
 
                                          clip_rect.width, clip_rect.height);
214
 
    }
215
 
 
216
 
  return FALSE;
217
 
}
218
 
 
219
 
static void
220
 
na_tray_child_init (NaTrayChild *child)
221
 
{
222
 
}
223
 
 
224
 
 
225
 
static void
226
 
na_tray_child_get_preferred_width (GtkWidget* self,
227
 
                                     gint* minimum_width,
228
 
                                     gint* natural_width)
229
 
{
230
 
        *minimum_width = *natural_width = 24;
231
 
}
232
 
static void
233
 
na_tray_child_get_preferred_height (GtkWidget* self,
234
 
                                     gint* minimum_height,
235
 
                                     gint* natural_height)
236
 
{
237
 
        *minimum_height = *natural_height = 24;
238
 
}
239
 
 
240
 
static void
241
 
na_tray_child_class_init (NaTrayChildClass *klass)
242
 
{
243
 
  GObjectClass *gobject_class;
244
 
  GtkWidgetClass *widget_class;
245
 
 
246
 
  gobject_class = (GObjectClass *)klass;
247
 
  widget_class = (GtkWidgetClass *)klass;
248
 
 
249
 
  gobject_class->finalize = na_tray_child_finalize;
250
 
  widget_class->style_set = na_tray_child_style_set;
251
 
  widget_class->realize = na_tray_child_realize;
252
 
  widget_class->size_allocate = na_tray_child_size_allocate;
253
 
  widget_class->draw = na_tray_child_draw;
254
 
  // we force the size of the icons, because some programs (like xchat) don't respect the _NET_SYSTEM_TRAY_ICON_SIZE property.
255
 
  widget_class->get_preferred_width = na_tray_child_get_preferred_width;
256
 
  widget_class->get_preferred_height = na_tray_child_get_preferred_height;
257
 
}
258
 
 
259
 
GtkWidget *
260
 
na_tray_child_new (GdkScreen *screen,
261
 
                   Window     icon_window)
262
 
{
263
 
  XWindowAttributes window_attributes;
264
 
  Display *xdisplay;
265
 
  NaTrayChild *child;
266
 
  GdkVisual *visual;
267
 
  gboolean visual_has_alpha;
268
 
  int red_prec, green_prec, blue_prec, depth;
269
 
  int result;
270
 
 
271
 
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
272
 
  g_return_val_if_fail (icon_window != None, NULL);
273
 
 
274
 
  xdisplay = GDK_SCREEN_XDISPLAY (screen);
275
 
 
276
 
  /* We need to determine the visual of the window we are embedding and create
277
 
   * the socket in the same visual.
278
 
   */
279
 
 
280
 
  gdk_error_trap_push ();
281
 
  result = XGetWindowAttributes (xdisplay, icon_window,
282
 
                                 &window_attributes);
283
 
  gdk_error_trap_pop_ignored ();
284
 
 
285
 
  if (!result) /* Window already gone */
286
 
    return NULL;
287
 
 
288
 
  visual = gdk_x11_screen_lookup_visual (screen,
289
 
                                         window_attributes.visual->visualid);
290
 
  if (!visual) /* Icon window is on another screen? */
291
 
    return NULL;
292
 
 
293
 
  child = g_object_new (NA_TYPE_TRAY_CHILD, NULL);
294
 
  child->icon_window = icon_window;
295
 
 
296
 
  gtk_widget_set_visual (GTK_WIDGET (child), visual);
297
 
 
298
 
  /* We have alpha if the visual has something other than red, green,
299
 
   * and blue */
300
 
  gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
301
 
  gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
302
 
  gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
303
 
  depth = gdk_visual_get_depth (visual);
304
 
 
305
 
  visual_has_alpha = red_prec + blue_prec + green_prec < depth;
306
 
  child->has_alpha = (visual_has_alpha &&
307
 
                      gdk_display_supports_composite (gdk_screen_get_display (screen)));
308
 
 
309
 
  child->composited = child->has_alpha;
310
 
 
311
 
  return GTK_WIDGET (child);
312
 
}
313
 
 
314
 
char *
315
 
na_tray_child_get_title (NaTrayChild *child)
316
 
{
317
 
  char *retval = NULL;
318
 
  GdkDisplay *display;
319
 
  Atom utf8_string, atom, type;
320
 
  int result;
321
 
  int format;
322
 
  gulong nitems;
323
 
  gulong bytes_after;
324
 
  gchar *val;
325
 
 
326
 
  g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL);
327
 
 
328
 
  display = gtk_widget_get_display (GTK_WIDGET (child));
329
 
 
330
 
  utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
331
 
  atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
332
 
 
333
 
  gdk_error_trap_push ();
334
 
 
335
 
  result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
336
 
                               child->icon_window,
337
 
                               atom,
338
 
                               0, G_MAXLONG,
339
 
                               False, utf8_string,
340
 
                               &type, &format, &nitems,
341
 
                               &bytes_after, (guchar **)&val);
342
 
  
343
 
  if (gdk_error_trap_pop () || result != Success)
344
 
    return NULL;
345
 
 
346
 
  if (type != utf8_string ||
347
 
      format != 8 ||
348
 
      nitems == 0)
349
 
    {
350
 
      if (val)
351
 
        XFree (val);
352
 
      return NULL;
353
 
    }
354
 
 
355
 
  if (!g_utf8_validate (val, nitems, NULL))
356
 
    {
357
 
      XFree (val);
358
 
      return NULL;
359
 
    }
360
 
 
361
 
  retval = g_strndup (val, nitems);
362
 
 
363
 
  XFree (val);
364
 
 
365
 
  return retval;
366
 
}
367
 
 
368
 
/**
369
 
 * na_tray_child_has_alpha;
370
 
 * @child: a #NaTrayChild
371
 
 *
372
 
 * Checks if the child has an ARGB visual and real alpha transparence.
373
 
 * (as opposed to faked alpha transparency with an parent-relative
374
 
 * background)
375
 
 *
376
 
 * Return value: %TRUE if the child has an alpha transparency
377
 
 */
378
 
gboolean
379
 
na_tray_child_has_alpha (NaTrayChild *child)
380
 
{
381
 
  g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
382
 
 
383
 
  return child->has_alpha;
384
 
}
385
 
 
386
 
/**
387
 
 * na_tray_child_set_composited;
388
 
 * @child: a #NaTrayChild
389
 
 * @composited: %TRUE if the child's window should be redirected
390
 
 *
391
 
 * Sets whether the #GdkWindow of the child should be set redirected
392
 
 * using gdk_window_set_composited(). By default this is based off of
393
 
 * na_tray_child_has_alpha(), but it may be useful to override it in
394
 
 * certain circumstances; for example, if the #NaTrayChild is added
395
 
 * to a parent window and that parent window is composited against the
396
 
 * background.
397
 
 */
398
 
void
399
 
na_tray_child_set_composited (NaTrayChild *child,
400
 
                              gboolean     composited)
401
 
{
402
 
  g_return_if_fail (NA_IS_TRAY_CHILD (child));
403
 
 
404
 
  if (child->composited == composited)
405
 
    return;
406
 
 
407
 
  child->composited = composited;
408
 
  if (gtk_widget_get_realized (GTK_WIDGET (child)))
409
 
    gdk_window_set_composited (gtk_widget_get_window (GTK_WIDGET (child)),
410
 
                               composited);
411
 
}
412
 
 
413
 
/* If we are faking transparency with a window-relative background, force a
414
 
 * redraw of the icon. This should be called if the background changes or if
415
 
 * the child is shifted with respect to the background.
416
 
 */
417
 
void
418
 
na_tray_child_force_redraw (NaTrayChild *child)
419
 
{
420
 
  GtkWidget *widget = GTK_WIDGET (child);
421
 
 
422
 
  if (gtk_widget_get_mapped (widget) && child->parent_relative_bg)
423
 
    {
424
 
#if 1
425
 
      /* Sending an ExposeEvent might cause redraw problems if the
426
 
       * icon is expecting the server to clear-to-background before
427
 
       * the redraw. It should be ok for GtkStatusIcon or EggTrayIcon.
428
 
       */
429
 
      Display *xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget));
430
 
      XEvent xev;
431
 
      GdkWindow *plug_window;
432
 
      GtkAllocation allocation;
433
 
 
434
 
      plug_window = gtk_socket_get_plug_window (GTK_SOCKET (child));
435
 
      gtk_widget_get_allocation (widget, &allocation);
436
 
 
437
 
      xev.xexpose.type = Expose;
438
 
      xev.xexpose.window = GDK_WINDOW_XID (plug_window);
439
 
      xev.xexpose.x = 0;
440
 
      xev.xexpose.y = 0;
441
 
      xev.xexpose.width = allocation.width;
442
 
      xev.xexpose.height = allocation.height;
443
 
      xev.xexpose.count = 0;
444
 
 
445
 
      gdk_error_trap_push ();
446
 
      XSendEvent (xdisplay,
447
 
                  xev.xexpose.window,
448
 
                  False, ExposureMask,
449
 
                  &xev);
450
 
      gdk_error_trap_pop_ignored ();
451
 
#else
452
 
      /* Hiding and showing is the safe way to do it, but can result in more
453
 
       * flickering.
454
 
       */
455
 
      gdk_window_hide (widget->window);
456
 
      gdk_window_show (widget->window);
457
 
#endif
458
 
    }
459
 
}
460
 
 
461
 
/* from libwnck/xutils.c, comes as LGPLv2+ */
462
 
static char *
463
 
latin1_to_utf8 (const char *latin1)
464
 
{
465
 
  GString *str;
466
 
  const char *p;
467
 
 
468
 
  str = g_string_new (NULL);
469
 
 
470
 
  p = latin1;
471
 
  while (*p)
472
 
    {
473
 
      g_string_append_unichar (str, (gunichar) *p);
474
 
      ++p;
475
 
    }
476
 
 
477
 
  return g_string_free (str, FALSE);
478
 
}
479
 
 
480
 
/* derived from libwnck/xutils.c, comes as LGPLv2+ */
481
 
static void
482
 
_get_wmclass (Display *xdisplay,
483
 
              Window   xwindow,
484
 
              char   **res_class,
485
 
              char   **res_name)
486
 
{
487
 
  XClassHint ch;
488
 
 
489
 
  ch.res_name = NULL;
490
 
  ch.res_class = NULL;
491
 
 
492
 
  gdk_error_trap_push ();
493
 
  XGetClassHint (xdisplay, xwindow, &ch);
494
 
  gdk_error_trap_pop_ignored ();
495
 
 
496
 
  if (res_class)
497
 
    *res_class = NULL;
498
 
 
499
 
  if (res_name)
500
 
    *res_name = NULL;
501
 
 
502
 
  if (ch.res_name)
503
 
    {
504
 
      if (res_name)
505
 
        *res_name = latin1_to_utf8 (ch.res_name);
506
 
 
507
 
      XFree (ch.res_name);
508
 
    }
509
 
 
510
 
  if (ch.res_class)
511
 
    {
512
 
      if (res_class)
513
 
        *res_class = latin1_to_utf8 (ch.res_class);
514
 
 
515
 
      XFree (ch.res_class);
516
 
    }
517
 
}
518
 
 
519
 
/**
520
 
 * na_tray_child_get_wm_class;
521
 
 * @child: a #NaTrayChild
522
 
 * @res_name: return location for a string containing the application name of
523
 
 * @child, or %NULL
524
 
 * @res_class: return location for a string containing the application class of
525
 
 * @child, or %NULL
526
 
 *
527
 
 * Fetches the resource associated with @child.
528
 
 */
529
 
void
530
 
na_tray_child_get_wm_class (NaTrayChild  *child,
531
 
                            char        **res_name,
532
 
                            char        **res_class)
533
 
{
534
 
  GdkDisplay *display;
535
 
 
536
 
  g_return_if_fail (NA_IS_TRAY_CHILD (child));
537
 
 
538
 
  display = gtk_widget_get_display (GTK_WIDGET (child));
539
 
 
540
 
  _get_wmclass (GDK_DISPLAY_XDISPLAY (display),
541
 
                child->icon_window,
542
 
                res_class,
543
 
                res_name);
544
 
}