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' ]