2008-10-31 15:20:54 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
|
2009-11-24 14:07:40 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2009-04-21 21:21:06 +00:00
|
|
|
#include <dirent.h>
|
2008-12-18 20:57:37 +00:00
|
|
|
#include <errno.h>
|
2009-04-21 21:21:06 +00:00
|
|
|
#include <fcntl.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
#include <math.h>
|
2011-08-28 05:32:12 +00:00
|
|
|
#include <stdarg.h>
|
2009-01-19 23:06:59 +00:00
|
|
|
#include <stdlib.h>
|
2008-12-18 20:57:37 +00:00
|
|
|
#include <string.h>
|
2009-04-21 21:21:06 +00:00
|
|
|
#include <unistd.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#endif
|
2012-11-04 18:53:49 +00:00
|
|
|
#include <locale.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
|
|
|
|
#include <X11/extensions/Xfixes.h>
|
|
|
|
#include <canberra.h>
|
2012-11-04 18:53:49 +00:00
|
|
|
#include <canberra-gtk.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
#include <clutter/x11/clutter-x11.h>
|
|
|
|
#include <gdk/gdkx.h>
|
2009-07-07 03:13:58 +00:00
|
|
|
#include <gio/gio.h>
|
2011-03-15 22:31:16 +00:00
|
|
|
#include <girepository.h>
|
2017-03-31 09:03:23 +00:00
|
|
|
#include <meta/meta-backend.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
#include <meta/display.h>
|
|
|
|
#include <meta/util.h>
|
2011-12-19 16:37:24 +00:00
|
|
|
#include <meta/meta-shaped-texture.h>
|
2013-09-11 15:35:58 +00:00
|
|
|
#include <meta/meta-cursor-tracker.h>
|
2017-04-21 10:13:33 +00:00
|
|
|
#include <meta/meta-settings.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
|
2015-02-13 09:27:27 +00:00
|
|
|
#ifdef HAVE_SYSTEMD
|
|
|
|
#include <systemd/sd-journal.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2011-05-20 19:50:54 +00:00
|
|
|
/* Memory report bits */
|
|
|
|
#ifdef HAVE_MALLINFO
|
|
|
|
#include <malloc.h>
|
|
|
|
#endif
|
|
|
|
|
2015-04-13 04:07:35 +00:00
|
|
|
#if defined __OpenBSD__ || defined __FreeBSD__
|
2013-10-31 10:46:59 +00:00
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#endif
|
|
|
|
|
2011-03-05 15:49:24 +00:00
|
|
|
#include "shell-enum-types.h"
|
|
|
|
#include "shell-global-private.h"
|
|
|
|
#include "shell-perf-log.h"
|
|
|
|
#include "shell-window-tracker.h"
|
|
|
|
#include "shell-wm.h"
|
|
|
|
#include "st.h"
|
2008-11-19 19:54:17 +00:00
|
|
|
|
2011-05-05 22:09:59 +00:00
|
|
|
static ShellGlobal *the_object = NULL;
|
|
|
|
|
2008-10-31 04:22:44 +00:00
|
|
|
struct _ShellGlobal {
|
|
|
|
GObject parent;
|
2011-07-13 16:22:32 +00:00
|
|
|
|
|
|
|
ClutterStage *stage;
|
|
|
|
Window stage_xwindow;
|
2013-09-11 15:44:24 +00:00
|
|
|
GdkWindow *ibus_window;
|
2011-07-13 16:22:32 +00:00
|
|
|
|
|
|
|
MetaDisplay *meta_display;
|
|
|
|
GdkDisplay *gdk_display;
|
|
|
|
Display *xdisplay;
|
|
|
|
MetaScreen *meta_screen;
|
|
|
|
GdkScreen *gdk_screen;
|
|
|
|
|
2012-05-16 21:11:57 +00:00
|
|
|
char *session_mode;
|
2011-06-21 14:09:09 +00:00
|
|
|
|
2009-04-29 18:01:09 +00:00
|
|
|
XserverRegion input_region;
|
2009-10-24 17:40:13 +00:00
|
|
|
|
|
|
|
GjsContext *js_context;
|
2010-10-19 18:55:43 +00:00
|
|
|
MetaPlugin *plugin;
|
2008-11-21 14:02:09 +00:00
|
|
|
ShellWM *wm;
|
2010-05-05 21:05:42 +00:00
|
|
|
GSettings *settings;
|
2009-09-10 05:36:05 +00:00
|
|
|
const char *datadir;
|
2008-12-22 22:05:56 +00:00
|
|
|
const char *imagedir;
|
2010-05-04 11:24:06 +00:00
|
|
|
const char *userdatadir;
|
2013-10-13 22:46:50 +00:00
|
|
|
GFile *userdatadir_path;
|
2013-01-17 19:39:54 +00:00
|
|
|
GFile *runtime_state_path;
|
|
|
|
|
2013-10-13 22:46:50 +00:00
|
|
|
StFocusManager *focus_manager;
|
|
|
|
|
2010-05-09 04:34:15 +00:00
|
|
|
guint work_count;
|
|
|
|
GSList *leisure_closures;
|
|
|
|
guint leisure_function_id;
|
2010-07-23 00:39:44 +00:00
|
|
|
|
|
|
|
/* For sound notifications */
|
|
|
|
ca_context *sound_context;
|
2011-01-05 14:47:27 +00:00
|
|
|
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
gboolean has_modal;
|
2014-06-27 00:34:15 +00:00
|
|
|
gboolean frame_timestamps;
|
2014-07-16 14:05:02 +00:00
|
|
|
gboolean frame_finish_timestamp;
|
2008-10-31 04:22:44 +00:00
|
|
|
};
|
|
|
|
|
2008-10-31 15:20:54 +00:00
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
|
2012-05-16 21:11:57 +00:00
|
|
|
PROP_SESSION_MODE,
|
2008-11-19 22:54:42 +00:00
|
|
|
PROP_SCREEN,
|
2010-06-03 15:14:58 +00:00
|
|
|
PROP_GDK_SCREEN,
|
2011-07-13 16:34:31 +00:00
|
|
|
PROP_DISPLAY,
|
2008-10-31 15:20:54 +00:00
|
|
|
PROP_SCREEN_WIDTH,
|
2008-10-31 18:04:38 +00:00
|
|
|
PROP_SCREEN_HEIGHT,
|
2008-11-08 19:33:33 +00:00
|
|
|
PROP_STAGE,
|
2008-11-21 14:02:09 +00:00
|
|
|
PROP_WINDOW_GROUP,
|
2013-02-12 21:00:41 +00:00
|
|
|
PROP_TOP_WINDOW_GROUP,
|
2008-12-22 22:05:56 +00:00
|
|
|
PROP_WINDOW_MANAGER,
|
2010-05-05 21:05:42 +00:00
|
|
|
PROP_SETTINGS,
|
2009-09-10 05:36:05 +00:00
|
|
|
PROP_DATADIR,
|
2009-04-23 21:10:37 +00:00
|
|
|
PROP_IMAGEDIR,
|
2010-05-04 11:24:06 +00:00
|
|
|
PROP_USERDATADIR,
|
2010-10-05 14:09:40 +00:00
|
|
|
PROP_FOCUS_MANAGER,
|
2014-06-27 00:34:15 +00:00
|
|
|
PROP_FRAME_TIMESTAMPS,
|
2014-07-16 14:05:02 +00:00
|
|
|
PROP_FRAME_FINISH_TIMESTAMP,
|
2008-10-31 15:20:54 +00:00
|
|
|
};
|
|
|
|
|
2011-01-05 14:47:27 +00:00
|
|
|
/* Signals */
|
|
|
|
enum
|
|
|
|
{
|
2011-03-15 22:31:16 +00:00
|
|
|
NOTIFY_ERROR,
|
2011-01-05 14:47:27 +00:00
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
2008-10-31 04:22:44 +00:00
|
|
|
G_DEFINE_TYPE(ShellGlobal, shell_global, G_TYPE_OBJECT);
|
|
|
|
|
2011-01-05 14:47:27 +00:00
|
|
|
static guint shell_global_signals [LAST_SIGNAL] = { 0 };
|
|
|
|
|
2008-10-31 15:20:54 +00:00
|
|
|
static void
|
|
|
|
shell_global_set_property(GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
2010-07-02 17:36:13 +00:00
|
|
|
ShellGlobal *global = SHELL_GLOBAL (object);
|
|
|
|
|
2008-10-31 15:20:54 +00:00
|
|
|
switch (prop_id)
|
|
|
|
{
|
2012-05-16 21:11:57 +00:00
|
|
|
case PROP_SESSION_MODE:
|
|
|
|
g_clear_pointer (&global->session_mode, g_free);
|
|
|
|
global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
|
2011-06-21 14:09:09 +00:00
|
|
|
break;
|
2014-06-27 00:34:15 +00:00
|
|
|
case PROP_FRAME_TIMESTAMPS:
|
|
|
|
global->frame_timestamps = g_value_get_boolean (value);
|
|
|
|
break;
|
2014-07-16 14:05:02 +00:00
|
|
|
case PROP_FRAME_FINISH_TIMESTAMP:
|
|
|
|
global->frame_finish_timestamp = g_value_get_boolean (value);
|
|
|
|
break;
|
2008-10-31 15:20:54 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_global_get_property(GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = SHELL_GLOBAL (object);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
2012-05-16 21:11:57 +00:00
|
|
|
case PROP_SESSION_MODE:
|
|
|
|
g_value_set_string (value, shell_global_get_session_mode (global));
|
|
|
|
break;
|
2008-11-19 22:54:42 +00:00
|
|
|
case PROP_SCREEN:
|
2011-07-13 16:22:32 +00:00
|
|
|
g_value_set_object (value, global->meta_screen);
|
2008-11-19 22:54:42 +00:00
|
|
|
break;
|
2010-06-03 15:14:58 +00:00
|
|
|
case PROP_GDK_SCREEN:
|
2011-07-13 16:22:32 +00:00
|
|
|
g_value_set_object (value, global->gdk_screen);
|
2010-06-03 15:14:58 +00:00
|
|
|
break;
|
2011-07-13 16:34:31 +00:00
|
|
|
case PROP_DISPLAY:
|
|
|
|
g_value_set_object (value, global->meta_display);
|
|
|
|
break;
|
2008-10-31 15:20:54 +00:00
|
|
|
case PROP_SCREEN_WIDTH:
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
|
2011-03-24 20:12:33 +00:00
|
|
|
meta_screen_get_size (global->meta_screen, &width, &height);
|
2008-10-31 15:20:54 +00:00
|
|
|
g_value_set_int (value, width);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PROP_SCREEN_HEIGHT:
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
|
2011-03-24 20:12:33 +00:00
|
|
|
meta_screen_get_size (global->meta_screen, &width, &height);
|
2008-10-31 15:20:54 +00:00
|
|
|
g_value_set_int (value, height);
|
|
|
|
}
|
|
|
|
break;
|
2008-10-31 18:04:38 +00:00
|
|
|
case PROP_STAGE:
|
2011-07-13 16:22:32 +00:00
|
|
|
g_value_set_object (value, global->stage);
|
2008-10-31 18:04:38 +00:00
|
|
|
break;
|
2008-11-08 19:33:33 +00:00
|
|
|
case PROP_WINDOW_GROUP:
|
2011-03-24 20:12:33 +00:00
|
|
|
g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen));
|
2008-11-08 19:33:33 +00:00
|
|
|
break;
|
2013-02-12 21:00:41 +00:00
|
|
|
case PROP_TOP_WINDOW_GROUP:
|
|
|
|
g_value_set_object (value, meta_get_top_window_group_for_screen (global->meta_screen));
|
|
|
|
break;
|
2008-11-21 14:02:09 +00:00
|
|
|
case PROP_WINDOW_MANAGER:
|
|
|
|
g_value_set_object (value, global->wm);
|
|
|
|
break;
|
2010-05-05 21:05:42 +00:00
|
|
|
case PROP_SETTINGS:
|
|
|
|
g_value_set_object (value, global->settings);
|
|
|
|
break;
|
2009-09-10 05:36:05 +00:00
|
|
|
case PROP_DATADIR:
|
|
|
|
g_value_set_string (value, global->datadir);
|
|
|
|
break;
|
2008-12-22 22:05:56 +00:00
|
|
|
case PROP_IMAGEDIR:
|
|
|
|
g_value_set_string (value, global->imagedir);
|
|
|
|
break;
|
2010-05-04 11:24:06 +00:00
|
|
|
case PROP_USERDATADIR:
|
|
|
|
g_value_set_string (value, global->userdatadir);
|
2009-04-23 21:10:37 +00:00
|
|
|
break;
|
2010-10-05 14:09:40 +00:00
|
|
|
case PROP_FOCUS_MANAGER:
|
|
|
|
g_value_set_object (value, global->focus_manager);
|
|
|
|
break;
|
2014-06-27 00:34:15 +00:00
|
|
|
case PROP_FRAME_TIMESTAMPS:
|
|
|
|
g_value_set_boolean (value, global->frame_timestamps);
|
|
|
|
break;
|
2014-07-16 14:05:02 +00:00
|
|
|
case PROP_FRAME_FINISH_TIMESTAMP:
|
|
|
|
g_value_set_boolean (value, global->frame_finish_timestamp);
|
|
|
|
break;
|
2008-10-31 15:20:54 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 04:22:44 +00:00
|
|
|
static void
|
2008-12-22 22:05:56 +00:00
|
|
|
shell_global_init (ShellGlobal *global)
|
2008-10-31 04:22:44 +00:00
|
|
|
{
|
2008-12-22 22:05:56 +00:00
|
|
|
const char *datadir = g_getenv ("GNOME_SHELL_DATADIR");
|
2011-02-25 16:20:27 +00:00
|
|
|
const char *shell_js = g_getenv("GNOME_SHELL_JS");
|
|
|
|
char *imagedir, **search_path;
|
2013-01-17 19:39:54 +00:00
|
|
|
char *path;
|
|
|
|
const char *byteorder_string;
|
2008-12-22 22:05:56 +00:00
|
|
|
|
|
|
|
if (!datadir)
|
|
|
|
datadir = GNOME_SHELL_DATADIR;
|
2009-09-10 05:36:05 +00:00
|
|
|
global->datadir = datadir;
|
2008-12-22 22:05:56 +00:00
|
|
|
|
|
|
|
/* We make sure imagedir ends with a '/', since the JS won't have
|
|
|
|
* access to g_build_filename() and so will end up just
|
|
|
|
* concatenating global.imagedir to a filename.
|
|
|
|
*/
|
|
|
|
imagedir = g_build_filename (datadir, "images/", NULL);
|
|
|
|
if (g_file_test (imagedir, G_FILE_TEST_IS_DIR))
|
|
|
|
global->imagedir = imagedir;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_free (imagedir);
|
|
|
|
global->imagedir = g_strdup_printf ("%s/", datadir);
|
|
|
|
}
|
2009-04-23 21:10:37 +00:00
|
|
|
|
|
|
|
/* Ensure config dir exists for later use */
|
2010-05-04 11:24:06 +00:00
|
|
|
global->userdatadir = g_build_filename (g_get_user_data_dir (), "gnome-shell", NULL);
|
2010-05-06 07:42:53 +00:00
|
|
|
g_mkdir_with_parents (global->userdatadir, 0700);
|
2013-10-13 22:46:50 +00:00
|
|
|
global->userdatadir_path = g_file_new_for_path (global->userdatadir);
|
2010-05-05 21:05:42 +00:00
|
|
|
|
2013-01-17 19:39:54 +00:00
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
|
|
byteorder_string = "LE";
|
|
|
|
#else
|
|
|
|
byteorder_string = "BE";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* And the runtime state */
|
|
|
|
path = g_strdup_printf ("%s/gnome-shell/runtime-state-%s.%s",
|
|
|
|
g_get_user_runtime_dir (),
|
|
|
|
byteorder_string,
|
|
|
|
XDisplayName (NULL));
|
|
|
|
(void) g_mkdir_with_parents (path, 0700);
|
|
|
|
global->runtime_state_path = g_file_new_for_path (path);
|
2016-09-21 21:24:28 +00:00
|
|
|
g_free (path);
|
2013-01-17 19:39:54 +00:00
|
|
|
|
2010-05-05 21:05:42 +00:00
|
|
|
global->settings = g_settings_new ("org.gnome.shell");
|
2009-04-22 00:23:06 +00:00
|
|
|
|
2012-11-04 18:53:49 +00:00
|
|
|
global->sound_context = ca_gtk_context_get ();
|
|
|
|
ca_context_change_props (global->sound_context,
|
|
|
|
CA_PROP_APPLICATION_NAME, "GNOME Shell",
|
|
|
|
CA_PROP_APPLICATION_ID, "org.gnome.Shell",
|
|
|
|
CA_PROP_APPLICATION_ICON_NAME, "start-here",
|
|
|
|
CA_PROP_APPLICATION_LANGUAGE, setlocale (LC_MESSAGES, NULL),
|
|
|
|
NULL);
|
2010-07-23 00:39:44 +00:00
|
|
|
ca_context_open (global->sound_context);
|
2011-02-25 16:20:27 +00:00
|
|
|
|
2013-07-02 06:54:53 +00:00
|
|
|
if (shell_js)
|
|
|
|
{
|
2014-05-19 23:14:21 +00:00
|
|
|
int i, j;
|
2013-07-02 06:54:53 +00:00
|
|
|
search_path = g_strsplit (shell_js, ":", -1);
|
2014-05-19 23:14:21 +00:00
|
|
|
|
|
|
|
/* The naive g_strsplit above will split 'resource:///foo/bar' into 'resource',
|
|
|
|
* '///foo/bar'. Combine these back together by looking for a literal 'resource'
|
|
|
|
* in the array. */
|
|
|
|
for (i = 0, j = 0; search_path[i];)
|
|
|
|
{
|
|
|
|
char *out;
|
|
|
|
|
|
|
|
if (strcmp (search_path[i], "resource") == 0 && search_path[i + 1] != NULL)
|
|
|
|
{
|
|
|
|
out = g_strconcat (search_path[i], ":", search_path[i + 1], NULL);
|
|
|
|
g_free (search_path[i]);
|
|
|
|
g_free (search_path[i + 1]);
|
|
|
|
i += 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
out = search_path[i];
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
search_path[j++] = out;
|
|
|
|
}
|
|
|
|
|
|
|
|
search_path[j] = NULL; /* NULL-terminate the now possibly shorter array */
|
2013-07-02 06:54:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
search_path = g_malloc0 (2 * sizeof (char *));
|
|
|
|
search_path[0] = g_strdup ("resource:///org/gnome/shell");
|
|
|
|
}
|
|
|
|
|
2011-02-25 16:20:27 +00:00
|
|
|
global->js_context = g_object_new (GJS_TYPE_CONTEXT,
|
|
|
|
"search-path", search_path,
|
|
|
|
NULL);
|
2011-09-15 23:36:07 +00:00
|
|
|
|
2011-02-25 16:20:27 +00:00
|
|
|
g_strfreev (search_path);
|
2008-10-31 04:22:44 +00:00
|
|
|
}
|
|
|
|
|
2011-05-05 22:09:59 +00:00
|
|
|
static void
|
|
|
|
shell_global_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = SHELL_GLOBAL (object);
|
|
|
|
|
2016-12-06 01:31:08 +00:00
|
|
|
g_clear_object (&global->js_context);
|
2011-05-05 22:09:59 +00:00
|
|
|
g_object_unref (global->settings);
|
|
|
|
|
|
|
|
the_object = NULL;
|
|
|
|
|
2013-10-13 22:46:50 +00:00
|
|
|
g_clear_object (&global->userdatadir_path);
|
2013-01-17 19:39:54 +00:00
|
|
|
g_clear_object (&global->runtime_state_path);
|
|
|
|
|
2011-05-05 22:09:59 +00:00
|
|
|
G_OBJECT_CLASS(shell_global_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2008-10-31 04:22:44 +00:00
|
|
|
static void
|
2008-10-31 15:20:54 +00:00
|
|
|
shell_global_class_init (ShellGlobalClass *klass)
|
2008-10-31 04:22:44 +00:00
|
|
|
{
|
2008-10-31 15:20:54 +00:00
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
gobject_class->get_property = shell_global_get_property;
|
|
|
|
gobject_class->set_property = shell_global_set_property;
|
2011-05-05 22:09:59 +00:00
|
|
|
gobject_class->finalize = shell_global_finalize;
|
2008-10-31 15:20:54 +00:00
|
|
|
|
2011-03-15 22:31:16 +00:00
|
|
|
shell_global_signals[NOTIFY_ERROR] =
|
|
|
|
g_signal_new ("notify-error",
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0,
|
2011-10-18 22:19:32 +00:00
|
|
|
NULL, NULL, NULL,
|
2011-03-15 22:31:16 +00:00
|
|
|
G_TYPE_NONE, 2,
|
|
|
|
G_TYPE_STRING,
|
|
|
|
G_TYPE_STRING);
|
|
|
|
|
2012-05-16 21:11:57 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_SESSION_MODE,
|
|
|
|
g_param_spec_string ("session-mode",
|
|
|
|
"Session Mode",
|
|
|
|
"The session mode to use",
|
|
|
|
"user",
|
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
2008-11-19 22:54:42 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_SCREEN,
|
|
|
|
g_param_spec_object ("screen",
|
|
|
|
"Screen",
|
|
|
|
"Metacity screen object for the shell",
|
|
|
|
META_TYPE_SCREEN,
|
|
|
|
G_PARAM_READABLE));
|
2010-06-03 15:14:58 +00:00
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_GDK_SCREEN,
|
|
|
|
g_param_spec_object ("gdk-screen",
|
|
|
|
"GdkScreen",
|
|
|
|
"Gdk screen object for the shell",
|
|
|
|
GDK_TYPE_SCREEN,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
2008-10-31 15:20:54 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_SCREEN_WIDTH,
|
|
|
|
g_param_spec_int ("screen-width",
|
|
|
|
"Screen Width",
|
|
|
|
"Screen width, in pixels",
|
|
|
|
0, G_MAXINT, 1,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_SCREEN_HEIGHT,
|
|
|
|
g_param_spec_int ("screen-height",
|
|
|
|
"Screen Height",
|
|
|
|
"Screen height, in pixels",
|
|
|
|
0, G_MAXINT, 1,
|
|
|
|
G_PARAM_READABLE));
|
2011-07-13 16:34:31 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_DISPLAY,
|
|
|
|
g_param_spec_object ("display",
|
|
|
|
"Display",
|
|
|
|
"Metacity display object for the shell",
|
|
|
|
META_TYPE_DISPLAY,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
2008-10-31 18:04:38 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_STAGE,
|
|
|
|
g_param_spec_object ("stage",
|
|
|
|
"Stage",
|
|
|
|
"Stage holding the desktop scene graph",
|
|
|
|
CLUTTER_TYPE_ACTOR,
|
|
|
|
G_PARAM_READABLE));
|
2008-11-08 19:33:33 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_WINDOW_GROUP,
|
|
|
|
g_param_spec_object ("window-group",
|
|
|
|
"Window Group",
|
|
|
|
"Actor holding window actors",
|
|
|
|
CLUTTER_TYPE_ACTOR,
|
|
|
|
G_PARAM_READABLE));
|
2013-02-12 21:00:41 +00:00
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_TOP_WINDOW_GROUP,
|
|
|
|
g_param_spec_object ("top-window-group",
|
|
|
|
"Top Window Group",
|
|
|
|
"Actor holding override-redirect windows",
|
|
|
|
CLUTTER_TYPE_ACTOR,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
2008-11-21 14:02:09 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_WINDOW_MANAGER,
|
|
|
|
g_param_spec_object ("window-manager",
|
|
|
|
"Window Manager",
|
|
|
|
"Window management interface",
|
|
|
|
SHELL_TYPE_WM,
|
|
|
|
G_PARAM_READABLE));
|
2010-05-05 21:05:42 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_SETTINGS,
|
|
|
|
g_param_spec_object ("settings",
|
|
|
|
"Settings",
|
|
|
|
"GSettings instance for gnome-shell configuration",
|
|
|
|
G_TYPE_SETTINGS,
|
|
|
|
G_PARAM_READABLE));
|
2009-09-10 05:36:05 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_DATADIR,
|
|
|
|
g_param_spec_string ("datadir",
|
|
|
|
"Data directory",
|
|
|
|
"Directory containing gnome-shell data files",
|
|
|
|
NULL,
|
|
|
|
G_PARAM_READABLE));
|
2008-12-22 22:05:56 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_IMAGEDIR,
|
|
|
|
g_param_spec_string ("imagedir",
|
|
|
|
"Image directory",
|
|
|
|
"Directory containing gnome-shell image files",
|
|
|
|
NULL,
|
|
|
|
G_PARAM_READABLE));
|
2009-04-23 21:10:37 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
2010-05-04 11:24:06 +00:00
|
|
|
PROP_USERDATADIR,
|
|
|
|
g_param_spec_string ("userdatadir",
|
|
|
|
"User data directory",
|
|
|
|
"Directory containing gnome-shell user data",
|
2009-04-23 21:10:37 +00:00
|
|
|
NULL,
|
|
|
|
G_PARAM_READABLE));
|
2010-10-05 14:09:40 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_FOCUS_MANAGER,
|
|
|
|
g_param_spec_object ("focus-manager",
|
|
|
|
"Focus manager",
|
|
|
|
"The shell's StFocusManager",
|
|
|
|
ST_TYPE_FOCUS_MANAGER,
|
|
|
|
G_PARAM_READABLE));
|
2014-06-27 00:34:15 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_FRAME_TIMESTAMPS,
|
|
|
|
g_param_spec_boolean ("frame-timestamps",
|
|
|
|
"Frame Timestamps",
|
|
|
|
"Whether to log frame timestamps in the performance log",
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READWRITE));
|
2014-07-16 14:05:02 +00:00
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_FRAME_FINISH_TIMESTAMP,
|
|
|
|
g_param_spec_boolean ("frame-finish-timestamp",
|
|
|
|
"Frame Finish Timestamps",
|
|
|
|
"Whether at the end of a frame to call glFinish and log paintCompletedTimestamp",
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READWRITE));
|
2008-10-31 04:22:44 +00:00
|
|
|
}
|
|
|
|
|
2013-10-09 17:30:29 +00:00
|
|
|
/*
|
|
|
|
* _shell_global_init: (skip)
|
2011-08-28 05:32:12 +00:00
|
|
|
* @first_property_name: the name of the first property
|
|
|
|
* @...: the value of the first property, followed optionally by more
|
|
|
|
* name/value pairs, followed by %NULL
|
2013-10-09 17:30:29 +00:00
|
|
|
*
|
2011-08-28 05:32:12 +00:00
|
|
|
* Initializes the shell global singleton with the construction-time
|
|
|
|
* properties.
|
|
|
|
*
|
|
|
|
* There are currently no such properties, so @first_property_name should
|
|
|
|
* always be %NULL.
|
|
|
|
*
|
|
|
|
* This call must be called before shell_global_get() and shouldn't be called
|
|
|
|
* more than once.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_shell_global_init (const char *first_property_name,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
va_list argument_list;
|
|
|
|
|
|
|
|
g_return_if_fail (the_object == NULL);
|
|
|
|
|
|
|
|
va_start (argument_list, first_property_name);
|
|
|
|
the_object = SHELL_GLOBAL (g_object_new_valist (SHELL_TYPE_GLOBAL,
|
|
|
|
first_property_name,
|
|
|
|
argument_list));
|
|
|
|
va_end (argument_list);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2008-10-31 18:24:29 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get:
|
|
|
|
*
|
|
|
|
* Gets the singleton global object that represents the desktop.
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): the singleton global object
|
|
|
|
*/
|
2008-10-31 04:22:44 +00:00
|
|
|
ShellGlobal *
|
|
|
|
shell_global_get (void)
|
|
|
|
{
|
|
|
|
return the_object;
|
|
|
|
}
|
|
|
|
|
2016-12-06 01:31:08 +00:00
|
|
|
/**
|
|
|
|
* _shell_global_destroy_gjs_context: (skip)
|
|
|
|
* @self: global object
|
|
|
|
*
|
|
|
|
* Destroys the GjsContext held by ShellGlobal, in order to break reference
|
|
|
|
* counting cycles. (The GjsContext holds a reference to ShellGlobal because
|
|
|
|
* it's available as window.global inside JS.)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_shell_global_destroy_gjs_context (ShellGlobal *self)
|
|
|
|
{
|
|
|
|
g_clear_object (&self->js_context);
|
|
|
|
}
|
|
|
|
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
static guint32
|
|
|
|
get_current_time_maybe_roundtrip (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
guint32 time;
|
|
|
|
|
|
|
|
time = shell_global_get_current_time (global);
|
|
|
|
if (time != CurrentTime)
|
|
|
|
return time;
|
|
|
|
|
|
|
|
return meta_display_get_current_time_roundtrip (global->meta_display);
|
|
|
|
}
|
|
|
|
|
2010-07-02 17:36:13 +00:00
|
|
|
static void
|
|
|
|
focus_window_changed (MetaDisplay *display,
|
|
|
|
GParamSpec *param,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = user_data;
|
|
|
|
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
if (global->has_modal)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* If the stage window became unfocused, drop the key focus
|
|
|
|
* on Clutter's side. */
|
|
|
|
if (!meta_stage_is_focused (global->meta_screen))
|
|
|
|
clutter_stage_set_key_focus (global->stage, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ClutterActor *
|
|
|
|
get_key_focused_actor (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
ClutterActor *actor;
|
|
|
|
|
|
|
|
actor = clutter_stage_get_key_focus (global->stage);
|
|
|
|
|
|
|
|
/* If there's no explicit key focus, clutter_stage_get_key_focus()
|
|
|
|
* returns the stage. This is a terrible API. */
|
|
|
|
if (actor == CLUTTER_ACTOR (global->stage))
|
|
|
|
actor = NULL;
|
|
|
|
|
|
|
|
return actor;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sync_stage_window_focus (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
ClutterActor *actor;
|
|
|
|
|
|
|
|
if (global->has_modal)
|
|
|
|
return;
|
|
|
|
|
|
|
|
actor = get_key_focused_actor (global);
|
|
|
|
|
|
|
|
/* An actor got key focus and the stage needs to be focused. */
|
|
|
|
if (actor != NULL && !meta_stage_is_focused (global->meta_screen))
|
|
|
|
meta_focus_stage_window (global->meta_screen,
|
|
|
|
get_current_time_maybe_roundtrip (global));
|
|
|
|
|
|
|
|
/* An actor dropped key focus. Focus the default window. */
|
|
|
|
else if (actor == NULL && meta_stage_is_focused (global->meta_screen))
|
|
|
|
meta_screen_focus_default_window (global->meta_screen,
|
|
|
|
get_current_time_maybe_roundtrip (global));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
focus_actor_changed (ClutterStage *stage,
|
|
|
|
GParamSpec *param,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = user_data;
|
|
|
|
sync_stage_window_focus (global);
|
2010-07-02 17:36:13 +00:00
|
|
|
}
|
|
|
|
|
2013-05-18 22:51:58 +00:00
|
|
|
static void
|
|
|
|
sync_input_region (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
MetaScreen *screen = global->meta_screen;
|
|
|
|
|
2014-08-13 22:04:48 +00:00
|
|
|
if (global->has_modal)
|
2013-05-18 22:51:58 +00:00
|
|
|
meta_set_stage_input_region (screen, None);
|
|
|
|
else
|
|
|
|
meta_set_stage_input_region (screen, global->input_region);
|
|
|
|
}
|
|
|
|
|
2009-04-29 18:01:09 +00:00
|
|
|
/**
|
|
|
|
* shell_global_set_stage_input_region:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
* @rectangles: (element-type Meta.Rectangle): a list of #MetaRectangle
|
|
|
|
* describing the input region.
|
|
|
|
*
|
|
|
|
* Sets the area of the stage that is responsive to mouse clicks when
|
2013-05-18 23:06:50 +00:00
|
|
|
* we don't have a modal or grab.
|
2009-04-29 18:01:09 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_set_stage_input_region (ShellGlobal *global,
|
|
|
|
GSList *rectangles)
|
|
|
|
{
|
|
|
|
MetaRectangle *rect;
|
|
|
|
XRectangle *rects;
|
|
|
|
int nrects, i;
|
|
|
|
GSList *r;
|
|
|
|
|
|
|
|
g_return_if_fail (SHELL_IS_GLOBAL (global));
|
|
|
|
|
|
|
|
nrects = g_slist_length (rectangles);
|
|
|
|
rects = g_new (XRectangle, nrects);
|
|
|
|
for (r = rectangles, i = 0; r; r = r->next, i++)
|
|
|
|
{
|
|
|
|
rect = (MetaRectangle *)r->data;
|
|
|
|
rects[i].x = rect->x;
|
|
|
|
rects[i].y = rect->y;
|
|
|
|
rects[i].width = rect->width;
|
|
|
|
rects[i].height = rect->height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (global->input_region)
|
2011-07-13 16:22:32 +00:00
|
|
|
XFixesDestroyRegion (global->xdisplay, global->input_region);
|
2009-04-29 18:01:09 +00:00
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
|
2009-04-29 18:01:09 +00:00
|
|
|
g_free (rects);
|
|
|
|
|
2013-05-18 22:51:58 +00:00
|
|
|
sync_input_region (global);
|
2008-10-31 04:22:44 +00:00
|
|
|
}
|
|
|
|
|
2012-02-14 19:33:56 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_stage:
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): The default #ClutterStage
|
|
|
|
*/
|
|
|
|
ClutterStage *
|
|
|
|
shell_global_get_stage (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
return global->stage;
|
|
|
|
}
|
|
|
|
|
2009-06-25 21:42:46 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_screen:
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): The default #MetaScreen
|
|
|
|
*/
|
|
|
|
MetaScreen *
|
|
|
|
shell_global_get_screen (ShellGlobal *global)
|
|
|
|
{
|
2011-07-13 16:22:32 +00:00
|
|
|
return global->meta_screen;
|
2009-06-25 21:42:46 +00:00
|
|
|
}
|
|
|
|
|
2010-06-03 15:14:58 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_gdk_screen:
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): Gdk screen object for the shell
|
|
|
|
*/
|
|
|
|
GdkScreen *
|
|
|
|
shell_global_get_gdk_screen (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
|
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
return global->gdk_screen;
|
2010-06-03 15:14:58 +00:00
|
|
|
}
|
|
|
|
|
2011-07-13 16:34:31 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_display:
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): The default #MetaDisplay
|
|
|
|
*/
|
|
|
|
MetaDisplay *
|
|
|
|
shell_global_get_display (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
return global->meta_display;
|
|
|
|
}
|
|
|
|
|
2008-11-02 04:18:41 +00:00
|
|
|
/**
|
2010-10-19 18:55:43 +00:00
|
|
|
* shell_global_get_window_actors:
|
2008-11-02 04:18:41 +00:00
|
|
|
*
|
2010-10-19 18:55:43 +00:00
|
|
|
* Gets the list of #MetaWindowActor for the plugin's screen
|
2008-11-02 04:18:41 +00:00
|
|
|
*
|
2014-09-10 20:41:40 +00:00
|
|
|
* Return value: (element-type Meta.WindowActor) (transfer container): the list of windows
|
2008-11-02 04:18:41 +00:00
|
|
|
*/
|
|
|
|
GList *
|
2010-10-19 18:55:43 +00:00
|
|
|
shell_global_get_window_actors (ShellGlobal *global)
|
2008-11-02 04:18:41 +00:00
|
|
|
{
|
2014-09-10 20:41:40 +00:00
|
|
|
GList *filtered = NULL;
|
|
|
|
GList *l;
|
|
|
|
|
2008-11-07 18:42:23 +00:00
|
|
|
g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
|
2008-11-02 04:18:41 +00:00
|
|
|
|
2014-09-10 20:41:40 +00:00
|
|
|
for (l = meta_get_window_actors (global->meta_screen); l; l = l->next)
|
|
|
|
if (!meta_window_actor_is_destroyed (l->data))
|
|
|
|
filtered = g_list_prepend (filtered, l->data);
|
|
|
|
|
|
|
|
return g_list_reverse (filtered);
|
2008-11-02 04:18:41 +00:00
|
|
|
}
|
|
|
|
|
2010-01-19 17:59:29 +00:00
|
|
|
static void
|
|
|
|
global_stage_notify_width (GObject *gobject,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = SHELL_GLOBAL (data);
|
|
|
|
|
|
|
|
g_object_notify (G_OBJECT (global), "screen-width");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
global_stage_notify_height (GObject *gobject,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = SHELL_GLOBAL (data);
|
|
|
|
|
|
|
|
g_object_notify (G_OBJECT (global), "screen-height");
|
|
|
|
}
|
|
|
|
|
2013-03-02 17:13:26 +00:00
|
|
|
static gboolean
|
2013-03-02 12:25:37 +00:00
|
|
|
global_stage_before_paint (gpointer data)
|
2010-05-09 17:24:13 +00:00
|
|
|
{
|
2014-06-27 00:34:15 +00:00
|
|
|
ShellGlobal *global = SHELL_GLOBAL (data);
|
|
|
|
|
|
|
|
if (global->frame_timestamps)
|
|
|
|
shell_perf_log_event (shell_perf_log_get_default (),
|
|
|
|
"clutter.stagePaintStart");
|
2013-03-02 17:13:26 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2010-05-09 17:24:13 +00:00
|
|
|
}
|
|
|
|
|
2013-03-02 17:13:26 +00:00
|
|
|
static gboolean
|
2014-07-16 14:05:02 +00:00
|
|
|
load_gl_symbol (const char *name,
|
|
|
|
void **func)
|
|
|
|
{
|
|
|
|
*func = cogl_get_proc_address (name);
|
|
|
|
if (!*func)
|
|
|
|
{
|
|
|
|
g_warning ("failed to resolve required GL symbol \"%s\"\n", name);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
global_stage_after_paint (ClutterStage *stage,
|
|
|
|
ShellGlobal *global)
|
2010-05-09 17:24:13 +00:00
|
|
|
{
|
2014-07-16 14:05:02 +00:00
|
|
|
/* At this point, we've finished all layout and painting, but haven't
|
|
|
|
* actually flushed or swapped */
|
|
|
|
|
|
|
|
if (global->frame_timestamps && global->frame_finish_timestamp)
|
|
|
|
{
|
|
|
|
/* It's interesting to find out when the paint actually finishes
|
|
|
|
* on the GPU. We could wait for this asynchronously with
|
|
|
|
* ARB_timer_query (see https://bugzilla.gnome.org/show_bug.cgi?id=732350
|
|
|
|
* for an implementation of this), but what we actually would
|
|
|
|
* find out then is the latency for drawing a frame, not how much
|
|
|
|
* GPU work was needed, since frames can overlap. Calling glFinish()
|
|
|
|
* is a fairly reliable way to separate out adjacent frames
|
|
|
|
* and measure the amount of GPU work. This is turned on with a
|
|
|
|
* separate property from ::frame-timestamps, since it should not
|
|
|
|
* be turned on if we're trying to actual measure latency or frame
|
|
|
|
* rate.
|
|
|
|
*/
|
|
|
|
static void (*finish) (void);
|
|
|
|
|
|
|
|
if (!finish)
|
|
|
|
load_gl_symbol ("glFinish", (void **)&finish);
|
|
|
|
|
2014-07-26 14:45:28 +00:00
|
|
|
cogl_flush ();
|
2014-07-16 14:05:02 +00:00
|
|
|
finish ();
|
|
|
|
|
|
|
|
shell_perf_log_event (shell_perf_log_get_default (),
|
|
|
|
"clutter.paintCompletedTimestamp");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
global_stage_after_swap (gpointer data)
|
|
|
|
{
|
|
|
|
/* Everything is done, we're ready for a new frame */
|
|
|
|
|
2014-06-27 00:34:15 +00:00
|
|
|
ShellGlobal *global = SHELL_GLOBAL (data);
|
|
|
|
|
|
|
|
if (global->frame_timestamps)
|
|
|
|
shell_perf_log_event (shell_perf_log_get_default (),
|
|
|
|
"clutter.stagePaintDone");
|
2013-03-02 17:13:26 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2010-05-09 17:24:13 +00:00
|
|
|
}
|
|
|
|
|
2014-02-18 22:25:10 +00:00
|
|
|
static void
|
2017-04-21 10:13:33 +00:00
|
|
|
update_scaling_factor (ShellGlobal *global,
|
|
|
|
MetaSettings *settings)
|
2014-02-18 22:25:10 +00:00
|
|
|
{
|
|
|
|
ClutterStage *stage = CLUTTER_STAGE (global->stage);
|
|
|
|
StThemeContext *context = st_theme_context_get_for_stage (stage);
|
2017-03-31 09:03:23 +00:00
|
|
|
int scaling_factor;
|
|
|
|
|
2017-04-21 10:13:33 +00:00
|
|
|
scaling_factor = meta_settings_get_ui_scaling_factor (settings);
|
2017-03-31 09:03:23 +00:00
|
|
|
g_object_set (context, "scale-factor", scaling_factor, NULL);
|
2014-02-18 22:25:10 +00:00
|
|
|
}
|
|
|
|
|
2017-03-31 09:03:23 +00:00
|
|
|
static void
|
2017-04-21 10:13:33 +00:00
|
|
|
ui_scaling_factor_changed (MetaSettings *settings,
|
|
|
|
ShellGlobal *global)
|
2017-03-31 09:03:23 +00:00
|
|
|
{
|
2017-04-21 10:13:33 +00:00
|
|
|
update_scaling_factor (global, settings);
|
2017-03-31 09:03:23 +00:00
|
|
|
}
|
|
|
|
|
2011-11-21 16:56:24 +00:00
|
|
|
/* This is an IBus workaround. The flow of events with IBus is that every time
|
|
|
|
* it gets gets a key event, it:
|
|
|
|
*
|
|
|
|
* Sends it to the daemon via D-Bus asynchronously
|
|
|
|
* When it gets an reply, synthesizes a new GdkEvent and puts it into the
|
|
|
|
* GDK event queue with gdk_event_put(), including
|
|
|
|
* IBUS_FORWARD_MASK = 1 << 25 in the state to prevent a loop.
|
|
|
|
*
|
|
|
|
* (Normally, IBus uses the GTK+ key snooper mechanism to get the key
|
|
|
|
* events early, but since our key events aren't visible to GTK+ key snoopers,
|
|
|
|
* IBus will instead get the events via the standard
|
|
|
|
* GtkIMContext.filter_keypress() mechanism.)
|
|
|
|
*
|
|
|
|
* There are a number of potential problems here; probably the worst
|
|
|
|
* problem is that IBus doesn't forward the timestamp with the event
|
|
|
|
* so that every key event that gets delivered ends up with
|
|
|
|
* GDK_CURRENT_TIME. This creates some very subtle bugs; for example
|
|
|
|
* if you have IBus running and a keystroke is used to trigger
|
|
|
|
* launching an application, focus stealing prevention won't work
|
|
|
|
* right. http://code.google.com/p/ibus/issues/detail?id=1184
|
|
|
|
*
|
|
|
|
* In any case, our normal flow of key events is:
|
|
|
|
*
|
|
|
|
* GDK filter function => clutter_x11_handle_event => clutter actor
|
|
|
|
*
|
|
|
|
* So, if we see a key event that gets delivered via the GDK event handler
|
|
|
|
* function - then we know it must be one of these synthesized events, and
|
|
|
|
* we should push it back to clutter.
|
|
|
|
*
|
|
|
|
* To summarize, the full key event flow with IBus is:
|
|
|
|
*
|
|
|
|
* GDK filter function
|
|
|
|
* => Mutter
|
|
|
|
* => gnome_shell_plugin_xevent_filter()
|
|
|
|
* => clutter_x11_handle_event()
|
|
|
|
* => clutter event delivery to actor
|
|
|
|
* => gtk_im_context_filter_event()
|
|
|
|
* => sent to IBus daemon
|
|
|
|
* => response received from IBus daemon
|
|
|
|
* => gdk_event_put()
|
|
|
|
* => GDK event handler
|
|
|
|
* => <this function>
|
|
|
|
* => clutter_event_put()
|
|
|
|
* => clutter event delivery to actor
|
|
|
|
*
|
|
|
|
* Anything else we see here we just pass on to the normal GDK event handler
|
|
|
|
* gtk_main_do_event().
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
gnome_shell_gdk_event_handler (GdkEvent *event_gdk,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
if (event_gdk->type == GDK_KEY_PRESS || event_gdk->type == GDK_KEY_RELEASE)
|
|
|
|
{
|
2013-09-11 15:44:24 +00:00
|
|
|
ShellGlobal *global = data;
|
2011-11-21 16:56:24 +00:00
|
|
|
|
2013-09-11 15:44:24 +00:00
|
|
|
if (event_gdk->key.window == global->ibus_window)
|
2011-11-21 16:56:24 +00:00
|
|
|
{
|
|
|
|
ClutterDeviceManager *device_manager = clutter_device_manager_get_default ();
|
2014-02-27 19:01:30 +00:00
|
|
|
ClutterInputDevice *keyboard = clutter_device_manager_get_device (device_manager,
|
|
|
|
META_VIRTUAL_CORE_KEYBOARD_ID);
|
2011-11-21 16:56:24 +00:00
|
|
|
|
|
|
|
ClutterEvent *event_clutter = clutter_event_new ((event_gdk->type == GDK_KEY_PRESS) ?
|
|
|
|
CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE);
|
|
|
|
event_clutter->key.time = event_gdk->key.time;
|
|
|
|
event_clutter->key.flags = CLUTTER_EVENT_NONE;
|
2013-09-11 15:44:24 +00:00
|
|
|
event_clutter->key.stage = CLUTTER_STAGE (global->stage);
|
2011-11-21 16:56:24 +00:00
|
|
|
event_clutter->key.source = NULL;
|
|
|
|
|
|
|
|
/* This depends on ClutterModifierType and GdkModifierType being
|
|
|
|
* identical, which they are currently. (They both match the X
|
|
|
|
* modifier state in the low 16-bits and have the same extensions.) */
|
|
|
|
event_clutter->key.modifier_state = event_gdk->key.state;
|
|
|
|
|
|
|
|
event_clutter->key.keyval = event_gdk->key.keyval;
|
|
|
|
event_clutter->key.hardware_keycode = event_gdk->key.hardware_keycode;
|
|
|
|
event_clutter->key.unicode_value = gdk_keyval_to_unicode (event_clutter->key.keyval);
|
|
|
|
event_clutter->key.device = keyboard;
|
|
|
|
|
|
|
|
clutter_event_put (event_clutter);
|
|
|
|
clutter_event_free (event_clutter);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_main_do_event (event_gdk);
|
|
|
|
}
|
|
|
|
|
2013-09-11 16:11:55 +00:00
|
|
|
static void
|
|
|
|
entry_cursor_func (StEntry *entry,
|
|
|
|
gboolean use_ibeam,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = user_data;
|
|
|
|
|
|
|
|
meta_screen_set_cursor (global->meta_screen, use_ibeam ? META_CURSOR_IBEAM : META_CURSOR_DEFAULT);
|
|
|
|
}
|
|
|
|
|
2008-10-31 04:22:44 +00:00
|
|
|
void
|
2010-10-19 18:55:43 +00:00
|
|
|
_shell_global_set_plugin (ShellGlobal *global,
|
|
|
|
MetaPlugin *plugin)
|
2008-10-31 04:22:44 +00:00
|
|
|
{
|
2017-03-31 09:03:23 +00:00
|
|
|
MetaBackend *backend;
|
2017-04-21 10:13:33 +00:00
|
|
|
MetaSettings *settings;
|
2017-03-31 09:03:23 +00:00
|
|
|
|
2008-10-31 15:20:54 +00:00
|
|
|
g_return_if_fail (SHELL_IS_GLOBAL (global));
|
2008-11-21 14:02:09 +00:00
|
|
|
g_return_if_fail (global->plugin == NULL);
|
2008-10-31 15:20:54 +00:00
|
|
|
|
|
|
|
global->plugin = plugin;
|
2008-11-21 14:02:09 +00:00
|
|
|
global->wm = shell_wm_new (plugin);
|
2010-01-19 17:59:29 +00:00
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
global->meta_screen = meta_plugin_get_screen (plugin);
|
|
|
|
global->meta_display = meta_screen_get_display (global->meta_screen);
|
|
|
|
global->xdisplay = meta_display_get_xdisplay (global->meta_display);
|
2010-05-11 14:35:27 +00:00
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
global->gdk_display = gdk_x11_lookup_xdisplay (global->xdisplay);
|
|
|
|
global->gdk_screen = gdk_display_get_screen (global->gdk_display,
|
|
|
|
meta_screen_get_screen_number (global->meta_screen));
|
|
|
|
|
2012-03-01 03:38:35 +00:00
|
|
|
global->stage = CLUTTER_STAGE (meta_get_stage_for_screen (global->meta_screen));
|
2013-09-11 15:53:58 +00:00
|
|
|
|
|
|
|
if (meta_is_wayland_compositor ())
|
|
|
|
{
|
|
|
|
/* When Mutter is acting as its own display server then the
|
|
|
|
stage does not have a window, so create a different window
|
|
|
|
which we use to communicate with IBus, and leave stage_xwindow
|
|
|
|
as None.
|
|
|
|
*/
|
|
|
|
|
|
|
|
GdkWindowAttr attributes;
|
|
|
|
|
|
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
|
|
attributes.width = 100;
|
|
|
|
attributes.height = 100;
|
|
|
|
attributes.window_type = GDK_WINDOW_TOPLEVEL;
|
|
|
|
|
|
|
|
global->ibus_window = gdk_window_new (NULL,
|
|
|
|
&attributes,
|
|
|
|
0 /* attributes_mask */);
|
|
|
|
global->stage_xwindow = None;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
global->stage_xwindow = clutter_x11_get_stage_window (global->stage);
|
|
|
|
global->ibus_window = gdk_x11_window_foreign_new_for_display (global->gdk_display,
|
|
|
|
global->stage_xwindow);
|
|
|
|
}
|
|
|
|
|
2013-09-11 15:44:24 +00:00
|
|
|
st_im_text_set_event_window (global->ibus_window);
|
2013-09-11 16:11:55 +00:00
|
|
|
st_entry_set_cursor_func (entry_cursor_func, global);
|
2011-07-13 16:22:32 +00:00
|
|
|
|
|
|
|
g_signal_connect (global->stage, "notify::width",
|
2010-05-11 14:35:27 +00:00
|
|
|
G_CALLBACK (global_stage_notify_width), global);
|
2011-07-13 16:22:32 +00:00
|
|
|
g_signal_connect (global->stage, "notify::height",
|
2010-05-11 14:35:27 +00:00
|
|
|
G_CALLBACK (global_stage_notify_height), global);
|
2010-05-09 17:24:13 +00:00
|
|
|
|
2013-03-02 12:25:37 +00:00
|
|
|
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
|
2013-03-02 17:13:26 +00:00
|
|
|
global_stage_before_paint,
|
2014-06-27 00:34:15 +00:00
|
|
|
global, NULL);
|
2013-03-02 12:25:37 +00:00
|
|
|
|
2014-07-16 14:05:02 +00:00
|
|
|
g_signal_connect (global->stage, "after-paint",
|
|
|
|
G_CALLBACK (global_stage_after_paint), global);
|
|
|
|
|
2013-03-02 12:25:37 +00:00
|
|
|
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
|
2014-07-16 14:05:02 +00:00
|
|
|
global_stage_after_swap,
|
2014-06-27 00:34:15 +00:00
|
|
|
global, NULL);
|
2010-05-09 17:24:13 +00:00
|
|
|
|
|
|
|
shell_perf_log_define_event (shell_perf_log_get_default(),
|
|
|
|
"clutter.stagePaintStart",
|
|
|
|
"Start of stage page repaint",
|
|
|
|
"");
|
2014-07-16 14:05:02 +00:00
|
|
|
shell_perf_log_define_event (shell_perf_log_get_default(),
|
|
|
|
"clutter.paintCompletedTimestamp",
|
|
|
|
"Paint completion on GPU",
|
|
|
|
"");
|
2010-05-09 17:24:13 +00:00
|
|
|
shell_perf_log_define_event (shell_perf_log_get_default(),
|
|
|
|
"clutter.stagePaintDone",
|
2014-07-16 14:05:02 +00:00
|
|
|
"End of frame, possibly including swap time",
|
2010-05-09 17:24:13 +00:00
|
|
|
"");
|
2010-07-02 17:36:13 +00:00
|
|
|
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
g_signal_connect (global->stage, "notify::key-focus",
|
|
|
|
G_CALLBACK (focus_actor_changed), global);
|
2011-07-13 16:22:32 +00:00
|
|
|
g_signal_connect (global->meta_display, "notify::focus-window",
|
2010-07-02 17:36:13 +00:00
|
|
|
G_CALLBACK (focus_window_changed), global);
|
2010-10-05 14:09:40 +00:00
|
|
|
|
2017-03-31 09:03:23 +00:00
|
|
|
backend = meta_get_backend ();
|
2017-04-21 10:13:33 +00:00
|
|
|
settings = meta_backend_get_settings (backend);
|
|
|
|
g_signal_connect (settings, "ui-scaling-factor-changed",
|
2017-03-31 09:03:23 +00:00
|
|
|
G_CALLBACK (ui_scaling_factor_changed), global);
|
2014-02-18 22:25:10 +00:00
|
|
|
|
2013-09-11 15:44:24 +00:00
|
|
|
gdk_event_handler_set (gnome_shell_gdk_event_handler, global, NULL);
|
2011-11-21 16:56:24 +00:00
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
global->focus_manager = st_focus_manager_get_for_stage (global->stage);
|
2014-02-18 22:25:10 +00:00
|
|
|
|
2017-04-21 10:13:33 +00:00
|
|
|
update_scaling_factor (global, settings);
|
2008-10-31 04:22:44 +00:00
|
|
|
}
|
2008-11-19 19:54:17 +00:00
|
|
|
|
2011-02-25 16:20:27 +00:00
|
|
|
GjsContext *
|
|
|
|
_shell_global_get_gjs_context (ShellGlobal *global)
|
2009-10-24 17:40:13 +00:00
|
|
|
{
|
2011-02-25 16:20:27 +00:00
|
|
|
return global->js_context;
|
2009-10-24 17:40:13 +00:00
|
|
|
}
|
|
|
|
|
2008-11-19 19:54:17 +00:00
|
|
|
/**
|
2009-08-12 04:22:46 +00:00
|
|
|
* shell_global_begin_modal:
|
2008-11-24 19:07:18 +00:00
|
|
|
* @global: a #ShellGlobal
|
2008-11-19 19:54:17 +00:00
|
|
|
*
|
2009-08-12 04:22:46 +00:00
|
|
|
* Grabs the keyboard and mouse to the stage window. The stage will
|
|
|
|
* receive all keyboard and mouse events until shell_global_end_modal()
|
|
|
|
* is called. This is used to implement "modes" for the shell, such as the
|
|
|
|
* overview mode or the "looking glass" debug overlay, that block
|
|
|
|
* application and normal key shortcuts.
|
|
|
|
*
|
2011-11-02 16:24:49 +00:00
|
|
|
* Returns: %TRUE if we succesfully entered the mode. %FALSE if we couldn't
|
2009-08-12 04:22:46 +00:00
|
|
|
* enter the mode. Failure may occur because an application has the pointer
|
|
|
|
* or keyboard grabbed, because Mutter is in a mode itself like moving a
|
|
|
|
* window or alt-Tab window selection, or because shell_global_begin_modal()
|
|
|
|
* was previouly called.
|
2008-11-19 19:54:17 +00:00
|
|
|
*/
|
2008-11-24 19:07:18 +00:00
|
|
|
gboolean
|
2011-10-21 07:12:17 +00:00
|
|
|
shell_global_begin_modal (ShellGlobal *global,
|
|
|
|
guint32 timestamp,
|
|
|
|
MetaModalOptions options)
|
2008-11-19 19:54:17 +00:00
|
|
|
{
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
/* Make it an error to call begin_modal while we already
|
|
|
|
* have a modal active. */
|
|
|
|
if (global->has_modal)
|
|
|
|
return FALSE;
|
|
|
|
|
2013-08-13 14:43:23 +00:00
|
|
|
global->has_modal = meta_plugin_begin_modal (global->plugin, options, timestamp);
|
2013-05-18 22:54:44 +00:00
|
|
|
sync_input_region (global);
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
return global->has_modal;
|
2008-11-24 19:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-08-12 04:22:46 +00:00
|
|
|
* shell_global_end_modal:
|
2008-11-24 19:07:18 +00:00
|
|
|
* @global: a #ShellGlobal
|
|
|
|
*
|
2009-08-12 04:22:46 +00:00
|
|
|
* Undoes the effect of shell_global_begin_modal().
|
2008-11-24 19:07:18 +00:00
|
|
|
*/
|
|
|
|
void
|
2009-08-12 04:22:46 +00:00
|
|
|
shell_global_end_modal (ShellGlobal *global,
|
|
|
|
guint32 timestamp)
|
2008-11-24 19:07:18 +00:00
|
|
|
{
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
if (!global->has_modal)
|
|
|
|
return;
|
|
|
|
|
2010-10-19 18:55:43 +00:00
|
|
|
meta_plugin_end_modal (global->plugin, timestamp);
|
Rework window / actor focus handling
The duality of the Clutter's key focus and mutter's window focus has long been
a problem for us in lots of case, and caused us to create large and complicated
hacks to get around the issue, including GrabHelper's focus grab model.
Instead of doing this, tie basic focus management into the core of gnome-shell,
instead of requiring complex "application-level" management to get it done
right.
Do this by making sure that only one of an actor or window can be focused at
the same time, and apply the appropriate logic to drop one or the other,
reactively.
Modals are considered a special case, as we grab all keyboard events, but at
the X level, the client window still has focus. Make sure to not do any input
synchronization when we have a modal.
At the same time, remove the FOCUSED input mode, as it's no longer necessary.
https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-18 04:18:13 +00:00
|
|
|
global->has_modal = FALSE;
|
|
|
|
|
|
|
|
/* If the stage window is unfocused, ensure that there's no
|
|
|
|
* actor focused on Clutter's side. */
|
|
|
|
if (!meta_stage_is_focused (global->meta_screen))
|
|
|
|
clutter_stage_set_key_focus (global->stage, NULL);
|
|
|
|
|
|
|
|
/* An actor dropped key focus. Focus the default window. */
|
|
|
|
else if (get_key_focused_actor (global) && meta_stage_is_focused (global->meta_screen))
|
|
|
|
meta_screen_focus_default_window (global->meta_screen,
|
|
|
|
get_current_time_maybe_roundtrip (global));
|
2013-05-18 22:54:44 +00:00
|
|
|
|
|
|
|
sync_input_region (global);
|
2008-11-19 19:54:17 +00:00
|
|
|
}
|
2008-12-18 20:57:37 +00:00
|
|
|
|
2009-04-21 21:21:06 +00:00
|
|
|
/* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
|
|
|
|
*
|
|
|
|
* Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering
|
|
|
|
*
|
|
|
|
* http://bugzilla.gnome.org/show_bug.cgi?id=469231
|
|
|
|
* http://bugzilla.gnome.org/show_bug.cgi?id=357585
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_cloexec (void *data, gint fd)
|
|
|
|
{
|
|
|
|
if (fd >= GPOINTER_TO_INT (data))
|
|
|
|
fcntl (fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef HAVE_FDWALK
|
|
|
|
static int
|
|
|
|
fdwalk (int (*cb)(void *data, int fd), void *data)
|
|
|
|
{
|
|
|
|
gint open_max;
|
|
|
|
gint fd;
|
|
|
|
gint res = 0;
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
|
|
struct rlimit rl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
DIR *d;
|
|
|
|
|
|
|
|
if ((d = opendir("/proc/self/fd"))) {
|
|
|
|
struct dirent *de;
|
|
|
|
|
|
|
|
while ((de = readdir(d))) {
|
|
|
|
glong l;
|
|
|
|
gchar *e = NULL;
|
|
|
|
|
|
|
|
if (de->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
l = strtol(de->d_name, &e, 10);
|
|
|
|
if (errno != 0 || !e || *e)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fd = (gint) l;
|
|
|
|
|
|
|
|
if ((glong) fd != l)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (fd == dirfd(d))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((res = cb (data, fd)) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(d);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If /proc is not mounted or not accessible we fall back to the old
|
|
|
|
* rlimit trick */
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
|
|
|
|
open_max = rl.rlim_max;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
open_max = sysconf (_SC_OPEN_MAX);
|
|
|
|
|
|
|
|
for (fd = 0; fd < open_max; fd++)
|
|
|
|
if ((res = cb (data, fd)) != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
pre_exec_close_fds(void)
|
|
|
|
{
|
|
|
|
fdwalk (set_cloexec, GINT_TO_POINTER(3));
|
|
|
|
}
|
|
|
|
|
2008-12-18 20:57:37 +00:00
|
|
|
/**
|
|
|
|
* shell_global_reexec_self:
|
|
|
|
* @global: A #ShellGlobal
|
|
|
|
*
|
|
|
|
* Restart the current process. Only intended for development purposes.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_reexec_self (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
GPtrArray *arr;
|
|
|
|
gsize len;
|
2014-05-30 09:26:39 +00:00
|
|
|
|
2016-09-24 18:06:00 +00:00
|
|
|
#if defined __linux__ || defined __sun
|
2008-12-18 20:57:37 +00:00
|
|
|
char *buf;
|
|
|
|
char *buf_p;
|
|
|
|
char *buf_end;
|
|
|
|
GError *error = NULL;
|
2013-10-31 10:46:59 +00:00
|
|
|
|
2008-12-18 20:57:37 +00:00
|
|
|
if (!g_file_get_contents ("/proc/self/cmdline", &buf, &len, &error))
|
|
|
|
{
|
|
|
|
g_warning ("failed to get /proc/self/cmdline: %s", error->message);
|
|
|
|
return;
|
|
|
|
}
|
2014-05-30 09:26:39 +00:00
|
|
|
|
2008-12-18 20:57:37 +00:00
|
|
|
buf_end = buf+len;
|
|
|
|
arr = g_ptr_array_new ();
|
|
|
|
/* The cmdline file is NUL-separated */
|
|
|
|
for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
|
|
|
|
g_ptr_array_add (arr, buf_p);
|
2013-10-31 10:46:59 +00:00
|
|
|
|
2009-04-21 21:21:06 +00:00
|
|
|
g_ptr_array_add (arr, NULL);
|
2014-05-30 09:26:39 +00:00
|
|
|
#elif defined __OpenBSD__
|
|
|
|
gchar **args, **args_p;
|
|
|
|
gint mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
|
|
|
|
|
|
|
|
if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
args = g_malloc0 (len);
|
|
|
|
|
|
|
|
if (sysctl (mib, G_N_ELEMENTS (mib), args, &len, NULL, 0) == -1) {
|
|
|
|
g_warning ("failed to get command line args: %d", errno);
|
|
|
|
g_free (args);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
arr = g_ptr_array_new ();
|
|
|
|
for (args_p = args; *args_p != NULL; args_p++) {
|
|
|
|
g_ptr_array_add (arr, *args_p);
|
|
|
|
}
|
|
|
|
|
2015-04-13 04:07:35 +00:00
|
|
|
g_ptr_array_add (arr, NULL);
|
|
|
|
#elif defined __FreeBSD__
|
|
|
|
char *buf;
|
|
|
|
char *buf_p;
|
|
|
|
char *buf_end;
|
|
|
|
gint mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid() };
|
|
|
|
|
|
|
|
if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
buf = g_malloc0 (len);
|
|
|
|
|
|
|
|
if (sysctl (mib, G_N_ELEMENTS (mib), buf, &len, NULL, 0) == -1) {
|
|
|
|
g_warning ("failed to get command line args: %d", errno);
|
|
|
|
g_free (buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf_end = buf+len;
|
|
|
|
arr = g_ptr_array_new ();
|
|
|
|
/* The value returned by sysctl is NUL-separated */
|
|
|
|
for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1)
|
|
|
|
g_ptr_array_add (arr, buf_p);
|
|
|
|
|
2014-05-30 09:26:39 +00:00
|
|
|
g_ptr_array_add (arr, NULL);
|
|
|
|
#else
|
|
|
|
return;
|
|
|
|
#endif
|
2009-04-21 21:21:06 +00:00
|
|
|
|
|
|
|
/* Close all file descriptors other than stdin/stdout/stderr, otherwise
|
|
|
|
* they will leak and stay open after the exec. In particular, this is
|
|
|
|
* important for file descriptors that represent mapped graphics buffer
|
|
|
|
* objects.
|
|
|
|
*/
|
|
|
|
pre_exec_close_fds ();
|
|
|
|
|
2011-10-17 19:19:46 +00:00
|
|
|
meta_display_unmanage_screen (shell_global_get_display (global),
|
|
|
|
shell_global_get_screen (global),
|
|
|
|
shell_global_get_current_time (global));
|
|
|
|
|
2008-12-18 20:57:37 +00:00
|
|
|
execvp (arr->pdata[0], (char**)arr->pdata);
|
|
|
|
g_warning ("failed to reexec: %s", g_strerror (errno));
|
|
|
|
g_ptr_array_free (arr, TRUE);
|
2015-04-13 04:07:35 +00:00
|
|
|
#if defined __linux__ || defined __FreeBSD__
|
2014-05-30 09:26:39 +00:00
|
|
|
g_free (buf);
|
|
|
|
#elif defined __OpenBSD__
|
|
|
|
g_free (args);
|
|
|
|
#endif
|
2009-01-09 01:09:35 +00:00
|
|
|
}
|
2009-01-19 23:06:59 +00:00
|
|
|
|
2015-02-13 09:27:27 +00:00
|
|
|
/**
|
|
|
|
* shell_global_log_structured:
|
|
|
|
* @message: A message to print
|
|
|
|
* @keys: (allow-none) (array zero-terminated=1) (element-type utf8): Optional structured data
|
|
|
|
*
|
|
|
|
* Log structured data in an operating-system specific fashion. The
|
|
|
|
* parameter @opts should be an array of UTF-8 KEY=VALUE strings.
|
|
|
|
* This function does not support binary data. See
|
|
|
|
* http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
|
|
|
|
* or more information about fields that can be used on a systemd
|
|
|
|
* system.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_log_structured (const char *message,
|
|
|
|
const char *const *keys)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
|
|
const char *const*iter;
|
|
|
|
char *msgkey;
|
|
|
|
guint i, n_opts;
|
|
|
|
struct iovec *iovs;
|
|
|
|
|
|
|
|
for (n_opts = 0, iter = keys; *iter; iter++, n_opts++)
|
|
|
|
;
|
|
|
|
|
|
|
|
n_opts++; /* Add one for MESSAGE= */
|
|
|
|
iovs = g_alloca (sizeof (struct iovec) * n_opts);
|
|
|
|
|
|
|
|
for (i = 0, iter = keys; *iter; iter++, i++) {
|
|
|
|
iovs[i].iov_base = (char*)keys[i];
|
|
|
|
iovs[i].iov_len = strlen (keys[i]);
|
|
|
|
}
|
|
|
|
g_assert(i == n_opts-1);
|
|
|
|
msgkey = g_strconcat ("MESSAGE=", message, NULL);
|
|
|
|
iovs[i].iov_base = msgkey;
|
|
|
|
iovs[i].iov_len = strlen (msgkey);
|
|
|
|
|
|
|
|
// The code location isn't useful since we're wrapping
|
|
|
|
sd_journal_sendv (iovs, n_opts);
|
|
|
|
g_free (msgkey);
|
|
|
|
#else
|
|
|
|
g_print ("%s\n", message);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-03-15 22:31:16 +00:00
|
|
|
/**
|
|
|
|
* shell_global_notify_error:
|
|
|
|
* @global: a #ShellGlobal
|
|
|
|
* @msg: Error message
|
|
|
|
* @details: Error details
|
|
|
|
*
|
|
|
|
* Show a system error notification. Use this function
|
|
|
|
* when a user-initiated action results in a non-fatal problem
|
|
|
|
* from causes that may not be under system control. For
|
|
|
|
* example, an application crash.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_notify_error (ShellGlobal *global,
|
|
|
|
const char *msg,
|
|
|
|
const char *details)
|
|
|
|
{
|
|
|
|
g_signal_emit_by_name (global, "notify-error", msg, details);
|
|
|
|
}
|
|
|
|
|
2011-01-05 14:47:27 +00:00
|
|
|
/**
|
|
|
|
* shell_global_init_xdnd:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
*
|
|
|
|
* Enables tracking of Xdnd events
|
|
|
|
*/
|
|
|
|
void shell_global_init_xdnd (ShellGlobal *global)
|
|
|
|
{
|
2011-07-13 16:22:32 +00:00
|
|
|
Window output_window = meta_get_overlay_window (global->meta_screen);
|
2011-01-05 14:47:27 +00:00
|
|
|
long xdnd_version = 5;
|
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
XChangeProperty (global->xdisplay, global->stage_xwindow,
|
|
|
|
gdk_x11_get_xatom_by_name ("XdndAware"), XA_ATOM,
|
|
|
|
32, PropModeReplace, (const unsigned char *)&xdnd_version, 1);
|
2011-01-05 14:47:27 +00:00
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
XChangeProperty (global->xdisplay, output_window,
|
|
|
|
gdk_x11_get_xatom_by_name ("XdndProxy"), XA_WINDOW,
|
|
|
|
32, PropModeReplace, (const unsigned char *)&global->stage_xwindow, 1);
|
2011-01-05 14:47:27 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XdndProxy is additionally set on the proxy window as verification that the
|
|
|
|
* XdndProxy property on the target window isn't a left-over
|
|
|
|
*/
|
2011-07-13 16:22:32 +00:00
|
|
|
XChangeProperty (global->xdisplay, global->stage_xwindow,
|
|
|
|
gdk_x11_get_xatom_by_name ("XdndProxy"), XA_WINDOW,
|
|
|
|
32, PropModeReplace, (const unsigned char *)&global->stage_xwindow, 1);
|
2011-01-05 14:47:27 +00:00
|
|
|
}
|
|
|
|
|
2009-10-06 14:53:25 +00:00
|
|
|
/**
|
2010-04-08 18:41:54 +00:00
|
|
|
* shell_global_get_pointer:
|
2009-10-06 14:53:25 +00:00
|
|
|
* @global: the #ShellGlobal
|
2010-04-08 18:41:54 +00:00
|
|
|
* @x: (out): the X coordinate of the pointer, in global coordinates
|
|
|
|
* @y: (out): the Y coordinate of the pointer, in global coordinates
|
|
|
|
* @mods: (out): the current set of modifier keys that are pressed down
|
2009-10-06 14:53:25 +00:00
|
|
|
*
|
2010-04-08 18:41:54 +00:00
|
|
|
* Gets the pointer coordinates and current modifier key state.
|
2009-10-06 14:53:25 +00:00
|
|
|
*/
|
2010-04-08 18:41:54 +00:00
|
|
|
void
|
|
|
|
shell_global_get_pointer (ShellGlobal *global,
|
|
|
|
int *x,
|
|
|
|
int *y,
|
|
|
|
ClutterModifierType *mods)
|
2009-10-06 14:53:25 +00:00
|
|
|
{
|
2013-08-14 14:59:39 +00:00
|
|
|
ClutterModifierType raw_mods;
|
|
|
|
MetaCursorTracker *tracker;
|
|
|
|
|
|
|
|
tracker = meta_cursor_tracker_get_for_screen (global->meta_screen);
|
|
|
|
meta_cursor_tracker_get_pointer (tracker, x, y, &raw_mods);
|
|
|
|
|
|
|
|
*mods = raw_mods & CLUTTER_MODIFIER_MASK;
|
2009-10-06 14:53:25 +00:00
|
|
|
}
|
2009-10-07 21:20:33 +00:00
|
|
|
|
2011-01-19 15:29:50 +00:00
|
|
|
/**
|
|
|
|
* shell_global_sync_pointer:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
*
|
|
|
|
* Ensures that clutter is aware of the current pointer position,
|
|
|
|
* causing enter and leave events to be emitted if the pointer moved
|
|
|
|
* behind our back (ie, during a pointer grab).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_sync_pointer (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
int x, y;
|
2013-08-14 14:59:39 +00:00
|
|
|
ClutterModifierType mods;
|
2011-01-19 15:29:50 +00:00
|
|
|
ClutterMotionEvent event;
|
|
|
|
|
2013-08-14 14:59:39 +00:00
|
|
|
shell_global_get_pointer (global, &x, &y, &mods);
|
2011-01-19 15:29:50 +00:00
|
|
|
|
|
|
|
event.type = CLUTTER_MOTION;
|
|
|
|
event.time = shell_global_get_current_time (global);
|
2015-04-27 17:08:24 +00:00
|
|
|
event.flags = CLUTTER_EVENT_FLAG_SYNTHETIC;
|
2011-07-13 16:22:32 +00:00
|
|
|
event.stage = global->stage;
|
2011-01-19 15:29:50 +00:00
|
|
|
event.x = x;
|
|
|
|
event.y = y;
|
|
|
|
event.modifier_state = mods;
|
|
|
|
event.axes = NULL;
|
2014-02-27 19:01:30 +00:00
|
|
|
event.device = clutter_device_manager_get_device (clutter_device_manager_get_default (),
|
|
|
|
META_VIRTUAL_CORE_POINTER_ID);
|
2011-01-19 15:29:50 +00:00
|
|
|
|
|
|
|
/* Leaving event.source NULL will force clutter to look it up, which
|
|
|
|
* will generate enter/leave events as a side effect, if they are
|
|
|
|
* needed. We need a better way to do this though... see
|
|
|
|
* http://bugzilla.clutter-project.org/show_bug.cgi?id=2615.
|
|
|
|
*/
|
|
|
|
event.source = NULL;
|
|
|
|
|
|
|
|
clutter_event_put ((ClutterEvent *)&event);
|
|
|
|
}
|
|
|
|
|
2010-05-05 21:05:42 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_settings:
|
|
|
|
* @global: A #ShellGlobal
|
|
|
|
*
|
|
|
|
* Get the global GSettings instance.
|
|
|
|
*
|
2010-09-02 00:48:11 +00:00
|
|
|
* Return value: (transfer none): The GSettings object
|
2010-05-05 21:05:42 +00:00
|
|
|
*/
|
|
|
|
GSettings *
|
|
|
|
shell_global_get_settings (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
return global->settings;
|
|
|
|
}
|
|
|
|
|
2014-06-05 16:09:47 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_overrides_settings:
|
|
|
|
* @global: A #ShellGlobal
|
|
|
|
*
|
|
|
|
* Get the session overrides GSettings instance.
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): The GSettings object
|
|
|
|
*/
|
|
|
|
GSettings *
|
|
|
|
shell_global_get_overrides_settings (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
static GSettings *settings = NULL;
|
|
|
|
const char *schema;
|
|
|
|
|
|
|
|
g_return_val_if_fail (SHELL_IS_GLOBAL (global), NULL);
|
|
|
|
|
|
|
|
if (!settings)
|
|
|
|
{
|
|
|
|
if (strcmp (global->session_mode, "classic") == 0)
|
|
|
|
schema = "org.gnome.shell.extensions.classic-overrides";
|
|
|
|
else if (strcmp (global->session_mode, "user") == 0)
|
|
|
|
schema = "org.gnome.shell.overrides";
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
settings = g_settings_new (schema);
|
|
|
|
}
|
|
|
|
|
|
|
|
return settings;
|
|
|
|
}
|
|
|
|
|
2009-12-03 20:59:52 +00:00
|
|
|
/**
|
|
|
|
* shell_global_get_current_time:
|
|
|
|
* @global: A #ShellGlobal
|
|
|
|
*
|
|
|
|
* Returns: the current X server time from the current Clutter, Gdk, or X
|
|
|
|
* event. If called from outside an event handler, this may return
|
|
|
|
* %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
|
|
|
|
* out-of-date timestamp.
|
|
|
|
*/
|
|
|
|
guint32
|
|
|
|
shell_global_get_current_time (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
guint32 time;
|
|
|
|
|
|
|
|
/* meta_display_get_current_time() will return the correct time
|
|
|
|
when handling an X or Gdk event, but will return CurrentTime
|
|
|
|
from some Clutter event callbacks.
|
|
|
|
|
|
|
|
clutter_get_current_event_time() will return the correct time
|
2012-12-23 09:15:51 +00:00
|
|
|
from a Clutter event callback, but may return CLUTTER_CURRENT_TIME
|
2009-12-03 20:59:52 +00:00
|
|
|
timestamp if called at other times.
|
|
|
|
|
|
|
|
So we try meta_display_get_current_time() first, since we
|
|
|
|
can recognize a "wrong" answer from that, and then fall back
|
|
|
|
to clutter_get_current_event_time().
|
|
|
|
*/
|
|
|
|
|
2011-07-13 16:22:32 +00:00
|
|
|
time = meta_display_get_current_time (global->meta_display);
|
2009-12-03 20:59:52 +00:00
|
|
|
if (time != CLUTTER_CURRENT_TIME)
|
2012-12-23 09:15:51 +00:00
|
|
|
return time;
|
2009-12-03 20:59:52 +00:00
|
|
|
|
2012-12-23 09:15:51 +00:00
|
|
|
return clutter_get_current_event_time ();
|
2009-12-03 20:59:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-09-02 00:48:11 +00:00
|
|
|
* shell_global_create_app_launch_context:
|
2009-12-03 20:59:52 +00:00
|
|
|
* @global: A #ShellGlobal
|
2014-01-19 17:34:32 +00:00
|
|
|
* @timestamp: the timestamp for the launch (or 0 for current time)
|
|
|
|
* @workspace: a workspace index, or -1 to indicate the current one
|
2009-12-03 20:59:52 +00:00
|
|
|
*
|
|
|
|
* Create a #GAppLaunchContext set up with the correct timestamp, and
|
|
|
|
* targeted to activate on the current workspace.
|
|
|
|
*
|
2010-09-02 00:48:11 +00:00
|
|
|
* Return value: (transfer full): A new #GAppLaunchContext
|
2009-12-03 20:59:52 +00:00
|
|
|
*/
|
|
|
|
GAppLaunchContext *
|
2014-01-19 17:34:32 +00:00
|
|
|
shell_global_create_app_launch_context (ShellGlobal *global,
|
2016-07-27 13:46:38 +00:00
|
|
|
guint32 timestamp,
|
2014-01-19 17:34:32 +00:00
|
|
|
int workspace)
|
2009-12-03 20:59:52 +00:00
|
|
|
{
|
|
|
|
GdkAppLaunchContext *context;
|
|
|
|
|
2011-10-19 21:52:11 +00:00
|
|
|
context = gdk_display_get_app_launch_context (global->gdk_display);
|
2009-12-03 20:59:52 +00:00
|
|
|
|
2014-01-19 17:34:32 +00:00
|
|
|
if (timestamp == 0)
|
|
|
|
timestamp = shell_global_get_current_time (global);
|
|
|
|
gdk_app_launch_context_set_timestamp (context, timestamp);
|
|
|
|
|
|
|
|
if (workspace < 0)
|
|
|
|
workspace = meta_screen_get_active_workspace_index (global->meta_screen);
|
|
|
|
gdk_app_launch_context_set_desktop (context, workspace);
|
2009-12-03 20:59:52 +00:00
|
|
|
|
|
|
|
return (GAppLaunchContext *)context;
|
|
|
|
}
|
2010-03-26 20:38:03 +00:00
|
|
|
|
2010-05-09 04:34:15 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
ShellLeisureFunction func;
|
|
|
|
gpointer user_data;
|
|
|
|
GDestroyNotify notify;
|
|
|
|
} LeisureClosure;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
run_leisure_functions (gpointer data)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = data;
|
|
|
|
GSList *closures;
|
|
|
|
GSList *iter;
|
|
|
|
|
|
|
|
global->leisure_function_id = 0;
|
|
|
|
|
|
|
|
/* We started more work since we scheduled the idle */
|
|
|
|
if (global->work_count > 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2010-05-21 21:25:41 +00:00
|
|
|
/* No leisure closures, so we are done */
|
|
|
|
if (global->leisure_closures == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
2010-05-09 04:34:15 +00:00
|
|
|
closures = global->leisure_closures;
|
|
|
|
global->leisure_closures = NULL;
|
|
|
|
|
|
|
|
for (iter = closures; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
LeisureClosure *closure = closures->data;
|
|
|
|
closure->func (closure->user_data);
|
|
|
|
|
|
|
|
if (closure->notify)
|
|
|
|
closure->notify (closure->user_data);
|
|
|
|
|
|
|
|
g_slice_free (LeisureClosure, closure);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_slist_free (closures);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
schedule_leisure_functions (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
/* This is called when we think we are ready to run leisure functions
|
|
|
|
* by our own accounting. We try to handle other types of business
|
|
|
|
* (like ClutterAnimation) by adding a low priority idle function.
|
|
|
|
*
|
|
|
|
* This won't work properly if the mainloop goes idle waiting for
|
|
|
|
* the vertical blanking interval or waiting for work being done
|
|
|
|
* in another thread.
|
|
|
|
*/
|
|
|
|
if (!global->leisure_function_id)
|
2014-04-10 17:27:23 +00:00
|
|
|
{
|
|
|
|
global->leisure_function_id = g_idle_add_full (G_PRIORITY_LOW,
|
|
|
|
run_leisure_functions,
|
|
|
|
global, NULL);
|
|
|
|
g_source_set_name_by_id (global->leisure_function_id, "[gnome-shell] run_leisure_functions");
|
|
|
|
}
|
2010-05-09 04:34:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_begin_work:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
*
|
|
|
|
* Marks that we are currently doing work. This is used to to track
|
|
|
|
* whether we are busy for the purposes of shell_global_run_at_leisure().
|
|
|
|
* A count is kept and shell_global_end_work() must be called exactly
|
|
|
|
* as many times as shell_global_begin_work().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_begin_work (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
global->work_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_end_work:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
*
|
|
|
|
* Marks the end of work that we started with shell_global_begin_work().
|
|
|
|
* If no other work is ongoing and functions have been added with
|
|
|
|
* shell_global_run_at_leisure(), they will be run at the next
|
|
|
|
* opportunity.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_end_work (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
g_return_if_fail (global->work_count > 0);
|
|
|
|
|
|
|
|
global->work_count--;
|
2010-05-21 21:25:41 +00:00
|
|
|
if (global->work_count == 0)
|
2010-05-09 04:34:15 +00:00
|
|
|
schedule_leisure_functions (global);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_run_at_leisure:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
* @func: function to call at leisure
|
|
|
|
* @user_data: data to pass to @func
|
|
|
|
* @notify: function to call to free @user_data
|
|
|
|
*
|
|
|
|
* Schedules a function to be called the next time the shell is idle.
|
|
|
|
* Idle means here no animations, no redrawing, and no ongoing background
|
|
|
|
* work. Since there is currently no way to hook into the Clutter master
|
|
|
|
* clock and know when is running, the implementation here is somewhat
|
|
|
|
* approximation. Animations done through the shell's Tweener module will
|
|
|
|
* be handled properly, but other animations may be detected as terminating
|
|
|
|
* early if they can be drawn fast enough so that the event loop goes idle
|
|
|
|
* between frames.
|
|
|
|
*
|
|
|
|
* The intent of this function is for performance measurement runs
|
|
|
|
* where a number of actions should be run serially and each action is
|
|
|
|
* timed individually. Using this function for other purposes will
|
|
|
|
* interfere with the ability to use it for performance measurement so
|
|
|
|
* should be avoided.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_run_at_leisure (ShellGlobal *global,
|
|
|
|
ShellLeisureFunction func,
|
|
|
|
gpointer user_data,
|
|
|
|
GDestroyNotify notify)
|
|
|
|
{
|
|
|
|
LeisureClosure *closure = g_slice_new (LeisureClosure);
|
|
|
|
closure->func = func;
|
|
|
|
closure->user_data = user_data;
|
|
|
|
closure->notify = notify;
|
|
|
|
|
|
|
|
global->leisure_closures = g_slist_append (global->leisure_closures,
|
|
|
|
closure);
|
|
|
|
|
|
|
|
if (global->work_count == 0)
|
|
|
|
schedule_leisure_functions (global);
|
|
|
|
}
|
2010-07-23 00:39:44 +00:00
|
|
|
|
2012-11-04 18:53:49 +00:00
|
|
|
static void
|
|
|
|
build_ca_proplist_for_event (ca_proplist *props,
|
2012-11-05 17:10:24 +00:00
|
|
|
const char *event_property,
|
2012-11-04 18:53:49 +00:00
|
|
|
const char *event_id,
|
|
|
|
const char *event_description,
|
|
|
|
ClutterEvent *for_event)
|
|
|
|
{
|
2012-11-05 17:10:24 +00:00
|
|
|
ca_proplist_sets (props, event_property, event_id);
|
2012-11-04 18:53:49 +00:00
|
|
|
ca_proplist_sets (props, CA_PROP_EVENT_DESCRIPTION, event_description);
|
|
|
|
ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
|
|
|
|
|
|
|
|
if (for_event)
|
|
|
|
{
|
|
|
|
if (clutter_event_type (for_event) != CLUTTER_KEY_PRESS &&
|
|
|
|
clutter_event_type (for_event) != CLUTTER_KEY_RELEASE)
|
|
|
|
{
|
|
|
|
ClutterPoint point;
|
|
|
|
|
|
|
|
clutter_event_get_position (for_event, &point);
|
|
|
|
|
|
|
|
ca_proplist_setf (props, CA_PROP_EVENT_MOUSE_X, "%d", (int)point.x);
|
|
|
|
ca_proplist_setf (props, CA_PROP_EVENT_MOUSE_Y, "%d", (int)point.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clutter_event_type (for_event) == CLUTTER_BUTTON_PRESS ||
|
|
|
|
clutter_event_type (for_event) == CLUTTER_BUTTON_RELEASE)
|
|
|
|
{
|
|
|
|
gint button;
|
|
|
|
|
|
|
|
button = clutter_event_get_button (for_event);
|
|
|
|
ca_proplist_setf (props, CA_PROP_EVENT_MOUSE_BUTTON, "%d", button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-23 00:39:44 +00:00
|
|
|
/**
|
|
|
|
* shell_global_play_theme_sound:
|
|
|
|
* @global: the #ShellGlobal
|
2011-02-15 18:23:36 +00:00
|
|
|
* @id: an id, used to cancel later (0 if not needed)
|
2010-07-23 00:39:44 +00:00
|
|
|
* @name: the sound name
|
2014-05-28 19:54:02 +00:00
|
|
|
* @for_event: (nullable): a #ClutterEvent in response to which the sound is played
|
2010-07-23 00:39:44 +00:00
|
|
|
*
|
|
|
|
* Plays a simple sound picked according to Freedesktop sound theme.
|
|
|
|
* Really just a workaround for libcanberra not being introspected.
|
|
|
|
*/
|
|
|
|
void
|
2012-11-04 18:53:49 +00:00
|
|
|
shell_global_play_theme_sound (ShellGlobal *global,
|
|
|
|
guint id,
|
|
|
|
const char *name,
|
|
|
|
const char *description,
|
|
|
|
ClutterEvent *for_event)
|
2010-07-23 00:39:44 +00:00
|
|
|
{
|
2012-11-04 18:53:49 +00:00
|
|
|
ca_proplist *props;
|
|
|
|
|
|
|
|
ca_proplist_create (&props);
|
2012-11-05 17:10:24 +00:00
|
|
|
build_ca_proplist_for_event (props, CA_PROP_EVENT_ID, name, description, for_event);
|
2012-11-04 18:53:49 +00:00
|
|
|
|
|
|
|
ca_context_play_full (global->sound_context, id, props, NULL, NULL);
|
|
|
|
|
|
|
|
ca_proplist_destroy (props);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_play_theme_sound_full:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
* @id: an id, used to cancel later (0 if not needed)
|
|
|
|
* @name: the sound name
|
|
|
|
* @description: the localized description of the event that triggered this alert
|
2014-05-28 19:54:02 +00:00
|
|
|
* @for_event: (nullable): a #ClutterEvent in response to which the sound is played
|
2012-11-04 18:53:49 +00:00
|
|
|
* @application_id: application on behalf of which the sound is played
|
|
|
|
* @application_name:
|
|
|
|
*
|
|
|
|
* Plays a simple sound picked according to Freedesktop sound theme.
|
|
|
|
* Really just a workaround for libcanberra not being introspected.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_play_theme_sound_full (ShellGlobal *global,
|
|
|
|
guint id,
|
|
|
|
const char *name,
|
|
|
|
const char *description,
|
|
|
|
ClutterEvent *for_event,
|
|
|
|
const char *application_id,
|
|
|
|
const char *application_name)
|
|
|
|
{
|
|
|
|
ca_proplist *props;
|
|
|
|
|
|
|
|
ca_proplist_create (&props);
|
2012-11-05 17:10:24 +00:00
|
|
|
build_ca_proplist_for_event (props, CA_PROP_EVENT_ID, name, description, for_event);
|
2012-11-04 18:53:49 +00:00
|
|
|
ca_proplist_sets (props, CA_PROP_APPLICATION_ID, application_id);
|
|
|
|
ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, application_name);
|
|
|
|
|
|
|
|
ca_context_play_full (global->sound_context, id, props, NULL, NULL);
|
|
|
|
|
|
|
|
ca_proplist_destroy (props);
|
2011-02-15 18:23:36 +00:00
|
|
|
}
|
|
|
|
|
2012-11-05 17:10:24 +00:00
|
|
|
/**
|
|
|
|
* shell_global_play_sound_file_full:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
* @id: an id, used to cancel later (0 if not needed)
|
|
|
|
* @file_name: the file name to play
|
|
|
|
* @description: the localized description of the event that triggered this alert
|
2014-05-28 19:54:02 +00:00
|
|
|
* @for_event: (nullable): a #ClutterEvent in response to which the sound is played
|
2012-11-05 17:10:24 +00:00
|
|
|
* @application_id: application on behalf of which the sound is played
|
|
|
|
* @application_name:
|
|
|
|
*
|
|
|
|
* Like shell_global_play_theme_sound_full(), but with an explicit path
|
|
|
|
* instead of a themed sound.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_play_sound_file_full (ShellGlobal *global,
|
|
|
|
guint id,
|
|
|
|
const char *file_name,
|
|
|
|
const char *description,
|
|
|
|
ClutterEvent *for_event,
|
|
|
|
const char *application_id,
|
|
|
|
const char *application_name)
|
|
|
|
{
|
|
|
|
ca_proplist *props;
|
|
|
|
|
|
|
|
ca_proplist_create (&props);
|
|
|
|
build_ca_proplist_for_event (props, CA_PROP_MEDIA_FILENAME, file_name, description, for_event);
|
|
|
|
ca_proplist_sets (props, CA_PROP_APPLICATION_ID, application_id);
|
|
|
|
ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, application_name);
|
|
|
|
|
|
|
|
ca_context_play_full (global->sound_context, id, props, NULL, NULL);
|
|
|
|
|
|
|
|
ca_proplist_destroy (props);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_play_sound_file:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
* @id: an id, used to cancel later (0 if not needed)
|
|
|
|
* @file_name: the file name to play
|
|
|
|
* @description: the localized description of the event that triggered this alert
|
2014-05-28 19:54:02 +00:00
|
|
|
* @for_event: (nullable): a #ClutterEvent in response to which the sound is played
|
2012-11-05 17:10:24 +00:00
|
|
|
*
|
|
|
|
* Like shell_global_play_theme_sound(), but with an explicit path
|
|
|
|
* instead of a themed sound.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_play_sound_file (ShellGlobal *global,
|
|
|
|
guint id,
|
|
|
|
const char *file_name,
|
|
|
|
const char *description,
|
|
|
|
ClutterEvent *for_event)
|
|
|
|
{
|
|
|
|
ca_proplist *props;
|
|
|
|
|
|
|
|
ca_proplist_create (&props);
|
|
|
|
build_ca_proplist_for_event (props, CA_PROP_MEDIA_FILENAME, file_name, description, for_event);
|
|
|
|
|
|
|
|
ca_context_play_full (global->sound_context, id, props, NULL, NULL);
|
|
|
|
|
|
|
|
ca_proplist_destroy (props);
|
|
|
|
}
|
|
|
|
|
2011-02-15 18:23:36 +00:00
|
|
|
/**
|
|
|
|
* shell_global_cancel_theme_sound:
|
|
|
|
* @global: the #ShellGlobal
|
|
|
|
* @id: the id previously passed to shell_global_play_theme_sound()
|
|
|
|
*
|
|
|
|
* Cancels a sound notification.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_cancel_theme_sound (ShellGlobal *global,
|
|
|
|
guint id)
|
|
|
|
{
|
|
|
|
ca_context_cancel (global->sound_context, id);
|
2010-07-23 00:39:44 +00:00
|
|
|
}
|
2011-01-05 14:47:27 +00:00
|
|
|
|
2012-05-16 21:11:57 +00:00
|
|
|
const char *
|
|
|
|
shell_global_get_session_mode (ShellGlobal *global)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (SHELL_IS_GLOBAL (global), "user");
|
|
|
|
|
|
|
|
return global->session_mode;
|
2011-06-21 14:09:09 +00:00
|
|
|
}
|
2013-01-17 19:39:54 +00:00
|
|
|
|
2013-10-13 22:46:50 +00:00
|
|
|
static void
|
|
|
|
save_variant (GFile *dir,
|
|
|
|
const char *property_name,
|
|
|
|
GVariant *variant)
|
2013-01-17 19:39:54 +00:00
|
|
|
{
|
2013-10-13 22:46:50 +00:00
|
|
|
GFile *path = g_file_get_child (dir, property_name);
|
2013-01-17 19:39:54 +00:00
|
|
|
|
2013-10-14 04:00:24 +00:00
|
|
|
if (variant == NULL || g_variant_get_data (variant) == NULL)
|
2013-01-17 19:39:54 +00:00
|
|
|
(void) g_file_delete (path, NULL, NULL);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gsize size = g_variant_get_size (variant);
|
|
|
|
g_file_replace_contents (path, g_variant_get_data (variant), size,
|
|
|
|
NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
}
|
2013-10-13 22:41:03 +00:00
|
|
|
|
|
|
|
g_object_unref (path);
|
2013-01-17 19:39:54 +00:00
|
|
|
}
|
|
|
|
|
2013-10-13 22:46:50 +00:00
|
|
|
static GVariant *
|
|
|
|
load_variant (GFile *dir,
|
|
|
|
const char *property_type,
|
|
|
|
const char *property_name)
|
2013-01-17 19:39:54 +00:00
|
|
|
{
|
|
|
|
GVariant *res = NULL;
|
|
|
|
GMappedFile *mfile;
|
2013-10-13 22:46:50 +00:00
|
|
|
GFile *path = g_file_get_child (dir, property_name);
|
2013-01-17 19:39:54 +00:00
|
|
|
char *pathstr;
|
|
|
|
GError *local_error = NULL;
|
|
|
|
|
|
|
|
pathstr = g_file_get_path (path);
|
|
|
|
mfile = g_mapped_file_new (pathstr, FALSE, &local_error);
|
|
|
|
if (!mfile)
|
|
|
|
{
|
|
|
|
if (!g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
|
|
|
{
|
|
|
|
g_warning ("Failed to open runtime state: %s", local_error->message);
|
|
|
|
}
|
|
|
|
g_clear_error (&local_error);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GBytes *bytes = g_mapped_file_get_bytes (mfile);
|
2013-10-14 14:52:45 +00:00
|
|
|
res = g_variant_new_from_bytes (G_VARIANT_TYPE (property_type), bytes, TRUE);
|
2013-01-17 19:39:54 +00:00
|
|
|
g_bytes_unref (bytes);
|
|
|
|
g_mapped_file_unref (mfile);
|
|
|
|
}
|
|
|
|
|
2013-10-13 22:41:03 +00:00
|
|
|
g_object_unref (path);
|
|
|
|
g_free (pathstr);
|
|
|
|
|
2013-01-17 19:39:54 +00:00
|
|
|
return res;
|
|
|
|
}
|
2013-10-13 22:46:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_set_runtime_state:
|
|
|
|
* @global: a #ShellGlobal
|
|
|
|
* @property_name: Name of the property
|
2014-05-28 19:54:02 +00:00
|
|
|
* @variant: (nullable): A #GVariant, or %NULL to unset
|
2013-10-13 22:46:50 +00:00
|
|
|
*
|
|
|
|
* Change the value of serialized runtime state.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_set_runtime_state (ShellGlobal *global,
|
|
|
|
const char *property_name,
|
|
|
|
GVariant *variant)
|
|
|
|
{
|
|
|
|
save_variant (global->runtime_state_path, property_name, variant);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_get_runtime_state:
|
|
|
|
* @global: a #ShellGlobal
|
|
|
|
* @property_type: Expected data type
|
|
|
|
* @property_name: Name of the property
|
|
|
|
*
|
|
|
|
* The shell maintains "runtime" state which does not persist across
|
|
|
|
* logout or reboot.
|
|
|
|
*
|
|
|
|
* Returns: (transfer floating): The value of a serialized property, or %NULL if none stored
|
|
|
|
*/
|
|
|
|
GVariant *
|
|
|
|
shell_global_get_runtime_state (ShellGlobal *global,
|
|
|
|
const char *property_type,
|
|
|
|
const char *property_name)
|
|
|
|
{
|
|
|
|
return load_variant (global->runtime_state_path, property_type, property_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_set_persistent_state:
|
|
|
|
* @global: a #ShellGlobal
|
|
|
|
* @property_name: Name of the property
|
2014-05-28 19:54:02 +00:00
|
|
|
* @variant: (nullable): A #GVariant, or %NULL to unset
|
2013-10-13 22:46:50 +00:00
|
|
|
*
|
|
|
|
* Change the value of serialized persistent state.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_global_set_persistent_state (ShellGlobal *global,
|
|
|
|
const char *property_name,
|
|
|
|
GVariant *variant)
|
|
|
|
{
|
|
|
|
save_variant (global->userdatadir_path, property_name, variant);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_global_get_persistent_state:
|
|
|
|
* @global: a #ShellGlobal
|
|
|
|
* @property_type: Expected data type
|
|
|
|
* @property_name: Name of the property
|
|
|
|
*
|
|
|
|
* The shell maintains "persistent" state which will persist after
|
|
|
|
* logout or reboot.
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): The value of a serialized property, or %NULL if none stored
|
|
|
|
*/
|
|
|
|
GVariant *
|
|
|
|
shell_global_get_persistent_state (ShellGlobal *global,
|
|
|
|
const char *property_type,
|
|
|
|
const char *property_name)
|
|
|
|
{
|
|
|
|
return load_variant (global->userdatadir_path, property_type, property_name);
|
|
|
|
}
|