1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 #include "config.h"
4 
5 #include <dirent.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <math.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #ifdef HAVE_SYS_RESOURCE_H
14 #include <sys/resource.h>
15 #endif
16 #include <locale.h>
17 
18 #include <X11/extensions/Xfixes.h>
19 #include <gdk/gdkx.h>
20 #include <gio/gio.h>
21 #include <girepository.h>
22 #include <meta/meta-backend.h>
23 #include <meta/meta-context.h>
24 #include <meta/display.h>
25 #include <meta/util.h>
26 #include <meta/meta-shaped-texture.h>
27 #include <meta/meta-cursor-tracker.h>
28 #include <meta/meta-settings.h>
29 #include <meta/meta-workspace-manager.h>
30 #include <meta/meta-x11-display.h>
31 
32 #define GNOME_DESKTOP_USE_UNSTABLE_API
33 #include <libgnome-desktop/gnome-systemd.h>
34 
35 #if defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
36 #include <sys/sysctl.h>
37 #endif
38 
39 #include "shell-enum-types.h"
40 #include "shell-global-private.h"
41 #include "shell-perf-log.h"
42 #include "shell-window-tracker.h"
43 #include "shell-wm.h"
44 #include "shell-util.h"
45 #include "st.h"
46 #include "switcheroo-control.h"
47 
48 static ShellGlobal *the_object = NULL;
49 
50 struct _ShellGlobal {
51   GObject parent;
52 
53   ClutterStage *stage;
54 
55   MetaBackend *backend;
56   MetaContext *meta_context;
57   MetaDisplay *meta_display;
58   MetaWorkspaceManager *workspace_manager;
59   Display *xdisplay;
60 
61   char *session_mode;
62 
63   XserverRegion input_region;
64 
65   GjsContext *js_context;
66   MetaPlugin *plugin;
67   ShellWM *wm;
68   GSettings *settings;
69   const char *datadir;
70   char *imagedir;
71   char *userdatadir;
72   GFile *userdatadir_path;
73   GFile *runtime_state_path;
74 
75   StFocusManager *focus_manager;
76 
77   guint work_count;
78   GSList *leisure_closures;
79   guint leisure_function_id;
80 
81   GHashTable *save_ops;
82 
83   gboolean has_modal;
84   gboolean frame_timestamps;
85   gboolean frame_finish_timestamp;
86 
87   GDBusProxy *switcheroo_control;
88   GCancellable *switcheroo_cancellable;
89 };
90 
91 enum {
92   PROP_0,
93 
94   PROP_SESSION_MODE,
95   PROP_BACKEND,
96   PROP_CONTEXT,
97   PROP_DISPLAY,
98   PROP_WORKSPACE_MANAGER,
99   PROP_SCREEN_WIDTH,
100   PROP_SCREEN_HEIGHT,
101   PROP_STAGE,
102   PROP_WINDOW_GROUP,
103   PROP_TOP_WINDOW_GROUP,
104   PROP_WINDOW_MANAGER,
105   PROP_SETTINGS,
106   PROP_DATADIR,
107   PROP_IMAGEDIR,
108   PROP_USERDATADIR,
109   PROP_FOCUS_MANAGER,
110   PROP_FRAME_TIMESTAMPS,
111   PROP_FRAME_FINISH_TIMESTAMP,
112   PROP_SWITCHEROO_CONTROL,
113 };
114 
115 /* Signals */
116 enum
117 {
118  NOTIFY_ERROR,
119  LOCATE_POINTER,
120  LAST_SIGNAL
121 };
122 
123 G_DEFINE_TYPE(ShellGlobal, shell_global, G_TYPE_OBJECT);
124 
125 static guint shell_global_signals [LAST_SIGNAL] = { 0 };
126 
127 static void
got_switcheroo_control_gpus_property_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)128 got_switcheroo_control_gpus_property_cb (GObject      *source_object,
129                                          GAsyncResult *res,
130                                          gpointer      user_data)
131 {
132   ShellGlobal *global;
133   GError *error = NULL;
134   GVariant *gpus;
135 
136   gpus = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
137                                         res, &error);
138   if (!gpus)
139     {
140       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
141         g_debug ("Could not get GPUs property from switcheroo-control: %s", error->message);
142       g_clear_error (&error);
143       return;
144     }
145 
146   global = user_data;
147   g_dbus_proxy_set_cached_property (global->switcheroo_control, "GPUs", gpus);
148   g_object_notify (G_OBJECT (global), "switcheroo-control");
149 }
150 
151 static void
switcheroo_control_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)152 switcheroo_control_ready_cb (GObject      *source_object,
153                              GAsyncResult *res,
154                              gpointer      user_data)
155 {
156   ShellGlobal *global;
157   GError *error = NULL;
158   ShellNetHadessSwitcherooControl *control;
159   g_auto(GStrv) cached_props = NULL;
160 
161   control = shell_net_hadess_switcheroo_control_proxy_new_for_bus_finish (res, &error);
162   if (!control)
163     {
164       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
165         g_debug ("Could not get switcheroo-control GDBusProxy: %s", error->message);
166       g_clear_error (&error);
167       return;
168     }
169 
170   global = user_data;
171   global->switcheroo_control = G_DBUS_PROXY (control);
172   g_debug ("Got switcheroo-control proxy successfully");
173 
174   cached_props = g_dbus_proxy_get_cached_property_names (global->switcheroo_control);
175   if (cached_props != NULL && g_strv_contains ((const gchar * const *) cached_props, "GPUs"))
176     {
177       g_object_notify (G_OBJECT (global), "switcheroo-control");
178       return;
179     }
180   /* Delay property notification until we have all the properties gathered */
181 
182   g_dbus_connection_call (g_dbus_proxy_get_connection (global->switcheroo_control),
183                           g_dbus_proxy_get_name (global->switcheroo_control),
184                           g_dbus_proxy_get_object_path (global->switcheroo_control),
185                           "org.freedesktop.DBus.Properties",
186                           "Get",
187                           g_variant_new ("(ss)",
188                                          g_dbus_proxy_get_interface_name (global->switcheroo_control),
189                                          "GPUs"),
190                           NULL,
191                           G_DBUS_CALL_FLAGS_NONE,
192                           -1,
193                           global->switcheroo_cancellable,
194                           got_switcheroo_control_gpus_property_cb,
195                           user_data);
196 }
197 
198 static void
shell_global_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)199 shell_global_set_property(GObject         *object,
200                           guint            prop_id,
201                           const GValue    *value,
202                           GParamSpec      *pspec)
203 {
204   ShellGlobal *global = SHELL_GLOBAL (object);
205 
206   switch (prop_id)
207     {
208     case PROP_SESSION_MODE:
209       g_clear_pointer (&global->session_mode, g_free);
210       global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
211       break;
212     case PROP_FRAME_TIMESTAMPS:
213       global->frame_timestamps = g_value_get_boolean (value);
214       break;
215     case PROP_FRAME_FINISH_TIMESTAMP:
216       global->frame_finish_timestamp = g_value_get_boolean (value);
217       break;
218     default:
219       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220       break;
221     }
222 }
223 
224 static void
shell_global_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)225 shell_global_get_property(GObject         *object,
226                           guint            prop_id,
227                           GValue          *value,
228                           GParamSpec      *pspec)
229 {
230   ShellGlobal *global = SHELL_GLOBAL (object);
231 
232   switch (prop_id)
233     {
234     case PROP_SESSION_MODE:
235       g_value_set_string (value, shell_global_get_session_mode (global));
236       break;
237     case PROP_BACKEND:
238       g_value_set_object (value, global->backend);
239       break;
240     case PROP_CONTEXT:
241       g_value_set_object (value, global->meta_context);
242       break;
243     case PROP_DISPLAY:
244       g_value_set_object (value, global->meta_display);
245       break;
246     case PROP_WORKSPACE_MANAGER:
247       g_value_set_object (value, global->workspace_manager);
248       break;
249     case PROP_SCREEN_WIDTH:
250       {
251         int width, height;
252 
253         meta_display_get_size (global->meta_display, &width, &height);
254         g_value_set_int (value, width);
255       }
256       break;
257     case PROP_SCREEN_HEIGHT:
258       {
259         int width, height;
260 
261         meta_display_get_size (global->meta_display, &width, &height);
262         g_value_set_int (value, height);
263       }
264       break;
265     case PROP_STAGE:
266       g_value_set_object (value, global->stage);
267       break;
268     case PROP_WINDOW_GROUP:
269       g_value_set_object (value, meta_get_window_group_for_display (global->meta_display));
270       break;
271     case PROP_TOP_WINDOW_GROUP:
272       g_value_set_object (value, meta_get_top_window_group_for_display (global->meta_display));
273       break;
274     case PROP_WINDOW_MANAGER:
275       g_value_set_object (value, global->wm);
276       break;
277     case PROP_SETTINGS:
278       g_value_set_object (value, global->settings);
279       break;
280     case PROP_DATADIR:
281       g_value_set_string (value, global->datadir);
282       break;
283     case PROP_IMAGEDIR:
284       g_value_set_string (value, global->imagedir);
285       break;
286     case PROP_USERDATADIR:
287       g_value_set_string (value, global->userdatadir);
288       break;
289     case PROP_FOCUS_MANAGER:
290       g_value_set_object (value, global->focus_manager);
291       break;
292     case PROP_FRAME_TIMESTAMPS:
293       g_value_set_boolean (value, global->frame_timestamps);
294       break;
295     case PROP_FRAME_FINISH_TIMESTAMP:
296       g_value_set_boolean (value, global->frame_finish_timestamp);
297       break;
298     case PROP_SWITCHEROO_CONTROL:
299       g_value_set_object (value, global->switcheroo_control);
300       break;
301     default:
302       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
303       break;
304     }
305 }
306 
307 static void
switcheroo_appeared_cb(GDBusConnection * connection,const char * name,const char * name_owner,gpointer user_data)308 switcheroo_appeared_cb (GDBusConnection *connection,
309                         const char     *name,
310                         const char     *name_owner,
311                         gpointer        user_data)
312 {
313   ShellGlobal *global = user_data;
314 
315   g_debug ("switcheroo-control appeared");
316   shell_net_hadess_switcheroo_control_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
317                                                          G_DBUS_PROXY_FLAGS_NONE,
318                                                          "net.hadess.SwitcherooControl",
319                                                          "/net/hadess/SwitcherooControl",
320                                                          global->switcheroo_cancellable,
321                                                          switcheroo_control_ready_cb,
322                                                          global);
323 }
324 
325 static void
switcheroo_vanished_cb(GDBusConnection * connection,const char * name,gpointer user_data)326 switcheroo_vanished_cb (GDBusConnection *connection,
327                         const char      *name,
328                         gpointer         user_data)
329 {
330   ShellGlobal *global = user_data;
331 
332   g_debug ("switcheroo-control vanished");
333   g_clear_object (&global->switcheroo_control);
334   g_object_notify (G_OBJECT (global), "switcheroo-control");
335 }
336 
337 static void
shell_global_init(ShellGlobal * global)338 shell_global_init (ShellGlobal *global)
339 {
340   const char *datadir = g_getenv ("GNOME_SHELL_DATADIR");
341   const char *shell_js = g_getenv("GNOME_SHELL_JS");
342   char *imagedir, **search_path;
343   char *path;
344   const char *byteorder_string;
345 
346   if (!datadir)
347     datadir = GNOME_SHELL_DATADIR;
348   global->datadir = datadir;
349 
350   /* We make sure imagedir ends with a '/', since the JS won't have
351    * access to g_build_filename() and so will end up just
352    * concatenating global.imagedir to a filename.
353    */
354   imagedir = g_build_filename (datadir, "images/", NULL);
355   if (g_file_test (imagedir, G_FILE_TEST_IS_DIR))
356     global->imagedir = imagedir;
357   else
358     {
359       g_free (imagedir);
360       global->imagedir = g_strdup_printf ("%s/", datadir);
361     }
362 
363   /* Ensure config dir exists for later use */
364   global->userdatadir = g_build_filename (g_get_user_data_dir (), "gnome-shell", NULL);
365   g_mkdir_with_parents (global->userdatadir, 0700);
366   global->userdatadir_path = g_file_new_for_path (global->userdatadir);
367 
368 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
369   byteorder_string = "LE";
370 #else
371   byteorder_string = "BE";
372 #endif
373 
374   /* And the runtime state */
375   path = g_strdup_printf ("%s/gnome-shell/runtime-state-%s.%s",
376                           g_get_user_runtime_dir (),
377                           byteorder_string,
378                           XDisplayName (NULL));
379   (void) g_mkdir_with_parents (path, 0700);
380   global->runtime_state_path = g_file_new_for_path (path);
381   g_free (path);
382 
383   global->settings = g_settings_new ("org.gnome.shell");
384 
385   if (shell_js)
386     {
387       int i, j;
388       search_path = g_strsplit (shell_js, ":", -1);
389 
390       /* The naive g_strsplit above will split 'resource:///foo/bar' into 'resource',
391        * '///foo/bar'. Combine these back together by looking for a literal 'resource'
392        * in the array. */
393       for (i = 0, j = 0; search_path[i];)
394         {
395           char *out;
396 
397           if (strcmp (search_path[i], "resource") == 0 && search_path[i + 1] != NULL)
398             {
399               out = g_strconcat (search_path[i], ":", search_path[i + 1], NULL);
400               g_free (search_path[i]);
401               g_free (search_path[i + 1]);
402               i += 2;
403             }
404           else
405             {
406               out = search_path[i];
407               i += 1;
408             }
409 
410           search_path[j++] = out;
411         }
412 
413       search_path[j] = NULL; /* NULL-terminate the now possibly shorter array */
414     }
415   else
416     {
417       search_path = g_malloc0 (2 * sizeof (char *));
418       search_path[0] = g_strdup ("resource:///org/gnome/shell");
419     }
420 
421   global->js_context = g_object_new (GJS_TYPE_CONTEXT,
422                                      "search-path", search_path,
423                                      NULL);
424 
425   g_strfreev (search_path);
426 
427   global->save_ops = g_hash_table_new_full (g_file_hash,
428                                             (GEqualFunc) g_file_equal,
429                                             g_object_unref, g_object_unref);
430 
431   global->switcheroo_cancellable = g_cancellable_new ();
432   g_bus_watch_name (G_BUS_TYPE_SYSTEM,
433                     "net.hadess.SwitcherooControl",
434                     G_BUS_NAME_WATCHER_FLAGS_NONE,
435                     switcheroo_appeared_cb,
436                     switcheroo_vanished_cb,
437                     global,
438                     NULL);
439 }
440 
441 static void
shell_global_finalize(GObject * object)442 shell_global_finalize (GObject *object)
443 {
444   ShellGlobal *global = SHELL_GLOBAL (object);
445 
446   g_clear_object (&global->js_context);
447   g_object_unref (global->settings);
448 
449   the_object = NULL;
450 
451   g_cancellable_cancel (global->switcheroo_cancellable);
452   g_clear_object (&global->switcheroo_cancellable);
453 
454   g_clear_object (&global->userdatadir_path);
455   g_clear_object (&global->runtime_state_path);
456 
457   g_free (global->session_mode);
458   g_free (global->imagedir);
459   g_free (global->userdatadir);
460 
461   g_hash_table_unref (global->save_ops);
462 
463   G_OBJECT_CLASS(shell_global_parent_class)->finalize (object);
464 }
465 
466 static void
shell_global_class_init(ShellGlobalClass * klass)467 shell_global_class_init (ShellGlobalClass *klass)
468 {
469   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
470 
471   gobject_class->get_property = shell_global_get_property;
472   gobject_class->set_property = shell_global_set_property;
473   gobject_class->finalize = shell_global_finalize;
474 
475   shell_global_signals[NOTIFY_ERROR] =
476       g_signal_new ("notify-error",
477                     G_TYPE_FROM_CLASS (klass),
478                     G_SIGNAL_RUN_LAST,
479                     0,
480                     NULL, NULL, NULL,
481                     G_TYPE_NONE, 2,
482                     G_TYPE_STRING,
483                     G_TYPE_STRING);
484   shell_global_signals[LOCATE_POINTER] =
485       g_signal_new ("locate-pointer",
486                     G_TYPE_FROM_CLASS (klass),
487                     G_SIGNAL_RUN_LAST,
488                     0,
489                     NULL, NULL, NULL,
490                     G_TYPE_NONE, 0);
491 
492   g_object_class_install_property (gobject_class,
493                                    PROP_SESSION_MODE,
494                                    g_param_spec_string ("session-mode",
495                                                         "Session Mode",
496                                                         "The session mode to use",
497                                                         "user",
498                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
499 
500   g_object_class_install_property (gobject_class,
501                                    PROP_SCREEN_WIDTH,
502                                    g_param_spec_int ("screen-width",
503                                                      "Screen Width",
504                                                      "Screen width, in pixels",
505                                                      0, G_MAXINT, 1,
506                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
507 
508   g_object_class_install_property (gobject_class,
509                                    PROP_SCREEN_HEIGHT,
510                                    g_param_spec_int ("screen-height",
511                                                      "Screen Height",
512                                                      "Screen height, in pixels",
513                                                      0, G_MAXINT, 1,
514                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
515   g_object_class_install_property (gobject_class,
516                                    PROP_BACKEND,
517                                    g_param_spec_object ("backend",
518                                                         "Backend",
519                                                         "MetaBackend object",
520                                                         META_TYPE_BACKEND,
521                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
522   g_object_class_install_property (gobject_class,
523                                    PROP_CONTEXT,
524                                    g_param_spec_object ("context",
525                                                         "Context",
526                                                         "MetaContext object",
527                                                         META_TYPE_CONTEXT,
528                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
529   g_object_class_install_property (gobject_class,
530                                    PROP_DISPLAY,
531                                    g_param_spec_object ("display",
532                                                         "Display",
533                                                         "Metacity display object for the shell",
534                                                         META_TYPE_DISPLAY,
535                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
536 
537   g_object_class_install_property (gobject_class,
538                                    PROP_WORKSPACE_MANAGER,
539                                    g_param_spec_object ("workspace-manager",
540                                                         "Workspace manager",
541                                                         "Workspace manager",
542                                                         META_TYPE_WORKSPACE_MANAGER,
543                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
544 
545   g_object_class_install_property (gobject_class,
546                                    PROP_STAGE,
547                                    g_param_spec_object ("stage",
548                                                         "Stage",
549                                                         "Stage holding the desktop scene graph",
550                                                         CLUTTER_TYPE_ACTOR,
551                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
552   g_object_class_install_property (gobject_class,
553                                    PROP_WINDOW_GROUP,
554                                    g_param_spec_object ("window-group",
555                                                         "Window Group",
556                                                         "Actor holding window actors",
557                                                         CLUTTER_TYPE_ACTOR,
558                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
559 
560   g_object_class_install_property (gobject_class,
561                                      PROP_TOP_WINDOW_GROUP,
562                                      g_param_spec_object ("top-window-group",
563                                                           "Top Window Group",
564                                                           "Actor holding override-redirect windows",
565                                                           CLUTTER_TYPE_ACTOR,
566                                                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
567 
568   g_object_class_install_property (gobject_class,
569                                    PROP_WINDOW_MANAGER,
570                                    g_param_spec_object ("window-manager",
571                                                         "Window Manager",
572                                                         "Window management interface",
573                                                         SHELL_TYPE_WM,
574                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
575   g_object_class_install_property (gobject_class,
576                                    PROP_SETTINGS,
577                                    g_param_spec_object ("settings",
578                                                         "Settings",
579                                                         "GSettings instance for gnome-shell configuration",
580                                                         G_TYPE_SETTINGS,
581                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
582   g_object_class_install_property (gobject_class,
583                                    PROP_DATADIR,
584                                    g_param_spec_string ("datadir",
585                                                         "Data directory",
586                                                         "Directory containing gnome-shell data files",
587                                                         NULL,
588                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
589   g_object_class_install_property (gobject_class,
590                                    PROP_IMAGEDIR,
591                                    g_param_spec_string ("imagedir",
592                                                         "Image directory",
593                                                         "Directory containing gnome-shell image files",
594                                                         NULL,
595                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
596   g_object_class_install_property (gobject_class,
597                                    PROP_USERDATADIR,
598                                    g_param_spec_string ("userdatadir",
599                                                         "User data directory",
600                                                         "Directory containing gnome-shell user data",
601                                                         NULL,
602                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
603   g_object_class_install_property (gobject_class,
604                                    PROP_FOCUS_MANAGER,
605                                    g_param_spec_object ("focus-manager",
606                                                         "Focus manager",
607                                                         "The shell's StFocusManager",
608                                                         ST_TYPE_FOCUS_MANAGER,
609                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
610   g_object_class_install_property (gobject_class,
611                                    PROP_FRAME_TIMESTAMPS,
612                                    g_param_spec_boolean ("frame-timestamps",
613                                                          "Frame Timestamps",
614                                                          "Whether to log frame timestamps in the performance log",
615                                                          FALSE,
616                                                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
617   g_object_class_install_property (gobject_class,
618                                    PROP_FRAME_FINISH_TIMESTAMP,
619                                    g_param_spec_boolean ("frame-finish-timestamp",
620                                                          "Frame Finish Timestamps",
621                                                          "Whether at the end of a frame to call glFinish and log paintCompletedTimestamp",
622                                                          FALSE,
623                                                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
624   g_object_class_install_property (gobject_class,
625                                    PROP_SWITCHEROO_CONTROL,
626                                    g_param_spec_object ("switcheroo-control",
627                                                         "switcheroo-control",
628                                                         "D-Bus Proxy for switcheroo-control daemon",
629                                                         G_TYPE_DBUS_PROXY,
630                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
631 }
632 
633 /*
634  * _shell_global_init: (skip)
635  * @first_property_name: the name of the first property
636  * @...: the value of the first property, followed optionally by more
637  *  name/value pairs, followed by %NULL
638  *
639  * Initializes the shell global singleton with the construction-time
640  * properties.
641  *
642  * There are currently no such properties, so @first_property_name should
643  * always be %NULL.
644  *
645  * This call must be called before shell_global_get() and shouldn't be called
646  * more than once.
647  */
648 void
_shell_global_init(const char * first_property_name,...)649 _shell_global_init (const char *first_property_name,
650                     ...)
651 {
652   va_list argument_list;
653 
654   g_return_if_fail (the_object == NULL);
655 
656   va_start (argument_list, first_property_name);
657   the_object = SHELL_GLOBAL (g_object_new_valist (SHELL_TYPE_GLOBAL,
658                                                   first_property_name,
659                                                   argument_list));
660   va_end (argument_list);
661 
662 }
663 
664 /**
665  * shell_global_get:
666  *
667  * Gets the singleton global object that represents the desktop.
668  *
669  * Return value: (transfer none): the singleton global object
670  */
671 ShellGlobal *
shell_global_get(void)672 shell_global_get (void)
673 {
674   return the_object;
675 }
676 
677 /**
678  * _shell_global_destroy_gjs_context: (skip)
679  * @self: global object
680  *
681  * Destroys the GjsContext held by ShellGlobal, in order to break reference
682  * counting cycles. (The GjsContext holds a reference to ShellGlobal because
683  * it's available as window.global inside JS.)
684  */
685 void
_shell_global_destroy_gjs_context(ShellGlobal * self)686 _shell_global_destroy_gjs_context (ShellGlobal *self)
687 {
688   g_clear_object (&self->js_context);
689 }
690 
691 static guint32
get_current_time_maybe_roundtrip(ShellGlobal * global)692 get_current_time_maybe_roundtrip (ShellGlobal *global)
693 {
694   guint32 time;
695 
696   time = shell_global_get_current_time (global);
697   if (time != CurrentTime)
698     return time;
699 
700   return meta_display_get_current_time_roundtrip (global->meta_display);
701 }
702 
703 static void
focus_window_changed(MetaDisplay * display,GParamSpec * param,gpointer user_data)704 focus_window_changed (MetaDisplay *display,
705                       GParamSpec  *param,
706                       gpointer     user_data)
707 {
708   ShellGlobal *global = user_data;
709 
710   if (global->has_modal)
711     return;
712 
713   /* If the stage window became unfocused, drop the key focus
714    * on Clutter's side. */
715   if (!meta_stage_is_focused (global->meta_display))
716     clutter_stage_set_key_focus (global->stage, NULL);
717 }
718 
719 static ClutterActor *
get_key_focused_actor(ShellGlobal * global)720 get_key_focused_actor (ShellGlobal *global)
721 {
722   ClutterActor *actor;
723 
724   actor = clutter_stage_get_key_focus (global->stage);
725 
726   /* If there's no explicit key focus, clutter_stage_get_key_focus()
727    * returns the stage. This is a terrible API. */
728   if (actor == CLUTTER_ACTOR (global->stage))
729     actor = NULL;
730 
731   return actor;
732 }
733 
734 static void
sync_stage_window_focus(ShellGlobal * global)735 sync_stage_window_focus (ShellGlobal *global)
736 {
737   ClutterActor *actor;
738 
739   if (global->has_modal)
740     return;
741 
742   actor = get_key_focused_actor (global);
743 
744   /* An actor got key focus and the stage needs to be focused. */
745   if (actor != NULL && !meta_stage_is_focused (global->meta_display))
746     meta_focus_stage_window (global->meta_display,
747                              get_current_time_maybe_roundtrip (global));
748 
749   /* An actor dropped key focus. Focus the default window. */
750   else if (actor == NULL && meta_stage_is_focused (global->meta_display))
751     meta_display_focus_default_window (global->meta_display,
752                                        get_current_time_maybe_roundtrip (global));
753 }
754 
755 static void
focus_actor_changed(ClutterStage * stage,GParamSpec * param,gpointer user_data)756 focus_actor_changed (ClutterStage *stage,
757                      GParamSpec   *param,
758                      gpointer      user_data)
759 {
760   ShellGlobal *global = user_data;
761   sync_stage_window_focus (global);
762 }
763 
764 static void
sync_input_region(ShellGlobal * global)765 sync_input_region (ShellGlobal *global)
766 {
767   MetaDisplay *display = global->meta_display;
768   MetaX11Display *x11_display = meta_display_get_x11_display (display);
769 
770   if (global->has_modal)
771     meta_x11_display_set_stage_input_region (x11_display, None);
772   else
773     meta_x11_display_set_stage_input_region (x11_display, global->input_region);
774 }
775 
776 /**
777  * shell_global_set_stage_input_region:
778  * @global: the #ShellGlobal
779  * @rectangles: (element-type Meta.Rectangle): a list of #MetaRectangle
780  * describing the input region.
781  *
782  * Sets the area of the stage that is responsive to mouse clicks when
783  * we don't have a modal or grab.
784  */
785 void
shell_global_set_stage_input_region(ShellGlobal * global,GSList * rectangles)786 shell_global_set_stage_input_region (ShellGlobal *global,
787                                      GSList      *rectangles)
788 {
789   MetaRectangle *rect;
790   XRectangle *rects;
791   int nrects, i;
792   GSList *r;
793 
794   g_return_if_fail (SHELL_IS_GLOBAL (global));
795 
796   if (meta_is_wayland_compositor ())
797     return;
798 
799   nrects = g_slist_length (rectangles);
800   rects = g_new (XRectangle, nrects);
801   for (r = rectangles, i = 0; r; r = r->next, i++)
802     {
803       rect = (MetaRectangle *)r->data;
804       rects[i].x = rect->x;
805       rects[i].y = rect->y;
806       rects[i].width = rect->width;
807       rects[i].height = rect->height;
808     }
809 
810   if (global->input_region)
811     XFixesDestroyRegion (global->xdisplay, global->input_region);
812 
813   global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
814   g_free (rects);
815 
816   sync_input_region (global);
817 }
818 
819 /**
820  * shell_global_get_stage:
821  *
822  * Return value: (transfer none): The default #ClutterStage
823  */
824 ClutterStage *
shell_global_get_stage(ShellGlobal * global)825 shell_global_get_stage (ShellGlobal  *global)
826 {
827   return global->stage;
828 }
829 
830 /**
831  * shell_global_get_display:
832  *
833  * Return value: (transfer none): The default #MetaDisplay
834  */
835 MetaDisplay *
shell_global_get_display(ShellGlobal * global)836 shell_global_get_display (ShellGlobal  *global)
837 {
838   return global->meta_display;
839 }
840 
841 /**
842  * shell_global_get_window_actors:
843  *
844  * Gets the list of #MetaWindowActor for the plugin's screen
845  *
846  * Return value: (element-type Meta.WindowActor) (transfer container): the list of windows
847  */
848 GList *
shell_global_get_window_actors(ShellGlobal * global)849 shell_global_get_window_actors (ShellGlobal *global)
850 {
851   GList *filtered = NULL;
852   GList *l;
853 
854   g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
855 
856   for (l = meta_get_window_actors (global->meta_display); l; l = l->next)
857     if (!meta_window_actor_is_destroyed (l->data))
858       filtered = g_list_prepend (filtered, l->data);
859 
860   return g_list_reverse (filtered);
861 }
862 
863 static void
global_stage_notify_width(GObject * gobject,GParamSpec * pspec,gpointer data)864 global_stage_notify_width (GObject    *gobject,
865                            GParamSpec *pspec,
866                            gpointer    data)
867 {
868   ShellGlobal *global = SHELL_GLOBAL (data);
869 
870   g_object_notify (G_OBJECT (global), "screen-width");
871 }
872 
873 static void
global_stage_notify_height(GObject * gobject,GParamSpec * pspec,gpointer data)874 global_stage_notify_height (GObject    *gobject,
875                             GParamSpec *pspec,
876                             gpointer    data)
877 {
878   ShellGlobal *global = SHELL_GLOBAL (data);
879 
880   g_object_notify (G_OBJECT (global), "screen-height");
881 }
882 
883 static gboolean
global_stage_before_paint(gpointer data)884 global_stage_before_paint (gpointer data)
885 {
886   ShellGlobal *global = SHELL_GLOBAL (data);
887 
888   if (global->frame_timestamps)
889     shell_perf_log_event (shell_perf_log_get_default (),
890                           "clutter.stagePaintStart");
891 
892   return TRUE;
893 }
894 
895 static gboolean
load_gl_symbol(const char * name,void ** func)896 load_gl_symbol (const char  *name,
897                 void       **func)
898 {
899   *func = cogl_get_proc_address (name);
900   if (!*func)
901     {
902       g_warning ("failed to resolve required GL symbol \"%s\"\n", name);
903       return FALSE;
904     }
905   return TRUE;
906 }
907 
908 static void
global_stage_after_paint(ClutterStage * stage,ClutterStageView * stage_view,ShellGlobal * global)909 global_stage_after_paint (ClutterStage     *stage,
910                           ClutterStageView *stage_view,
911                           ShellGlobal      *global)
912 {
913   /* At this point, we've finished all layout and painting, but haven't
914    * actually flushed or swapped */
915 
916   if (global->frame_timestamps && global->frame_finish_timestamp)
917     {
918       /* It's interesting to find out when the paint actually finishes
919        * on the GPU. We could wait for this asynchronously with
920        * ARB_timer_query (see https://bugzilla.gnome.org/show_bug.cgi?id=732350
921        * for an implementation of this), but what we actually would
922        * find out then is the latency for drawing a frame, not how much
923        * GPU work was needed, since frames can overlap. Calling glFinish()
924        * is a fairly reliable way to separate out adjacent frames
925        * and measure the amount of GPU work. This is turned on with a
926        * separate property from ::frame-timestamps, since it should not
927        * be turned on if we're trying to actual measure latency or frame
928        * rate.
929        */
930       static void (*finish) (void);
931 
932       if (!finish)
933         load_gl_symbol ("glFinish", (void **)&finish);
934 
935       cogl_flush ();
936       finish ();
937 
938       shell_perf_log_event (shell_perf_log_get_default (),
939                             "clutter.paintCompletedTimestamp");
940     }
941 }
942 
943 static gboolean
global_stage_after_swap(gpointer data)944 global_stage_after_swap (gpointer data)
945 {
946   /* Everything is done, we're ready for a new frame */
947 
948   ShellGlobal *global = SHELL_GLOBAL (data);
949 
950   if (global->frame_timestamps)
951     shell_perf_log_event (shell_perf_log_get_default (),
952                           "clutter.stagePaintDone");
953 
954   return TRUE;
955 }
956 
957 static void
update_scaling_factor(ShellGlobal * global,MetaSettings * settings)958 update_scaling_factor (ShellGlobal  *global,
959                        MetaSettings *settings)
960 {
961   ClutterStage *stage = CLUTTER_STAGE (global->stage);
962   StThemeContext *context = st_theme_context_get_for_stage (stage);
963   int scaling_factor;
964 
965   scaling_factor = meta_settings_get_ui_scaling_factor (settings);
966   g_object_set (context, "scale-factor", scaling_factor, NULL);
967 }
968 
969 static void
ui_scaling_factor_changed(MetaSettings * settings,ShellGlobal * global)970 ui_scaling_factor_changed (MetaSettings *settings,
971                            ShellGlobal  *global)
972 {
973   update_scaling_factor (global, settings);
974 }
975 
976 static void
entry_cursor_func(StEntry * entry,gboolean use_ibeam,gpointer user_data)977 entry_cursor_func (StEntry  *entry,
978                    gboolean  use_ibeam,
979                    gpointer  user_data)
980 {
981   ShellGlobal *global = user_data;
982 
983   meta_display_set_cursor (global->meta_display,
984                            use_ibeam ? META_CURSOR_IBEAM : META_CURSOR_DEFAULT);
985 }
986 
987 static void
on_x11_display_closed(MetaDisplay * display,ShellGlobal * global)988 on_x11_display_closed (MetaDisplay *display,
989                        ShellGlobal *global)
990 {
991   g_signal_handlers_disconnect_by_data (global->stage, global);
992 }
993 
994 void
_shell_global_set_plugin(ShellGlobal * global,MetaPlugin * plugin)995 _shell_global_set_plugin (ShellGlobal *global,
996                           MetaPlugin  *plugin)
997 {
998   MetaDisplay *display;
999   MetaBackend *backend;
1000   MetaSettings *settings;
1001 
1002   g_return_if_fail (SHELL_IS_GLOBAL (global));
1003   g_return_if_fail (global->plugin == NULL);
1004 
1005   global->backend = meta_get_backend ();
1006   global->plugin = plugin;
1007   global->wm = shell_wm_new (plugin);
1008 
1009   display = meta_plugin_get_display (plugin);
1010   global->meta_display = display;
1011   global->meta_context = meta_display_get_context (display);
1012   global->workspace_manager = meta_display_get_workspace_manager (display);
1013 
1014   global->stage = CLUTTER_STAGE (meta_get_stage_for_display (display));
1015 
1016   if (!meta_is_wayland_compositor ())
1017     {
1018       MetaX11Display *x11_display = meta_display_get_x11_display (display);
1019       global->xdisplay = meta_x11_display_get_xdisplay (x11_display);
1020     }
1021 
1022   st_entry_set_cursor_func (entry_cursor_func, global);
1023   st_clipboard_set_selection (meta_display_get_selection (display));
1024 
1025   g_signal_connect (global->stage, "notify::width",
1026                     G_CALLBACK (global_stage_notify_width), global);
1027   g_signal_connect (global->stage, "notify::height",
1028                     G_CALLBACK (global_stage_notify_height), global);
1029 
1030   clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
1031                                          global_stage_before_paint,
1032                                          global, NULL);
1033 
1034   g_signal_connect (global->stage, "after-paint",
1035                     G_CALLBACK (global_stage_after_paint), global);
1036 
1037   clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
1038                                          global_stage_after_swap,
1039                                          global, NULL);
1040 
1041   shell_perf_log_define_event (shell_perf_log_get_default(),
1042                                "clutter.stagePaintStart",
1043                                "Start of stage page repaint",
1044                                "");
1045   shell_perf_log_define_event (shell_perf_log_get_default(),
1046                                "clutter.paintCompletedTimestamp",
1047                                "Paint completion on GPU",
1048                                "");
1049   shell_perf_log_define_event (shell_perf_log_get_default(),
1050                                "clutter.stagePaintDone",
1051                                "End of frame, possibly including swap time",
1052                                "");
1053 
1054   g_signal_connect (global->stage, "notify::key-focus",
1055                     G_CALLBACK (focus_actor_changed), global);
1056   g_signal_connect (global->meta_display, "notify::focus-window",
1057                     G_CALLBACK (focus_window_changed), global);
1058 
1059   if (global->xdisplay)
1060     g_signal_connect_object (global->meta_display, "x11-display-closing",
1061                              G_CALLBACK (on_x11_display_closed), global, 0);
1062 
1063   backend = meta_get_backend ();
1064   settings = meta_backend_get_settings (backend);
1065   g_signal_connect (settings, "ui-scaling-factor-changed",
1066                     G_CALLBACK (ui_scaling_factor_changed), global);
1067 
1068   global->focus_manager = st_focus_manager_get_for_stage (global->stage);
1069 
1070   update_scaling_factor (global, settings);
1071 }
1072 
1073 GjsContext *
_shell_global_get_gjs_context(ShellGlobal * global)1074 _shell_global_get_gjs_context (ShellGlobal *global)
1075 {
1076   return global->js_context;
1077 }
1078 
1079 /**
1080  * shell_global_begin_modal:
1081  * @global: a #ShellGlobal
1082  *
1083  * Grabs the keyboard and mouse to the stage window. The stage will
1084  * receive all keyboard and mouse events until shell_global_end_modal()
1085  * is called. This is used to implement "modes" for the shell, such as the
1086  * overview mode or the "looking glass" debug overlay, that block
1087  * application and normal key shortcuts.
1088  *
1089  * Returns: %TRUE if we successfully entered the mode. %FALSE if we couldn't
1090  *  enter the mode. Failure may occur because an application has the pointer
1091  *  or keyboard grabbed, because Mutter is in a mode itself like moving a
1092  *  window or alt-Tab window selection, or because shell_global_begin_modal()
1093  *  was previously called.
1094  */
1095 gboolean
shell_global_begin_modal(ShellGlobal * global,guint32 timestamp,MetaModalOptions options)1096 shell_global_begin_modal (ShellGlobal       *global,
1097                           guint32           timestamp,
1098                           MetaModalOptions  options)
1099 {
1100   if (!meta_display_get_compositor (global->meta_display))
1101     return FALSE;
1102 
1103   /* Make it an error to call begin_modal while we already
1104    * have a modal active. */
1105   if (global->has_modal)
1106     return FALSE;
1107 
1108   global->has_modal = meta_plugin_begin_modal (global->plugin, options, timestamp);
1109   if (!meta_is_wayland_compositor ())
1110     sync_input_region (global);
1111   return global->has_modal;
1112 }
1113 
1114 /**
1115  * shell_global_end_modal:
1116  * @global: a #ShellGlobal
1117  *
1118  * Undoes the effect of shell_global_begin_modal().
1119  */
1120 void
shell_global_end_modal(ShellGlobal * global,guint32 timestamp)1121 shell_global_end_modal (ShellGlobal *global,
1122                         guint32      timestamp)
1123 {
1124   if (!meta_display_get_compositor (global->meta_display))
1125     return;
1126 
1127   if (!global->has_modal)
1128     return;
1129 
1130   meta_plugin_end_modal (global->plugin, timestamp);
1131   global->has_modal = FALSE;
1132 
1133   /* If the stage window is unfocused, ensure that there's no
1134    * actor focused on Clutter's side. */
1135   if (!meta_stage_is_focused (global->meta_display))
1136     clutter_stage_set_key_focus (global->stage, NULL);
1137 
1138   /* An actor dropped key focus. Focus the default window. */
1139   else if (get_key_focused_actor (global) && meta_stage_is_focused (global->meta_display))
1140     meta_display_focus_default_window (global->meta_display,
1141                                        get_current_time_maybe_roundtrip (global));
1142 
1143   if (!meta_is_wayland_compositor ())
1144     sync_input_region (global);
1145 }
1146 
1147 /* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
1148  *
1149  * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering
1150  *
1151  * http://bugzilla.gnome.org/show_bug.cgi?id=469231
1152  * http://bugzilla.gnome.org/show_bug.cgi?id=357585
1153  */
1154 
1155 static int
set_cloexec(void * data,gint fd)1156 set_cloexec (void *data, gint fd)
1157 {
1158   if (fd >= GPOINTER_TO_INT (data))
1159     fcntl (fd, F_SETFD, FD_CLOEXEC);
1160 
1161   return 0;
1162 }
1163 
1164 #ifndef HAVE_FDWALK
1165 static int
fdwalk(int (* cb)(void * data,int fd),void * data)1166 fdwalk (int (*cb)(void *data, int fd), void *data)
1167 {
1168   gint open_max;
1169   gint fd;
1170   gint res = 0;
1171 
1172 #ifdef HAVE_SYS_RESOURCE_H
1173   struct rlimit rl;
1174 #endif
1175 
1176 #ifdef __linux__
1177   DIR *d;
1178 
1179   if ((d = opendir("/proc/self/fd"))) {
1180       struct dirent *de;
1181 
1182       while ((de = readdir(d))) {
1183           glong l;
1184           gchar *e = NULL;
1185 
1186           if (de->d_name[0] == '.')
1187               continue;
1188 
1189           errno = 0;
1190           l = strtol(de->d_name, &e, 10);
1191           if (errno != 0 || !e || *e)
1192               continue;
1193 
1194           fd = (gint) l;
1195 
1196           if ((glong) fd != l)
1197               continue;
1198 
1199           if (fd == dirfd(d))
1200               continue;
1201 
1202           if ((res = cb (data, fd)) != 0)
1203               break;
1204         }
1205 
1206       closedir(d);
1207       return res;
1208   }
1209 
1210   /* If /proc is not mounted or not accessible we fall back to the old
1211    * rlimit trick */
1212 
1213 #endif
1214 
1215 #ifdef HAVE_SYS_RESOURCE_H
1216   if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
1217       open_max = rl.rlim_max;
1218   else
1219 #endif
1220       open_max = sysconf (_SC_OPEN_MAX);
1221 
1222   for (fd = 0; fd < open_max; fd++)
1223       if ((res = cb (data, fd)) != 0)
1224           break;
1225 
1226   return res;
1227 }
1228 #endif
1229 
1230 static void
pre_exec_close_fds(void)1231 pre_exec_close_fds(void)
1232 {
1233   fdwalk (set_cloexec, GINT_TO_POINTER(3));
1234 }
1235 
1236 /**
1237  * shell_global_reexec_self:
1238  * @global: A #ShellGlobal
1239  *
1240  * Restart the current process.  Only intended for development purposes.
1241  */
1242 void
shell_global_reexec_self(ShellGlobal * global)1243 shell_global_reexec_self (ShellGlobal *global)
1244 {
1245   GPtrArray *arr;
1246   gsize len;
1247 
1248 #if defined __linux__ || defined __sun
1249   char *buf;
1250   char *buf_p;
1251   char *buf_end;
1252   g_autoptr (GError) error = NULL;
1253 
1254   if (!g_file_get_contents ("/proc/self/cmdline", &buf, &len, &error))
1255     {
1256       g_warning ("failed to get /proc/self/cmdline: %s", error->message);
1257       return;
1258     }
1259 
1260   buf_end = buf+len;
1261   arr = g_ptr_array_new ();
1262   /* The cmdline file is NUL-separated */
1263   for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
1264     g_ptr_array_add (arr, buf_p);
1265 
1266   g_ptr_array_add (arr, NULL);
1267 #elif defined __OpenBSD__
1268   gchar **args, **args_p;
1269   gint mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
1270 
1271   if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
1272     return;
1273 
1274   args = g_malloc0 (len);
1275 
1276   if (sysctl (mib, G_N_ELEMENTS (mib), args, &len, NULL, 0) == -1) {
1277     g_warning ("failed to get command line args: %d", errno);
1278     g_free (args);
1279     return;
1280   }
1281 
1282   arr = g_ptr_array_new ();
1283   for (args_p = args; *args_p != NULL; args_p++) {
1284     g_ptr_array_add (arr, *args_p);
1285   }
1286 
1287   g_ptr_array_add (arr, NULL);
1288 #elif defined __FreeBSD__ || defined __DragonFly__
1289   char *buf;
1290   char *buf_p;
1291   char *buf_end;
1292   gint mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid() };
1293 
1294   if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
1295     return;
1296 
1297   buf = g_malloc0 (len);
1298 
1299   if (sysctl (mib, G_N_ELEMENTS (mib), buf, &len, NULL, 0) == -1) {
1300     g_warning ("failed to get command line args: %d", errno);
1301     g_free (buf);
1302     return;
1303   }
1304 
1305   buf_end = buf+len;
1306   arr = g_ptr_array_new ();
1307   /* The value returned by sysctl is NUL-separated */
1308   for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
1309     g_ptr_array_add (arr, buf_p);
1310 
1311   g_ptr_array_add (arr, NULL);
1312 #else
1313   return;
1314 #endif
1315 
1316   /* Close all file descriptors other than stdin/stdout/stderr, otherwise
1317    * they will leak and stay open after the exec. In particular, this is
1318    * important for file descriptors that represent mapped graphics buffer
1319    * objects.
1320    */
1321   pre_exec_close_fds ();
1322 
1323   meta_display_close (shell_global_get_display (global),
1324                       shell_global_get_current_time (global));
1325 
1326   execvp (arr->pdata[0], (char**)arr->pdata);
1327   g_warning ("failed to reexec: %s", g_strerror (errno));
1328   g_ptr_array_free (arr, TRUE);
1329 #if defined __linux__ || defined __FreeBSD__ || defined __DragonFly__
1330   g_free (buf);
1331 #elif defined __OpenBSD__
1332   g_free (args);
1333 #endif
1334 }
1335 
1336 /**
1337  * shell_global_notify_error:
1338  * @global: a #ShellGlobal
1339  * @msg: Error message
1340  * @details: Error details
1341  *
1342  * Show a system error notification.  Use this function
1343  * when a user-initiated action results in a non-fatal problem
1344  * from causes that may not be under system control.  For
1345  * example, an application crash.
1346  */
1347 void
shell_global_notify_error(ShellGlobal * global,const char * msg,const char * details)1348 shell_global_notify_error (ShellGlobal  *global,
1349                            const char   *msg,
1350                            const char   *details)
1351 {
1352   g_signal_emit_by_name (global, "notify-error", msg, details);
1353 }
1354 
1355 /**
1356  * shell_global_get_pointer:
1357  * @global: the #ShellGlobal
1358  * @x: (out): the X coordinate of the pointer, in global coordinates
1359  * @y: (out): the Y coordinate of the pointer, in global coordinates
1360  * @mods: (out): the current set of modifier keys that are pressed down
1361  *
1362  * Gets the pointer coordinates and current modifier key state.
1363  */
1364 void
shell_global_get_pointer(ShellGlobal * global,int * x,int * y,ClutterModifierType * mods)1365 shell_global_get_pointer (ShellGlobal         *global,
1366                           int                 *x,
1367                           int                 *y,
1368                           ClutterModifierType *mods)
1369 {
1370   ClutterModifierType raw_mods;
1371   MetaCursorTracker *tracker;
1372   graphene_point_t point;
1373 
1374   tracker = meta_cursor_tracker_get_for_display (global->meta_display);
1375   meta_cursor_tracker_get_pointer (tracker, &point, &raw_mods);
1376 
1377   if (x)
1378     *x = point.x;
1379   if (y)
1380     *y = point.y;
1381 
1382   *mods = raw_mods & CLUTTER_MODIFIER_MASK;
1383 }
1384 
1385 /**
1386  * shell_global_get_switcheroo_control:
1387  * @global: A #ShellGlobal
1388  *
1389  * Get the global #GDBusProxy instance for the switcheroo-control
1390  * daemon.
1391  *
1392  * Return value: (transfer none): the #GDBusProxy for the daemon,
1393  *   or %NULL on error.
1394  */
1395 GDBusProxy *
shell_global_get_switcheroo_control(ShellGlobal * global)1396 shell_global_get_switcheroo_control (ShellGlobal  *global)
1397 {
1398   return global->switcheroo_control;
1399 }
1400 
1401 /**
1402  * shell_global_get_settings:
1403  * @global: A #ShellGlobal
1404  *
1405  * Get the global GSettings instance.
1406  *
1407  * Return value: (transfer none): The GSettings object
1408  */
1409 GSettings *
shell_global_get_settings(ShellGlobal * global)1410 shell_global_get_settings (ShellGlobal *global)
1411 {
1412   return global->settings;
1413 }
1414 
1415 /**
1416  * shell_global_get_current_time:
1417  * @global: A #ShellGlobal
1418  *
1419  * Returns: the current X server time from the current Clutter, Gdk, or X
1420  * event. If called from outside an event handler, this may return
1421  * %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
1422  * out-of-date timestamp.
1423  */
1424 guint32
shell_global_get_current_time(ShellGlobal * global)1425 shell_global_get_current_time (ShellGlobal *global)
1426 {
1427   guint32 time;
1428 
1429   /* meta_display_get_current_time() will return the correct time
1430      when handling an X or Gdk event, but will return CurrentTime
1431      from some Clutter event callbacks.
1432 
1433      clutter_get_current_event_time() will return the correct time
1434      from a Clutter event callback, but may return CLUTTER_CURRENT_TIME
1435      timestamp if called at other times.
1436 
1437      So we try meta_display_get_current_time() first, since we
1438      can recognize a "wrong" answer from that, and then fall back
1439      to clutter_get_current_event_time().
1440    */
1441 
1442   time = meta_display_get_current_time (global->meta_display);
1443   if (time != CLUTTER_CURRENT_TIME)
1444     return time;
1445 
1446   return clutter_get_current_event_time ();
1447 }
1448 
1449 static void
shell_global_app_launched_cb(GAppLaunchContext * context,GAppInfo * info,GVariant * platform_data,gpointer user_data)1450 shell_global_app_launched_cb (GAppLaunchContext *context,
1451                               GAppInfo          *info,
1452                               GVariant          *platform_data,
1453                               gpointer           user_data)
1454 {
1455   gint32 pid;
1456   const gchar *app_name;
1457 
1458   if (!g_variant_lookup (platform_data, "pid", "i", &pid))
1459     return;
1460 
1461   app_name = g_app_info_get_id (info);
1462   if (app_name == NULL)
1463     app_name = g_app_info_get_executable (info);
1464 
1465   /* Start async request; we don't care about the result */
1466   gnome_start_systemd_scope (app_name,
1467                              pid,
1468                              NULL,
1469                              NULL,
1470                              NULL, NULL, NULL);
1471 }
1472 
1473 /**
1474  * shell_global_create_app_launch_context:
1475  * @global: A #ShellGlobal
1476  * @timestamp: the timestamp for the launch (or 0 for current time)
1477  * @workspace: a workspace index, or -1 to indicate the current one
1478  *
1479  * Create a #GAppLaunchContext set up with the correct timestamp, and
1480  * targeted to activate on the current workspace.
1481  *
1482  * Return value: (transfer full): A new #GAppLaunchContext
1483  */
1484 GAppLaunchContext *
shell_global_create_app_launch_context(ShellGlobal * global,guint32 timestamp,int workspace)1485 shell_global_create_app_launch_context (ShellGlobal *global,
1486                                         guint32      timestamp,
1487                                         int          workspace)
1488 {
1489   MetaWorkspaceManager *workspace_manager = global->workspace_manager;
1490   MetaStartupNotification *sn;
1491   MetaLaunchContext *context;
1492   MetaWorkspace *ws = NULL;
1493 
1494   sn = meta_display_get_startup_notification (global->meta_display);
1495   context = meta_startup_notification_create_launcher (sn);
1496 
1497   if (timestamp == 0)
1498     timestamp = shell_global_get_current_time (global);
1499   meta_launch_context_set_timestamp (context, timestamp);
1500 
1501   if (workspace < 0)
1502     ws = meta_workspace_manager_get_active_workspace (workspace_manager);
1503   else
1504     ws = meta_workspace_manager_get_workspace_by_index (workspace_manager, workspace);
1505 
1506   meta_launch_context_set_workspace (context, ws);
1507 
1508   g_signal_connect (context,
1509                     "launched",
1510                     G_CALLBACK (shell_global_app_launched_cb),
1511                     NULL);
1512 
1513   return (GAppLaunchContext *) context;
1514 }
1515 
1516 typedef struct
1517 {
1518   ShellLeisureFunction func;
1519   gpointer user_data;
1520   GDestroyNotify notify;
1521 } LeisureClosure;
1522 
1523 static gboolean
run_leisure_functions(gpointer data)1524 run_leisure_functions (gpointer data)
1525 {
1526   ShellGlobal *global = data;
1527   GSList *closures;
1528   GSList *iter;
1529 
1530   global->leisure_function_id = 0;
1531 
1532   /* We started more work since we scheduled the idle */
1533   if (global->work_count > 0)
1534     return FALSE;
1535 
1536   /* No leisure closures, so we are done */
1537   if (global->leisure_closures == NULL)
1538     return FALSE;
1539 
1540   closures = global->leisure_closures;
1541   global->leisure_closures = NULL;
1542 
1543   for (iter = closures; iter; iter = iter->next)
1544     {
1545       LeisureClosure *closure = closures->data;
1546       closure->func (closure->user_data);
1547 
1548       if (closure->notify)
1549         closure->notify (closure->user_data);
1550 
1551       g_free (closure);
1552     }
1553 
1554   g_slist_free (closures);
1555 
1556   return FALSE;
1557 }
1558 
1559 static void
schedule_leisure_functions(ShellGlobal * global)1560 schedule_leisure_functions (ShellGlobal *global)
1561 {
1562   /* This is called when we think we are ready to run leisure functions
1563    * by our own accounting. We try to handle other types of business
1564    * (like ClutterAnimation) by adding a low priority idle function.
1565    *
1566    * This won't work properly if the mainloop goes idle waiting for
1567    * the vertical blanking interval or waiting for work being done
1568    * in another thread.
1569    */
1570   if (!global->leisure_function_id)
1571     {
1572       global->leisure_function_id = g_idle_add_full (G_PRIORITY_LOW,
1573                                                      run_leisure_functions,
1574                                                      global, NULL);
1575       g_source_set_name_by_id (global->leisure_function_id, "[gnome-shell] run_leisure_functions");
1576     }
1577 }
1578 
1579 /**
1580  * shell_global_begin_work:
1581  * @global: the #ShellGlobal
1582  *
1583  * Marks that we are currently doing work. This is used to to track
1584  * whether we are busy for the purposes of shell_global_run_at_leisure().
1585  * A count is kept and shell_global_end_work() must be called exactly
1586  * as many times as shell_global_begin_work().
1587  */
1588 void
shell_global_begin_work(ShellGlobal * global)1589 shell_global_begin_work (ShellGlobal *global)
1590 {
1591   global->work_count++;
1592 }
1593 
1594 /**
1595  * shell_global_end_work:
1596  * @global: the #ShellGlobal
1597  *
1598  * Marks the end of work that we started with shell_global_begin_work().
1599  * If no other work is ongoing and functions have been added with
1600  * shell_global_run_at_leisure(), they will be run at the next
1601  * opportunity.
1602  */
1603 void
shell_global_end_work(ShellGlobal * global)1604 shell_global_end_work (ShellGlobal *global)
1605 {
1606   g_return_if_fail (global->work_count > 0);
1607 
1608   global->work_count--;
1609   if (global->work_count == 0)
1610     schedule_leisure_functions (global);
1611 
1612 }
1613 
1614 /**
1615  * shell_global_run_at_leisure:
1616  * @global: the #ShellGlobal
1617  * @func: function to call at leisure
1618  * @user_data: data to pass to @func
1619  * @notify: function to call to free @user_data
1620  *
1621  * Schedules a function to be called the next time the shell is idle.
1622  * Idle means here no animations, no redrawing, and no ongoing background
1623  * work. Since there is currently no way to hook into the Clutter master
1624  * clock and know when is running, the implementation here is somewhat
1625  * approximation. Animations may be detected as terminating early if they
1626  * can be drawn fast enough so that the event loop goes idle between frames.
1627  *
1628  * The intent of this function is for performance measurement runs
1629  * where a number of actions should be run serially and each action is
1630  * timed individually. Using this function for other purposes will
1631  * interfere with the ability to use it for performance measurement so
1632  * should be avoided.
1633  */
1634 void
shell_global_run_at_leisure(ShellGlobal * global,ShellLeisureFunction func,gpointer user_data,GDestroyNotify notify)1635 shell_global_run_at_leisure (ShellGlobal         *global,
1636                              ShellLeisureFunction func,
1637                              gpointer             user_data,
1638                              GDestroyNotify       notify)
1639 {
1640   LeisureClosure *closure = g_new (LeisureClosure, 1);
1641   closure->func = func;
1642   closure->user_data = user_data;
1643   closure->notify = notify;
1644 
1645   global->leisure_closures = g_slist_append (global->leisure_closures,
1646                                              closure);
1647 
1648   if (global->work_count == 0)
1649     schedule_leisure_functions (global);
1650 }
1651 
1652 const char *
shell_global_get_session_mode(ShellGlobal * global)1653 shell_global_get_session_mode (ShellGlobal *global)
1654 {
1655   g_return_val_if_fail (SHELL_IS_GLOBAL (global), "user");
1656 
1657   return global->session_mode;
1658 }
1659 
1660 static void
delete_variant_cb(GObject * object,GAsyncResult * result,gpointer user_data)1661 delete_variant_cb (GObject      *object,
1662                    GAsyncResult *result,
1663                    gpointer      user_data)
1664 {
1665   ShellGlobal *global = user_data;
1666   GError *error = NULL;
1667 
1668   if (!g_file_delete_finish (G_FILE (object), result, &error))
1669     {
1670       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1671           !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1672         {
1673           g_warning ("Could not delete runtime/persistent state file: %s\n",
1674                      error->message);
1675         }
1676 
1677       g_error_free (error);
1678     }
1679 
1680   g_hash_table_remove (global->save_ops, object);
1681 }
1682 
1683 static void
replace_contents_worker(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1684 replace_contents_worker (GTask        *task,
1685                          gpointer      source_object,
1686                          gpointer      task_data,
1687                          GCancellable *cancellable)
1688 {
1689   GFile *file = source_object;
1690   GBytes *bytes = task_data;
1691   GError *error = NULL;
1692   const gchar *data;
1693   gsize len;
1694 
1695   data = g_bytes_get_data (bytes, &len);
1696 
1697   if (!g_file_replace_contents (file, data, len, NULL, FALSE,
1698                                 G_FILE_CREATE_REPLACE_DESTINATION,
1699                                 NULL, cancellable, &error))
1700     g_task_return_error (task, g_steal_pointer (&error));
1701   else
1702     g_task_return_boolean (task, TRUE);
1703 }
1704 
1705 static void
replace_contents_async(GFile * path,GBytes * bytes,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1706 replace_contents_async (GFile               *path,
1707                         GBytes              *bytes,
1708                         GCancellable        *cancellable,
1709                         GAsyncReadyCallback  callback,
1710                         gpointer             user_data)
1711 {
1712   g_autoptr(GTask) task = NULL;
1713 
1714   g_assert (G_IS_FILE (path));
1715   g_assert (bytes != NULL);
1716   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
1717 
1718   task = g_task_new (path, cancellable, callback, user_data);
1719   g_task_set_source_tag (task, replace_contents_async);
1720   g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
1721   g_task_run_in_thread (task, replace_contents_worker);
1722 }
1723 
1724 static gboolean
replace_contents_finish(GFile * file,GAsyncResult * result,GError ** error)1725 replace_contents_finish (GFile         *file,
1726                          GAsyncResult  *result,
1727                          GError       **error)
1728 {
1729   return g_task_propagate_boolean (G_TASK (result), error);
1730 }
1731 
1732 static void
replace_variant_cb(GObject * object,GAsyncResult * result,gpointer user_data)1733 replace_variant_cb (GObject      *object,
1734                     GAsyncResult *result,
1735                     gpointer      user_data)
1736 {
1737   ShellGlobal *global = user_data;
1738   GError *error = NULL;
1739 
1740   if (!replace_contents_finish (G_FILE (object), result, &error))
1741     {
1742       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1743         {
1744           g_warning ("Could not replace runtime/persistent state file: %s\n",
1745                      error->message);
1746         }
1747 
1748       g_error_free (error);
1749     }
1750 
1751   g_hash_table_remove (global->save_ops, object);
1752 }
1753 
1754 static void
save_variant(ShellGlobal * global,GFile * dir,const char * property_name,GVariant * variant)1755 save_variant (ShellGlobal *global,
1756               GFile       *dir,
1757               const char  *property_name,
1758               GVariant    *variant)
1759 {
1760   GFile *path = g_file_get_child (dir, property_name);
1761   GCancellable *cancellable;
1762 
1763   cancellable = g_hash_table_lookup (global->save_ops, path);
1764   g_cancellable_cancel (cancellable);
1765 
1766   cancellable = g_cancellable_new ();
1767   g_hash_table_insert (global->save_ops, g_object_ref (path), cancellable);
1768 
1769   if (variant == NULL || g_variant_get_data (variant) == NULL)
1770     {
1771       g_file_delete_async (path, G_PRIORITY_DEFAULT, cancellable,
1772                            delete_variant_cb, global);
1773     }
1774   else
1775     {
1776       g_autoptr(GBytes) bytes = NULL;
1777 
1778       bytes = g_bytes_new_with_free_func (g_variant_get_data (variant),
1779                                           g_variant_get_size (variant),
1780                                           (GDestroyNotify)g_variant_unref,
1781                                           g_variant_ref (variant));
1782       /* g_file_replace_contents_async() can potentially fsync() from the
1783        * calling thread when completing the asynchronous task. Instead, we
1784        * want to force that fsync() to a thread to avoid blocking the
1785        * compositor main loop. Using our own replace_contents_async()
1786        * simply executes the operation synchronously from a thread.
1787        */
1788       replace_contents_async (path, bytes, cancellable, replace_variant_cb, global);
1789     }
1790 
1791   g_object_unref (path);
1792 }
1793 
1794 static GVariant *
load_variant(GFile * dir,const char * property_type,const char * property_name)1795 load_variant (GFile      *dir,
1796               const char *property_type,
1797               const char *property_name)
1798 {
1799   GVariant *res = NULL;
1800   GMappedFile *mfile;
1801   GFile *path = g_file_get_child (dir, property_name);
1802   char *pathstr;
1803   GError *local_error = NULL;
1804 
1805   pathstr = g_file_get_path (path);
1806   mfile = g_mapped_file_new (pathstr, FALSE, &local_error);
1807   if (!mfile)
1808     {
1809       if (!g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
1810         {
1811           g_warning ("Failed to open runtime state: %s", local_error->message);
1812         }
1813       g_clear_error (&local_error);
1814     }
1815   else
1816     {
1817       GBytes *bytes = g_mapped_file_get_bytes (mfile);
1818       res = g_variant_new_from_bytes (G_VARIANT_TYPE (property_type), bytes, FALSE);
1819       g_bytes_unref (bytes);
1820       g_mapped_file_unref (mfile);
1821     }
1822 
1823   g_object_unref (path);
1824   g_free (pathstr);
1825 
1826   return res;
1827 }
1828 
1829 /**
1830  * shell_global_set_runtime_state:
1831  * @global: a #ShellGlobal
1832  * @property_name: Name of the property
1833  * @variant: (nullable): A #GVariant, or %NULL to unset
1834  *
1835  * Change the value of serialized runtime state.
1836  */
1837 void
shell_global_set_runtime_state(ShellGlobal * global,const char * property_name,GVariant * variant)1838 shell_global_set_runtime_state (ShellGlobal  *global,
1839                                 const char   *property_name,
1840                                 GVariant     *variant)
1841 {
1842   save_variant (global, global->runtime_state_path, property_name, variant);
1843 }
1844 
1845 /**
1846  * shell_global_get_runtime_state:
1847  * @global: a #ShellGlobal
1848  * @property_type: Expected data type
1849  * @property_name: Name of the property
1850  *
1851  * The shell maintains "runtime" state which does not persist across
1852  * logout or reboot.
1853  *
1854  * Returns: (transfer floating): The value of a serialized property, or %NULL if none stored
1855  */
1856 GVariant *
shell_global_get_runtime_state(ShellGlobal * global,const char * property_type,const char * property_name)1857 shell_global_get_runtime_state (ShellGlobal  *global,
1858                                 const char   *property_type,
1859                                 const char   *property_name)
1860 {
1861   return load_variant (global->runtime_state_path, property_type, property_name);
1862 }
1863 
1864 /**
1865  * shell_global_set_persistent_state:
1866  * @global: a #ShellGlobal
1867  * @property_name: Name of the property
1868  * @variant: (nullable): A #GVariant, or %NULL to unset
1869  *
1870  * Change the value of serialized persistent state.
1871  */
1872 void
shell_global_set_persistent_state(ShellGlobal * global,const char * property_name,GVariant * variant)1873 shell_global_set_persistent_state (ShellGlobal *global,
1874                                    const char  *property_name,
1875                                    GVariant    *variant)
1876 {
1877   save_variant (global, global->userdatadir_path, property_name, variant);
1878 }
1879 
1880 /**
1881  * shell_global_get_persistent_state:
1882  * @global: a #ShellGlobal
1883  * @property_type: Expected data type
1884  * @property_name: Name of the property
1885  *
1886  * The shell maintains "persistent" state which will persist after
1887  * logout or reboot.
1888  *
1889  * Returns: (transfer none): The value of a serialized property, or %NULL if none stored
1890  */
1891 GVariant *
shell_global_get_persistent_state(ShellGlobal * global,const char * property_type,const char * property_name)1892 shell_global_get_persistent_state (ShellGlobal  *global,
1893                                    const char   *property_type,
1894                                    const char   *property_name)
1895 {
1896   return load_variant (global->userdatadir_path, property_type, property_name);
1897 }
1898 
1899 void
_shell_global_locate_pointer(ShellGlobal * global)1900 _shell_global_locate_pointer (ShellGlobal *global)
1901 {
1902   g_signal_emit (global, shell_global_signals[LOCATE_POINTER], 0);
1903 }
1904