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