No issues found
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
17 #include <X11/extensions/Xfixes.h>
18 #include <cogl-pango/cogl-pango.h>
19 #include <canberra.h>
20 #include <clutter/glx/clutter-glx.h>
21 #include <clutter/x11/clutter-x11.h>
22 #include <gdk/gdkx.h>
23 #include <gio/gio.h>
24 #include <gjs/gjs-module.h>
25 #include <girepository.h>
26 #include <meta/display.h>
27 #include <meta/util.h>
28 #include <meta/meta-shaped-texture.h>
29
30 /* Memory report bits */
31 #ifdef HAVE_MALLINFO
32 #include <malloc.h>
33 #endif
34
35 #include "shell-enum-types.h"
36 #include "shell-global-private.h"
37 #include "shell-jsapi-compat-private.h"
38 #include "shell-perf-log.h"
39 #include "shell-window-tracker.h"
40 #include "shell-wm.h"
41 #include "st.h"
42
43 static ShellGlobal *the_object = NULL;
44
45 static void grab_notify (GtkWidget *widget, gboolean is_grab, gpointer user_data);
46 static void shell_global_on_gc (GjsContext *context,
47 ShellGlobal *global);
48
49 struct _ShellGlobal {
50 GObject parent;
51
52 ClutterStage *stage;
53 Window stage_xwindow;
54 GdkWindow *stage_gdk_window;
55
56 MetaDisplay *meta_display;
57 GdkDisplay *gdk_display;
58 Display *xdisplay;
59 MetaScreen *meta_screen;
60 GdkScreen *gdk_screen;
61
62 char *session_mode;
63
64 /* We use this window to get a notification from GTK+ when
65 * a widget in our process does a GTK+ grab. See
66 * http://bugzilla.gnome.org/show_bug.cgi?id=570641
67 *
68 * This window is never mapped or shown.
69 */
70 GtkWindow *grab_notifier;
71 gboolean gtk_grab_active;
72
73 ShellStageInputMode input_mode;
74 XserverRegion input_region;
75
76 GjsContext *js_context;
77 MetaPlugin *plugin;
78 ShellWM *wm;
79 GSettings *settings;
80 const char *datadir;
81 const char *imagedir;
82 const char *userdatadir;
83 StFocusManager *focus_manager;
84
85 guint work_count;
86 GSList *leisure_closures;
87 guint leisure_function_id;
88
89 /* For sound notifications */
90 ca_context *sound_context;
91
92 guint32 xdnd_timestamp;
93
94 gint64 last_gc_end_time;
95 };
96
97 enum {
98 PROP_0,
99
100 PROP_SESSION_MODE,
101 PROP_OVERLAY_GROUP,
102 PROP_SCREEN,
103 PROP_GDK_SCREEN,
104 PROP_DISPLAY,
105 PROP_SCREEN_WIDTH,
106 PROP_SCREEN_HEIGHT,
107 PROP_STAGE,
108 PROP_STAGE_INPUT_MODE,
109 PROP_WINDOW_GROUP,
110 PROP_BACKGROUND_ACTOR,
111 PROP_WINDOW_MANAGER,
112 PROP_SETTINGS,
113 PROP_DATADIR,
114 PROP_IMAGEDIR,
115 PROP_USERDATADIR,
116 PROP_FOCUS_MANAGER,
117 };
118
119 /* Signals */
120 enum
121 {
122 XDND_POSITION_CHANGED,
123 XDND_LEAVE,
124 XDND_ENTER,
125 NOTIFY_ERROR,
126 LAST_SIGNAL
127 };
128
129 G_DEFINE_TYPE(ShellGlobal, shell_global, G_TYPE_OBJECT);
130
131 static guint shell_global_signals [LAST_SIGNAL] = { 0 };
132
133 static void
134 shell_global_set_property(GObject *object,
135 guint prop_id,
136 const GValue *value,
137 GParamSpec *pspec)
138 {
139 ShellGlobal *global = SHELL_GLOBAL (object);
140
141 switch (prop_id)
142 {
143 case PROP_STAGE_INPUT_MODE:
144 shell_global_set_stage_input_mode (global, g_value_get_enum (value));
145 break;
146 case PROP_SESSION_MODE:
147 g_clear_pointer (&global->session_mode, g_free);
148 global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
149 break;
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 break;
153 }
154 }
155
156 static void
157 shell_global_get_property(GObject *object,
158 guint prop_id,
159 GValue *value,
160 GParamSpec *pspec)
161 {
162 ShellGlobal *global = SHELL_GLOBAL (object);
163
164 switch (prop_id)
165 {
166 case PROP_SESSION_MODE:
167 g_value_set_string (value, shell_global_get_session_mode (global));
168 break;
169 case PROP_OVERLAY_GROUP:
170 g_value_set_object (value, meta_get_overlay_group_for_screen (global->meta_screen));
171 break;
172 case PROP_SCREEN:
173 g_value_set_object (value, global->meta_screen);
174 break;
175 case PROP_GDK_SCREEN:
176 g_value_set_object (value, global->gdk_screen);
177 break;
178 case PROP_DISPLAY:
179 g_value_set_object (value, global->meta_display);
180 break;
181 case PROP_SCREEN_WIDTH:
182 {
183 int width, height;
184
185 meta_screen_get_size (global->meta_screen, &width, &height);
186 g_value_set_int (value, width);
187 }
188 break;
189 case PROP_SCREEN_HEIGHT:
190 {
191 int width, height;
192
193 meta_screen_get_size (global->meta_screen, &width, &height);
194 g_value_set_int (value, height);
195 }
196 break;
197 case PROP_STAGE:
198 g_value_set_object (value, global->stage);
199 break;
200 case PROP_STAGE_INPUT_MODE:
201 g_value_set_enum (value, global->input_mode);
202 break;
203 case PROP_WINDOW_GROUP:
204 g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen));
205 break;
206 case PROP_BACKGROUND_ACTOR:
207 g_value_set_object (value, meta_get_background_actor_for_screen (global->meta_screen));
208 break;
209 case PROP_WINDOW_MANAGER:
210 g_value_set_object (value, global->wm);
211 break;
212 case PROP_SETTINGS:
213 g_value_set_object (value, global->settings);
214 break;
215 case PROP_DATADIR:
216 g_value_set_string (value, global->datadir);
217 break;
218 case PROP_IMAGEDIR:
219 g_value_set_string (value, global->imagedir);
220 break;
221 case PROP_USERDATADIR:
222 g_value_set_string (value, global->userdatadir);
223 break;
224 case PROP_FOCUS_MANAGER:
225 g_value_set_object (value, global->focus_manager);
226 break;
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
229 break;
230 }
231 }
232
233 static void
234 shell_global_init (ShellGlobal *global)
235 {
236 const char *datadir = g_getenv ("GNOME_SHELL_DATADIR");
237 const char *shell_js = g_getenv("GNOME_SHELL_JS");
238 char *imagedir, **search_path;
239
240 if (!datadir)
241 datadir = GNOME_SHELL_DATADIR;
242 global->datadir = datadir;
243
244 /* We make sure imagedir ends with a '/', since the JS won't have
245 * access to g_build_filename() and so will end up just
246 * concatenating global.imagedir to a filename.
247 */
248 imagedir = g_build_filename (datadir, "images/", NULL);
249 if (g_file_test (imagedir, G_FILE_TEST_IS_DIR))
250 global->imagedir = imagedir;
251 else
252 {
253 g_free (imagedir);
254 global->imagedir = g_strdup_printf ("%s/", datadir);
255 }
256
257 /* Ensure config dir exists for later use */
258 global->userdatadir = g_build_filename (g_get_user_data_dir (), "gnome-shell", NULL);
259 g_mkdir_with_parents (global->userdatadir, 0700);
260
261 global->settings = g_settings_new ("org.gnome.shell");
262
263 global->grab_notifier = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
264 g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
265 global->gtk_grab_active = FALSE;
266
267 global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
268
269 ca_context_create (&global->sound_context);
270 ca_context_change_props (global->sound_context, CA_PROP_APPLICATION_NAME, PACKAGE_NAME, CA_PROP_APPLICATION_ID, "org.gnome.Shell", NULL);
271 ca_context_open (global->sound_context);
272
273 if (!shell_js)
274 shell_js = JSDIR;
275 search_path = g_strsplit (shell_js, ":", -1);
276 global->js_context = g_object_new (GJS_TYPE_CONTEXT,
277 "search-path", search_path,
278 "js-version", "1.8",
279 "gc-notifications", TRUE,
280 NULL);
281 g_signal_connect (global->js_context, "gc", G_CALLBACK (shell_global_on_gc), global);
282
283 g_strfreev (search_path);
284 }
285
286 static void
287 shell_global_finalize (GObject *object)
288 {
289 ShellGlobal *global = SHELL_GLOBAL (object);
290
291 g_object_unref (global->js_context);
292 gtk_widget_destroy (GTK_WIDGET (global->grab_notifier));
293 g_object_unref (global->settings);
294
295 the_object = NULL;
296
297 G_OBJECT_CLASS(shell_global_parent_class)->finalize (object);
298 }
299
300 static void
301 shell_global_class_init (ShellGlobalClass *klass)
302 {
303 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
304
305 gobject_class->get_property = shell_global_get_property;
306 gobject_class->set_property = shell_global_set_property;
307 gobject_class->finalize = shell_global_finalize;
308
309 /* Emitted from gnome-shell-plugin.c during event handling */
310 shell_global_signals[XDND_POSITION_CHANGED] =
311 g_signal_new ("xdnd-position-changed",
312 G_TYPE_FROM_CLASS (klass),
313 G_SIGNAL_RUN_LAST,
314 0,
315 NULL, NULL, NULL,
316 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
317
318 /* Emitted from gnome-shell-plugin.c during event handling */
319 shell_global_signals[XDND_LEAVE] =
320 g_signal_new ("xdnd-leave",
321 G_TYPE_FROM_CLASS (klass),
322 G_SIGNAL_RUN_LAST,
323 0,
324 NULL, NULL, NULL,
325 G_TYPE_NONE, 0);
326
327 /* Emitted from gnome-shell-plugin.c during event handling */
328 shell_global_signals[XDND_ENTER] =
329 g_signal_new ("xdnd-enter",
330 G_TYPE_FROM_CLASS (klass),
331 G_SIGNAL_RUN_LAST,
332 0,
333 NULL, NULL, NULL,
334 G_TYPE_NONE, 0);
335
336 shell_global_signals[NOTIFY_ERROR] =
337 g_signal_new ("notify-error",
338 G_TYPE_FROM_CLASS (klass),
339 G_SIGNAL_RUN_LAST,
340 0,
341 NULL, NULL, NULL,
342 G_TYPE_NONE, 2,
343 G_TYPE_STRING,
344 G_TYPE_STRING);
345
346 g_object_class_install_property (gobject_class,
347 PROP_SESSION_MODE,
348 g_param_spec_string ("session-mode",
349 "Session Mode",
350 "The session mode to use",
351 "user",
352 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
353 g_object_class_install_property (gobject_class,
354 PROP_OVERLAY_GROUP,
355 g_param_spec_object ("overlay-group",
356 "Overlay Group",
357 "Actor holding objects that appear above the desktop contents",
358 CLUTTER_TYPE_ACTOR,
359 G_PARAM_READABLE));
360 g_object_class_install_property (gobject_class,
361 PROP_SCREEN,
362 g_param_spec_object ("screen",
363 "Screen",
364 "Metacity screen object for the shell",
365 META_TYPE_SCREEN,
366 G_PARAM_READABLE));
367
368 g_object_class_install_property (gobject_class,
369 PROP_GDK_SCREEN,
370 g_param_spec_object ("gdk-screen",
371 "GdkScreen",
372 "Gdk screen object for the shell",
373 GDK_TYPE_SCREEN,
374 G_PARAM_READABLE));
375
376 g_object_class_install_property (gobject_class,
377 PROP_SCREEN_WIDTH,
378 g_param_spec_int ("screen-width",
379 "Screen Width",
380 "Screen width, in pixels",
381 0, G_MAXINT, 1,
382 G_PARAM_READABLE));
383
384 g_object_class_install_property (gobject_class,
385 PROP_SCREEN_HEIGHT,
386 g_param_spec_int ("screen-height",
387 "Screen Height",
388 "Screen height, in pixels",
389 0, G_MAXINT, 1,
390 G_PARAM_READABLE));
391 g_object_class_install_property (gobject_class,
392 PROP_DISPLAY,
393 g_param_spec_object ("display",
394 "Display",
395 "Metacity display object for the shell",
396 META_TYPE_DISPLAY,
397 G_PARAM_READABLE));
398
399 g_object_class_install_property (gobject_class,
400 PROP_STAGE,
401 g_param_spec_object ("stage",
402 "Stage",
403 "Stage holding the desktop scene graph",
404 CLUTTER_TYPE_ACTOR,
405 G_PARAM_READABLE));
406 g_object_class_install_property (gobject_class,
407 PROP_STAGE_INPUT_MODE,
408 g_param_spec_enum ("stage-input-mode",
409 "Stage input mode",
410 "The stage input mode",
411 SHELL_TYPE_STAGE_INPUT_MODE,
412 SHELL_STAGE_INPUT_MODE_NORMAL,
413 G_PARAM_READWRITE));
414 g_object_class_install_property (gobject_class,
415 PROP_WINDOW_GROUP,
416 g_param_spec_object ("window-group",
417 "Window Group",
418 "Actor holding window actors",
419 CLUTTER_TYPE_ACTOR,
420 G_PARAM_READABLE));
421 g_object_class_install_property (gobject_class,
422 PROP_BACKGROUND_ACTOR,
423 g_param_spec_object ("background-actor",
424 "Background Actor",
425 "Actor drawing root window background",
426 CLUTTER_TYPE_ACTOR,
427 G_PARAM_READABLE));
428 g_object_class_install_property (gobject_class,
429 PROP_WINDOW_MANAGER,
430 g_param_spec_object ("window-manager",
431 "Window Manager",
432 "Window management interface",
433 SHELL_TYPE_WM,
434 G_PARAM_READABLE));
435 g_object_class_install_property (gobject_class,
436 PROP_SETTINGS,
437 g_param_spec_object ("settings",
438 "Settings",
439 "GSettings instance for gnome-shell configuration",
440 G_TYPE_SETTINGS,
441 G_PARAM_READABLE));
442 g_object_class_install_property (gobject_class,
443 PROP_DATADIR,
444 g_param_spec_string ("datadir",
445 "Data directory",
446 "Directory containing gnome-shell data files",
447 NULL,
448 G_PARAM_READABLE));
449 g_object_class_install_property (gobject_class,
450 PROP_IMAGEDIR,
451 g_param_spec_string ("imagedir",
452 "Image directory",
453 "Directory containing gnome-shell image files",
454 NULL,
455 G_PARAM_READABLE));
456 g_object_class_install_property (gobject_class,
457 PROP_USERDATADIR,
458 g_param_spec_string ("userdatadir",
459 "User data directory",
460 "Directory containing gnome-shell user data",
461 NULL,
462 G_PARAM_READABLE));
463 g_object_class_install_property (gobject_class,
464 PROP_FOCUS_MANAGER,
465 g_param_spec_object ("focus-manager",
466 "Focus manager",
467 "The shell's StFocusManager",
468 ST_TYPE_FOCUS_MANAGER,
469 G_PARAM_READABLE));
470 }
471
472 /**•
473 * _shell_global_init: (skip)•
474 * @first_property_name: the name of the first property
475 * @...: the value of the first property, followed optionally by more
476 * name/value pairs, followed by %NULL
477 *•
478 * Initializes the shell global singleton with the construction-time
479 * properties.
480 *
481 * There are currently no such properties, so @first_property_name should
482 * always be %NULL.
483 *
484 * This call must be called before shell_global_get() and shouldn't be called
485 * more than once.
486 */
487 void
488 _shell_global_init (const char *first_property_name,
489 ...)
490 {
491 va_list argument_list;
492
493 g_return_if_fail (the_object == NULL);
494
495 va_start (argument_list, first_property_name);
496 the_object = SHELL_GLOBAL (g_object_new_valist (SHELL_TYPE_GLOBAL,
497 first_property_name,
498 argument_list));
499 va_end (argument_list);
500
501 }
502
503 /**
504 * shell_global_get:
505 *
506 * Gets the singleton global object that represents the desktop.
507 *
508 * Return value: (transfer none): the singleton global object
509 */
510 ShellGlobal *
511 shell_global_get (void)
512 {
513 return the_object;
514 }
515
516 static void
517 focus_window_changed (MetaDisplay *display,
518 GParamSpec *param,
519 gpointer user_data)
520 {
521 ShellGlobal *global = user_data;
522
523 if (global->input_mode == SHELL_STAGE_INPUT_MODE_FOCUSED &&
524 meta_display_get_focus_window (display) != NULL)
525 shell_global_set_stage_input_mode (global, SHELL_STAGE_INPUT_MODE_NORMAL);
526 }
527
528 static void
529 shell_global_focus_stage (ShellGlobal *global)
530 {
531 XSetInputFocus (global->xdisplay, global->stage_xwindow,
532 RevertToPointerRoot,
533 shell_global_get_current_time (global));
534 }
535
536 /**
537 * shell_global_set_stage_input_mode:
538 * @global: the #ShellGlobal
539 * @mode: the stage input mode
540 *
541 * Sets the input mode of the stage; when @mode is
542 * %SHELL_STAGE_INPUT_MODE_NONREACTIVE, then the stage does not absorb
543 * any clicks, but just passes them through to underlying windows.
544 * When it is %SHELL_STAGE_INPUT_MODE_NORMAL, then the stage accepts
545 * clicks in the region defined by
546 * shell_global_set_stage_input_region() but passes through clicks
547 * outside that region. When it is %SHELL_STAGE_INPUT_MODE_FULLSCREEN,
548 * the stage absorbs all input.
549 *
550 * When the input mode is %SHELL_STAGE_INPUT_MODE_FOCUSED, the pointer
551 * is handled as with %SHELL_STAGE_INPUT_MODE_NORMAL, but additionally
552 * the stage window has the keyboard focus. If the stage loses the
553 * focus (eg, because the user clicked into a window) the input mode
554 * will revert to %SHELL_STAGE_INPUT_MODE_NORMAL.
555 *
556 * Note that whenever a mutter-internal Gtk widget has a pointer grab,
557 * the shell behaves as though it was in
558 * %SHELL_STAGE_INPUT_MODE_NONREACTIVE, to ensure that the widget gets
559 * any clicks it is expecting.
560 */
561 void
562 shell_global_set_stage_input_mode (ShellGlobal *global,
563 ShellStageInputMode mode)
564 {
565 MetaScreen *screen;
566
567 g_return_if_fail (SHELL_IS_GLOBAL (global));
568
569 screen = meta_plugin_get_screen (global->plugin);
570
571 if (mode == SHELL_STAGE_INPUT_MODE_NONREACTIVE || global->gtk_grab_active)
572 meta_empty_stage_input_region (screen);
573 else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
574 meta_set_stage_input_region (screen, None);
575 else
576 meta_set_stage_input_region (screen, global->input_region);
577
578 if (mode == SHELL_STAGE_INPUT_MODE_FOCUSED)
579 shell_global_focus_stage (global);
580
581 if (mode != global->input_mode)
582 {
583 global->input_mode = mode;
584 g_object_notify (G_OBJECT (global), "stage-input-mode");
585 }
586 }
587
588 /**
589 * shell_global_set_cursor:
590 * @global: A #ShellGlobal
591 * @type: the type of the cursor
592 *
593 * Set the cursor on the stage window.
594 */
595 void
596 shell_global_set_cursor (ShellGlobal *global,
597 ShellCursor type)
598 {
599 const char *name;
600 GdkCursor *cursor;
601
602 switch (type)
603 {
604 case SHELL_CURSOR_DND_IN_DRAG:
605 name = "dnd-none";
606 break;
607 case SHELL_CURSOR_DND_MOVE:
608 name = "dnd-move";
609 break;
610 case SHELL_CURSOR_DND_COPY:
611 name = "dnd-copy";
612 break;
613 case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
614 name = "dnd-none";
615 break;
616 case SHELL_CURSOR_POINTING_HAND:
617 name = "hand";
618 break;
619 default:
620 g_return_if_reached ();
621 }
622
623 cursor = gdk_cursor_new_from_name (global->gdk_display, name);
624 if (!cursor)
625 {
626 GdkCursorType cursor_type;
627 switch (type)
628 {
629 case SHELL_CURSOR_DND_IN_DRAG:
630 cursor_type = GDK_FLEUR;
631 break;
632 case SHELL_CURSOR_DND_MOVE:
633 cursor_type = GDK_TARGET;
634 break;
635 case SHELL_CURSOR_DND_COPY:
636 cursor_type = GDK_PLUS;
637 break;
638 case SHELL_CURSOR_POINTING_HAND:
639 cursor_type = GDK_HAND2;
640 break;
641 case SHELL_CURSOR_DND_UNSUPPORTED_TARGET:
642 cursor_type = GDK_X_CURSOR;
643 break;
644 default:
645 g_return_if_reached ();
646 }
647 cursor = gdk_cursor_new (cursor_type);
648 }
649
650 gdk_window_set_cursor (global->stage_gdk_window, cursor);
651
652 g_object_unref (cursor);
653 }
654
655 /**
656 * shell_global_unset_cursor:
657 * @global: A #ShellGlobal
658 *
659 * Unset the cursor on the stage window.
660 */
661 void
662 shell_global_unset_cursor (ShellGlobal *global)
663 {
664 gdk_window_set_cursor (global->stage_gdk_window, NULL);
665 }
666
667 /**
668 * shell_global_set_stage_input_region:
669 * @global: the #ShellGlobal
670 * @rectangles: (element-type Meta.Rectangle): a list of #MetaRectangle
671 * describing the input region.
672 *
673 * Sets the area of the stage that is responsive to mouse clicks when
674 * the stage mode is %SHELL_STAGE_INPUT_MODE_NORMAL (but does not change the
675 * current stage mode).
676 */
677 void
678 shell_global_set_stage_input_region (ShellGlobal *global,
679 GSList *rectangles)
680 {
681 MetaRectangle *rect;
682 XRectangle *rects;
683 int nrects, i;
684 GSList *r;
685
686 g_return_if_fail (SHELL_IS_GLOBAL (global));
687
688 nrects = g_slist_length (rectangles);
689 rects = g_new (XRectangle, nrects);
690 for (r = rectangles, i = 0; r; r = r->next, i++)
691 {
692 rect = (MetaRectangle *)r->data;
693 rects[i].x = rect->x;
694 rects[i].y = rect->y;
695 rects[i].width = rect->width;
696 rects[i].height = rect->height;
697 }
698
699 if (global->input_region)
700 XFixesDestroyRegion (global->xdisplay, global->input_region);
701
702 global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
703 g_free (rects);
704
705 /* set_stage_input_mode() will figure out whether or not we
706 * should actually change the input region right now.
707 */
708 shell_global_set_stage_input_mode (global, global->input_mode);
709 }
710
711 /**
712 * shell_global_get_stage:
713 *
714 * Return value: (transfer none): The default #ClutterStage
715 */
716 ClutterStage *
717 shell_global_get_stage (ShellGlobal *global)
718 {
719 return global->stage;
720 }
721
722 /**
723 * shell_global_get_screen:
724 *
725 * Return value: (transfer none): The default #MetaScreen
726 */
727 MetaScreen *
728 shell_global_get_screen (ShellGlobal *global)
729 {
730 return global->meta_screen;
731 }
732
733 /**
734 * shell_global_get_gdk_screen:
735 *
736 * Return value: (transfer none): Gdk screen object for the shell
737 */
738 GdkScreen *
739 shell_global_get_gdk_screen (ShellGlobal *global)
740 {
741 g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
742
743 return global->gdk_screen;
744 }
745
746 /**
747 * shell_global_get_display:
748 *
749 * Return value: (transfer none): The default #MetaDisplay
750 */
751 MetaDisplay *
752 shell_global_get_display (ShellGlobal *global)
753 {
754 return global->meta_display;
755 }
756
757 /**
758 * shell_global_get_window_actors:
759 *
760 * Gets the list of #MetaWindowActor for the plugin's screen
761 *
762 * Return value: (element-type Meta.WindowActor) (transfer none): the list of windows
763 */
764 GList *
765 shell_global_get_window_actors (ShellGlobal *global)
766 {
767 g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
768
769 return meta_get_window_actors (global->meta_screen);
770 }
771
772 static void
773 global_stage_notify_width (GObject *gobject,
774 GParamSpec *pspec,
775 gpointer data)
776 {
777 ShellGlobal *global = SHELL_GLOBAL (data);
778
779 g_object_notify (G_OBJECT (global), "screen-width");
780 }
781
782 static void
783 global_stage_notify_height (GObject *gobject,
784 GParamSpec *pspec,
785 gpointer data)
786 {
787 ShellGlobal *global = SHELL_GLOBAL (data);
788
789 g_object_notify (G_OBJECT (global), "screen-height");
790 }
791
792 static void
793 global_stage_before_paint (ClutterStage *stage,
794 ShellGlobal *global)
795 {
796 shell_perf_log_event (shell_perf_log_get_default (),
797 "clutter.stagePaintStart");
798 }
799
800 static void
801 global_stage_after_paint (ClutterStage *stage,
802 ShellGlobal *global)
803 {
804 shell_perf_log_event (shell_perf_log_get_default (),
805 "clutter.stagePaintDone");
806 }
807
808 static void
809 shell_fonts_init (ClutterStage *stage)
810 {
811 CoglPangoFontMap *fontmap;
812
813 /* Disable text mipmapping; it causes problems on pre-GEM Intel
814 * drivers and we should just be rendering text at the right
815 * size rather than scaling it. If we do effects where we dynamically
816 * zoom labels, then we might want to reconsider.
817 */
818 fontmap = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
819 cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE);
820 }
821
822 /* This is an IBus workaround. The flow of events with IBus is that every time
823 * it gets gets a key event, it:
824 *
825 * Sends it to the daemon via D-Bus asynchronously
826 * When it gets an reply, synthesizes a new GdkEvent and puts it into the
827 * GDK event queue with gdk_event_put(), including
828 * IBUS_FORWARD_MASK = 1 << 25 in the state to prevent a loop.
829 *
830 * (Normally, IBus uses the GTK+ key snooper mechanism to get the key
831 * events early, but since our key events aren't visible to GTK+ key snoopers,
832 * IBus will instead get the events via the standard
833 * GtkIMContext.filter_keypress() mechanism.)
834 *
835 * There are a number of potential problems here; probably the worst
836 * problem is that IBus doesn't forward the timestamp with the event
837 * so that every key event that gets delivered ends up with
838 * GDK_CURRENT_TIME. This creates some very subtle bugs; for example
839 * if you have IBus running and a keystroke is used to trigger
840 * launching an application, focus stealing prevention won't work
841 * right. http://code.google.com/p/ibus/issues/detail?id=1184
842 *
843 * In any case, our normal flow of key events is:
844 *
845 * GDK filter function => clutter_x11_handle_event => clutter actor
846 *
847 * So, if we see a key event that gets delivered via the GDK event handler
848 * function - then we know it must be one of these synthesized events, and
849 * we should push it back to clutter.
850 *
851 * To summarize, the full key event flow with IBus is:
852 *
853 * GDK filter function
854 * => Mutter
855 * => gnome_shell_plugin_xevent_filter()
856 * => clutter_x11_handle_event()
857 * => clutter event delivery to actor
858 * => gtk_im_context_filter_event()
859 * => sent to IBus daemon
860 * => response received from IBus daemon
861 * => gdk_event_put()
862 * => GDK event handler
863 * => <this function>
864 * => clutter_event_put()
865 * => clutter event delivery to actor
866 *
867 * Anything else we see here we just pass on to the normal GDK event handler
868 * gtk_main_do_event().
869 */
870 static void
871 gnome_shell_gdk_event_handler (GdkEvent *event_gdk,
872 gpointer data)
873 {
874 if (event_gdk->type == GDK_KEY_PRESS || event_gdk->type == GDK_KEY_RELEASE)
875 {
876 ClutterActor *stage;
877 Window stage_xwindow;
878
879 stage = CLUTTER_ACTOR (data);
880 stage_xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (stage));
881
882 if (GDK_WINDOW_XID (event_gdk->key.window) == stage_xwindow)
883 {
884 ClutterDeviceManager *device_manager = clutter_device_manager_get_default ();
885 ClutterInputDevice *keyboard = clutter_device_manager_get_core_device (device_manager,
886 CLUTTER_KEYBOARD_DEVICE);
887
888 ClutterEvent *event_clutter = clutter_event_new ((event_gdk->type == GDK_KEY_PRESS) ?
889 CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE);
890 event_clutter->key.time = event_gdk->key.time;
891 event_clutter->key.flags = CLUTTER_EVENT_NONE;
892 event_clutter->key.stage = CLUTTER_STAGE (stage);
893 event_clutter->key.source = NULL;
894
895 /* This depends on ClutterModifierType and GdkModifierType being
896 * identical, which they are currently. (They both match the X
897 * modifier state in the low 16-bits and have the same extensions.) */
898 event_clutter->key.modifier_state = event_gdk->key.state;
899
900 event_clutter->key.keyval = event_gdk->key.keyval;
901 event_clutter->key.hardware_keycode = event_gdk->key.hardware_keycode;
902 event_clutter->key.unicode_value = gdk_keyval_to_unicode (event_clutter->key.keyval);
903 event_clutter->key.device = keyboard;
904
905 clutter_event_put (event_clutter);
906 clutter_event_free (event_clutter);
907
908 return;
909 }
910 }
911
912 gtk_main_do_event (event_gdk);
913 }
914
915 void
916 _shell_global_set_plugin (ShellGlobal *global,
917 MetaPlugin *plugin)
918 {
919 g_return_if_fail (SHELL_IS_GLOBAL (global));
920 g_return_if_fail (global->plugin == NULL);
921
922 global->plugin = plugin;
923 global->wm = shell_wm_new (plugin);
924
925 global->meta_screen = meta_plugin_get_screen (plugin);
926 global->meta_display = meta_screen_get_display (global->meta_screen);
927 global->xdisplay = meta_display_get_xdisplay (global->meta_display);
928
929 global->gdk_display = gdk_x11_lookup_xdisplay (global->xdisplay);
930 global->gdk_screen = gdk_display_get_screen (global->gdk_display,
931 meta_screen_get_screen_number (global->meta_screen));
932
933 global->stage = CLUTTER_STAGE (meta_get_stage_for_screen (global->meta_screen));
934 global->stage_xwindow = clutter_x11_get_stage_window (global->stage);
935 global->stage_gdk_window = gdk_x11_window_foreign_new_for_display (global->gdk_display,
936 global->stage_xwindow);
937
938 g_signal_connect (global->stage, "notify::width",
939 G_CALLBACK (global_stage_notify_width), global);
940 g_signal_connect (global->stage, "notify::height",
941 G_CALLBACK (global_stage_notify_height), global);
942
943 g_signal_connect (global->stage, "paint",
944 G_CALLBACK (global_stage_before_paint), global);
945 g_signal_connect_after (global->stage, "paint",
946 G_CALLBACK (global_stage_after_paint), global);
947
948 shell_perf_log_define_event (shell_perf_log_get_default(),
949 "clutter.stagePaintStart",
950 "Start of stage page repaint",
951 "");
952 shell_perf_log_define_event (shell_perf_log_get_default(),
953 "clutter.stagePaintDone",
954 "End of stage page repaint",
955 "");
956
957 g_signal_connect (global->meta_display, "notify::focus-window",
958 G_CALLBACK (focus_window_changed), global);
959
960 shell_fonts_init (global->stage);
961
962 gdk_event_handler_set (gnome_shell_gdk_event_handler, global->stage, NULL);
963
964 global->focus_manager = st_focus_manager_get_for_stage (global->stage);
965 }
966
967 GjsContext *
968 _shell_global_get_gjs_context (ShellGlobal *global)
969 {
970 return global->js_context;
971 }
972
973 /**
974 * shell_global_begin_modal:
975 * @global: a #ShellGlobal
976 *
977 * Grabs the keyboard and mouse to the stage window. The stage will
978 * receive all keyboard and mouse events until shell_global_end_modal()
979 * is called. This is used to implement "modes" for the shell, such as the
980 * overview mode or the "looking glass" debug overlay, that block
981 * application and normal key shortcuts.
982 *
983 * Returns: %TRUE if we succesfully entered the mode. %FALSE if we couldn't
984 * enter the mode. Failure may occur because an application has the pointer
985 * or keyboard grabbed, because Mutter is in a mode itself like moving a
986 * window or alt-Tab window selection, or because shell_global_begin_modal()
987 * was previouly called.
988 */
989 gboolean
990 shell_global_begin_modal (ShellGlobal *global,
991 guint32 timestamp,
992 MetaModalOptions options)
993 {
994 return meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
995 }
996
997 /**
998 * shell_global_end_modal:
999 * @global: a #ShellGlobal
1000 *
1001 * Undoes the effect of shell_global_begin_modal().
1002 */
1003 void
1004 shell_global_end_modal (ShellGlobal *global,
1005 guint32 timestamp)
1006 {
1007 meta_plugin_end_modal (global->plugin, timestamp);
1008 }
1009
1010 /**
1011 * shell_global_create_pointer_barrier:
1012 * @global: a #ShellGlobal
1013 * @x1: left X coordinate
1014 * @y1: top Y coordinate
1015 * @x2: right X coordinate
1016 * @y2: bottom Y coordinate
1017 * @directions: The directions we're allowed to pass through
1018 *
1019 * If supported by X creates a pointer barrier.
1020 *
1021 * Return value: value you can pass to shell_global_destroy_pointer_barrier()
1022 */
1023 guint32
1024 shell_global_create_pointer_barrier (ShellGlobal *global,
1025 int x1, int y1, int x2, int y2,
1026 int directions)
1027 {
1028 #if HAVE_XFIXESCREATEPOINTERBARRIER
1029 return (guint32)
1030 XFixesCreatePointerBarrier (global->xdisplay,
1031 DefaultRootWindow (global->xdisplay),
1032 x1, y1,
1033 x2, y2,
1034 directions,
1035 0, NULL);
1036 #else
1037 return 0;
1038 #endif
1039 }
1040
1041 /**
1042 * shell_global_destroy_pointer_barrier:
1043 * @global: a #ShellGlobal
1044 * @barrier: a pointer barrier
1045 *
1046 * Destroys the @barrier created by shell_global_create_pointer_barrier().
1047 */
1048 void
1049 shell_global_destroy_pointer_barrier (ShellGlobal *global, guint32 barrier)
1050 {
1051 #if HAVE_XFIXESCREATEPOINTERBARRIER
1052 g_return_if_fail (barrier > 0);
1053
1054 XFixesDestroyPointerBarrier (global->xdisplay, (PointerBarrier)barrier);
1055 #endif
1056 }
1057
1058 /* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
1059 *
1060 * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering
1061 *
1062 * http://bugzilla.gnome.org/show_bug.cgi?id=469231
1063 * http://bugzilla.gnome.org/show_bug.cgi?id=357585
1064 */
1065
1066 static int
1067 set_cloexec (void *data, gint fd)
1068 {
1069 if (fd >= GPOINTER_TO_INT (data))
1070 fcntl (fd, F_SETFD, FD_CLOEXEC);
1071
1072 return 0;
1073 }
1074
1075 #ifndef HAVE_FDWALK
1076 static int
1077 fdwalk (int (*cb)(void *data, int fd), void *data)
1078 {
1079 gint open_max;
1080 gint fd;
1081 gint res = 0;
1082
1083 #ifdef HAVE_SYS_RESOURCE_H
1084 struct rlimit rl;
1085 #endif
1086
1087 #ifdef __linux__
1088 DIR *d;
1089
1090 if ((d = opendir("/proc/self/fd"))) {
1091 struct dirent *de;
1092
1093 while ((de = readdir(d))) {
1094 glong l;
1095 gchar *e = NULL;
1096
1097 if (de->d_name[0] == '.')
1098 continue;
1099
1100 errno = 0;
1101 l = strtol(de->d_name, &e, 10);
1102 if (errno != 0 || !e || *e)
1103 continue;
1104
1105 fd = (gint) l;
1106
1107 if ((glong) fd != l)
1108 continue;
1109
1110 if (fd == dirfd(d))
1111 continue;
1112
1113 if ((res = cb (data, fd)) != 0)
1114 break;
1115 }
1116
1117 closedir(d);
1118 return res;
1119 }
1120
1121 /* If /proc is not mounted or not accessible we fall back to the old
1122 * rlimit trick */
1123
1124 #endif
1125
1126 #ifdef HAVE_SYS_RESOURCE_H
1127 if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
1128 open_max = rl.rlim_max;
1129 else
1130 #endif
1131 open_max = sysconf (_SC_OPEN_MAX);
1132
1133 for (fd = 0; fd < open_max; fd++)
1134 if ((res = cb (data, fd)) != 0)
1135 break;
1136
1137 return res;
1138 }
1139 #endif
1140
1141 static void
1142 pre_exec_close_fds(void)
1143 {
1144 fdwalk (set_cloexec, GINT_TO_POINTER(3));
1145 }
1146
1147 /**
1148 * shell_global_reexec_self:
1149 * @global: A #ShellGlobal
1150 *
1151 * Restart the current process. Only intended for development purposes.
1152 */
1153 void
1154 shell_global_reexec_self (ShellGlobal *global)
1155 {
1156 GPtrArray *arr;
1157 gsize len;
1158 char *buf;
1159 char *buf_p;
1160 char *buf_end;
1161 GError *error = NULL;
1162
1163 /* Linux specific (I think, anyways). */
1164 if (!g_file_get_contents ("/proc/self/cmdline", &buf, &len, &error))
1165 {
1166 g_warning ("failed to get /proc/self/cmdline: %s", error->message);
1167 return;
1168 }
1169
1170 buf_end = buf+len;
1171 arr = g_ptr_array_new ();
1172 /* The cmdline file is NUL-separated */
1173 for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
1174 g_ptr_array_add (arr, buf_p);
1175
1176 g_ptr_array_add (arr, NULL);
1177
1178 /* Close all file descriptors other than stdin/stdout/stderr, otherwise
1179 * they will leak and stay open after the exec. In particular, this is
1180 * important for file descriptors that represent mapped graphics buffer
1181 * objects.
1182 */
1183 pre_exec_close_fds ();
1184
1185 meta_display_unmanage_screen (shell_global_get_display (global),
1186 shell_global_get_screen (global),
1187 shell_global_get_current_time (global));
1188
1189 execvp (arr->pdata[0], (char**)arr->pdata);
1190 g_warning ("failed to reexec: %s", g_strerror (errno));
1191 g_ptr_array_free (arr, TRUE);
1192 }
1193
1194 static void
1195 shell_global_on_gc (GjsContext *context,
1196 ShellGlobal *global)
1197 {
1198 global->last_gc_end_time = g_get_monotonic_time ();
1199 }
1200
1201 /**
1202 * shell_global_get_memory_info:
1203 * @global:
1204 * @meminfo: (out caller-allocates): Output location for memory information
1205 *
1206 * Load process-global data about memory usage.
1207 */
1208 void
1209 shell_global_get_memory_info (ShellGlobal *global,
1210 ShellMemoryInfo *meminfo)
1211 {
1212 JSContext *context;
1213 gint64 now;
1214
1215 #ifdef HAVE_MALLINFO
1216 {
1217 struct mallinfo info = mallinfo ();
1218 meminfo->glibc_uordblks = info.uordblks;
1219 }
1220 #else
1221 meminfo->glibc_uordblks = 0;
1222 #endif
1223
1224 context = gjs_context_get_native_context (global->js_context);
1225
1226 meminfo->js_bytes = JS_GetGCParameter (JS_GetRuntime (context), JSGC_BYTES);
1227
1228 meminfo->gjs_boxed = (unsigned int) gjs_counter_boxed.value;
1229 meminfo->gjs_gobject = (unsigned int) gjs_counter_object.value;
1230 meminfo->gjs_function = (unsigned int) gjs_counter_function.value;
1231 meminfo->gjs_closure = (unsigned int) gjs_counter_closure.value;
1232
1233 now = g_get_monotonic_time ();
1234
1235 meminfo->last_gc_seconds_ago = (now - global->last_gc_end_time) / G_TIME_SPAN_SECOND;
1236 }
1237
1238
1239 /**
1240 * shell_global_notify_error:
1241 * @global: a #ShellGlobal
1242 * @msg: Error message
1243 * @details: Error details
1244 *
1245 * Show a system error notification. Use this function
1246 * when a user-initiated action results in a non-fatal problem
1247 * from causes that may not be under system control. For
1248 * example, an application crash.
1249 */
1250 void
1251 shell_global_notify_error (ShellGlobal *global,
1252 const char *msg,
1253 const char *details)
1254 {
1255 g_signal_emit_by_name (global, "notify-error", msg, details);
1256 }
1257
1258 static void
1259 grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
1260 {
1261 ShellGlobal *global = SHELL_GLOBAL (user_data);
1262
1263 global->gtk_grab_active = !was_grabbed;
1264
1265 /* Update for the new setting of gtk_grab_active */
1266 shell_global_set_stage_input_mode (global, global->input_mode);
1267 }
1268
1269 /**
1270 * shell_global_init_xdnd:
1271 * @global: the #ShellGlobal
1272 *
1273 * Enables tracking of Xdnd events
1274 */
1275 void shell_global_init_xdnd (ShellGlobal *global)
1276 {
1277 Window output_window = meta_get_overlay_window (global->meta_screen);
1278 long xdnd_version = 5;
1279
1280 XChangeProperty (global->xdisplay, global->stage_xwindow,
1281 gdk_x11_get_xatom_by_name ("XdndAware"), XA_ATOM,
1282 32, PropModeReplace, (const unsigned char *)&xdnd_version, 1);
1283
1284 XChangeProperty (global->xdisplay, output_window,
1285 gdk_x11_get_xatom_by_name ("XdndProxy"), XA_WINDOW,
1286 32, PropModeReplace, (const unsigned char *)&global->stage_xwindow, 1);
1287
1288 /*
1289 * XdndProxy is additionally set on the proxy window as verification that the
1290 * XdndProxy property on the target window isn't a left-over
1291 */
1292 XChangeProperty (global->xdisplay, global->stage_xwindow,
1293 gdk_x11_get_xatom_by_name ("XdndProxy"), XA_WINDOW,
1294 32, PropModeReplace, (const unsigned char *)&global->stage_xwindow, 1);
1295 }
1296
1297 /**
1298 * shell_global_get_pointer:
1299 * @global: the #ShellGlobal
1300 * @x: (out): the X coordinate of the pointer, in global coordinates
1301 * @y: (out): the Y coordinate of the pointer, in global coordinates
1302 * @mods: (out): the current set of modifier keys that are pressed down
1303 *
1304 * Gets the pointer coordinates and current modifier key state.
1305 * This is a wrapper around gdk_display_get_pointer() that strips
1306 * out any un-declared modifier flags, to make gjs happy; see
1307 * https://bugzilla.gnome.org/show_bug.cgi?id=597292.
1308 */
1309 void
1310 shell_global_get_pointer (ShellGlobal *global,
1311 int *x,
1312 int *y,
1313 ClutterModifierType *mods)
1314 {
1315 GdkDeviceManager *gmanager;
1316 GdkDevice *gdevice;
1317 GdkScreen *gscreen;
1318 GdkModifierType raw_mods;
1319
1320 gmanager = gdk_display_get_device_manager (global->gdk_display);
1321 gdevice = gdk_device_manager_get_client_pointer (gmanager);
1322 gdk_device_get_position (gdevice, &gscreen, x, y);
1323 gdk_device_get_state (gdevice,
1324 gdk_screen_get_root_window (gscreen),
1325 NULL, &raw_mods);
1326 *mods = raw_mods & GDK_MODIFIER_MASK;
1327 }
1328
1329 /**
1330 * shell_global_sync_pointer:
1331 * @global: the #ShellGlobal
1332 *
1333 * Ensures that clutter is aware of the current pointer position,
1334 * causing enter and leave events to be emitted if the pointer moved
1335 * behind our back (ie, during a pointer grab).
1336 */
1337 void
1338 shell_global_sync_pointer (ShellGlobal *global)
1339 {
1340 int x, y;
1341 GdkModifierType mods;
1342 GdkDeviceManager *gmanager;
1343 GdkDevice *gdevice;
1344 GdkScreen *gscreen;
1345 ClutterMotionEvent event;
1346
1347 gmanager = gdk_display_get_device_manager (global->gdk_display);
1348 gdevice = gdk_device_manager_get_client_pointer (gmanager);
1349
1350 gdk_device_get_position (gdevice, &gscreen, &x, &y);
1351 gdk_device_get_state (gdevice,
1352 gdk_screen_get_root_window (gscreen),
1353 NULL, &mods);
1354
1355 event.type = CLUTTER_MOTION;
1356 event.time = shell_global_get_current_time (global);
1357 event.flags = 0;
1358 /* This is wrong: we should be setting event.stage to NULL if the
1359 * pointer is not inside the bounds of the stage given the current
1360 * stage_input_mode. For our current purposes however, this works.
1361 */
1362 event.stage = global->stage;
1363 event.x = x;
1364 event.y = y;
1365 event.modifier_state = mods;
1366 event.axes = NULL;
1367 event.device = clutter_device_manager_get_core_device (clutter_device_manager_get_default (),
1368 CLUTTER_POINTER_DEVICE);
1369
1370 /* Leaving event.source NULL will force clutter to look it up, which
1371 * will generate enter/leave events as a side effect, if they are
1372 * needed. We need a better way to do this though... see
1373 * http://bugzilla.clutter-project.org/show_bug.cgi?id=2615.
1374 */
1375 event.source = NULL;
1376
1377 clutter_event_put ((ClutterEvent *)&event);
1378 }
1379
1380 /**
1381 * shell_global_get_settings:
1382 * @global: A #ShellGlobal
1383 *
1384 * Get the global GSettings instance.
1385 *
1386 * Return value: (transfer none): The GSettings object
1387 */
1388 GSettings *
1389 shell_global_get_settings (ShellGlobal *global)
1390 {
1391 return global->settings;
1392 }
1393
1394 /**
1395 * shell_global_get_current_time:
1396 * @global: A #ShellGlobal
1397 *
1398 * Returns: the current X server time from the current Clutter, Gdk, or X
1399 * event. If called from outside an event handler, this may return
1400 * %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
1401 * out-of-date timestamp.
1402 */
1403 guint32
1404 shell_global_get_current_time (ShellGlobal *global)
1405 {
1406 guint32 time;
1407 const ClutterEvent *clutter_event;
1408
1409 /* In case we have a xdnd timestamp use it */
1410 if (global->xdnd_timestamp != 0)
1411 return global->xdnd_timestamp;
1412
1413 /* meta_display_get_current_time() will return the correct time
1414 when handling an X or Gdk event, but will return CurrentTime
1415 from some Clutter event callbacks.
1416
1417 clutter_get_current_event_time() will return the correct time
1418 from a Clutter event callback, but may return an out-of-date
1419 timestamp if called at other times.
1420
1421 So we try meta_display_get_current_time() first, since we
1422 can recognize a "wrong" answer from that, and then fall back
1423 to clutter_get_current_event_time().
1424 */
1425
1426 time = meta_display_get_current_time (global->meta_display);
1427 if (time != CLUTTER_CURRENT_TIME)
1428 return time;
1429 /*
1430 * We don't use clutter_get_current_event_time as it can give us a
1431 * too old timestamp if there is no current event.
1432 */
1433 clutter_event = clutter_get_current_event ();
1434
1435 if (clutter_event != NULL)
1436 return clutter_event_get_time (clutter_event);
1437 else
1438 return CLUTTER_CURRENT_TIME;
1439 }
1440
1441 /**
1442 * shell_global_create_app_launch_context:
1443 * @global: A #ShellGlobal
1444 *
1445 * Create a #GAppLaunchContext set up with the correct timestamp, and
1446 * targeted to activate on the current workspace.
1447 *
1448 * Return value: (transfer full): A new #GAppLaunchContext
1449 */
1450 GAppLaunchContext *
1451 shell_global_create_app_launch_context (ShellGlobal *global)
1452 {
1453 GdkAppLaunchContext *context;
1454
1455 context = gdk_display_get_app_launch_context (global->gdk_display);
1456 gdk_app_launch_context_set_timestamp (context, shell_global_get_current_time (global));
1457
1458 // Make sure that the app is opened on the current workspace even if
1459 // the user switches before it starts
1460 gdk_app_launch_context_set_desktop (context, meta_screen_get_active_workspace_index (global->meta_screen));
1461
1462 return (GAppLaunchContext *)context;
1463 }
1464
1465 typedef struct
1466 {
1467 ShellLeisureFunction func;
1468 gpointer user_data;
1469 GDestroyNotify notify;
1470 } LeisureClosure;
1471
1472 static gboolean
1473 run_leisure_functions (gpointer data)
1474 {
1475 ShellGlobal *global = data;
1476 GSList *closures;
1477 GSList *iter;
1478
1479 global->leisure_function_id = 0;
1480
1481 /* We started more work since we scheduled the idle */
1482 if (global->work_count > 0)
1483 return FALSE;
1484
1485 /* No leisure closures, so we are done */
1486 if (global->leisure_closures == NULL)
1487 return FALSE;
1488
1489 closures = global->leisure_closures;
1490 global->leisure_closures = NULL;
1491
1492 for (iter = closures; iter; iter = iter->next)
1493 {
1494 LeisureClosure *closure = closures->data;
1495 closure->func (closure->user_data);
1496
1497 if (closure->notify)
1498 closure->notify (closure->user_data);
1499
1500 g_slice_free (LeisureClosure, closure);
1501 }
1502
1503 g_slist_free (closures);
1504
1505 return FALSE;
1506 }
1507
1508 static void
1509 schedule_leisure_functions (ShellGlobal *global)
1510 {
1511 /* This is called when we think we are ready to run leisure functions
1512 * by our own accounting. We try to handle other types of business
1513 * (like ClutterAnimation) by adding a low priority idle function.
1514 *
1515 * This won't work properly if the mainloop goes idle waiting for
1516 * the vertical blanking interval or waiting for work being done
1517 * in another thread.
1518 */
1519 if (!global->leisure_function_id)
1520 global->leisure_function_id = g_idle_add_full (G_PRIORITY_LOW,
1521 run_leisure_functions,
1522 global, NULL);
1523 }
1524
1525 /**
1526 * shell_global_begin_work:
1527 * @global: the #ShellGlobal
1528 *
1529 * Marks that we are currently doing work. This is used to to track
1530 * whether we are busy for the purposes of shell_global_run_at_leisure().
1531 * A count is kept and shell_global_end_work() must be called exactly
1532 * as many times as shell_global_begin_work().
1533 */
1534 void
1535 shell_global_begin_work (ShellGlobal *global)
1536 {
1537 global->work_count++;
1538 }
1539
1540 /**
1541 * shell_global_end_work:
1542 * @global: the #ShellGlobal
1543 *
1544 * Marks the end of work that we started with shell_global_begin_work().
1545 * If no other work is ongoing and functions have been added with
1546 * shell_global_run_at_leisure(), they will be run at the next
1547 * opportunity.
1548 */
1549 void
1550 shell_global_end_work (ShellGlobal *global)
1551 {
1552 g_return_if_fail (global->work_count > 0);
1553
1554 global->work_count--;
1555 if (global->work_count == 0)
1556 schedule_leisure_functions (global);
1557
1558 }
1559
1560 /**
1561 * shell_global_run_at_leisure:
1562 * @global: the #ShellGlobal
1563 * @func: function to call at leisure
1564 * @user_data: data to pass to @func
1565 * @notify: function to call to free @user_data
1566 *
1567 * Schedules a function to be called the next time the shell is idle.
1568 * Idle means here no animations, no redrawing, and no ongoing background
1569 * work. Since there is currently no way to hook into the Clutter master
1570 * clock and know when is running, the implementation here is somewhat
1571 * approximation. Animations done through the shell's Tweener module will
1572 * be handled properly, but other animations may be detected as terminating
1573 * early if they can be drawn fast enough so that the event loop goes idle
1574 * between frames.
1575 *
1576 * The intent of this function is for performance measurement runs
1577 * where a number of actions should be run serially and each action is
1578 * timed individually. Using this function for other purposes will
1579 * interfere with the ability to use it for performance measurement so
1580 * should be avoided.
1581 */
1582 void
1583 shell_global_run_at_leisure (ShellGlobal *global,
1584 ShellLeisureFunction func,
1585 gpointer user_data,
1586 GDestroyNotify notify)
1587 {
1588 LeisureClosure *closure = g_slice_new (LeisureClosure);
1589 closure->func = func;
1590 closure->user_data = user_data;
1591 closure->notify = notify;
1592
1593 global->leisure_closures = g_slist_append (global->leisure_closures,
1594 closure);
1595
1596 if (global->work_count == 0)
1597 schedule_leisure_functions (global);
1598 }
1599
1600 /**
1601 * shell_global_play_theme_sound:
1602 * @global: the #ShellGlobal
1603 * @id: an id, used to cancel later (0 if not needed)
1604 * @name: the sound name
1605 *
1606 * Plays a simple sound picked according to Freedesktop sound theme.
1607 * Really just a workaround for libcanberra not being introspected.
1608 */
1609 void
1610 shell_global_play_theme_sound (ShellGlobal *global,
1611 guint id,
1612 const char *name)
1613 {
1614 ca_context_play (global->sound_context, id, CA_PROP_EVENT_ID, name, NULL);
1615 }
1616
1617 /**
1618 * shell_global_cancel_theme_sound:
1619 * @global: the #ShellGlobal
1620 * @id: the id previously passed to shell_global_play_theme_sound()
1621 *
1622 * Cancels a sound notification.
1623 */
1624 void
1625 shell_global_cancel_theme_sound (ShellGlobal *global,
1626 guint id)
1627 {
1628 ca_context_cancel (global->sound_context, id);
1629 }
1630
1631 /*
1632 * Process Xdnd events
1633 *
1634 * We pass the position and leave events to JS via a signal
1635 * where the actual drag & drop handling happens.
1636 *
1637 * http://www.freedesktop.org/wiki/Specifications/XDND
1638 */
1639 gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
1640 XEvent *xev)
1641 {
1642 Window output_window = meta_get_overlay_window (global->meta_screen);
1643
1644 if (xev->xany.window != output_window && xev->xany.window != global->stage_xwindow)
1645 return FALSE;
1646
1647 if (xev->xany.type == ClientMessage && xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndPosition"))
1648 {
1649 XEvent xevent;
1650 Window src = xev->xclient.data.l[0];
1651
1652 memset (&xevent, 0, sizeof(xevent));
1653 xevent.xany.type = ClientMessage;
1654 xevent.xany.display = global->xdisplay;
1655 xevent.xclient.window = src;
1656 xevent.xclient.message_type = gdk_x11_get_xatom_by_name ("XdndStatus");
1657 xevent.xclient.format = 32;
1658 xevent.xclient.data.l[0] = output_window;
1659 /* flags: bit 0: will we accept the drop? bit 1: do we want more position messages */
1660 xevent.xclient.data.l[1] = 2;
1661 xevent.xclient.data.l[4] = None;
1662
1663 XSendEvent (global->xdisplay, src, False, 0, &xevent);
1664
1665 /* Store the timestamp of the xdnd position event */
1666 global->xdnd_timestamp = xev->xclient.data.l[3];
1667 g_signal_emit_by_name (G_OBJECT (global), "xdnd-position-changed",
1668 (int)(xev->xclient.data.l[2] >> 16), (int)(xev->xclient.data.l[2] & 0xFFFF));
1669 global->xdnd_timestamp = 0;
1670
1671 return TRUE;
1672 }
1673 else if (xev->xany.type == ClientMessage && xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndLeave"))
1674 {
1675 g_signal_emit_by_name (G_OBJECT (global), "xdnd-leave");
1676
1677 return TRUE;
1678 }
1679 else if (xev->xany.type == ClientMessage && xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndEnter"))
1680 {
1681 g_signal_emit_by_name (G_OBJECT (global), "xdnd-enter");
1682
1683 return TRUE;
1684 }
1685
1686 return FALSE;
1687 }
1688
1689 const char *
1690 shell_global_get_session_mode (ShellGlobal *global)
1691 {
1692 g_return_val_if_fail (SHELL_IS_GLOBAL (global), "user");
1693
1694 return global->session_mode;
1695 }
1696
1697 /**
1698 * shell_global_create_xrootpmap_texture:
1699 * @global: The #ShellGlobal
1700 *
1701 * This returns the _XROOTPMAP_ID pixmap that gdm should have stuffed
1702 * in the root window. The goal here is to allow a smooth fade between
1703 * plymouth and the shell greeter. This is also a workaround for gjs not
1704 * supporting raw xlib types.
1705 *
1706 * Returns: (transfer floating): A #ClutterActor that represents the
1707 * _XROOTPMAP_ID pixmap property from the root window.
1708 */
1709 ClutterActor *
1710 shell_global_create_xrootpmap_texture (ShellGlobal *global)
1711 {
1712 Atom res_type;
1713 int res_format;
1714 unsigned long res_nitems, bytesafter;
1715 unsigned char *data;
1716 Pixmap root_pixmap_id = None;
1717
1718 g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
1719
1720 if (XGetWindowProperty (global->xdisplay,
1721 DefaultRootWindow (global->xdisplay),
1722 XInternAtom (global->xdisplay, "_XROOTPMAP_ID", False),
1723 0, G_MAXLONG, False, XA_PIXMAP,
1724 &res_type, &res_format, &res_nitems, &bytesafter, &data) == Success)
1725 {
1726 if (res_type == XA_PIXMAP && res_format == 32 && res_nitems == 1)
1727 root_pixmap_id = * (Pixmap *) data;
1728 XFree (data);
1729 }
1730
1731 if (root_pixmap_id != None)
1732 return clutter_x11_texture_pixmap_new_with_pixmap (root_pixmap_id);
1733 else
1734 return NULL;
1735 }