1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 #include "config.h"
4
5 #include <string.h>
6 #include <stdlib.h>
7
8 #include <X11/Xlib.h>
9 #include <X11/Xatom.h>
10 #include <gdk/gdk.h>
11 #include <gdk/gdkx.h>
12 #include <meta/display.h>
13 #include <meta/group.h>
14 #include <meta/util.h>
15 #include <meta/window.h>
16
17 #define SN_API_NOT_YET_FROZEN 1
18 #include <libsn/sn.h>
19
20 #include "shell-window-tracker-private.h"
21 #include "shell-app-private.h"
22 #include "shell-global.h"
23 #include "st.h"
24
25 /* This file includes modified code from
26 * desktop-data-engine/engine-dbus/hippo-application-monitor.c
27 * in the functions collecting application usage data.
28 * Written by Owen Taylor, originally licensed under LGPL 2.1.
29 * Copyright Red Hat, Inc. 2006-2008
30 */
31
32 /**
33 * SECTION:shell-window-tracker
34 * @short_description: Associate windows with applications
35 *
36 * Maintains a mapping from windows to applications (.desktop file ids).
37 * It currently implements this with some heuristics on the WM_CLASS X11
38 * property (and some static override regexps); in the future, we want to
39 * have it also track through startup-notification.
40 */
41
42 struct _ShellWindowTracker
43 {
44 GObject parent;
45
46 ShellApp *focus_app;
47
48 /* <MetaWindow * window, ShellApp *app> */
49 GHashTable *window_to_app;
50
51 /* <int, ShellApp *app> */
52 GHashTable *launched_pid_to_app;
53 };
54
55 G_DEFINE_TYPE (ShellWindowTracker, shell_window_tracker, G_TYPE_OBJECT);
56
57 enum {
58 PROP_0,
59 PROP_FOCUS_APP
60 };
61
62 enum {
63 STARTUP_SEQUENCE_CHANGED,
64 TRACKED_WINDOWS_CHANGED,
65
66 LAST_SIGNAL
67 };
68
69 static guint signals[LAST_SIGNAL] = { 0 };
70
71 static void shell_window_tracker_finalize (GObject *object);
72 static void set_focus_app (ShellWindowTracker *tracker,
73 ShellApp *new_focus_app);
74 static void on_focus_window_changed (MetaDisplay *display, GParamSpec *spec, ShellWindowTracker *tracker);
75
76 static void track_window (ShellWindowTracker *tracker, MetaWindow *window);
77 static void disassociate_window (ShellWindowTracker *tracker, MetaWindow *window);
78
79
80 static void
81 shell_window_tracker_get_property (GObject *gobject,
82 guint prop_id,
83 GValue *value,
84 GParamSpec *pspec)
85 {
86 ShellWindowTracker *tracker = SHELL_WINDOW_TRACKER (gobject);
87
88 switch (prop_id)
89 {
90 case PROP_FOCUS_APP:
91 g_value_set_object (value, tracker->focus_app);
92 break;
93 default:
94 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
95 break;
96 }
97 }
98
99 static void
100 shell_window_tracker_class_init (ShellWindowTrackerClass *klass)
101 {
102 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
103
104 gobject_class->get_property = shell_window_tracker_get_property;
105 gobject_class->finalize = shell_window_tracker_finalize;
106
107 g_object_class_install_property (gobject_class,
108 PROP_FOCUS_APP,
109 g_param_spec_object ("focus-app",
110 "Focus App",
111 "Focused application",
112 SHELL_TYPE_APP,
113 G_PARAM_READABLE));
114
115 signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed",
116 SHELL_TYPE_WINDOW_TRACKER,
117 G_SIGNAL_RUN_LAST,
118 0,
119 NULL, NULL, NULL,
120 G_TYPE_NONE, 1, SHELL_TYPE_STARTUP_SEQUENCE);
121 signals[TRACKED_WINDOWS_CHANGED] = g_signal_new ("tracked-windows-changed",
122 SHELL_TYPE_WINDOW_TRACKER,
123 G_SIGNAL_RUN_LAST,
124 0,
125 NULL, NULL, NULL,
126 G_TYPE_NONE, 0);
127 }
128
129 /**
130 * shell_window_tracker_is_window_interesting:
131 *
132 * The ShellWindowTracker associates certain kinds of windows with
133 * applications; however, others we don't want to
134 * appear in places where we want to give a list of windows
135 * for an application, such as the alt-tab dialog.
136 *
137 * An example of a window we don't want to show is the root
138 * desktop window. We skip all override-redirect types, and also
139 * exclude other window types like tooltip explicitly, though generally
140 * most of these should be override-redirect.
141 *
142 * Returns: %TRUE iff a window is "interesting"
143 */
144 gboolean
145 shell_window_tracker_is_window_interesting (MetaWindow *window)
146 {
147 if (meta_window_is_override_redirect (window)
148 || meta_window_is_skip_taskbar (window))
149 return FALSE;
150
151 switch (meta_window_get_window_type (window))
152 {
153 /* Definitely ignore these. */
154 case META_WINDOW_DESKTOP:
155 case META_WINDOW_DOCK:
156 case META_WINDOW_SPLASHSCREEN:
157 /* Should have already been handled by override_redirect above,
158 * but explicitly list here so we get the "unhandled enum"
159 * warning if in the future anything is added.*/
160 case META_WINDOW_DROPDOWN_MENU:
161 case META_WINDOW_POPUP_MENU:
162 case META_WINDOW_TOOLTIP:
163 case META_WINDOW_NOTIFICATION:
164 case META_WINDOW_COMBO:
165 case META_WINDOW_DND:
166 case META_WINDOW_OVERRIDE_OTHER:
167 return FALSE;
168 case META_WINDOW_NORMAL:
169 case META_WINDOW_DIALOG:
170 case META_WINDOW_MODAL_DIALOG:
171 case META_WINDOW_MENU:
172 case META_WINDOW_TOOLBAR:
173 case META_WINDOW_UTILITY:
174 break;
175 }
176
177 return TRUE;
178 }
179
180 /**
181 * get_app_from_window_group:
182 * @monitor: a #ShellWindowTracker
183 * @window: a #MetaWindow
184 *
185 * Check other windows in the group for @window to see if we have
186 * an application for one of them.
187 *
188 * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
189 */
190 static ShellApp*
191 get_app_from_window_group (ShellWindowTracker *tracker,
192 MetaWindow *window)
193 {
194 ShellApp *result;
195 GSList *group_windows;
196 MetaGroup *group;
197 GSList *iter;
198
199 group = meta_window_get_group (window);
200 if (group == NULL)
201 return NULL;
202
203 group_windows = meta_group_list_windows (group);
204
205 result = NULL;
206 /* Try finding a window in the group of type NORMAL; if we
207 * succeed, use that as our source. */
208 for (iter = group_windows; iter; iter = iter->next)
209 {
210 MetaWindow *group_window = iter->data;
211
212 if (meta_window_get_window_type (group_window) != META_WINDOW_NORMAL)
213 continue;
214
215 result = g_hash_table_lookup (tracker->window_to_app, group_window);
216 if (result)
217 break;
218 }
219
220 g_slist_free (group_windows);
221
222 if (result)
223 g_object_ref (result);
224
225 return result;
226 }
227
228 /**
229 * get_app_from_window_pid:
230 * @tracker: a #ShellWindowTracker
231 * @window: a #MetaWindow
232 *
233 * Check if the pid associated with @window corresponds to an
234 * application we launched.
235 *
236 * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
237 */
238 static ShellApp *
239 get_app_from_window_pid (ShellWindowTracker *tracker,
240 MetaWindow *window)
241 {
242 ShellApp *result;
243 int pid;
244
245 if (meta_window_is_remote (window))
246 return NULL;
247
248 pid = meta_window_get_pid (window);
249
250 if (pid == -1)
251 return NULL;
252
253 result = g_hash_table_lookup (tracker->launched_pid_to_app, GINT_TO_POINTER (pid));
254 if (result != NULL)
255 g_object_ref (result);
256
257 return result;
258 }
259
260 /**
261 * get_app_for_window:
262 *
263 * Determines the application associated with a window, using
264 * all available information such as the window's MetaGroup,
265 * and what we know about other windows.
266 *
267 * Returns: (transfer full): a #ShellApp, or NULL if none is found
268 */
269 static ShellApp *
270 get_app_for_window (ShellWindowTracker *tracker,
271 MetaWindow *window)
272 {
273 ShellAppSystem *app_system;
274 ShellApp *result = NULL;
275 const char *startup_id;
276
277 app_system = shell_app_system_get_default ();
278
279 /* First, we check whether we already know about this window,
280 * if so, just return that.
281 */
282 if (meta_window_get_window_type (window) == META_WINDOW_NORMAL
283 || meta_window_is_remote (window))
284 {
285 result = g_hash_table_lookup (tracker->window_to_app, window);
286 if (result != NULL)
287 {
288 g_object_ref (result);
289 return result;
290 }
291 }
292
293 if (meta_window_is_remote (window))
294 return _shell_app_new_for_window (window);
295
296 /* Check if the app's WM_CLASS specifies an app; this is
297 * canonical if it does.
298 */
299 result = shell_app_system_lookup_wmclass (app_system,
300 meta_window_get_wm_class (window));
301 if (result != NULL)
302 return g_object_ref (result);
303
304 result = get_app_from_window_pid (tracker, window);
305 if (result != NULL)
306 return result;
307
308 /* Now we check whether we have a match through startup-notification */
309 startup_id = meta_window_get_startup_id (window);
310 if (startup_id)
311 {
312 GSList *iter, *sequences;
313
314 sequences = shell_window_tracker_get_startup_sequences (tracker);
315 for (iter = sequences; iter; iter = iter->next)
316 {
317 ShellStartupSequence *sequence = iter->data;
318 const char *id = shell_startup_sequence_get_id (sequence);
319 if (strcmp (id, startup_id) != 0)
320 continue;
321
322 result = shell_startup_sequence_get_app (sequence);
323 if (result)
324 {
325 result = g_object_ref (result);
326 break;
327 }
328 }
329 }
330
331 /* If we didn't get a startup-notification match, see if we matched
332 * any other windows in the group.
333 */
334 if (result == NULL)
335 result = get_app_from_window_group (tracker, window);
336
337 /* Our last resort - we create a fake app from the window */
338 if (result == NULL)
339 result = _shell_app_new_for_window (window);
340
341 return result;
342 }
343
344 const char *
345 _shell_window_tracker_get_app_context (ShellWindowTracker *tracker, ShellApp *app)
346 {
347 return "";
348 }
349
350 static void
351 update_focus_app (ShellWindowTracker *self)
352 {
353 MetaWindow *new_focus_win;
354 ShellApp *new_focus_app;
355
356 new_focus_win = meta_display_get_focus_window (shell_global_get_display (shell_global_get ()));
357 new_focus_app = new_focus_win ? shell_window_tracker_get_window_app (self, new_focus_win) : NULL;
358
359 if (new_focus_app)
360 {
361 shell_app_update_window_actions (new_focus_app, new_focus_win);
362 shell_app_update_app_menu (new_focus_app, new_focus_win);
363 }
364
365 set_focus_app (self, new_focus_app);
366 }
367
368 static void
369 on_wm_class_changed (MetaWindow *window,
370 GParamSpec *pspec,
371 gpointer user_data)
372 {
373 ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
374
375 /* It's simplest to just treat this as a remove + add. */
376 disassociate_window (self, window);
377 track_window (self, window);
378 /* also just recaulcuate the focused app, in case it was the focused
379 window that changed */
380 update_focus_app (self);
381 }
382
383 static void
384 track_window (ShellWindowTracker *self,
385 MetaWindow *window)
386 {
387 ShellApp *app;
388
389 if (!shell_window_tracker_is_window_interesting (window))
390 return;
391
392 app = get_app_for_window (self, window);
393 if (!app)
394 return;
395
396 /* At this point we've stored the association from window -> application */
397 g_hash_table_insert (self->window_to_app, window, app);
398
399 g_signal_connect (window, "notify::wm-class", G_CALLBACK (on_wm_class_changed), self);
400
401 _shell_app_add_window (app, window);
402
403 g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
404 }
405
406 static void
407 shell_window_tracker_on_window_added (MetaWorkspace *workspace,
408 MetaWindow *window,
409 gpointer user_data)
410 {
411 ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
412
413 track_window (self, window);
414 }
415
416 static void
417 disassociate_window (ShellWindowTracker *self,
418 MetaWindow *window)
419 {
420 ShellApp *app;
421
422 app = g_hash_table_lookup (self->window_to_app, window);
423 if (!app)
424 return;
425
426 g_object_ref (app);
427
428 g_hash_table_remove (self->window_to_app, window);
429
430 if (shell_window_tracker_is_window_interesting (window))
431 {
432 _shell_app_remove_window (app, window);
433 g_signal_handlers_disconnect_by_func (window, G_CALLBACK(on_wm_class_changed), self);
434 }
435
436 g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
437
438 g_object_unref (app);
439 }
440
441 static void
442 shell_window_tracker_on_window_removed (MetaWorkspace *workspace,
443 MetaWindow *window,
444 gpointer user_data)
445 {
446 disassociate_window (SHELL_WINDOW_TRACKER (user_data), window);
447 }
448
449 static void
450 load_initial_windows (ShellWindowTracker *tracker)
451 {
452 GList *workspaces, *iter;
453 MetaScreen *screen = shell_global_get_screen (shell_global_get ());
454 workspaces = meta_screen_get_workspaces (screen);
455
456 for (iter = workspaces; iter; iter = iter->next)
457 {
458 MetaWorkspace *workspace = iter->data;
459 GList *windows = meta_workspace_list_windows (workspace);
460 GList *window_iter;
461
462 for (window_iter = windows; window_iter; window_iter = window_iter->next)
463 {
464 MetaWindow *window = window_iter->data;
465 track_window (tracker, window);
466 }
467
468 g_list_free (windows);
469 }
470 }
471
472 static void
473 shell_window_tracker_on_n_workspaces_changed (MetaScreen *screen,
474 GParamSpec *pspec,
475 gpointer user_data)
476 {
477 ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
478 GList *workspaces, *iter;
479
480 workspaces = meta_screen_get_workspaces (screen);
481
482 for (iter = workspaces; iter; iter = iter->next)
483 {
484 MetaWorkspace *workspace = iter->data;
485
486 /* This pair of disconnect/connect is idempotent if we were
487 * already connected, while ensuring we get connected for
488 * new workspaces.
489 */
490 g_signal_handlers_disconnect_by_func (workspace,
491 shell_window_tracker_on_window_added,
492 self);
493 g_signal_handlers_disconnect_by_func (workspace,
494 shell_window_tracker_on_window_removed,
495 self);
496
497 g_signal_connect (workspace, "window-added",
498 G_CALLBACK (shell_window_tracker_on_window_added), self);
499 g_signal_connect (workspace, "window-removed",
500 G_CALLBACK (shell_window_tracker_on_window_removed), self);
501 }
502 }
503
504 static void
505 init_window_tracking (ShellWindowTracker *self)
506 {
507 MetaDisplay *display;
508 MetaScreen *screen = shell_global_get_screen (shell_global_get ());
509
510 g_signal_connect (screen, "notify::n-workspaces",
511 G_CALLBACK (shell_window_tracker_on_n_workspaces_changed), self);
512 display = meta_screen_get_display (screen);
513 g_signal_connect (display, "notify::focus-window",
514 G_CALLBACK (on_focus_window_changed), self);
515
516 shell_window_tracker_on_n_workspaces_changed (screen, NULL, self);
517 }
518
519 static void
520 on_startup_sequence_changed (MetaScreen *screen,
521 SnStartupSequence *sequence,
522 ShellWindowTracker *self)
523 {
524 ShellApp *app;
525
526 app = shell_startup_sequence_get_app ((ShellStartupSequence*)sequence);
527 if (app)
528 _shell_app_handle_startup_sequence (app, sequence);
529
530 g_signal_emit (G_OBJECT (self), signals[STARTUP_SEQUENCE_CHANGED], 0, sequence);
531 }
532
533 static void
534 shell_window_tracker_init (ShellWindowTracker *self)
535 {
536 MetaScreen *screen;
537
538 self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
539 NULL, (GDestroyNotify) g_object_unref);
540
541 self->launched_pid_to_app = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref);
542
543 screen = shell_global_get_screen (shell_global_get ());
544
545 g_signal_connect (G_OBJECT (screen), "startup-sequence-changed",
546 G_CALLBACK (on_startup_sequence_changed), self);
547
548 load_initial_windows (self);
549 init_window_tracking (self);
550 }
551
552 static void
553 shell_window_tracker_finalize (GObject *object)
554 {
555 ShellWindowTracker *self = SHELL_WINDOW_TRACKER (object);
556
557 g_hash_table_destroy (self->window_to_app);
558 g_hash_table_destroy (self->launched_pid_to_app);
559
560 G_OBJECT_CLASS (shell_window_tracker_parent_class)->finalize(object);
561 }
562
563 /**
564 * shell_window_tracker_get_window_app:
565 * @tracker: An app monitor instance
566 * @metawin: A #MetaWindow
567 *
568 * Returns: (transfer full): Application associated with window
569 */
570 ShellApp *
571 shell_window_tracker_get_window_app (ShellWindowTracker *tracker,
572 MetaWindow *metawin)
573 {
574 MetaWindow *transient_for;
575 ShellApp *app;
576
577 transient_for = meta_window_get_transient_for (metawin);
578 if (transient_for != NULL)
579 metawin = transient_for;
580
581 app = g_hash_table_lookup (tracker->window_to_app, metawin);
582 if (app)
583 g_object_ref (app);
584
585 return app;
586 }
587
588
589 /**
590 * shell_window_tracker_get_app_from_pid:
591 * @tracker: A #ShellAppSystem
592 * @pid: A Unix process identifier
593 *
594 * Look up the application corresponding to a process.
595 *
596 * Returns: (transfer none): A #ShellApp, or %NULL if none
597 */
598 ShellApp *
599 shell_window_tracker_get_app_from_pid (ShellWindowTracker *tracker,
600 int pid)
601 {
602 GSList *running = shell_app_system_get_running (shell_app_system_get_default());
603 GSList *iter;
604 ShellApp *result = NULL;
605
606 for (iter = running; iter; iter = iter->next)
607 {
608 ShellApp *app = iter->data;
609 GSList *pids = shell_app_get_pids (app);
610 GSList *pids_iter;
611
612 for (pids_iter = pids; pids_iter; pids_iter = pids_iter->next)
613 {
614 int app_pid = GPOINTER_TO_INT (pids_iter->data);
615 if (app_pid == pid)
616 {
617 result = app;
618 break;
619 }
620 }
621 g_slist_free (pids);
622
623 if (result != NULL)
624 break;
625 }
626
627 g_slist_free (running);
628
629 return result;
630 }
631
632 static void
633 on_child_exited (GPid pid,
634 gint status,
635 gpointer unused_data)
636 {
637 ShellWindowTracker *tracker;
638
639 tracker = shell_window_tracker_get_default ();
640
641 g_hash_table_remove (tracker->launched_pid_to_app, GINT_TO_POINTER((gint)pid));
642 }
643
644 void
645 _shell_window_tracker_add_child_process_app (ShellWindowTracker *tracker,
646 GPid pid,
647 ShellApp *app)
648 {
649 gpointer pid_ptr = GINT_TO_POINTER((int)pid);
650
651 if (g_hash_table_lookup (tracker->launched_pid_to_app,
652 &pid_ptr))
653 return;
654
655 g_hash_table_insert (tracker->launched_pid_to_app,
656 pid_ptr,
657 g_object_ref (app));
658 g_child_watch_add (pid, on_child_exited, NULL);
659 /* TODO: rescan unassociated windows
660 * Unlikely in practice that the launched app gets ahead of us
661 * enough to map an X window before we get scheduled after the fork(),
662 * but adding this note for future reference.
663 */
664 }
665
666 static void
667 set_focus_app (ShellWindowTracker *tracker,
668 ShellApp *new_focus_app)
669 {
670 if (new_focus_app == tracker->focus_app)
671 return;
672
673 if (tracker->focus_app != NULL)
674 g_object_unref (tracker->focus_app);
675
676 tracker->focus_app = new_focus_app;
677
678 if (tracker->focus_app != NULL)
679 g_object_ref (tracker->focus_app);
680
681 g_object_notify (G_OBJECT (tracker), "focus-app");
682 }
683
684 static void
685 on_focus_window_changed (MetaDisplay *display,
686 GParamSpec *spec,
687 ShellWindowTracker *tracker)
688 {
689 update_focus_app (tracker);
690 }
691
692 /**
693 * shell_window_tracker_get_startup_sequences:
694 * @tracker:
695 *
696 * Returns: (transfer none) (element-type ShellStartupSequence): Currently active startup sequences
697 */
698 GSList *
699 shell_window_tracker_get_startup_sequences (ShellWindowTracker *self)
700 {
701 ShellGlobal *global = shell_global_get ();
702 MetaScreen *screen = shell_global_get_screen (global);
703 return meta_screen_get_startup_sequences (screen);
704 }
705
706 /* sn_startup_sequence_ref returns void, so make a
707 * wrapper which returns self */
708 static SnStartupSequence *
709 sequence_ref (SnStartupSequence *sequence)
710 {
711 sn_startup_sequence_ref (sequence);
712 return sequence;
713 }
714
715 GType
716 shell_startup_sequence_get_type (void)
717 {
718 static GType gtype = G_TYPE_INVALID;
719 if (gtype == G_TYPE_INVALID)
720 {
721 gtype = g_boxed_type_register_static ("ShellStartupSequence",
722 (GBoxedCopyFunc)sequence_ref,
723 (GBoxedFreeFunc)sn_startup_sequence_unref);
724 }
725 return gtype;
726 }
727
728 const char *
729 shell_startup_sequence_get_id (ShellStartupSequence *sequence)
730 {
731 return sn_startup_sequence_get_id ((SnStartupSequence*)sequence);
732 }
733
734 /**
735 * shell_startup_sequence_get_app:
736 * @sequence: A #ShellStartupSequence
737 *
738 * Returns: (transfer none): The application being launched, or %NULL if unknown.
739 */
740 ShellApp *
741 shell_startup_sequence_get_app (ShellStartupSequence *sequence)
742 {
743 const char *appid;
744 ShellAppSystem *appsys;
745 ShellApp *app;
746
747 appid = sn_startup_sequence_get_application_id ((SnStartupSequence*)sequence);
748 if (!appid)
749 return NULL;
750
751 appsys = shell_app_system_get_default ();
752 app = shell_app_system_lookup_app_for_path (appsys, appid);
753 return app;
754 }
755
756 const char *
757 shell_startup_sequence_get_name (ShellStartupSequence *sequence)
758 {
759 return sn_startup_sequence_get_name ((SnStartupSequence*)sequence);
760 }
761
762 gboolean
763 shell_startup_sequence_get_completed (ShellStartupSequence *sequence)
764 {
765 return sn_startup_sequence_get_completed ((SnStartupSequence*)sequence);
766 }
767
768 int
769 shell_startup_sequence_get_workspace (ShellStartupSequence *sequence)
770 {
771 return sn_startup_sequence_get_workspace ((SnStartupSequence*)sequence);
772 }
773
774 /**
775 * shell_startup_sequence_create_icon:
776 * @sequence:
777 * @size: Size in pixels of icon
778 *
779 * Returns: (transfer none): A new #ClutterTexture containing an icon for the sequence
780 */
781 ClutterActor *
782 shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size)
783 {
784 GIcon *themed;
785 const char *icon_name;
786 ClutterActor *texture;
787
788 icon_name = sn_startup_sequence_get_icon_name ((SnStartupSequence*)sequence);
789 if (!icon_name)
790 {
791 texture = clutter_texture_new ();
'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
(emitted by gcc)
792 clutter_actor_set_size (texture, size, size);
793 return texture;
794 }
795
796 themed = g_themed_icon_new (icon_name);
797 texture = st_texture_cache_load_gicon (st_texture_cache_get_default (),
798 NULL, themed, size);
799 g_object_unref (G_OBJECT (themed));
800 return texture;
801 }
802
803
804 /**
805 * shell_window_tracker_get_default:
806 *
807 * Return Value: (transfer none): The global #ShellWindowTracker instance
808 */
809 ShellWindowTracker *
810 shell_window_tracker_get_default ()
811 {
812 static ShellWindowTracker *instance;
813
814 if (instance == NULL)
815 instance = g_object_new (SHELL_TYPE_WINDOW_TRACKER, NULL);
816
817 return instance;
818 }