[gnome-settings-daemon/wip/benzea/keybindings-grabbing-rework-3-32: 3/3] media-keys: Rework key grabbing code



commit b3b401136d2c8b976f01876daf83ee7695f77359
Author: Benjamin Berg <bberg redhat com>
Date:   Mon Mar 4 14:49:09 2019 +0100

    media-keys: Rework key grabbing code
    
    This relies on a new shell API to ungrab keys in bulk. The approach now
    is that we keep a set of keys that need syncing. For this set of keys,
    we emit one Ungrab call and one Grab call in two steps.
    
    This likely fixes issues like the ones reported in #151. Another reason
    to do this is as a preparation to allow multiple keybindings per key.

 plugins/media-keys/gsd-media-keys-manager.c      | 429 ++++++++++++++---------
 plugins/media-keys/org.gnome.ShellKeyGrabber.xml |   8 +
 2 files changed, 269 insertions(+), 168 deletions(-)
---
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 929bfb1d..9375647c 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -84,7 +84,7 @@
 #define ALLOW_VOLUME_ABOVE_100_PERCENT_KEY "allow-volume-above-100-percent"
 
 #define SHELL_GRABBER_CALL_TIMEOUT G_MAXINT
-#define SHELL_GRABBER_RETRY_INTERVAL 1
+#define SHELL_GRABBER_RETRY_INTERVAL_MS 1000
 #define OSD_ALL_OUTPUTS -1
 
 /* How long to suppress power-button presses after resume,
@@ -148,13 +148,17 @@ typedef struct {
         char *custom_path;
         char *custom_command;
         guint accel_id;
-        gboolean ungrab_requested;
 } MediaKey;
 
 typedef struct {
         GsdMediaKeysManager *manager;
-        MediaKey *key;
-} GrabData;
+        GPtrArray *keys;
+
+        /* NOTE: This is to implement a custom cancellation handling where
+         *       we immediately emit an ungrab call if grabbing was cancelled.
+         */
+        gboolean cancelled;
+} GrabUngrabData;
 
 struct GsdMediaKeysManagerPrivate
 {
@@ -200,8 +204,9 @@ struct GsdMediaKeysManagerPrivate
         GsdShell        *shell_proxy;
         ShellKeyGrabber *key_grabber;
         GCancellable    *grab_cancellable;
-        GHashTable      *keys_pending_grab;
-        GHashTable      *keys_to_grab;
+        GHashTable      *keys_to_sync;
+        guint            keys_sync_source_id;
+        GrabUngrabData  *keys_sync_data;
 
         /* ScreenSaver stuff */
         GsdScreenSaver  *screen_saver_proxy;
@@ -248,12 +253,12 @@ 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 void     keys_sync_queue                    (GsdMediaKeysManager *manager,
+                                                    gboolean             immediate,
+                                                    gboolean             retry);
+static void     keys_sync_continue                 (GsdMediaKeysManager *manager);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsdMediaKeysManager, gsd_media_keys_manager, G_TYPE_OBJECT)
 
 static gpointer manager_object = NULL;
 
@@ -284,6 +289,26 @@ media_key_new (void)
         return media_key_ref (key);
 }
 
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MediaKey, media_key_unref)
+
+static void
+grab_ungrab_data_free (GrabUngrabData *data)
+{
+        /* NOTE: The manager pointer is not owned and is invalid if the
+         *       operation was cancelled.
+         *       Calculating the priv reference is always safe though. */
+        GsdMediaKeysManagerPrivate *priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (data->manager);
+
+        if (!data->cancelled && priv->keys_sync_data == data)
+                priv->keys_sync_data = NULL;
+
+        data->manager = NULL;
+        g_clear_pointer (&data->keys, g_ptr_array_unref);
+        g_free (data);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrabUngrabData, grab_ungrab_data_free)
+
 static void
 set_launch_context_env (GsdMediaKeysManager *manager,
                        GAppLaunchContext   *launch_context)
@@ -430,14 +455,55 @@ get_icon_name_for_volume (gboolean is_mic,
                return icon_names[n];
 }
 
-static gboolean
-retry_grabs (gpointer data)
+static void
+ungrab_accelerators_complete (GObject      *object,
+                              GAsyncResult *result,
+                              gpointer      user_data)
 {
-        GsdMediaKeysManager *manager = data;
+        g_autoptr(GrabUngrabData) data = user_data;
+        gboolean success = FALSE;
+        g_autoptr(GError) error = NULL;
+        gint i;
 
-        g_debug ("Retrying to grab accelerators");
-        grab_media_keys (manager);
-        return FALSE;
+        g_debug ("Ungrab call completed!");
+
+        if (!shell_key_grabber_call_ungrab_accelerators_finish (SHELL_KEY_GRABBER (object),
+                                                                &success, result, &error)) {
+                g_warning ("Failed to ungrab accelerators: %s", error->message);
+
+                if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
+                        keys_sync_queue (data->manager, FALSE, TRUE);
+                        return;
+                }
+
+                /* We are screwed at this point; we'll still keep going assuming that we don't
+                 * have the bindings registered anymore.
+                 * The only alternative would be to die and force cleanup of the all registered
+                 * grabs that way.
+                 */
+                keys_sync_continue (data->manager);
+                return;
+        }
+
+        if (!success) {
+                g_warning ("Failed to ungrab some accelerators, they were probably not registered!");
+        }
+
+        /* Clear the accelerator IDs. */
+        for (i = 0; i < data->keys->len; i++) {
+                MediaKey *key;
+
+                key = g_ptr_array_index (data->keys, i);
+
+                /* Always clear, as it would just fail again the next time. */
+                key->accel_id = 0;
+        }
+
+        /* Nothing left to do if the operation was cancelled */
+        if (data->cancelled)
+                return;
+
+        keys_sync_continue (data->manager);
 }
 
 static void
@@ -445,179 +511,193 @@ grab_accelerators_complete (GObject      *object,
                             GAsyncResult *result,
                             gpointer      user_data)
 {
-        GVariant *ret, *actions;
-        gboolean retry = FALSE;
-        GError *error = NULL;
-        GsdMediaKeysManager *manager = user_data;
+        g_autoptr(GrabUngrabData) data = user_data;
+        g_autoptr(GVariant) actions = NULL;
+        g_autoptr(GError) error = NULL;
+        gint i;
 
-        ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), result, &error);
-        g_variant_get (ret, "(@au)", &actions);
-        g_variant_unref (ret);
+        g_debug ("Grab call completed!");
 
-        if (error) {
-                retry = g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
-                if (!retry && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-                        g_warning ("Failed to grab accelerators: %s (%d)", error->message, error->code);
-                else if (retry)
-                        g_debug ("Failed to grab accelerators, will retry: %s (%d)", error->message, 
error->code);
-                g_error_free (error);
-        } else {
-                int i;
-                for (i = 0; i < manager->priv->keys->len; i++) {
-                        MediaKey *key;
+        if (!shell_key_grabber_call_grab_accelerators_finish (SHELL_KEY_GRABBER (object),
+                                                              &actions, result, &error)) {
+                g_warning ("Failed to grab accelerators: %s", error->message);
 
-                        key = g_ptr_array_index (manager->priv->keys, i);
-                        g_variant_get_child (actions, i, "u", &key->accel_id);
+                if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
+                        keys_sync_queue (data->manager, FALSE, TRUE);
+                        return;
                 }
-        }
 
-        if (retry) {
-                guint id;
-                id = g_timeout_add_seconds (SHELL_GRABBER_RETRY_INTERVAL,
-                                            retry_grabs, manager);
-                g_source_set_name_by_id (id, "[gnome-settings-daemon] retry_grabs");
+                /* We are screwed at this point; we'll still keep going assuming that we don't
+                 * have the bindings registered anymore.
+                 * The only alternative would be to restart and try again.
+                 */
+                keys_sync_continue (data->manager);
+
+                return;
         }
-}
 
-static void
-grab_media_keys (GsdMediaKeysManager *manager)
-{
-        GVariantBuilder builder;
-        int i;
+        /* Do an immediate ungrab if the operation was cancelled.
+         * This may happen on daemon shutdown for example. */
+        if (data->cancelled) {
+                g_debug ("Doing an immediate ungrab on the grabbed accelerators!");
 
-        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(suu)"));
+                shell_key_grabber_call_ungrab_accelerators (SHELL_KEY_GRABBER (object),
+                                                            actions,
+                                                            NULL,
+                                                            ungrab_accelerators_complete,
+                                                            g_steal_pointer (&data));
 
-        for (i = 0; i < manager->priv->keys->len; i++) {
+                return;
+        }
+
+        /* We need to stow away the accel_ids that have been registered successfully. */
+        for (i = 0; i < data->keys->len; i++) {
                 MediaKey *key;
-                char *tmp;
+                guint accel_id;
 
-                key = g_ptr_array_index (manager->priv->keys, i);
-                tmp = get_binding (manager, key);
-                g_variant_builder_add (&builder, "(suu)", tmp, key->modes, key->grab_flags);
-                g_free (tmp);
+                key = g_ptr_array_index (data->keys, i);
+                g_assert (key->accel_id == 0);
+
+                g_variant_get_child (actions, i, "u", &accel_id);
+                if (accel_id == 0) {
+                        g_autofree gchar *tmp = NULL;
+                        tmp = get_key_string (key);
+                        g_warning ("Failed to grab accelerator for keybinding %s", tmp);
+                } else {
+                        key->accel_id = accel_id;
+                }
         }
 
-        g_dbus_proxy_call (G_DBUS_PROXY (manager->priv->key_grabber),
-                           "GrabAccelerators",
-                           g_variant_new ("(@a(suu))",
-                                          g_variant_builder_end (&builder)),
-                           G_DBUS_CALL_FLAGS_NONE,
-                           SHELL_GRABBER_CALL_TIMEOUT,
-                           manager->priv->grab_cancellable,
-                           grab_accelerators_complete,
-                           manager);
+        keys_sync_continue (data->manager);
 }
 
 static void
-grab_accelerator_complete (GObject      *object,
-                           GAsyncResult *result,
-                           gpointer      user_data)
+keys_sync_continue (GsdMediaKeysManager *manager)
 {
-        char *keyname;
-        GrabData *data = user_data;
-        MediaKey *key = data->key;
-        GsdMediaKeysManager *manager = data->manager;
-        GError *error = NULL;
+       GsdMediaKeysManagerPrivate *priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager);
+        g_auto(GVariantBuilder) ungrab_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("au"));
+        g_auto(GVariantBuilder) grab_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(suu)"));
+        g_autoptr(GPtrArray) keys_being_ungrabbed = NULL;
+        g_autoptr(GPtrArray) keys_being_grabbed = NULL;
+        g_autoptr(GrabUngrabData) data = NULL;
+        GHashTableIter iter;
+        MediaKey *key;
+        gboolean need_ungrab = FALSE;
 
-        if (!shell_key_grabber_call_grab_accelerator_finish (SHELL_KEY_GRABBER (object),
-                                                             &key->accel_id, result, &error)) {
-                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-                        g_warning ("Failed to grab accelerator: %s", error->message);
-                g_error_free (error);
+        /* If the keys_to_sync hash table is empty at this point, then we are done. */
+        if (g_hash_table_size (priv->keys_to_sync) == 0) {
+                /* XXX: Mark sync operation as done! */
+                return;
         }
 
-        keyname = get_key_string (key);
-        g_hash_table_remove (manager->priv->keys_pending_grab, keyname);
+        keys_being_ungrabbed = g_ptr_array_new_with_free_func ((GDestroyNotify) media_key_unref);
+        keys_being_grabbed = g_ptr_array_new_with_free_func ((GDestroyNotify) media_key_unref);
 
-        if (key->ungrab_requested)
-                ungrab_media_key (key, manager);
+        /* Syncing keys is a two step process in principle, i.e. we first ungrab all keys
+         * and then grab the new ones.
+         * To make this work, this function will be called multiple times and it will
+         * either emit an ungrab or grab call or stop mark the operation as done.
+         */
 
-        media_key_unref (key);
-        g_slice_free (GrabData, data);
+        g_hash_table_iter_init (&iter, priv->keys_to_sync);
+        while (g_hash_table_iter_next (&iter, (gpointer*) &key, NULL)) {
+                g_autofree gchar *tmp = NULL;
 
-        if ((key = g_hash_table_lookup (manager->priv->keys_to_grab, keyname)) != NULL) {
-                grab_media_key (key, manager);
-                g_hash_table_remove (manager->priv->keys_to_grab, keyname);
-        }
-        g_free (keyname);
+                if (key->accel_id > 0) {
+                        g_variant_builder_add (&ungrab_builder, "u", key->accel_id);
+                        g_ptr_array_add (keys_being_ungrabbed, media_key_ref (key));
 
-}
+                        need_ungrab = TRUE;
+                }
 
-static void
-grab_media_key (MediaKey            *key,
-               GsdMediaKeysManager *manager)
-{
-       GrabData *data;
-       char *binding, *keyname;
+                /* XXX: Really skip this if the key is not in the list anymore (i.e. is being removed?) */
+                if (!g_ptr_array_find (priv->keys, key, NULL))
+                        continue;
 
-       keyname = get_key_string (key);
-       binding = get_binding (manager, key);
-        if (g_hash_table_lookup (manager->priv->keys_pending_grab, keyname)) {
-                g_hash_table_insert (manager->priv->keys_to_grab,
-                                     g_strdup (keyname), media_key_ref (key));
-                goto out;
+                tmp = get_binding (manager, key);
+                /* The key might not have a keybinding. */
+                if (tmp && strlen (tmp) > 0) {
+                        g_variant_builder_add (&grab_builder, "(suu)", tmp, key->modes, key->grab_flags);
+                        g_ptr_array_add (keys_being_grabbed, media_key_ref (key));
+                }
         }
 
-       data = g_slice_new0 (GrabData);
-       data->manager = manager;
-       data->key = media_key_ref (key);
+        data = g_new0 (GrabUngrabData, 1);
+        data->manager = manager;
 
-       shell_key_grabber_call_grab_accelerator (manager->priv->key_grabber,
-                                                binding, key->modes, key->grab_flags,
-                                                manager->priv->grab_cancellable,
-                                                grab_accelerator_complete,
-                                                data);
-        g_hash_table_add (manager->priv->keys_pending_grab, g_strdup (keyname));
- out:
-       g_free (keyname);
-       g_free (binding);
-}
+        /* These calls intentionally do not get a cancellable. See comment in
+         * GrabUngrabData.
+         */
+        priv->keys_sync_data = data;
 
-static void
-ungrab_accelerator_complete (GObject      *object,
-                             GAsyncResult *result,
-                             gpointer      user_data)
-{
-        GError *error = NULL;
+        if (need_ungrab) {
+                data->keys = g_steal_pointer (&keys_being_ungrabbed);
 
-        if (!shell_key_grabber_call_ungrab_accelerator_finish (SHELL_KEY_GRABBER (object),
-                                                               NULL, result, &error)) {
-                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-                        g_warning ("Failed to ungrab accelerator: %s", error->message);
-                g_error_free (error);
+                shell_key_grabber_call_ungrab_accelerators (priv->key_grabber,
+                                                            g_variant_builder_end (&ungrab_builder),
+                                                            NULL,
+                                                            ungrab_accelerators_complete,
+                                                            g_steal_pointer (&data));
+        } else {
+                data->keys = g_steal_pointer (&keys_being_grabbed);
+
+                g_hash_table_remove_all (priv->keys_to_sync);
+
+                shell_key_grabber_call_grab_accelerators (priv->key_grabber,
+                                                          g_variant_builder_end (&grab_builder),
+                                                          NULL,
+                                                          grab_accelerators_complete,
+                                                          g_steal_pointer (&data));
         }
 }
 
 static gboolean
-is_pending_grab (MediaKey            *key,
-                 GsdMediaKeysManager *manager)
+keys_sync_start (gpointer user_data)
 {
-       char *keyname = get_key_string (key);
-       const char *val;
-       gboolean pending_grab;
+        GsdMediaKeysManager *manager = GSD_MEDIA_KEYS_MANAGER (user_data);
+        GsdMediaKeysManagerPrivate *priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager);
 
-       val = g_hash_table_lookup (manager->priv->keys_pending_grab, keyname);
-       pending_grab = val != NULL;
-       g_free (keyname);
+        priv->keys_sync_source_id = 0;
+        g_assert (priv->keys_sync_data == NULL);
+        keys_sync_continue (manager);
 
-       return pending_grab;
+        return G_SOURCE_REMOVE;
 }
 
-static void
-ungrab_media_key (MediaKey            *key,
-                  GsdMediaKeysManager *manager)
+void
+keys_sync_queue (GsdMediaKeysManager *manager, gboolean immediate, gboolean retry)
 {
-        if (key->accel_id == 0) {
-                key->ungrab_requested = is_pending_grab (key, manager);
+        GsdMediaKeysManagerPrivate *priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager);
+        guint i;
+
+        if (priv->keys_sync_source_id)
+                g_source_remove (priv->keys_sync_source_id);
+
+        if (retry) {
+                /* Abort the currently running operation, and don't retry
+                 * immediately to avoid race condition if an operation was
+                 * already active. */
+                if (priv->keys_sync_data) {
+                        priv->keys_sync_data->cancelled = TRUE;
+                        priv->keys_sync_data = NULL;
+
+                        immediate = FALSE;
+                }
+
+                /* Mark all existing keys for sync. */
+                for (i = 0; i < priv->keys->len; i++) {
+                        MediaKey *key = g_ptr_array_index (priv->keys, i);
+                        g_hash_table_add (priv->keys_to_sync, media_key_ref (key));
+                }
+        } else if (priv->keys_sync_data) {
+                /* We are already actively syncing, no need to do anything. */
                 return;
         }
 
-       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;
+        priv->keys_sync_source_id =
+                g_timeout_add (immediate ? 0 : (retry ? SHELL_GRABBER_RETRY_INTERVAL_MS : 50),
+                               keys_sync_start,
+                               manager);
 }
 
 static void
@@ -652,8 +732,8 @@ gsettings_changed_cb (GSettings           *settings,
                 if (key->settings_key == NULL)
                         continue;
                 if (strcmp (settings_key, key->settings_key) == 0) {
-                        ungrab_media_key (key, manager);
-                        grab_media_key (key, manager);
+                        g_hash_table_add (manager->priv->keys_to_sync, media_key_ref (key));
+                        keys_sync_queue (manager, FALSE, FALSE);
                         break;
                 }
         }
@@ -715,7 +795,7 @@ update_custom_binding (GsdMediaKeysManager *manager,
                         continue;
                 if (strcmp (key->custom_path, path) == 0) {
                         g_debug ("Removing custom key binding %s", path);
-                        ungrab_media_key (key, manager);
+                        g_hash_table_add (manager->priv->keys_to_sync, media_key_ref (key));
                         g_ptr_array_remove_index_fast (manager->priv->keys, i);
                         break;
                 }
@@ -727,8 +807,10 @@ update_custom_binding (GsdMediaKeysManager *manager,
                 g_debug ("Adding new custom key binding %s", path);
                 g_ptr_array_add (manager->priv->keys, key);
 
-                grab_media_key (key, manager);
+                g_hash_table_add (manager->priv->keys_to_sync, media_key_ref (key));
         }
+
+        keys_sync_queue (manager, FALSE, FALSE);
 }
 
 static void
@@ -801,12 +883,13 @@ gsettings_custom_changed_cb (GSettings           *settings,
                 if (found)
                         continue;
 
-                ungrab_media_key (key, manager);
+                g_hash_table_add (manager->priv->keys_to_sync, media_key_ref (key));
                 g_hash_table_remove (manager->priv->custom_settings,
                                      key->custom_path);
                 g_ptr_array_remove_index_fast (manager->priv->keys, i);
                 --i; /* make up for the removed key */
         }
+        keys_sync_queue (manager, FALSE, FALSE);
         g_strfreev (bindings);
 }
 
@@ -861,7 +944,7 @@ init_kbd (GsdMediaKeysManager *manager)
         }
         g_strfreev (custom_paths);
 
-        grab_media_keys (manager);
+        keys_sync_queue (manager, TRUE, TRUE);
 
         gnome_settings_profile_end (NULL);
 }
@@ -2931,11 +3014,7 @@ start_media_keys_idle_cb (GsdMediaKeysManager *manager)
         gnome_settings_profile_start (NULL);
 
         manager->priv->keys = g_ptr_array_new_with_free_func ((GDestroyNotify) media_key_unref);
-
-        manager->priv->keys_pending_grab = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                                  g_free, NULL);
-        manager->priv->keys_to_grab = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                             g_free, (GDestroyNotify) media_key_unref);
+        manager->priv->keys_to_sync = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) 
media_key_unref, NULL);
 
         initialize_volume_handler (manager);
 
@@ -3034,7 +3113,6 @@ void
 gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
 {
         GsdMediaKeysManagerPrivate *priv = manager->priv;
-        int i;
 
         g_debug ("Stopping media_keys manager");
 
@@ -3103,19 +3181,34 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
         g_clear_pointer (&priv->introspection_data, g_dbus_node_info_unref);
         g_clear_object (&priv->connection);
 
-        if (priv->keys != NULL) {
-                for (i = 0; i < priv->keys->len; ++i) {
-                        MediaKey *key;
+        if (priv->keys_sync_data) {
+                /* Cancel ongoing sync. */
+                priv->keys_sync_data->cancelled = TRUE;
+                priv->keys_sync_data = NULL;
+        }
+        if (priv->keys_sync_source_id)
+                g_source_remove (priv->keys_sync_source_id);
+        priv->keys_sync_source_id = 0;
 
-                        key = g_ptr_array_index (manager->priv->keys, i);
-                        ungrab_media_key (key, manager);
+        /* Remove all grabs; i.e.:
+         *  - add all keys to the sync queue
+         *  - remove all keys from the interal keys list
+         *  - call the function to start a sync
+         *  - "cancel" the sync operation.
+         */
+        if (priv->keys != NULL) {
+                while (priv->keys->len) {
+                        MediaKey *key = g_ptr_array_index (priv->keys, 0);
+                        g_hash_table_add (priv->keys_to_sync, media_key_ref (key));
+                        g_ptr_array_remove_index_fast (priv->keys, 0);
                 }
-                g_ptr_array_free (priv->keys, TRUE);
-                priv->keys = NULL;
+
+                keys_sync_start (manager);
+
+                g_clear_pointer (&priv->keys, g_ptr_array_unref);
         }
 
-        g_clear_pointer (&priv->keys_pending_grab, g_hash_table_destroy);
-        g_clear_pointer (&priv->keys_to_grab, g_hash_table_destroy);
+        g_clear_pointer (&priv->keys_to_sync, g_hash_table_destroy);
 
         g_clear_object (&priv->key_grabber);
 
diff --git a/plugins/media-keys/org.gnome.ShellKeyGrabber.xml 
b/plugins/media-keys/org.gnome.ShellKeyGrabber.xml
index b99e0df2..df7b8d5a 100644
--- a/plugins/media-keys/org.gnome.ShellKeyGrabber.xml
+++ b/plugins/media-keys/org.gnome.ShellKeyGrabber.xml
@@ -7,10 +7,18 @@
       <arg type="u" direction="in" name="grabFlags"/>
       <arg type="u" direction="out" name="action"/>
     </method>
+    <method name="GrabAccelerators">
+      <arg type="a(suu)" direction="in" name="accelerator"/>
+      <arg type="au" direction="out" name="action"/>
+    </method>
     <method name="UngrabAccelerator">
       <arg type="u" direction="in" name="action"/>
       <arg type="b" direction="out" name="success"/>
     </method>
+    <method name="UngrabAccelerators">
+      <arg type="au" direction="in" name="action"/>
+      <arg type="b" direction="out" name="success"/>
+    </method>
     <signal name="AcceleratorActivated">
       <arg type="u" name="action"/>
       <arg type="a{sv}" name="parameters"/>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]