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, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #undef WNCK_DISABLE_DEPRECATED
25 
26 #include <config.h>
27 
28 #include <glib/gi18n-lib.h>
29 #include "util.h"
30 #include "xutils.h"
31 #include "private.h"
32 #include "inlinepixbufs.h"
33 #include <gdk/gdkx.h>
34 #include <string.h>
35 #ifdef HAVE_XRES
36 #include <X11/extensions/XRes.h>
37 #endif
38 
39 /**
40  * SECTION:resource
41  * @short_description: reading resource usage of X clients.
42  * @see_also: wnck_window_get_xid(), wnck_application_get_xid(), wnck_window_get_pid(), wnck_application_get_pid()
43  * @stability: Unstable
44  *
45  * libwnck provides an easy-to-use interface to the XRes X server extension to
46  * read resource usage of X clients, which can be defined either by the X
47  * window ID of one of their windows or by the process ID of their process.
48  */
49 
50 /**
51  * SECTION:misc
52  * @short_description: other additional features.
53  * @stability: Unstable
54  *
55  * These functions are utility functions providing some additional features to
56  * libwcnk users.
57  */
58 
59 /**
60  * wnck_gtk_window_set_dock_type:
61  * @window: a <classname>GtkWindow</classname>.
62  *
63  * Sets the semantic type of @window to %WNCK_WINDOW_DOCK.
64  *
65  * Deprecated:2.20: Use gdk_window_set_type_hint() instead.
66  */
67 void
wnck_gtk_window_set_dock_type(GtkWindow * window)68 wnck_gtk_window_set_dock_type (GtkWindow *window)
69 {
70   g_return_if_fail (GTK_IS_WINDOW (window));
71 
72   gdk_window_set_type_hint (gtk_widget_get_window (GTK_WIDGET (window)),
73 		  	    GDK_WINDOW_TYPE_HINT_DOCK);
74 }
75 
76 typedef enum
77 {
78   WNCK_EXT_UNKNOWN = 0,
79   WNCK_EXT_FOUND = 1,
80   WNCK_EXT_MISSING = 2
81 } WnckExtStatus;
82 
83 
84 #if 0
85 /* useful for debugging */
86 static void
87 _wnck_print_resource_usage (WnckResourceUsage *usage)
88 {
89   if (!usage)
90     return;
91 
92   g_print ("\twindows       : %d\n"
93            "\tGCs           : %d\n"
94            "\tfonts         : %d\n"
95            "\tpixmaps       : %d\n"
96            "\tpictures      : %d\n"
97            "\tglyphsets     : %d\n"
98            "\tcolormaps     : %d\n"
99            "\tpassive grabs : %d\n"
100            "\tcursors       : %d\n"
101            "\tunknowns      : %d\n"
102            "\tpixmap bytes  : %ld\n"
103            "\ttotal bytes   : ~%ld\n",
104            usage->n_windows,
105            usage->n_gcs,
106            usage->n_fonts,
107            usage->n_pixmaps,
108            usage->n_pictures,
109            usage->n_glyphsets,
110            usage->n_colormap_entries,
111            usage->n_passive_grabs,
112            usage->n_cursors,
113            usage->n_other,
114            usage->pixmap_bytes,
115            usage->total_bytes_estimate);
116 }
117 #endif
118 
119 static WnckExtStatus
wnck_init_resource_usage(GdkDisplay * gdisplay)120 wnck_init_resource_usage (GdkDisplay *gdisplay)
121 {
122   int event, error;
123   Display *xdisplay;
124   WnckExtStatus status;
125 
126   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
127 
128   status = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gdisplay),
129                                                "wnck-xres-status"));
130 
131   if (status == WNCK_EXT_UNKNOWN)
132     {
133 #ifdef HAVE_XRES
134       if (!XResQueryExtension (xdisplay, &event, &error))
135         status = WNCK_EXT_MISSING;
136       else
137         status = WNCK_EXT_FOUND;
138 #else
139       status = WNCK_EXT_MISSING;
140 #endif
141 
142       g_object_set_data (G_OBJECT (gdisplay),
143                          "wnck-xres-status",
144                          GINT_TO_POINTER (status));
145     }
146 
147   g_assert (status != WNCK_EXT_UNKNOWN);
148 
149   return status;
150 }
151 
152 /**
153  * wnck_xid_read_resource_usage:
154  * @gdk_display: a <classname>GdkDisplay</classname>.
155  * @xid: an X window ID.
156  * @usage: return location for the X resource usage of the application owning
157  * the X window ID @xid.
158  *
159  * Looks for the X resource usage of the application owning the X window ID
160  * @xid on display @gdisplay. If no resource usage can be found, then all
161  * fields of @usage are set to 0.
162  *
163  * To properly work, this function requires the XRes extension on the X server.
164  *
165  * Since: 2.6
166  */
167 void
wnck_xid_read_resource_usage(GdkDisplay * gdisplay,gulong xid,WnckResourceUsage * usage)168 wnck_xid_read_resource_usage (GdkDisplay        *gdisplay,
169                               gulong             xid,
170                               WnckResourceUsage *usage)
171 {
172   g_return_if_fail (usage != NULL);
173 
174   memset (usage, '\0', sizeof (*usage));
175 
176   if (wnck_init_resource_usage (gdisplay) == WNCK_EXT_MISSING)
177     return;
178 
179 #ifdef HAVE_XRES
180  {
181    Display *xdisplay;
182    XResType *types;
183    int n_types;
184    unsigned long pixmap_bytes;
185    int i;
186    Atom pixmap_atom;
187    Atom window_atom;
188    Atom gc_atom;
189    Atom picture_atom;
190    Atom glyphset_atom;
191    Atom font_atom;
192    Atom colormap_entry_atom;
193    Atom passive_grab_atom;
194    Atom cursor_atom;
195 
196    types = NULL;
197    n_types = 0;
198    pixmap_bytes = 0;
199 
200   _wnck_error_trap_push ();
201 
202   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
203 
204   XResQueryClientResources (xdisplay,
205                              xid, &n_types,
206                              &types);
207 
208    XResQueryClientPixmapBytes (xdisplay,
209                                xid, &pixmap_bytes);
210    _wnck_error_trap_pop ();
211 
212    usage->pixmap_bytes = pixmap_bytes;
213 
214    pixmap_atom = _wnck_atom_get ("PIXMAP");
215    window_atom = _wnck_atom_get ("WINDOW");
216    gc_atom = _wnck_atom_get ("GC");
217    font_atom = _wnck_atom_get ("FONT");
218    glyphset_atom = _wnck_atom_get ("GLYPHSET");
219    picture_atom = _wnck_atom_get ("PICTURE");
220    colormap_entry_atom = _wnck_atom_get ("COLORMAP ENTRY");
221    passive_grab_atom = _wnck_atom_get ("PASSIVE GRAB");
222    cursor_atom = _wnck_atom_get ("CURSOR");
223 
224    i = 0;
225    while (i < n_types)
226      {
227        int t = types[i].resource_type;
228 
229        if (t == pixmap_atom)
230          usage->n_pixmaps += types[i].count;
231        else if (t == window_atom)
232          usage->n_windows += types[i].count;
233        else if (t == gc_atom)
234          usage->n_gcs += types[i].count;
235        else if (t == picture_atom)
236          usage->n_pictures += types[i].count;
237        else if (t == glyphset_atom)
238          usage->n_glyphsets += types[i].count;
239        else if (t == font_atom)
240          usage->n_fonts += types[i].count;
241        else if (t == colormap_entry_atom)
242          usage->n_colormap_entries += types[i].count;
243        else if (t == passive_grab_atom)
244          usage->n_passive_grabs += types[i].count;
245        else if (t == cursor_atom)
246          usage->n_cursors += types[i].count;
247        else
248          usage->n_other += types[i].count;
249 
250        ++i;
251      }
252 
253    XFree(types);
254 
255    usage->total_bytes_estimate = usage->pixmap_bytes;
256 
257    /* FIXME look in the X server source and come up with better
258     * answers here. Ideally we change XRes to return a number
259     * like this since it can do things like divide the cost of
260     * a shared resource among those sharing it.
261     */
262    usage->total_bytes_estimate += usage->n_windows * 24;
263    usage->total_bytes_estimate += usage->n_gcs * 24;
264    usage->total_bytes_estimate += usage->n_pictures * 24;
265    usage->total_bytes_estimate += usage->n_glyphsets * 24;
266    usage->total_bytes_estimate += usage->n_fonts * 1024;
267    usage->total_bytes_estimate += usage->n_colormap_entries * 24;
268    usage->total_bytes_estimate += usage->n_passive_grabs * 24;
269    usage->total_bytes_estimate += usage->n_cursors * 24;
270    usage->total_bytes_estimate += usage->n_other * 24;
271  }
272 #else /* HAVE_XRES */
273   g_assert_not_reached ();
274 #endif /* HAVE_XRES */
275 }
276 
277 #ifdef HAVE_XRES
278 static void
wnck_pid_read_resource_usage_free_hash(gpointer data)279 wnck_pid_read_resource_usage_free_hash (gpointer data)
280 {
281   g_slice_free (gulong, data);
282 }
283 
284 static guint
wnck_gulong_hash(gconstpointer v)285 wnck_gulong_hash (gconstpointer v)
286 {
287   /* FIXME: this is obvioulsy wrong, but nearly 100% of the time, the gulong
288    * only contains guint values */
289   return *(const guint *) v;
290 }
291 
292 static gboolean
wnck_gulong_equal(gconstpointer a,gconstpointer b)293 wnck_gulong_equal (gconstpointer a,
294                    gconstpointer b)
295 {
296   return *((const gulong *) a) == *((const gulong *) b);
297 }
298 
299 static gulong
wnck_check_window_for_pid(Window win,XID match_xid,XID mask)300 wnck_check_window_for_pid (Window win,
301                            XID    match_xid,
302                            XID    mask)
303 {
304   if ((win & ~mask) == match_xid) {
305     return _wnck_get_pid (win);
306   }
307 
308   return 0;
309 }
310 
311 static void
wnck_find_pid_for_resource_r(Display * xdisplay,Window win_top,XID match_xid,XID mask,gulong * xid,gulong * pid)312 wnck_find_pid_for_resource_r (Display *xdisplay,
313                               Window   win_top,
314                               XID      match_xid,
315                               XID      mask,
316                               gulong  *xid,
317                               gulong  *pid)
318 {
319   Status   qtres;
320   int      err;
321   Window   dummy;
322   Window  *children;
323   guint    n_children;
324   int      i;
325   gulong   found_pid = 0;
326 
327   while (gtk_events_pending ())
328     gtk_main_iteration ();
329 
330   found_pid = wnck_check_window_for_pid (win_top, match_xid, mask);
331   if (found_pid != 0)
332     {
333       *xid = win_top;
334       *pid = found_pid;
335     }
336 
337   _wnck_error_trap_push ();
338   qtres = XQueryTree (xdisplay, win_top, &dummy, &dummy,
339                       &children, &n_children);
340   err = _wnck_error_trap_pop ();
341 
342   if (!qtres || err != Success)
343     return;
344 
345   for (i = 0; i < n_children; i++)
346     {
347       wnck_find_pid_for_resource_r (xdisplay, children[i],
348                                     match_xid, mask, xid, pid);
349 
350       if (*pid != 0)
351 	break;
352     }
353 
354   if (children)
355     XFree ((char *)children);
356 }
357 
358 struct xresclient_state
359 {
360   XResClient *clients;
361   int         n_clients;
362   int         next;
363   Display    *xdisplay;
364   GHashTable *hashtable_pid;
365 };
366 
367 static struct xresclient_state xres_state = { NULL, 0, -1, NULL, NULL };
368 static guint       xres_idleid = 0;
369 static GHashTable *xres_hashtable = NULL;
370 static time_t      start_update = 0;
371 static time_t      end_update = 0;
372 static guint       xres_removeid = 0;
373 
374 static void
wnck_pid_read_resource_usage_xres_state_free(gpointer data)375 wnck_pid_read_resource_usage_xres_state_free (gpointer data)
376 {
377   struct xresclient_state *state;
378 
379   state = (struct xresclient_state *) data;
380 
381   if (state->clients)
382     XFree (state->clients);
383   state->clients = NULL;
384 
385   state->n_clients = 0;
386   state->next = -1;
387   state->xdisplay = NULL;
388 
389   if (state->hashtable_pid)
390     g_hash_table_destroy (state->hashtable_pid);
391   state->hashtable_pid = NULL;
392 }
393 
394 static gboolean
wnck_pid_read_resource_usage_fill_cache(struct xresclient_state * state)395 wnck_pid_read_resource_usage_fill_cache (struct xresclient_state *state)
396 {
397   int    i;
398   gulong pid;
399   gulong xid;
400   XID    match_xid;
401 
402   if (state->next >= state->n_clients)
403     {
404       if (xres_hashtable)
405         g_hash_table_destroy (xres_hashtable);
406       xres_hashtable = state->hashtable_pid;
407       state->hashtable_pid = NULL;
408 
409       time (&end_update);
410 
411       xres_idleid = 0;
412       return FALSE;
413     }
414 
415   match_xid = (state->clients[state->next].resource_base &
416                ~state->clients[state->next].resource_mask);
417 
418   pid = 0;
419   xid = 0;
420 
421   for (i = 0; i < ScreenCount (state->xdisplay); i++)
422     {
423       Window root;
424 
425       root = RootWindow (state->xdisplay, i);
426 
427       if (root == None)
428         continue;
429 
430       wnck_find_pid_for_resource_r (state->xdisplay, root, match_xid,
431                                     state->clients[state->next].resource_mask,
432                                     &xid, &pid);
433 
434       if (pid != 0 && xid != 0)
435         break;
436     }
437 
438   if (pid != 0 && xid != 0)
439     {
440       gulong *key;
441       gulong *value;
442 
443       key = g_slice_new (gulong);
444       value = g_slice_new (gulong);
445       *key = pid;
446       *value = xid;
447       g_hash_table_insert (state->hashtable_pid, key, value);
448     }
449 
450   state->next++;
451 
452   return TRUE;
453 }
454 
455 static void
wnck_pid_read_resource_usage_start_build_cache(GdkDisplay * gdisplay)456 wnck_pid_read_resource_usage_start_build_cache (GdkDisplay *gdisplay)
457 {
458   Display *xdisplay;
459   int      err;
460 
461   if (xres_idleid != 0)
462     return;
463 
464   time (&start_update);
465 
466   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
467 
468   _wnck_error_trap_push ();
469   XResQueryClients (xdisplay, &xres_state.n_clients, &xres_state.clients);
470   err = _wnck_error_trap_pop ();
471 
472   if (err != Success)
473     return;
474 
475   xres_state.next = (xres_state.n_clients > 0) ? 0 : -1;
476   xres_state.xdisplay = xdisplay;
477   xres_state.hashtable_pid = g_hash_table_new_full (
478                                      wnck_gulong_hash,
479                                      wnck_gulong_equal,
480                                      wnck_pid_read_resource_usage_free_hash,
481                                      wnck_pid_read_resource_usage_free_hash);
482 
483   xres_idleid = g_idle_add_full (
484                         G_PRIORITY_HIGH_IDLE,
485                         (GSourceFunc) wnck_pid_read_resource_usage_fill_cache,
486                         &xres_state, wnck_pid_read_resource_usage_xres_state_free);
487 }
488 
489 static gboolean
wnck_pid_read_resource_usage_destroy_hash_table(gpointer data)490 wnck_pid_read_resource_usage_destroy_hash_table (gpointer data)
491 {
492   xres_removeid = 0;
493 
494   if (xres_hashtable)
495     g_hash_table_destroy (xres_hashtable);
496 
497   xres_hashtable = NULL;
498 
499   return FALSE;
500 }
501 
502 #define XRES_UPDATE_RATE_SEC 30
503 static gboolean
wnck_pid_read_resource_usage_from_cache(GdkDisplay * gdisplay,gulong pid,WnckResourceUsage * usage)504 wnck_pid_read_resource_usage_from_cache (GdkDisplay        *gdisplay,
505                                          gulong             pid,
506                                          WnckResourceUsage *usage)
507 {
508   gboolean  need_rebuild;
509   gulong   *xid_p;
510   int       cache_validity;
511 
512   if (end_update == 0)
513     time (&end_update);
514 
515   cache_validity = MAX (XRES_UPDATE_RATE_SEC, (end_update - start_update) * 2);
516 
517   /* we rebuild the cache if it was never built or if it's old */
518   need_rebuild = (xres_hashtable == NULL ||
519                   (end_update < time (NULL) - cache_validity));
520 
521   if (xres_hashtable)
522     {
523       /* clear the cache after quite some time, because it might not be used
524        * anymore */
525       if (xres_removeid != 0)
526         g_source_remove (xres_removeid);
527       xres_removeid = g_timeout_add_seconds (cache_validity * 2,
528                                              wnck_pid_read_resource_usage_destroy_hash_table,
529                                              NULL);
530     }
531 
532   if (need_rebuild)
533     wnck_pid_read_resource_usage_start_build_cache (gdisplay);
534 
535   if (xres_hashtable)
536     xid_p = g_hash_table_lookup (xres_hashtable, &pid);
537   else
538     xid_p = NULL;
539 
540   if (xid_p)
541     {
542       wnck_xid_read_resource_usage (gdisplay, *xid_p, usage);
543       return TRUE;
544     }
545 
546   return FALSE;
547 }
548 
549 static void
wnck_pid_read_resource_usage_no_cache(GdkDisplay * gdisplay,gulong pid,WnckResourceUsage * usage)550 wnck_pid_read_resource_usage_no_cache (GdkDisplay        *gdisplay,
551                                        gulong             pid,
552                                        WnckResourceUsage *usage)
553 {
554   Display *xdisplay;
555   int i;
556 
557   xdisplay = GDK_DISPLAY_XDISPLAY (gdisplay);
558 
559   i = 0;
560   while (i < ScreenCount (xdisplay))
561     {
562       WnckScreen *screen;
563       GList *windows;
564       GList *tmp;
565 
566       screen = wnck_screen_get (i);
567 
568       g_assert (screen != NULL);
569 
570       windows = wnck_screen_get_windows (screen);
571       tmp = windows;
572       while (tmp != NULL)
573         {
574           if (wnck_window_get_pid (tmp->data) == pid)
575             {
576               wnck_xid_read_resource_usage (gdisplay,
577                                             wnck_window_get_xid (tmp->data),
578                                             usage);
579 
580               /* stop on first window found */
581               return;
582             }
583 
584           tmp = tmp->next;
585         }
586 
587       ++i;
588     }
589 }
590 #endif /* HAVE_XRES */
591 
592 /**
593  * wnck_pid_read_resource_usage:
594  * @gdk_display: a <classname>GdkDisplay</classname>.
595  * @pid: a process ID.
596  * @usage: return location for the X resource usage of the application with
597  * process ID @pid.
598  *
599  * Looks for the X resource usage of the application with process ID @pid on
600  * display @gdisplay. If no resource usage can be found, then all fields of
601  * @usage are set to 0.
602  *
603  * In order to find the resource usage of an application that does not have an
604  * X window visible to libwnck (panel applets do not have any toplevel windows,
605  * for example), wnck_pid_read_resource_usage() walks through the whole tree of
606  * X windows. Since this walk is expensive in CPU, a cache is created. This
607  * cache is updated in the background. This means there is a non-null
608  * probability that no resource usage will be found for an application, even if
609  * it is an X client. If this happens, calling wnck_pid_read_resource_usage()
610  * again after a few seconds should work.
611  *
612  * To properly work, this function requires the XRes extension on the X server.
613  *
614  * Since: 2.6
615  */
616 void
wnck_pid_read_resource_usage(GdkDisplay * gdisplay,gulong pid,WnckResourceUsage * usage)617 wnck_pid_read_resource_usage (GdkDisplay        *gdisplay,
618                               gulong             pid,
619                               WnckResourceUsage *usage)
620 {
621   g_return_if_fail (usage != NULL);
622 
623   memset (usage, '\0', sizeof (*usage));
624 
625   if (wnck_init_resource_usage (gdisplay) == WNCK_EXT_MISSING)
626     return;
627 
628 #ifdef HAVE_XRES
629   if (!wnck_pid_read_resource_usage_from_cache (gdisplay, pid, usage))
630     /* the cache might not be built, might be outdated or might not contain
631      * data for a new X client, so try to fallback to something else */
632     wnck_pid_read_resource_usage_no_cache (gdisplay, pid, usage);
633 #endif /* HAVE_XRES */
634 }
635 
636 static WnckClientType client_type = 0;
637 
638 /**
639  * wnck_set_client_type:
640  * @ewmh_sourceindication_client_type: a role for the client.
641  *
642  * Sets the role of the libwnck user.
643  *
644  * The default role is %WNCK_CLIENT_TYPE_APPLICATION. Therefore, for
645  * applications providing some window management features, like pagers or
646  * tasklists, it is important to set the role to %WNCK_CLIENT_TYPE_PAGER for
647  * libwnck to properly work.
648  *
649  * Since: 2.14
650  */
651 void
wnck_set_client_type(WnckClientType ewmh_sourceindication_client_type)652 wnck_set_client_type (WnckClientType ewmh_sourceindication_client_type)
653 {
654   /* Clients constantly switching types makes no sense; this should only be
655    * set once.
656    */
657   if (client_type != 0)
658     g_critical ("wnck_set_client_type got called multiple times.\n");
659   else
660     client_type = ewmh_sourceindication_client_type;
661 }
662 
663 WnckClientType
_wnck_get_client_type(void)664 _wnck_get_client_type (void)
665 {
666   /* If the type hasn't been set yet, use the default--treat it as a
667    * normal application.
668    */
669   if (client_type == 0)
670     client_type = WNCK_CLIENT_TYPE_APPLICATION;
671 
672   return client_type;
673 }
674 
675 /**
676  * _make_gtk_label_bold:
677  *
678  * Switches the font of label to a bold equivalent.
679  * @label: The label.
680  **/
681 void
_make_gtk_label_bold(GtkLabel * label)682 _make_gtk_label_bold (GtkLabel *label)
683 {
684   PangoFontDescription *font_desc;
685 
686   font_desc = pango_font_description_new ();
687 
688   pango_font_description_set_weight (font_desc,
689                                      PANGO_WEIGHT_BOLD);
690 
691   /* This will only affect the weight of the font, the rest is
692    * from the current state of the widget, which comes from the
693    * theme or user prefs, since the font desc only has the
694    * weight flag turned on.
695    */
696   gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
697 
698   pango_font_description_free (font_desc);
699 }
700 
701 void
_make_gtk_label_normal(GtkLabel * label)702 _make_gtk_label_normal (GtkLabel *label)
703 {
704   PangoFontDescription *font_desc;
705 
706   font_desc = pango_font_description_new ();
707 
708   pango_font_description_set_weight (font_desc,
709                                      PANGO_WEIGHT_NORMAL);
710 
711   /* This will only affect the weight of the font, the rest is
712    * from the current state of the widget, which comes from the
713    * theme or user prefs, since the font desc only has the
714    * weight flag turned on.
715    */
716   gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
717 
718   pango_font_description_free (font_desc);
719 }
720 
721 #ifdef HAVE_STARTUP_NOTIFICATION
722 static gboolean
_wnck_util_sn_utf8_validator(const char * str,int max_len)723 _wnck_util_sn_utf8_validator (const char *str,
724                               int         max_len)
725 {
726   return g_utf8_validate (str, max_len, NULL);
727 }
728 #endif /* HAVE_STARTUP_NOTIFICATION */
729 
730 void
_wnck_init(void)731 _wnck_init (void)
732 {
733   static gboolean done = FALSE;
734 
735   if (!done)
736     {
737       bindtextdomain (GETTEXT_PACKAGE, WNCK_LOCALEDIR);
738       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
739 
740 #ifdef HAVE_STARTUP_NOTIFICATION
741       sn_set_utf8_validator (_wnck_util_sn_utf8_validator);
742 #endif /* HAVE_STARTUP_NOTIFICATION */
743 
744       done = TRUE;
745     }
746 }
747 
748 Display *
_wnck_get_default_display(void)749 _wnck_get_default_display (void)
750 {
751   /* FIXME: when we fix libwnck to not use the GDK default display, we will
752    * need to fix wnckprop accordingly. */
753   return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
754 }
755 
756 /* stock icon code Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org> */
757 typedef struct
758 {
759   char *stock_id;
760   const guint8 *icon_data;
761 } StockIcon;
762 
763 void
_wnck_stock_icons_init(void)764 _wnck_stock_icons_init (void)
765 {
766   GtkIconFactory *factory;
767   int i;
768   static gboolean done = FALSE;
769 
770   StockIcon items[] =
771   {
772     { WNCK_STOCK_DELETE,   stock_delete_data   },
773     { WNCK_STOCK_MINIMIZE, stock_minimize_data },
774     { WNCK_STOCK_MAXIMIZE, stock_maximize_data }
775   };
776 
777   if (done)
778     return;
779 
780   done = TRUE;
781 
782   factory = gtk_icon_factory_new ();
783   gtk_icon_factory_add_default (factory);
784 
785   for (i = 0; i < (gint) G_N_ELEMENTS (items); i++)
786     {
787       GtkIconSet *icon_set;
788       GdkPixbuf *pixbuf;
789 
790       pixbuf = gdk_pixbuf_new_from_inline (-1, items[i].icon_data,
791 					   FALSE,
792 					   NULL);
793 
794       icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
795       gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
796       gtk_icon_set_unref (icon_set);
797 
798       g_object_unref (G_OBJECT (pixbuf));
799     }
800 
801   g_object_unref (G_OBJECT (factory));
802 }
803