Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 5 additions & 45 deletions daemon-gtk3/DBus.vala
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* Copyright 2024-2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*/

[DBus (name = "org.pantheon.gala")]
public interface Gala.WMDBus : GLib.Object {
public abstract void perform_action (Gala.ActionType type) throws DBusError, IOError;
}

public struct Gala.Daemon.MonitorLabelInfo {
public int monitor;
public string label;
Expand All @@ -19,58 +14,23 @@ public struct Gala.Daemon.MonitorLabelInfo {

[DBus (name = "org.pantheon.gala.daemon")]
public class Gala.Daemon.DBus : GLib.Object {
private const string DBUS_NAME = "org.pantheon.gala";
private const string DBUS_OBJECT_PATH = "/org/pantheon/gala";

private const string DAEMON_DBUS_NAME = "org.pantheon.gala.daemon";
private const string DAEMON_DBUS_OBJECT_PATH = "/org/pantheon/gala/daemon";

private WMDBus? wm_proxy = null;
public signal void window_menu_action_invoked (int action);

private WindowMenu? window_menu;
private BackgroundMenu? background_menu;

private List<MonitorLabel> monitor_labels = new List<MonitorLabel> ();

construct {
Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala);
}

private void on_gala_get (GLib.Object? obj, GLib.AsyncResult? res) {
try {
wm_proxy = Bus.get_proxy.end (res);
} catch (Error e) {
warning ("Failed to get Gala proxy: %s", e.message);
}
}

private void lost_gala () {
wm_proxy = null;
}

private void gala_appeared () {
if (wm_proxy == null) {
Bus.get_proxy.begin<WMDBus> (BusType.SESSION, DBUS_NAME, DBUS_OBJECT_PATH, 0, null, on_gala_get);
}
}

private void perform_action (Gala.ActionType type) {
if (wm_proxy != null) {
try {
wm_proxy.perform_action (type);
} catch (Error e) {
warning ("Failed to perform Gala action over DBus: %s", e.message);
}
}
}

public void show_window_menu (Gala.WindowFlags flags, int display_width, int display_height, int x, int y) throws DBusError, IOError {
public void show_window_menu (int display_width, int display_height, int x, int y, DaemonWindowMenuItem[] items) throws DBusError, IOError {
if (window_menu == null) {
window_menu = new WindowMenu ();
window_menu.perform_action.connect (perform_action);
window_menu.action_invoked.connect ((action) => window_menu_action_invoked (action));
}

window_menu.update (flags);
window_menu.update (items);

show_menu (window_menu, display_width, display_height, x, y, true);
}
Expand Down
203 changes: 27 additions & 176 deletions daemon-gtk3/WindowMenu.vala
Original file line number Diff line number Diff line change
@@ -1,194 +1,45 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* Copyright 2024-2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*/

public class Gala.Daemon.WindowMenu : Gtk.Menu {
private static GLib.Settings gala_keybind_settings = new GLib.Settings ("io.elementary.desktop.wm.keybindings");
private static GLib.Settings keybind_settings = new GLib.Settings ("org.gnome.desktop.wm.keybindings");
public signal void action_invoked (int action);

public signal void perform_action (Gala.ActionType type);

private Granite.AccelLabel always_on_top_accellabel;
private Granite.AccelLabel close_accellabel;
private Granite.AccelLabel minimize_accellabel;
private Granite.AccelLabel move_accellabel;
private Granite.AccelLabel move_left_accellabel;
private Granite.AccelLabel move_right_accellabel;
private Granite.AccelLabel on_visible_workspace_accellabel;
private Granite.AccelLabel resize_accellabel;
private Granite.AccelLabel screenshot_accellabel;
private Gtk.MenuItem minimize;
private Gtk.MenuItem maximize;
private Gtk.MenuItem move;
private Gtk.MenuItem resize;
private Gtk.CheckMenuItem always_on_top;
private Gtk.CheckMenuItem on_visible_workspace;
private Gtk.MenuItem move_left;
private Gtk.MenuItem move_right;
private Gtk.MenuItem close;
private Gtk.MenuItem screenshot;

private ulong always_on_top_sid = 0U;
private ulong on_visible_workspace_sid = 0U;

construct {
minimize_accellabel = new Granite.AccelLabel (_("Hide"));

minimize = new Gtk.MenuItem ();
minimize.add (minimize_accellabel);
minimize.activate.connect (() => {
perform_action (Gala.ActionType.HIDE_CURRENT);
});

maximize = new Gtk.MenuItem ();
maximize.activate.connect (() => {
perform_action (Gala.ActionType.MAXIMIZE_CURRENT);
});

move_accellabel = new Granite.AccelLabel (_("Move"));

move = new Gtk.MenuItem ();
move.add (move_accellabel);
move.activate.connect (() => {
perform_action (Gala.ActionType.START_MOVE_CURRENT);
});

resize_accellabel = new Granite.AccelLabel (_("Resize"));

resize = new Gtk.MenuItem ();
resize.add (resize_accellabel);
resize.activate.connect (() => {
perform_action (Gala.ActionType.START_RESIZE_CURRENT);
});

always_on_top_accellabel = new Granite.AccelLabel (_("Always on Top"));

always_on_top = new Gtk.CheckMenuItem ();
always_on_top.add (always_on_top_accellabel);
always_on_top_sid = always_on_top.activate.connect (() => {
perform_action (Gala.ActionType.TOGGLE_ALWAYS_ON_TOP_CURRENT);
});

on_visible_workspace_accellabel = new Granite.AccelLabel (_("Always on Visible Workspace"));

on_visible_workspace = new Gtk.CheckMenuItem ();
on_visible_workspace.add (on_visible_workspace_accellabel);
on_visible_workspace_sid = on_visible_workspace.activate.connect (() => {
perform_action (Gala.ActionType.TOGGLE_ALWAYS_ON_VISIBLE_WORKSPACE_CURRENT);
});

move_left_accellabel = new Granite.AccelLabel (_("Move to Workspace Left"));

move_left = new Gtk.MenuItem ();
move_left.add (move_left_accellabel);
move_left.activate.connect (() => {
perform_action (Gala.ActionType.MOVE_CURRENT_WORKSPACE_LEFT);
});

move_right_accellabel = new Granite.AccelLabel (_("Move to Workspace Right"));
public void update (DaemonWindowMenuItem[] items) {
foreach (unowned var child in get_children ()) {
remove (child);
}

move_right = new Gtk.MenuItem ();
move_right.add (move_right_accellabel);
move_right.activate.connect (() => {
perform_action (Gala.ActionType.MOVE_CURRENT_WORKSPACE_RIGHT);
});
for (var i = 0; i < items.length; i++) {
var item = items[i];

screenshot_accellabel = new Granite.AccelLabel (_("Take Screenshot"));
var accel_label = new Granite.AccelLabel (item.display_name, item.keybinding);

screenshot = new Gtk.MenuItem ();
screenshot.add (screenshot_accellabel);
screenshot.activate.connect (() => {
perform_action (Gala.ActionType.SCREENSHOT_CURRENT);
});
if (item.type == BUTTON) {
var button = new Gtk.MenuItem () {
child = accel_label,
sensitive = item.sensitive
};

close_accellabel = new Granite.AccelLabel (_("Close"));
var i_copy = i;
button.activate.connect (() => action_invoked (i_copy));

close = new Gtk.MenuItem ();
close.add (close_accellabel);
close.activate.connect (() => {
perform_action (Gala.ActionType.CLOSE_CURRENT);
});
append (button);
} else if (item.type == TOGGLE) {
var button = new Gtk.CheckMenuItem () {
child = accel_label,
sensitive = item.sensitive,
active = item.toggle_state
};

append (screenshot);
append (new Gtk.SeparatorMenuItem ());
append (always_on_top);
append (on_visible_workspace);
append (move_left);
append (move_right);
append (new Gtk.SeparatorMenuItem ());
append (move);
append (resize);
append (maximize);
append (new Gtk.SeparatorMenuItem ());
append (minimize);
append (close);
}
var i_copy = i;
button.activate.connect (() => action_invoked (i_copy));

public void update (Gala.WindowFlags flags) {
minimize.visible = Gala.WindowFlags.CAN_HIDE in flags;
if (minimize.visible) {
minimize_accellabel.accel_string = keybind_settings.get_strv ("minimize")[0];
}

maximize.visible = Gala.WindowFlags.CAN_MAXIMIZE in flags;
if (maximize.visible) {
unowned string maximize_label;
if (Gala.WindowFlags.IS_MAXIMIZED in flags) {
maximize_label = (Gala.WindowFlags.IS_TILED in flags) ? _("Untile") : _("Unmaximize");
append (button);
} else {
maximize_label = _("Maximize");
append (new Gtk.SeparatorMenuItem ());
}

maximize.get_child ().destroy ();
maximize.add (
new Granite.AccelLabel (
maximize_label,
keybind_settings.get_strv ("toggle-maximized")[0]
)
);
}


move.visible = Gala.WindowFlags.ALLOWS_MOVE in flags;
if (move.visible) {
move_accellabel.accel_string = keybind_settings.get_strv ("begin-move")[0];
}

resize.visible = Gala.WindowFlags.ALLOWS_RESIZE in flags;
if (resize.visible) {
resize_accellabel.accel_string = keybind_settings.get_strv ("begin-resize")[0];
}

// Setting active causes signal fires on activate so
// we temporarily block those signals from emissions
SignalHandler.block (always_on_top, always_on_top_sid);
SignalHandler.block (on_visible_workspace, on_visible_workspace_sid);

always_on_top.active = Gala.WindowFlags.ALWAYS_ON_TOP in flags;
always_on_top_accellabel.accel_string = keybind_settings.get_strv ("always-on-top")[0];

on_visible_workspace.active = Gala.WindowFlags.ON_ALL_WORKSPACES in flags;
on_visible_workspace_accellabel.accel_string = keybind_settings.get_strv ("toggle-on-all-workspaces")[0];

SignalHandler.unblock (always_on_top, always_on_top_sid);
SignalHandler.unblock (on_visible_workspace, on_visible_workspace_sid);

move_right.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_RIGHT in flags;
if (move_right.sensitive) {
move_right_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-right")[0];
}

move_left.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_LEFT in flags;
if (move_left.sensitive) {
move_left_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-left")[0];
}

screenshot_accellabel.accel_string = gala_keybind_settings.get_strv ("window-screenshot")[0];

close.visible = Gala.WindowFlags.CAN_CLOSE in flags;
if (close.visible) {
close_accellabel.accel_string = keybind_settings.get_strv ("close")[0];
}
}
}
53 changes: 8 additions & 45 deletions daemon/DBus.vala
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* Copyright 2024-2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*/

[DBus (name = "org.pantheon.gala")]
public interface Gala.WMDBus : GLib.Object {
public abstract void perform_action (Gala.ActionType type) throws DBusError, IOError;
}

public struct Gala.Daemon.MonitorLabelInfo {
public int monitor;
public string label;
Expand All @@ -19,26 +14,21 @@ public struct Gala.Daemon.MonitorLabelInfo {

[DBus (name = "org.pantheon.gala.daemon")]
public class Gala.Daemon.DBus : GLib.Object {
private const string DBUS_NAME = "org.pantheon.gala";
private const string DBUS_OBJECT_PATH = "/org/pantheon/gala";

private const string DAEMON_DBUS_NAME = "org.pantheon.gala.daemon";
private const string DAEMON_DBUS_OBJECT_PATH = "/org/pantheon/gala/daemon";

private const string BG_MENU_ACTION_GROUP_PREFIX = "background-menu";
private const string BG_MENU_ACTION_PREFIX = BG_MENU_ACTION_GROUP_PREFIX + ".";

private WMDBus? wm_proxy = null;
public signal void window_menu_action_invoked (int action);

private Window window;
private WindowMenu? window_menu;
private WindowMenu window_menu;
private Gtk.PopoverMenu background_menu;

private List<MonitorLabel> monitor_labels = new List<MonitorLabel> ();

construct {
Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala);

window = new Window ();

var background_menu_top_section = new Menu ();
Expand Down Expand Up @@ -81,44 +71,17 @@ public class Gala.Daemon.DBus : GLib.Object {
window_menu = new WindowMenu ();
window_menu.set_parent (window.child);
window_menu.closed.connect (window.close);
window_menu.perform_action.connect ((type) => {
window_menu.action_invoked.connect ((action) => {
// Using Idle here because we need to wait until focus changes from the daemon window
Idle.add (() => {
perform_action (type);
window_menu_action_invoked (action);
return Source.REMOVE;
});
});
}

private void on_gala_get (GLib.Object? obj, GLib.AsyncResult? res) {
try {
wm_proxy = Bus.get_proxy.end (res);
} catch (Error e) {
warning ("Failed to get Gala proxy: %s", e.message);
}
}

private void lost_gala () {
wm_proxy = null;
}

private void gala_appeared () {
if (wm_proxy == null) {
Bus.get_proxy.begin<WMDBus> (BusType.SESSION, DBUS_NAME, DBUS_OBJECT_PATH, 0, null, on_gala_get);
}
}

private void perform_action (Gala.ActionType type) {
if (wm_proxy != null) {
try {
wm_proxy.perform_action (type);
} catch (Error e) {
warning ("Failed to perform Gala action over DBus: %s", e.message);
}
}
}

public void show_window_menu (Gala.WindowFlags flags, int display_width, int display_height, int x, int y) throws DBusError, IOError {
window_menu.update (flags);
public void show_window_menu (int display_width, int display_height, int x, int y, DaemonWindowMenuItem[] items) throws DBusError, IOError {
window_menu.update (items);

show_menu (window_menu, display_width, display_height, x, y);
}
Expand Down
Loading