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