[gnome-settings-daemon] media-keys: Refer key grabbing to the shell
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] media-keys: Refer key grabbing to the shell
- Date: Fri, 1 Mar 2013 16:11:24 +0000 (UTC)
commit b0cee1df30b4945f524611f354ff164d4a383262
Author: Florian Müllner <fmuellner gnome org>
Date: Thu Nov 15 17:41:24 2012 +0100
media-keys: Refer key grabbing to the shell
As events are not delivered to the root window when the shell itself
has grabbed the keyboard, shortcuts currently won't work in "special"
modes like the overview. While the shell does offer a mechanism to
selectively allow keybindings in these modes, it is only available
for shortcuts grabbed by the shell itself. In order to allow other
processes to use this feature as well, the shell now exposes a
simple grab API on DBus; start using this API to fix shortcuts
during compositor grabs.
https://bugzilla.gnome.org/show_bug.cgi?id=693016
plugins/media-keys/Makefile.am | 10 +
plugins/media-keys/gsd-media-keys-manager.c | 398 ++++++++++++----------
plugins/media-keys/org.gnome.ShellKeyGrabber.xml | 22 ++
3 files changed, 252 insertions(+), 178 deletions(-)
---
diff --git a/plugins/media-keys/Makefile.am b/plugins/media-keys/Makefile.am
index 4f09ca0..62a5a55 100644
--- a/plugins/media-keys/Makefile.am
+++ b/plugins/media-keys/Makefile.am
@@ -11,6 +11,8 @@ plugin_LTLIBRARIES = libmedia-keys.la
BUILT_SOURCES = \
gsd-marshal.h \
gsd-marshal.c \
+ shell-key-grabber.c \
+ shell-key-grabber.h \
$(NULL)
gsd-marshal.c: gsd-marshal.list
@@ -19,6 +21,13 @@ gsd-marshal.c: gsd-marshal.list
gsd-marshal.h: gsd-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gsd_marshal $< --header --internal > $@
+shell-key-grabber.c: shell-key-grabber.h
+shell-key-grabber.h: Makefile.am org.gnome.ShellKeyGrabber.xml
+ gdbus-codegen --interface-prefix org.gnome. \
+ --generate-c-code shell-key-grabber \
+ --c-namespace Shell \
+ org.gnome.ShellKeyGrabber.xml
+
libmedia_keys_la_SOURCES = \
gsd-media-keys-plugin.c \
gsd-media-keys-manager.h \
@@ -138,6 +147,7 @@ EXTRA_DIST = \
gsd-marshal.list \
README.media-keys-API \
test-media-window-clutter.c \
+ org.gnome.ShellKeyGrabber.xml \
$(plugin_in_files)
CLEANFILES = \
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 32eb148..5a3c959 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -52,6 +52,7 @@
#include "gsd-media-keys-manager.h"
#include "shortcuts-list.h"
+#include "shell-key-grabber.h"
#include "gsd-osd-window.h"
#include "gsd-screenshot-utils.h"
#include "gsd-input-helper.h"
@@ -73,8 +74,13 @@
#define GS_DBUS_PATH "/org/gnome/ScreenSaver"
#define GS_DBUS_INTERFACE "org.gnome.ScreenSaver"
+#define SHELL_DBUS_NAME "org.gnome.Shell"
+#define SHELL_DBUS_PATH "/org/gnome/Shell"
+
#define CUSTOM_BINDING_SCHEMA SETTINGS_BINDING_DIR ".custom-keybinding"
+#define SHELL_GRABBER_RETRY_INTERVAL 1
+
static const gchar introspection_xml[] =
"<node name='/org/gnome/SettingsDaemon/MediaKeys'>"
" <interface name='org.gnome.SettingsDaemon.MediaKeys'>"
@@ -122,9 +128,14 @@ typedef struct {
const char *hard_coded;
char *custom_path;
char *custom_command;
- Key *key;
+ guint accel_id;
} MediaKey;
+typedef struct {
+ GsdMediaKeysManager *manager;
+ MediaKey *key;
+} GrabData;
+
struct GsdMediaKeysManagerPrivate
{
/* Volume bits */
@@ -155,6 +166,10 @@ struct GsdMediaKeysManagerPrivate
GDBusProxy *power_screen_proxy;
GDBusProxy *power_keyboard_proxy;
+ /* shell grabber */
+ ShellKeyGrabber *key_grabber;
+ GCancellable *grab_cancellable;
+
/* systemd stuff */
GDBusProxy *logind_proxy;
gint inhibit_keys_fd;
@@ -182,6 +197,11 @@ static void register_manager (GsdMediaKeysManager *ma
static void custom_binding_changed (GSettings *settings,
const char *settings_key,
GsdMediaKeysManager *manager);
+static void grab_media_keys (GsdMediaKeysManager *manager);
+static void grab_media_key (MediaKey *key,
+ GsdMediaKeysManager *manager);
+static void ungrab_media_key (MediaKey *key,
+ GsdMediaKeysManager *manager);
G_DEFINE_TYPE (GsdMediaKeysManager, gsd_media_keys_manager, G_TYPE_OBJECT)
static gpointer manager_object = NULL;
@@ -214,7 +234,6 @@ media_key_free (MediaKey *key)
return;
g_free (key->custom_path);
g_free (key->custom_command);
- free_key (key->key);
g_free (key);
}
@@ -358,18 +377,6 @@ dialog_init (GsdMediaKeysManager *manager)
}
}
-static void
-print_key_parse_error (MediaKey *key,
- const char *str)
-{
- if (str == NULL || *str == '\0')
- return;
- if (key->settings_key != NULL)
- g_debug ("Unable to parse key '%s' for GSettings entry '%s'", str, key->settings_key);
- else
- g_debug ("Unable to parse hard-coded key '%s'", key->hard_coded);
-}
-
static char *
get_key_string (GsdMediaKeysManager *manager,
MediaKey *key)
@@ -388,37 +395,153 @@ get_key_string (GsdMediaKeysManager *manager,
g_assert_not_reached ();
}
+static void
+ensure_grab_cancellable (GsdMediaKeysManager *manager)
+{
+ if (manager->priv->grab_cancellable == NULL) {
+ manager->priv->grab_cancellable = g_cancellable_new ();
+ g_object_add_weak_pointer (G_OBJECT (manager->priv->grab_cancellable),
+ (gpointer *)&manager->priv->grab_cancellable);
+ } else {
+ g_object_ref (manager->priv->grab_cancellable);
+ }
+}
+
static gboolean
+retry_grabs (gpointer data)
+{
+ GsdMediaKeysManager *manager = data;
+
+ grab_media_keys (manager);
+ return FALSE;
+}
+
+static void
+grab_accelerators_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GVariant *actions;
+ gboolean retry = FALSE;
+ GError *error = NULL;
+ GsdMediaKeysManager *manager = user_data;
+
+ shell_key_grabber_call_grab_accelerators_finish (SHELL_KEY_GRABBER (object),
+ &actions, result, &error);
+
+ g_object_unref (manager->priv->grab_cancellable);
+
+ if (error) {
+ retry = (error->code == G_DBUS_ERROR_UNKNOWN_METHOD);
+ if (!retry)
+ g_warning ("%d: %s", error->code, error->message);
+ g_error_free (error);
+ } else {
+ int i;
+ for (i = 0; i < manager->priv->keys->len; i++) {
+ MediaKey *key;
+
+ key = g_ptr_array_index (manager->priv->keys, i);
+ g_variant_get_child (actions, i, "u", &key->accel_id);
+ }
+ }
+
+ if (retry)
+ g_timeout_add_seconds (SHELL_GRABBER_RETRY_INTERVAL,
+ retry_grabs, manager);
+}
+
+static void
+grab_media_keys (GsdMediaKeysManager *manager)
+{
+ GVariantBuilder builder;
+ int i;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+
+ for (i = 0; i < manager->priv->keys->len; i++) {
+ MediaKey *key;
+ char *tmp;
+
+ key = g_ptr_array_index (manager->priv->keys, i);
+ tmp = get_key_string (manager, key);
+ g_variant_builder_add (&builder, "(su)", tmp, ~0);
+ g_free (tmp);
+ }
+
+ ensure_grab_cancellable (manager);
+ shell_key_grabber_call_grab_accelerators (manager->priv->key_grabber,
+ g_variant_builder_end (&builder),
+ manager->priv->grab_cancellable,
+ grab_accelerators_complete,
+ manager);
+}
+
+static void
+grab_accelerator_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GrabData *data = user_data;
+ MediaKey *key = data->key;
+
+ shell_key_grabber_call_grab_accelerator_finish (SHELL_KEY_GRABBER (object),
+ &key->accel_id, result, NULL);
+
+ g_object_unref (data->manager->priv->grab_cancellable);
+ g_slice_free (GrabData, data);
+}
+
+static void
grab_media_key (MediaKey *key,
GsdMediaKeysManager *manager)
{
+ GrabData *data;
char *tmp;
- gboolean need_flush;
- need_flush = FALSE;
-
- if (key->key != NULL) {
- need_flush = TRUE;
- ungrab_key_unsafe (key->key, manager->priv->screens);
- }
-
- free_key (key->key);
- key->key = NULL;
+ ungrab_media_key (key, manager);
tmp = get_key_string (manager, key);
- key->key = parse_key (tmp);
- if (key->key == NULL) {
- print_key_parse_error (key, tmp);
- g_free (tmp);
- return need_flush;
- }
+ data = g_slice_new0 (GrabData);
+ data->manager = manager;
+ data->key = key;
- grab_key_unsafe (key->key, GSD_KEYGRAB_NORMAL, manager->priv->screens);
+ ensure_grab_cancellable (manager);
+ shell_key_grabber_call_grab_accelerator (manager->priv->key_grabber,
+ tmp, ~0,
+ manager->priv->grab_cancellable,
+ grab_accelerator_complete,
+ data);
g_free (tmp);
+}
+
+static void
+ungrab_accelerator_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GsdMediaKeysManager *manager = user_data;
+ shell_key_grabber_call_ungrab_accelerator_finish (SHELL_KEY_GRABBER (object),
+ NULL, result, NULL);
+ g_object_unref (manager->priv->grab_cancellable);
+}
+
+static void
+ungrab_media_key (MediaKey *key,
+ GsdMediaKeysManager *manager)
+{
+ if (key->accel_id == 0)
+ return;
- return TRUE;
+ ensure_grab_cancellable (manager);
+ shell_key_grabber_call_ungrab_accelerator (manager->priv->key_grabber,
+ key->accel_id,
+ manager->priv->grab_cancellable,
+ ungrab_accelerator_complete,
+ manager);
+ key->accel_id = 0;
}
static void
@@ -427,14 +550,11 @@ gsettings_changed_cb (GSettings *settings,
GsdMediaKeysManager *manager)
{
int i;
- gboolean need_flush = TRUE;
/* handled in gsettings_custom_changed_cb() */
if (g_str_equal (settings_key, "custom-keybindings"))
return;
- gdk_error_trap_push ();
-
/* Find the key that was modified */
for (i = 0; i < manager->priv->keys->len; i++) {
MediaKey *key;
@@ -445,16 +565,10 @@ gsettings_changed_cb (GSettings *settings,
if (key->settings_key == NULL)
continue;
if (strcmp (settings_key, key->settings_key) == 0) {
- if (grab_media_key (key, manager))
- need_flush = TRUE;
+ grab_media_key (key, manager);
break;
}
}
-
- if (need_flush)
- gdk_flush ();
- if (gdk_error_trap_pop ())
- g_warning ("Grab failed for some keys, another application may already have access the
them.");
}
static MediaKey *
@@ -511,16 +625,7 @@ update_custom_binding (GsdMediaKeysManager *manager,
continue;
if (strcmp (key->custom_path, path) == 0) {
g_debug ("Removing custom key binding %s", path);
- if (key->key) {
- gdk_error_trap_push ();
-
- ungrab_key_unsafe (key->key,
- manager->priv->screens);
-
- gdk_flush ();
- if (gdk_error_trap_pop ())
- g_warning ("Ungrab failed for custom key '%s'", path);
- }
+ ungrab_media_key (key, manager);
g_ptr_array_remove_index_fast (manager->priv->keys, i);
break;
}
@@ -532,14 +637,7 @@ update_custom_binding (GsdMediaKeysManager *manager,
g_debug ("Adding new custom key binding %s", path);
g_ptr_array_add (manager->priv->keys, key);
- gdk_error_trap_push ();
-
grab_media_key (key, manager);
-
- gdk_flush ();
- if (gdk_error_trap_pop ())
- g_warning ("Grab failed for custom key '%s'",
- key->custom_path);
}
}
@@ -590,16 +688,7 @@ gsettings_custom_changed_cb (GSettings *settings,
if (found)
continue;
- if (key->key) {
- gdk_error_trap_push ();
-
- ungrab_key_unsafe (key->key,
- manager->priv->screens);
-
- gdk_flush ();
- if (gdk_error_trap_pop ())
- g_warning ("Ungrab failed for custom key '%s'", key->custom_path);
- }
+ ungrab_media_key (key, manager);
g_hash_table_remove (manager->priv->custom_settings,
key->custom_path);
g_ptr_array_remove_index_fast (manager->priv->keys, i);
@@ -619,8 +708,6 @@ add_key (GsdMediaKeysManager *manager, guint i)
key->hard_coded = media_keys[i].hard_coded;
g_ptr_array_add (manager->priv->keys, key);
-
- grab_media_key (key, manager);
}
static void
@@ -631,8 +718,6 @@ init_kbd (GsdMediaKeysManager *manager)
gnome_settings_profile_start (NULL);
- gdk_error_trap_push ();
-
manager->priv->keys = g_ptr_array_new_with_free_func ((GDestroyNotify) media_key_free);
/* Media keys
@@ -660,14 +745,10 @@ init_kbd (GsdMediaKeysManager *manager)
continue;
}
g_ptr_array_add (manager->priv->keys, key);
-
- grab_media_key (key, manager);
}
g_strfreev (custom_paths);
- gdk_flush ();
- if (gdk_error_trap_pop ())
- g_warning ("Grab failed for some keys, another application may already have access the
them.");
+ grab_media_keys (manager);
gnome_settings_profile_end (NULL);
}
@@ -2067,93 +2148,46 @@ do_action (GsdMediaKeysManager *manager,
return FALSE;
}
-static GdkScreen *
-get_screen_from_root (GsdMediaKeysManager *manager,
- Window root)
+static void
+on_shell_restart (GObject *object,
+ GParamSpec *pspec,
+ GsdMediaKeysManager *manager)
{
- GSList *l;
+ char *owner;
- /* Look for which screen we're receiving events */
- for (l = manager->priv->screens; l != NULL; l = l->next) {
- GdkScreen *screen = (GdkScreen *) l->data;
- GdkWindow *window = gdk_screen_get_root_window (screen);
+ if (manager->priv->keys == NULL)
+ return;
- if (GDK_WINDOW_XID (window) == root)
- return screen;
- }
+ g_object_get (object, "g-name-owner", &owner, NULL);
+ if (!owner)
+ return;
+ g_free (owner);
- return NULL;
+ grab_media_keys (manager);
}
-static GdkFilterReturn
-filter_key_events (XEvent *xevent,
- GdkEvent *event,
- GsdMediaKeysManager *manager)
+static void
+on_accelerator_activated (ShellKeyGrabber *grabber,
+ guint accel_id,
+ guint deviceid,
+ GsdMediaKeysManager *manager)
{
- XIEvent *xiev;
- XIDeviceEvent *xev;
- XGenericEventCookie *cookie;
- guint i;
- guint deviceid;
-
- /* verify we have a key event */
- if (xevent->type != GenericEvent)
- return GDK_FILTER_CONTINUE;
- cookie = &xevent->xcookie;
- if (cookie->extension != manager->priv->opcode)
- return GDK_FILTER_CONTINUE;
-
- xiev = (XIEvent *) xevent->xcookie.data;
-
- if (xiev->evtype != XI_KeyPress &&
- xiev->evtype != XI_KeyRelease)
- return GDK_FILTER_CONTINUE;
-
- xev = (XIDeviceEvent *) xiev;
-
- deviceid = xev->sourceid;
+ guint i;
for (i = 0; i < manager->priv->keys->len; i++) {
MediaKey *key;
key = g_ptr_array_index (manager->priv->keys, i);
- if (match_xi2_key (key->key, xev)) {
- switch (key->key_type) {
- case VOLUME_DOWN_KEY:
- case VOLUME_UP_KEY:
- case VOLUME_DOWN_QUIET_KEY:
- case VOLUME_UP_QUIET_KEY:
- case SCREEN_BRIGHTNESS_UP_KEY:
- case SCREEN_BRIGHTNESS_DOWN_KEY:
- case KEYBOARD_BRIGHTNESS_UP_KEY:
- case KEYBOARD_BRIGHTNESS_DOWN_KEY:
- /* auto-repeatable keys */
- if (xiev->evtype != XI_KeyPress)
- return GDK_FILTER_CONTINUE;
- break;
- default:
- if (xiev->evtype != XI_KeyRelease) {
- return GDK_FILTER_CONTINUE;
- }
- }
-
- manager->priv->current_screen = get_screen_from_root (manager, xev->root);
-
- if (key->key_type == CUSTOM_KEY) {
- do_custom_action (manager, key, xev->time);
- return GDK_FILTER_REMOVE;
- }
-
- if (do_action (manager, deviceid, key->key_type, xev->time) == FALSE) {
- return GDK_FILTER_REMOVE;
- } else {
- return GDK_FILTER_CONTINUE;
- }
- }
- }
+ if (key->accel_id != accel_id)
+ continue;
- return GDK_FILTER_CONTINUE;
+ if (key->key_type == CUSTOM_KEY)
+ do_custom_action (manager, key, GDK_CURRENT_TIME);
+ else
+ do_action (manager, deviceid, key->key_type, GDK_CURRENT_TIME);
+ return;
+ }
}
static void
@@ -2212,10 +2246,32 @@ initialize_volume_handler (GsdMediaKeysManager *manager)
gnome_settings_profile_end ("gvc_mixer_control_new");
}
+static void
+on_key_grabber_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GsdMediaKeysManager *manager = data;
+
+ manager->priv->key_grabber =
+ shell_key_grabber_proxy_new_for_bus_finish (result, NULL);
+
+ g_object_unref (manager->priv->grab_cancellable);
+
+ if (!manager->priv->key_grabber)
+ return;
+
+ g_signal_connect (manager->priv->key_grabber, "notify::g-name-owner",
+ G_CALLBACK (on_shell_restart), manager);
+ g_signal_connect (manager->priv->key_grabber, "accelerator-activated",
+ G_CALLBACK (on_accelerator_activated), manager);
+
+ init_kbd (manager);
+}
+
static gboolean
start_media_keys_idle_cb (GsdMediaKeysManager *manager)
{
- GSList *l;
char *theme_name;
g_debug ("Starting media_keys manager");
@@ -2264,20 +2320,14 @@ start_media_keys_idle_cb (GsdMediaKeysManager *manager)
manager->priv->icon_theme = g_settings_get_string (manager->priv->interface_settings, "icon-theme");
init_screens (manager);
- init_kbd (manager);
- /* Start filtering the events */
- for (l = manager->priv->screens; l != NULL; l = l->next) {
- gnome_settings_profile_start ("gdk_window_add_filter");
-
- g_debug ("adding key filter for screen: %d",
- gdk_screen_get_number (l->data));
-
- gdk_window_add_filter (gdk_screen_get_root_window (l->data),
- (GdkFilterFunc) filter_key_events,
- manager);
- gnome_settings_profile_end ("gdk_window_add_filter");
- }
+ ensure_grab_cancellable (manager);
+ shell_key_grabber_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ SHELL_DBUS_NAME,
+ SHELL_DBUS_PATH,
+ manager->priv->grab_cancellable,
+ on_key_grabber_ready, manager);
gnome_settings_profile_end (NULL);
@@ -2317,7 +2367,6 @@ void
gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
{
GsdMediaKeysManagerPrivate *priv = manager->priv;
- GSList *ls;
int i;
g_debug ("Stopping media_keys manager");
@@ -2328,12 +2377,6 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
priv->bus_cancellable = NULL;
}
- for (ls = priv->screens; ls != NULL; ls = ls->next) {
- gdk_window_remove_filter (gdk_screen_get_root_window (ls->data),
- (GdkFilterFunc) filter_key_events,
- manager);
- }
-
if (manager->priv->gtksettings != NULL) {
g_signal_handlers_disconnect_by_func (manager->priv->gtksettings, sound_theme_changed,
manager);
manager->priv->gtksettings = NULL;
@@ -2362,20 +2405,19 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
g_clear_object (&priv->connection);
if (priv->keys != NULL) {
- gdk_error_trap_push ();
for (i = 0; i < priv->keys->len; ++i) {
MediaKey *key;
key = g_ptr_array_index (manager->priv->keys, i);
-
- if (key->key)
- ungrab_key_unsafe (key->key, priv->screens);
+ ungrab_media_key (key, manager);
}
g_ptr_array_free (priv->keys, TRUE);
priv->keys = NULL;
+ }
- gdk_flush ();
- gdk_error_trap_pop_ignored ();
+ if (priv->grab_cancellable != NULL) {
+ g_cancellable_cancel (priv->grab_cancellable);
+ g_clear_object (&priv->grab_cancellable);
}
g_clear_pointer (&priv->screens, g_slist_free);
diff --git a/plugins/media-keys/org.gnome.ShellKeyGrabber.xml
b/plugins/media-keys/org.gnome.ShellKeyGrabber.xml
new file mode 100644
index 0000000..7b60959
--- /dev/null
+++ b/plugins/media-keys/org.gnome.ShellKeyGrabber.xml
@@ -0,0 +1,22 @@
+<node>
+ <interface name="org.gnome.Shell">
+ <annotation name="org.gtk.GDBus.C.Name" value="KeyGrabber"/>
+ <method name="GrabAccelerator">
+ <arg type="s" direction="in" name="accelerator"/>
+ <arg type="u" direction="in" name="flags"/>
+ <arg type="u" direction="out" name="action"/>
+ </method>
+ <method name="GrabAccelerators">
+ <arg type="a(su)" direction="in" name="accelerators"/>
+ <arg type="au" direction="out" name="actions"/>
+ </method>
+ <method name="UngrabAccelerator">
+ <arg type="u" direction="in" name="action"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+ <signal name="AcceleratorActivated">
+ <arg type="u" name="action"/>
+ <arg type="u" name="device"/>
+ </signal>
+ </interface>
+</node>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]