diff --git a/data/dock.gschema.xml b/data/dock.gschema.xml
index 96664d1c3..8ecc90868 100644
--- a/data/dock.gschema.xml
+++ b/data/dock.gschema.xml
@@ -27,4 +27,51 @@
An ordered array of app id's to show as launchers
+
+
+ 1']]]>
+ Launch the first dock item
+
+
+
+ 2']]]>
+ Launch the second dock item
+
+
+
+ 3']]]>
+ Launch the third dock item
+
+
+
+ 4']]]>
+ Launch the fourth dock item
+
+
+
+ 5']]]>
+ Launch the fith dock item
+
+
+
+ 6']]]>
+ Launch the sixth dock item
+
+
+
+ 7']]]>
+ Launch the seventh dock item
+
+
+
+ 8']]]>
+ Launch the eigth dock item
+
+
+
+ 9']]]>
+ Launch the nineth dock item
+
+
+
diff --git a/src/Application.vala b/src/Application.vala
index 85eb5a61a..207ed5fb8 100644
--- a/src/Application.vala
+++ b/src/Application.vala
@@ -12,6 +12,7 @@ public class Dock.Application : Gtk.Application {
base.startup ();
Granite.init ();
+ ShellKeyGrabber.init ();
unowned var granite_settings = Granite.Settings.get_default ();
unowned var gtk_settings = Gtk.Settings.get_default ();
diff --git a/src/DBus/ShellKeyGrabber.vala b/src/DBus/ShellKeyGrabber.vala
new file mode 100644
index 000000000..52fc228f1
--- /dev/null
+++ b/src/DBus/ShellKeyGrabber.vala
@@ -0,0 +1,124 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0
+ * SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
+ */
+
+/**
+ * ActionMode:
+ * @NONE: block action
+ * @NORMAL: allow action when in window mode, e.g. when the focus is in an application window
+ * @OVERVIEW: allow action while the overview is active
+ * @LOCK_SCREEN: allow action when the screen is locked, e.g. when the screen shield is shown
+ * @UNLOCK_SCREEN: allow action in the unlock dialog
+ * @LOGIN_SCREEN: allow action in the login screen
+ * @SYSTEM_MODAL: allow action when a system modal dialog (e.g. authentification or session dialogs) is open
+ * @LOOKING_GLASS: allow action in looking glass
+ * @POPUP: allow action while a shell menu is open
+ */
+
+[Flags]
+public enum ActionMode {
+ NONE = 0,
+ NORMAL = 1 << 0,
+ OVERVIEW = 1 << 1,
+ LOCK_SCREEN = 1 << 2,
+ UNLOCK_SCREEN = 1 << 3,
+ LOGIN_SCREEN = 1 << 4,
+ SYSTEM_MODAL = 1 << 5,
+ LOOKING_GLASS = 1 << 6,
+ POPUP = 1 << 7,
+}
+
+[Flags]
+public enum Meta.KeyBindingFlags {
+ NONE = 0,
+ PER_WINDOW = 1 << 0,
+ BUILTIN = 1 << 1,
+ IS_REVERSED = 1 << 2,
+ NON_MASKABLE = 1 << 3,
+ IGNORE_AUTOREPEAT = 1 << 4,
+}
+
+public struct Accelerator {
+ public string name;
+ public ActionMode mode_flags;
+ public Meta.KeyBindingFlags grab_flags;
+}
+
+[DBus (name = "org.gnome.Shell")]
+public interface ShellKeyGrabber : GLib.Object {
+ public abstract signal void accelerator_activated (uint action, GLib.HashTable parameters_dict);
+
+ public abstract uint grab_accelerator (string accelerator, ActionMode mode_flags, Meta.KeyBindingFlags grab_flags) throws GLib.DBusError, GLib.IOError;
+ public abstract uint[] grab_accelerators (Accelerator[] accelerators) throws GLib.DBusError, GLib.IOError;
+ public abstract bool ungrab_accelerator (uint action) throws GLib.DBusError, GLib.IOError;
+ public abstract bool ungrab_accelerators (uint[] actions) throws GLib.DBusError, GLib.IOError;
+
+ private static Settings settings;
+ private static ShellKeyGrabber? instance;
+
+ private static HashTable saved_action_ids;
+
+ public static void init () {
+ settings = new Settings ("io.elementary.dock.keybindings");
+ saved_action_ids = new HashTable (null, null);
+
+ settings.changed.connect (() => {
+ ungrab_keybindings ();
+ setup_grabs ();
+ });
+
+ Bus.watch_name (BusType.SESSION, "org.gnome.Shell", BusNameWatcherFlags.NONE, () => on_watch.begin (), () => instance = null);
+ }
+
+ private static async void on_watch () {
+ try {
+ instance = yield Bus.get_proxy (SESSION, "org.gnome.Shell", "/org/gnome/Shell");
+ setup_grabs ();
+ } catch (Error e) {
+ warning ("Failed to connect to bus for keyboard shortcut grabs: %s", e.message);
+ }
+ }
+
+ private static void setup_grabs () requires (instance != null) {
+ for (int i = 1; i <= 9; i++) {
+ var keybindings = settings.get_strv ("launch-dock-%d".printf (i));
+ Accelerator[] accelerators = {};
+ for (int j = 0; j < keybindings.length; j++) {
+ accelerators += Accelerator () {
+ name = keybindings[j],
+ mode_flags = ActionMode.NONE,
+ grab_flags = Meta.KeyBindingFlags.NONE
+ };
+
+ try {
+ foreach (var id in instance.grab_accelerators (accelerators)) {
+ saved_action_ids[id] = i;
+ }
+ } catch (Error e) {
+ critical ("Couldn't grab accelerators: %s", e.message);
+ }
+ }
+ }
+
+ instance.accelerator_activated.connect (on_accelerator_activated);
+ }
+
+ private static void on_accelerator_activated (uint action, GLib.HashTable parameters_dict) {
+ if (!(action in saved_action_ids)) {
+ return;
+ }
+
+ Dock.LauncherManager.get_default ().launch (saved_action_ids[action]);
+ }
+
+ private static void ungrab_keybindings () requires (instance != null) {
+ var actions = saved_action_ids.get_keys_as_array ();
+
+ try {
+ instance.ungrab_accelerators (actions);
+ } catch (Error e) {
+ critical ("Couldn't ungrab accelerators: %s", e.message);
+ }
+ }
+}
diff --git a/src/LauncherManager.vala b/src/LauncherManager.vala
index 68502793e..94dd638bf 100644
--- a/src/LauncherManager.vala
+++ b/src/LauncherManager.vala
@@ -313,6 +313,15 @@
settings.set_strv ("launchers", new_pinned_ids);
}
+ public void launch (uint index) {
+ if (index < 1 || index > launchers.length ()) {
+ return;
+ }
+
+ var context = Gdk.Display.get_default ().get_app_launch_context ();
+ launchers.nth (index - 1).data.app.launch (context);
+ }
+
public void add_launcher_for_id (string app_id) {
if (app_id in id_to_app) {
id_to_app[app_id].pinned = true;
diff --git a/src/meson.build b/src/meson.build
index 0ed8fe590..30e3413d8 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -8,6 +8,7 @@ sources = [
'MainWindow.vala',
'PoofPopover.vala',
'DBus' / 'ItemInterface.vala',
+ 'DBus' / 'ShellKeyGrabber.vala',
'DBus' / 'SwitcherooControl.vala',
'DBus' / 'Unity.vala'
]