1 /* util header */
2 /* vim: set sw=2 et: */
3 
4 /*
5  * Copyright (C) 2001 Havoc Pennington
6  * Copyright (C) 2006-2007 Vincent Untz
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include <glib/gi18n-lib.h>
25 #include "util.h"
26 #include "xutils.h"
27 #include "private.h"
28 #include <gdk/gdkx.h>
29 #include <string.h>
30 #ifdef HAVE_XRES
31 #include <X11/extensions/XRes.h>
32 #endif
33 
34 /**
35  * SECTION:resource
36  * @short_description: reading resource usage of X clients.
37  * @see_also: wnck_window_get_xid(), wnck_application_get_xid(), wnck_window_get_pid(), wnck_application_get_pid()
38  * @stability: Unstable
39  *
40  * libwnck provides an easy-to-use interface to the XRes X server extension to
41  * read resource usage of X clients, which can be defined either by the X
42  * window ID of one of their windows or by the process ID of their process.
43  */
44 
45 /**
46  * SECTION:misc
47  * @short_description: other additional features.
48  * @stability: Unstable
49  *
50  * These functions are utility functions providing some additional features to
51  * libwnck users.
52  */
53 
54 /**
55  * SECTION:icons
56  * @short_description: icons related functions.
57  * @stability: Unstable
58  *
59  * These functions are utility functions to manage icons for #WnckWindow and
60  * #WnckApplication.
61  */
62 
63 typedef enum
64 {
65   WNCK_EXT_UNKNOWN = 0,
66   WNCK_EXT_FOUND = 1,
67   WNCK_EXT_MISSING = 2
68 } WnckExtStatus;
69 
70 
71 #if 0
72 /* useful for debugging */
73 static void
74 _wnck_print_resource_usage (WnckResourceUsage *usage)
75 {
76   if (!usage)
77     return;
78 
79   g_print ("\twindows       : %d\n"
80            "\tGCs           : %d\n"
81            "\tfonts         : %d\n"
82            "\tpixmaps       : %d\n"
83            "\tpictures      : %d\n"
84            "\tglyphsets     : %d\n"
85            "\tcolormaps     : %d\n"
86            "\tpassive grabs : %d\n"
87            "\tcursors       : %d\n"
88            "\tunknowns      : %d\n"
89            "\tpixmap bytes  : %ld\n"
90            "\ttotal bytes   : ~%ld\n",
91            usage->n_windows,
92            usage->n_gcs,
93            usage->n_fonts,
94            usage->n_pixmaps,
95            usage->n_pictures,
96            usage->n_glyphsets,
97            usage->n_colormap_entries,
98            usage->n_passive_grabs,
99            usage->n_cursors,
100            usage->n_other,
101            usage->pixmap_bytes,
102            usage->total_bytes_estimate);
103 }
104 #endif
105 
106 static WnckExtStatus
wnck_init_resource_usage(GdkDisplay * gdisplay)107 wnck_init_resource_usage (GdkDisplay *gdisplay)
108 {
109   WnckExtStatus status;
110 
111   status = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gdisplay),
112                                                "wnck-xres-status"));
113 
114   if (status == WNCK_EXT_UNKNOWN)
115     {
116 #ifdef HAVE_XRES
117       Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
118       int event, error;
119 
120       if (!XResQueryExtension (xdisplay, &event, &error))
121         status = WNCK_EXT_MISSING;
122       else
123         status = WNCK_EXT_FOUND;
124 #else
125       status = WNCK_EXT_MISSING;
126 #endif
127 
128       g_object_set_data (G_OBJECT (gdisplay),
129                          "wnck-xres-status",
130                          GINT_TO_POINTER (status));
131     }
132 
133   g_assert (status != WNCK_EXT_UNKNOWN);
134 
135   return status;
136 }
137 
138 /**
139  * wnck_xid_read_resource_usage:
140  * @gdk_display: a <classname>GdkDisplay</classname>.
141  * @xid: an X window ID.
142  * @usage: return location for the X resource usage of the application owning
143  * the X window ID @xid.
144  *
145  * Looks for the X resource usage of the application owning the X window ID
146  * @xid on display @gdisplay. If no resource usage can be found, then all
147  * fields of @usage are set to 0.
148  *
149  * To properly work, this function requires the XRes extension on the X server.
150  *
151  * Since: 2.6
152  */
153 void
wnck_xid_read_resource_usage(GdkDisplay * gdisplay,gulong xid,WnckResourceUsage * usage)154 wnck_xid_read_resource_usage (GdkDisplay        *gdisplay,
155                               gulong             xid,
156                               WnckResourceUsage *usage)
157 {
158   g_return_if_fail (usage != NULL);
159 
160   memset (usage, '\0', sizeof (*usage));
161 
162   if (wnck_init_resource_usage (gdisplay) == WNCK_EXT_MISSING)
163     return;
164 
165 #ifdef HAVE_XRES
166  {
167    Display *xdisplay;
168    XResType *types;
169    int n_types;
170    unsigned long pixmap_bytes;
171    int i;
172    Atom pixmap_atom;
173    Atom window_atom;
174    Atom gc_atom;
175    Atom picture_atom;
176    Atom glyphset_atom;
177    Atom font_atom;
178    Atom colormap_entry_atom;
179    Atom passive_grab_atom;
180    Atom cursor_atom;
181 
182    types = NULL;
183    n_types = 0;
184    pixmap_bytes = 0;
185 
186   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
187 
188   _wnck_error_trap_push (xdisplay);
189 
190   XResQueryClientResources (xdisplay,
191                              xid, &n_types,
192                              &types);
193 
194    XResQueryClientPixmapBytes (xdisplay,
195                                xid, &pixmap_bytes);
196    _wnck_error_trap_pop (xdisplay);
197 
198    usage->pixmap_bytes = pixmap_bytes;
199 
200    pixmap_atom = _wnck_atom_get ("PIXMAP");
201    window_atom = _wnck_atom_get ("WINDOW");
202    gc_atom = _wnck_atom_get ("GC");
203    font_atom = _wnck_atom_get ("FONT");
204    glyphset_atom = _wnck_atom_get ("GLYPHSET");
205    picture_atom = _wnck_atom_get ("PICTURE");
206    colormap_entry_atom = _wnck_atom_get ("COLORMAP ENTRY");
207    passive_grab_atom = _wnck_atom_get ("PASSIVE GRAB");
208    cursor_atom = _wnck_atom_get ("CURSOR");
209 
210    i = 0;
211    while (i < n_types)
212      {
213        guint t = types[i].resource_type;
214 
215        if (t == pixmap_atom)
216          usage->n_pixmaps += types[i].count;
217        else if (t == window_atom)
218          usage->n_windows += types[i].count;
219        else if (t == gc_atom)
220          usage->n_gcs += types[i].count;
221        else if (t == picture_atom)
222          usage->n_pictures += types[i].count;
223        else if (t == glyphset_atom)
224          usage->n_glyphsets += types[i].count;
225        else if (t == font_atom)
226          usage->n_fonts += types[i].count;
227        else if (t == colormap_entry_atom)
228          usage->n_colormap_entries += types[i].count;
229        else if (t == passive_grab_atom)
230          usage->n_passive_grabs += types[i].count;
231        else if (t == cursor_atom)
232          usage->n_cursors += types[i].count;
233        else
234          usage->n_other += types[i].count;
235 
236        ++i;
237      }
238 
239    XFree(types);
240 
241    usage->total_bytes_estimate = usage->pixmap_bytes;
242 
243    /* FIXME look in the X server source and come up with better
244     * answers here. Ideally we change XRes to return a number
245     * like this since it can do things like divide the cost of
246     * a shared resource among those sharing it.
247     */
248    usage->total_bytes_estimate += usage->n_windows * 24;
249    usage->total_bytes_estimate += usage->n_gcs * 24;
250    usage->total_bytes_estimate += usage->n_pictures * 24;
251    usage->total_bytes_estimate += usage->n_glyphsets * 24;
252    usage->total_bytes_estimate += usage->n_fonts * 1024;
253    usage->total_bytes_estimate += usage->n_colormap_entries * 24;
254    usage->total_bytes_estimate += usage->n_passive_grabs * 24;
255    usage->total_bytes_estimate += usage->n_cursors * 24;
256    usage->total_bytes_estimate += usage->n_other * 24;
257  }
258 #else /* HAVE_XRES */
259   g_assert_not_reached ();
260 #endif /* HAVE_XRES */
261 }
262 
263 #ifdef HAVE_XRES
264 static void
wnck_pid_read_resource_usage_free_hash(gpointer data)265 wnck_pid_read_resource_usage_free_hash (gpointer data)
266 {
267   g_slice_free (gulong, data);
268 }
269 
270 static guint
wnck_gulong_hash(gconstpointer v)271 wnck_gulong_hash (gconstpointer v)
272 {
273   /* FIXME: this is obvioulsy wrong, but nearly 100% of the time, the gulong
274    * only contains guint values */
275   return *(const guint *) v;
276 }
277 
278 static gboolean
wnck_gulong_equal(gconstpointer a,gconstpointer b)279 wnck_gulong_equal (gconstpointer a,
280                    gconstpointer b)
281 {
282   return *((const gulong *) a) == *((const gulong *) b);
283 }
284 
285 static gulong
wnck_check_window_for_pid(Screen * screen,Window win,XID match_xid,XID mask)286 wnck_check_window_for_pid (Screen *screen,
287                            Window  win,
288                            XID     match_xid,
289                            XID     mask)
290 {
291   if ((win & ~mask) == match_xid) {
292     return _wnck_get_pid (screen, win);
293   }
294 
295   return 0;
296 }
297 
298 static void
wnck_find_pid_for_resource_r(Display * xdisplay,Screen * screen,Window win_top,XID match_xid,XID mask,gulong * xid,gulong * pid)299 wnck_find_pid_for_resource_r (Display *xdisplay,
300                               Screen  *screen,
301                               Window   win_top,
302                               XID      match_xid,
303                               XID      mask,
304                               gulong  *xid,
305                               gulong  *pid)
306 {
307   Status   qtres;
308   int      err;
309   Window   dummy;
310   Window  *children;
311   guint    n_children;
312   guint    i;
313   gulong   found_pid = 0;
314 
315   while (gtk_events_pending ())
316     gtk_main_iteration ();
317 
318   found_pid = wnck_check_window_for_pid (screen, win_top, match_xid, mask);
319   if (found_pid != 0)
320     {
321       *xid = win_top;
322       *pid = found_pid;
323     }
324 
325   _wnck_error_trap_push (xdisplay);
326   qtres = XQueryTree (xdisplay, win_top, &dummy, &dummy,
327                       &children, &n_children);
328   err = _wnck_error_trap_pop (xdisplay);
329 
330   if (!qtres || err != Success)
331     return;
332 
333   for (i = 0; i < n_children; i++)
334     {
335       wnck_find_pid_for_resource_r (xdisplay, screen, children[i],
336                                     match_xid, mask, xid, pid);
337 
338       if (*pid != 0)
339 	break;
340     }
341 
342   if (children)
343     XFree ((char *)children);
344 }
345 
346 struct xresclient_state
347 {
348   XResClient *clients;
349   int         n_clients;
350   int         next;
351   Display    *xdisplay;
352   GHashTable *hashtable_pid;
353 };
354 
355 static struct xresclient_state xres_state = { NULL, 0, -1, NULL, NULL };
356 static guint       xres_idleid = 0;
357 static GHashTable *xres_hashtable = NULL;
358 static time_t      start_update = 0;
359 static time_t      end_update = 0;
360 static guint       xres_removeid = 0;
361 
362 static void
wnck_pid_read_resource_usage_xres_state_free(gpointer data)363 wnck_pid_read_resource_usage_xres_state_free (gpointer data)
364 {
365   struct xresclient_state *state;
366 
367   state = (struct xresclient_state *) data;
368 
369   if (state->clients)
370     XFree (state->clients);
371   state->clients = NULL;
372 
373   state->n_clients = 0;
374   state->next = -1;
375   state->xdisplay = NULL;
376 
377   if (state->hashtable_pid)
378     g_hash_table_destroy (state->hashtable_pid);
379   state->hashtable_pid = NULL;
380 }
381 
382 static gboolean
wnck_pid_read_resource_usage_fill_cache(struct xresclient_state * state)383 wnck_pid_read_resource_usage_fill_cache (struct xresclient_state *state)
384 {
385   int    i;
386   gulong pid;
387   gulong xid;
388   XID    match_xid;
389 
390   if (state->next >= state->n_clients)
391     {
392       if (xres_hashtable)
393         g_hash_table_destroy (xres_hashtable);
394       xres_hashtable = state->hashtable_pid;
395       state->hashtable_pid = NULL;
396 
397       time (&end_update);
398 
399       xres_idleid = 0;
400       return FALSE;
401     }
402 
403   match_xid = (state->clients[state->next].resource_base &
404                ~state->clients[state->next].resource_mask);
405 
406   pid = 0;
407   xid = 0;
408 
409   for (i = 0; i < ScreenCount (state->xdisplay); i++)
410     {
411       Screen *screen;
412       Window  root;
413 
414       screen = ScreenOfDisplay (state->xdisplay, i);
415       root = RootWindow (state->xdisplay, i);
416 
417       if (root == None)
418         continue;
419 
420       wnck_find_pid_for_resource_r (state->xdisplay, screen, root, match_xid,
421                                     state->clients[state->next].resource_mask,
422                                     &xid, &pid);
423 
424       if (pid != 0 && xid != 0)
425         break;
426     }
427 
428   if (pid != 0 && xid != 0)
429     {
430       gulong *key;
431       gulong *value;
432 
433       key = g_slice_new (gulong);
434       value = g_slice_new (gulong);
435       *key = pid;
436       *value = xid;
437       g_hash_table_insert (state->hashtable_pid, key, value);
438     }
439 
440   state->next++;
441 
442   return TRUE;
443 }
444 
445 static void
wnck_pid_read_resource_usage_start_build_cache(GdkDisplay * gdisplay)446 wnck_pid_read_resource_usage_start_build_cache (GdkDisplay *gdisplay)
447 {
448   Display *xdisplay;
449   int      err;
450 
451   if (xres_idleid != 0)
452     return;
453 
454   time (&start_update);
455 
456   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
457 
458   _wnck_error_trap_push (xdisplay);
459   XResQueryClients (xdisplay, &xres_state.n_clients, &xres_state.clients);
460   err = _wnck_error_trap_pop (xdisplay);
461 
462   if (err != Success)
463     return;
464 
465   xres_state.next = (xres_state.n_clients > 0) ? 0 : -1;
466   xres_state.xdisplay = xdisplay;
467   xres_state.hashtable_pid = g_hash_table_new_full (
468                                      wnck_gulong_hash,
469                                      wnck_gulong_equal,
470                                      wnck_pid_read_resource_usage_free_hash,
471                                      wnck_pid_read_resource_usage_free_hash);
472 
473   xres_idleid = g_idle_add_full (
474                         G_PRIORITY_HIGH_IDLE,
475                         (GSourceFunc) wnck_pid_read_resource_usage_fill_cache,
476                         &xres_state, wnck_pid_read_resource_usage_xres_state_free);
477 }
478 
479 static gboolean
wnck_pid_read_resource_usage_destroy_hash_table(gpointer data)480 wnck_pid_read_resource_usage_destroy_hash_table (gpointer data)
481 {
482   xres_removeid = 0;
483 
484   if (xres_hashtable)
485     g_hash_table_destroy (xres_hashtable);
486 
487   xres_hashtable = NULL;
488 
489   return FALSE;
490 }
491 
492 #define XRES_UPDATE_RATE_SEC 30
493 static gboolean
wnck_pid_read_resource_usage_from_cache(GdkDisplay * gdisplay,gulong pid,WnckResourceUsage * usage)494 wnck_pid_read_resource_usage_from_cache (GdkDisplay        *gdisplay,
495                                          gulong             pid,
496                                          WnckResourceUsage *usage)
497 {
498   gboolean  need_rebuild;
499   gulong   *xid_p;
500   int       cache_validity;
501 
502   if (end_update == 0)
503     time (&end_update);
504 
505   cache_validity = MAX (XRES_UPDATE_RATE_SEC, (end_update - start_update) * 2);
506 
507   /* we rebuild the cache if it was never built or if it's old */
508   need_rebuild = (xres_hashtable == NULL ||
509                   (end_update < time (NULL) - cache_validity));
510 
511   if (xres_hashtable)
512     {
513       /* clear the cache after quite some time, because it might not be used
514        * anymore */
515       if (xres_removeid != 0)
516         g_source_remove (xres_removeid);
517       xres_removeid = g_timeout_add_seconds (cache_validity * 2,
518                                              wnck_pid_read_resource_usage_destroy_hash_table,
519                                              NULL);
520     }
521 
522   if (need_rebuild)
523     wnck_pid_read_resource_usage_start_build_cache (gdisplay);
524 
525   if (xres_hashtable)
526     xid_p = g_hash_table_lookup (xres_hashtable, &pid);
527   else
528     xid_p = NULL;
529 
530   if (xid_p)
531     {
532       wnck_xid_read_resource_usage (gdisplay, *xid_p, usage);
533       return TRUE;
534     }
535 
536   return FALSE;
537 }
538 
539 static void
wnck_pid_read_resource_usage_no_cache(GdkDisplay * gdisplay,gulong pid,WnckResourceUsage * usage)540 wnck_pid_read_resource_usage_no_cache (GdkDisplay        *gdisplay,
541                                        gulong             pid,
542                                        WnckResourceUsage *usage)
543 {
544   Display *xdisplay;
545   int i;
546 
547   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
548 
549   i = 0;
550   while (i < ScreenCount (xdisplay))
551     {
552       WnckScreen *screen;
553       GList *windows;
554       GList *tmp;
555 
556       screen = wnck_screen_get (i);
557 
558       g_assert (screen != NULL);
559 
560       windows = wnck_screen_get_windows (screen);
561       tmp = windows;
562       while (tmp != NULL)
563         {
564           if (wnck_window_get_pid (tmp->data) == (int) pid)
565             {
566               wnck_xid_read_resource_usage (gdisplay,
567                                             wnck_window_get_xid (tmp->data),
568                                             usage);
569 
570               /* stop on first window found */
571               return;
572             }
573 
574           tmp = tmp->next;
575         }
576 
577       ++i;
578     }
579 }
580 #endif /* HAVE_XRES */
581 
582 /**
583  * wnck_pid_read_resource_usage:
584  * @gdk_display: a <classname>GdkDisplay</classname>.
585  * @pid: a process ID.
586  * @usage: return location for the X resource usage of the application with
587  * process ID @pid.
588  *
589  * Looks for the X resource usage of the application with process ID @pid on
590  * display @gdisplay. If no resource usage can be found, then all fields of
591  * @usage are set to 0.
592  *
593  * In order to find the resource usage of an application that does not have an
594  * X window visible to libwnck (panel applets do not have any toplevel windows,
595  * for example), wnck_pid_read_resource_usage() walks through the whole tree of
596  * X windows. Since this walk is expensive in CPU, a cache is created. This
597  * cache is updated in the background. This means there is a non-null
598  * probability that no resource usage will be found for an application, even if
599  * it is an X client. If this happens, calling wnck_pid_read_resource_usage()
600  * again after a few seconds should work.
601  *
602  * To properly work, this function requires the XRes extension on the X server.
603  *
604  * Since: 2.6
605  */
606 void
wnck_pid_read_resource_usage(GdkDisplay * gdisplay,gulong pid,WnckResourceUsage * usage)607 wnck_pid_read_resource_usage (GdkDisplay        *gdisplay,
608                               gulong             pid,
609                               WnckResourceUsage *usage)
610 {
611   g_return_if_fail (usage != NULL);
612 
613   memset (usage, '\0', sizeof (*usage));
614 
615   if (wnck_init_resource_usage (gdisplay) == WNCK_EXT_MISSING)
616     return;
617 
618 #ifdef HAVE_XRES
619   if (!wnck_pid_read_resource_usage_from_cache (gdisplay, pid, usage))
620     /* the cache might not be built, might be outdated or might not contain
621      * data for a new X client, so try to fallback to something else */
622     wnck_pid_read_resource_usage_no_cache (gdisplay, pid, usage);
623 #endif /* HAVE_XRES */
624 }
625 
626 static WnckClientType client_type = 0;
627 
628 /**
629  * wnck_set_client_type:
630  * @ewmh_sourceindication_client_type: a role for the client.
631  *
632  * Sets the role of the libwnck user.
633  *
634  * The default role is %WNCK_CLIENT_TYPE_APPLICATION. Therefore, for
635  * applications providing some window management features, like pagers or
636  * tasklists, it is important to set the role to %WNCK_CLIENT_TYPE_PAGER for
637  * libwnck to properly work.
638  *
639  * This function should only be called once per program. Additional calls
640  * with the same client type will be silently ignored. An attempt to change
641  * the client type to a differnet value after it has already been set will
642  * be ignored and a critical warning will be logged.
643  *
644  * Since: 2.14
645  */
646 void
wnck_set_client_type(WnckClientType ewmh_sourceindication_client_type)647 wnck_set_client_type (WnckClientType ewmh_sourceindication_client_type)
648 {
649   /* Clients constantly switching types makes no sense; this should only be
650    * set once.
651    */
652   if (client_type != 0 && client_type != ewmh_sourceindication_client_type)
653     g_critical ("wnck_set_client_type: changing the client type is not supported.\n");
654   else
655     client_type = ewmh_sourceindication_client_type;
656 }
657 
658 WnckClientType
_wnck_get_client_type(void)659 _wnck_get_client_type (void)
660 {
661   /* If the type hasn't been set yet, use the default--treat it as a
662    * normal application.
663    */
664   if (client_type == 0)
665     client_type = WNCK_CLIENT_TYPE_APPLICATION;
666 
667   return client_type;
668 }
669 
670 static gsize default_icon_size = WNCK_DEFAULT_ICON_SIZE;
671 
672 /**
673  * wnck_set_default_icon_size:
674  * @size: the default size for windows and application standard icons.
675  *
676  * The default main icon size is %WNCK_DEFAULT_ICON_SIZE. This function allows
677  * to change this value.
678  *
679  * Since: 2.4.6
680  */
681 void
wnck_set_default_icon_size(gsize size)682 wnck_set_default_icon_size (gsize size)
683 {
684   default_icon_size = size;
685 }
686 
687 gsize
_wnck_get_default_icon_size(void)688 _wnck_get_default_icon_size (void)
689 {
690   return default_icon_size;
691 }
692 
693 static gsize default_mini_icon_size = WNCK_DEFAULT_MINI_ICON_SIZE;
694 
695 /**
696  * wnck_set_default_mini_icon_size:
697  * @size: the default size for windows and application mini icons.
698  *
699  * The default main icon size is %WNCK_DEFAULT_MINI_ICON_SIZE. This function
700  * allows to change this value.
701  *
702  * Since: 2.4.6
703  */
704 void
wnck_set_default_mini_icon_size(gsize size)705 wnck_set_default_mini_icon_size (gsize size)
706 {
707   int default_screen;
708   WnckScreen *screen;
709   GList *l;
710 
711   default_mini_icon_size = size;
712 
713   default_screen = DefaultScreen (_wnck_get_default_display ());
714   screen = _wnck_screen_get_existing (default_screen);
715 
716   if (WNCK_IS_SCREEN (screen))
717     {
718       /* Make applications and icons to reload their icons */
719       for (l = wnck_screen_get_windows (screen); l; l = l->next)
720         {
721           WnckWindow *window = WNCK_WINDOW (l->data);
722           WnckApplication *application = wnck_window_get_application (window);
723 
724           _wnck_window_load_icons (window);
725 
726           if (WNCK_IS_APPLICATION (application))
727             _wnck_application_load_icons (application);
728         }
729     }
730 }
731 
732 gsize
_wnck_get_default_mini_icon_size(void)733 _wnck_get_default_mini_icon_size (void)
734 {
735   return default_mini_icon_size;
736 }
737 
738 /**
739  * _make_gtk_label_bold:
740  * @label: The label.
741  *
742  * Switches the font of label to a bold equivalent.
743  **/
744 void
_make_gtk_label_bold(GtkLabel * label)745 _make_gtk_label_bold (GtkLabel *label)
746 {
747   GtkStyleContext *context;
748 
749   _wnck_ensure_fallback_style ();
750 
751   context = gtk_widget_get_style_context (GTK_WIDGET (label));
752   gtk_style_context_add_class (context, "wnck-needs-attention");
753 }
754 
755 void
_make_gtk_label_normal(GtkLabel * label)756 _make_gtk_label_normal (GtkLabel *label)
757 {
758   GtkStyleContext *context;
759 
760   context = gtk_widget_get_style_context (GTK_WIDGET (label));
761   gtk_style_context_remove_class (context, "wnck-needs-attention");
762 }
763 
764 #ifdef HAVE_STARTUP_NOTIFICATION
765 static gboolean
_wnck_util_sn_utf8_validator(const char * str,int max_len)766 _wnck_util_sn_utf8_validator (const char *str,
767                               int         max_len)
768 {
769   return g_utf8_validate (str, max_len, NULL);
770 }
771 #endif /* HAVE_STARTUP_NOTIFICATION */
772 
773 void
_wnck_init(void)774 _wnck_init (void)
775 {
776   static gboolean done = FALSE;
777 
778   if (!done)
779     {
780       bindtextdomain (GETTEXT_PACKAGE, WNCK_LOCALEDIR);
781       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
782 
783 #ifdef HAVE_STARTUP_NOTIFICATION
784       sn_set_utf8_validator (_wnck_util_sn_utf8_validator);
785 #endif /* HAVE_STARTUP_NOTIFICATION */
786 
787       done = TRUE;
788     }
789 }
790 
791 Display *
_wnck_get_default_display(void)792 _wnck_get_default_display (void)
793 {
794   GdkDisplay *display = gdk_display_get_default ();
795   /* FIXME: when we fix libwnck to not use the GDK default display, we will
796    * need to fix wnckprop accordingly. */
797   if (!GDK_IS_X11_DISPLAY (display))
798     {
799       g_warning ("libwnck is designed to work in X11 only, no valid display found");
800       return NULL;
801     }
802 
803   return GDK_DISPLAY_XDISPLAY (display);
804 }
805 
806 /**
807  * wnck_shutdown:
808  *
809  * Makes libwnck stop listening to events and tear down all resources from
810  * libwnck. This should be done if you are not going to need the state change
811  * notifications for an extended period of time, to avoid wakeups with every
812  * key and focus event.
813  *
814  * After this, all pointers to Wnck object you might still hold are invalid.
815  *
816  * Due to the fact that <link
817  * linkend="getting-started.pitfalls.memory-management">Wnck objects are all
818  * owned by libwnck</link>, users of this API through introspection should be
819  * extremely careful: they must explicitly clear variables referencing objects
820  * before this call. Failure to do so might result in crashes.
821  *
822  * Since: 3.4
823  */
824 void
wnck_shutdown(void)825 wnck_shutdown (void)
826 {
827   _wnck_event_filter_shutdown ();
828 
829   /* Warning: this is hacky :-)
830    *
831    * Shutting down all WnckScreen objects will automatically unreference (and
832    * finalize) all WnckWindow objects, but not the WnckClassGroup and
833    * WnckApplication objects.
834    * Therefore we need to manually shut down all WnckClassGroup and
835    * WnckApplication objects first, since they reference the WnckScreen they're
836    * on.
837    * On the other side, shutting down the WnckScreen objects will results in
838    * all WnckWindow objects getting unreferenced and finalized, and must
839    * actually be done before shutting down global WnckWindow structures
840    * (because the WnckScreen has a list of WnckWindow that will get mis-used
841    * otherwise). */
842   _wnck_class_group_shutdown_all ();
843   _wnck_application_shutdown_all ();
844   _wnck_screen_shutdown_all ();
845   _wnck_window_shutdown_all ();
846 
847 #ifdef HAVE_XRES
848   if (xres_removeid != 0)
849     g_source_remove (xres_removeid);
850   xres_removeid = 0;
851   wnck_pid_read_resource_usage_destroy_hash_table (NULL);
852 #endif
853 }
854 
855 void
_wnck_ensure_fallback_style(void)856 _wnck_ensure_fallback_style (void)
857 {
858   static gboolean css_loaded = FALSE;
859   GtkCssProvider *provider;
860   guint priority;
861 
862   if (css_loaded)
863     return;
864 
865   provider = gtk_css_provider_new ();
866   gtk_css_provider_load_from_resource (provider, "/org/gnome/libwnck/wnck.css");
867 
868   priority = GTK_STYLE_PROVIDER_PRIORITY_FALLBACK;
869   gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
870                                              GTK_STYLE_PROVIDER (provider),
871                                              priority);
872 
873   g_object_unref (provider);
874 
875   css_loaded = TRUE;
876 }
877