From f8346f0d4a8577c1cf81036cc09324727cc98175 Mon Sep 17 00:00:00 2001 From: Leonhard <106322251+leolost2605@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:35:01 +0200 Subject: [PATCH] wayland: Implement make_center (#1942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Danielle Foré --- protocol/pantheon-desktop-shell-v1.xml | 16 +++++++++ protocol/pantheon-desktop-shell.vapi | 4 +++ src/PantheonShell.vala | 39 +++++++++++++++++++-- src/ShellClients/CenteredWindow.vala | 41 +++++++++++++++++++++++ src/ShellClients/ShellClientsManager.vala | 29 +++++++++++----- src/meson.build | 1 + 6 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 src/ShellClients/CenteredWindow.vala diff --git a/protocol/pantheon-desktop-shell-v1.xml b/protocol/pantheon-desktop-shell-v1.xml index 18a7da64..63d9d598 100644 --- a/protocol/pantheon-desktop-shell-v1.xml +++ b/protocol/pantheon-desktop-shell-v1.xml @@ -111,5 +111,21 @@ Tell the shell to keep the surface above on all workspaces + + + + Request to keep the surface centered. This will cause keyboard focus + to not be granted automatically but having to be requested via focus. + + + + + + Request keyboard focus, taking it away from any other window. + Keyboard focus must always be manually be requested and is + - in contrast to normal windows - never automatically granted + by the compositor. + + diff --git a/protocol/pantheon-desktop-shell.vapi b/protocol/pantheon-desktop-shell.vapi index b699c45c..4c57de97 100644 --- a/protocol/pantheon-desktop-shell.vapi +++ b/protocol/pantheon-desktop-shell.vapi @@ -56,6 +56,8 @@ namespace Pantheon.Desktop { public static Wl.Interface iface; public Destroy destroy; public SetKeepAbove set_keep_above; + public MakeCentered make_centered; + public Focus focus; } [CCode (has_target = false, has_typedef = false)] @@ -75,5 +77,7 @@ namespace Pantheon.Desktop { [CCode (has_target = false, has_typedef = false)] public delegate void SetKeepAbove (Wl.Client client, Wl.Resource resource); [CCode (has_target = false, has_typedef = false)] + public delegate void MakeCentered (Wl.Client client, Wl.Resource resource); + [CCode (has_target = false, has_typedef = false)] public delegate void Destroy (Wl.Client client, Wl.Resource resource); } diff --git a/src/PantheonShell.vala b/src/PantheonShell.vala index 1a3b93f0..1e0f173f 100644 --- a/src/PantheonShell.vala +++ b/src/PantheonShell.vala @@ -55,7 +55,7 @@ namespace Gala { wayland_pantheon_panel_interface = { destroy_panel_surface, set_anchor, - focus, + focus_panel, set_size, set_hide_mode, }; @@ -67,9 +67,13 @@ namespace Gala { wayland_pantheon_extended_behavior_interface = { destroy_extended_behavior_surface, set_keep_above, + make_centered, + focus_extended_behavior, }; PanelSurface.quark = GLib.Quark.from_string ("-gala-wayland-panel-surface-data"); + WidgetSurface.quark = GLib.Quark.from_string ("-gala-wayland-widget-surface-data"); + ExtendedBehaviorSurface.quark = GLib.Quark.from_string ("-gala-wayland-extended-behavior-surface-data"); shell_global = Wl.Global.create (wl_disp, ref Pantheon.Desktop.ShellInterface.iface, 1, (client, version, id) => { unowned var resource = client.create_resource (ref Pantheon.Desktop.ShellInterface.iface, (int) version, id); @@ -260,15 +264,29 @@ namespace Gala { ShellClientsManager.get_instance ().set_anchor (window, side); } - internal static void focus (Wl.Client client, Wl.Resource resource) { + internal static void focus_panel (Wl.Client client, Wl.Resource resource) { unowned PanelSurface? panel_surface = resource.get_user_data (); if (panel_surface.wayland_surface == null) { warning ("Window tried to focus but wayland surface is null."); return; } + focus (panel_surface.wayland_surface); + } + + internal static void focus_extended_behavior (Wl.Client client, Wl.Resource resource) { + unowned ExtendedBehaviorSurface? extended_behavior_surface = resource.get_user_data (); + if (extended_behavior_surface.wayland_surface == null) { + warning ("Window tried to focus but wayland surface is null."); + return; + } + + focus (extended_behavior_surface.wayland_surface); + } + + internal static void focus (Object wayland_surface) { Meta.Window? window; - panel_surface.wayland_surface.get ("window", out window, null); + wayland_surface.get ("window", out window, null); if (window == null) { warning ("Window tried to focus but wayland surface had no associated window."); return; @@ -326,6 +344,21 @@ namespace Gala { window.make_above (); } + internal static void make_centered (Wl.Client client, Wl.Resource resource) { + unowned ExtendedBehaviorSurface? eb_surface = resource.get_user_data (); + if (eb_surface.wayland_surface == null) { + return; + } + + Meta.Window? window; + eb_surface.wayland_surface.get ("window", out window, null); + if (window == null) { + return; + } + + ShellClientsManager.get_instance ().make_centered (window); + } + internal static void destroy_panel_surface (Wl.Client client, Wl.Resource resource) { resource.destroy (); } diff --git a/src/ShellClients/CenteredWindow.vala b/src/ShellClients/CenteredWindow.vala new file mode 100644 index 00000000..1363a2ee --- /dev/null +++ b/src/ShellClients/CenteredWindow.vala @@ -0,0 +1,41 @@ +/* + * Copyright 2024 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.CenteredWindow : Object { + public WindowManager wm { get; construct; } + public Meta.Window window { get; construct; } + + public CenteredWindow (WindowManager wm, Meta.Window window) { + Object (wm: wm, window: window); + } + + construct { + window.size_changed.connect (position_window); + window.stick (); + + var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager (); + monitor_manager.monitors_changed.connect (() => position_window ()); + + position_window (); + + window.shown.connect (() => window.focus (wm.get_display ().get_current_time ())); + } + + private void position_window () { + var display = wm.get_display (); + var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); + var window_rect = window.get_frame_rect (); + + var x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; + var y = monitor_geom.y + (monitor_geom.height - window_rect.height) / 2; + + Idle.add (() => { + window.move_frame (false, x, y); + return Source.REMOVE; + }); + } +} diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index 37074668..bd322af7 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -26,6 +26,7 @@ public class Gala.ShellClientsManager : Object { private ManagedClient[] protocol_clients = {}; private GLib.HashTable windows = new GLib.HashTable (null, null); + private GLib.HashTable centered_windows = new GLib.HashTable (null, null); private ShellClientsManager (WindowManager wm) { Object (wm: wm); @@ -97,20 +98,22 @@ public class Gala.ShellClientsManager : Object { } } - public void set_anchor (Meta.Window window, Meta.Side side) { - if (window in windows) { - windows[window].update_anchor (side); - return; - } - -#if HAS_MUTTER46 + private void make_dock (Meta.Window window) { foreach (var client in protocol_clients) { if (client.wayland_client.owns_window (window)) { client.wayland_client.make_dock (window); break; } } -#endif + } + + public void set_anchor (Meta.Window window, Meta.Side side) { + if (window in windows) { + windows[window].update_anchor (side); + return; + } + + make_dock (window); // TODO: Return if requested by window that's not a trusted client? windows[window] = new PanelWindow (wm, window, side); @@ -143,4 +146,14 @@ public class Gala.ShellClientsManager : Object { windows[window].set_hide_mode (hide_mode); } + + public void make_centered (Meta.Window window) { + if (window in centered_windows) { + return; + } + + make_dock (window); + + centered_windows[window] = new CenteredWindow (wm, window); + } } diff --git a/src/meson.build b/src/meson.build index afe387c9..5351711d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -40,6 +40,7 @@ gala_bin_sources = files( 'HotCorners/Barrier.vala', 'HotCorners/HotCorner.vala', 'HotCorners/HotCornerManager.vala', + 'ShellClients/CenteredWindow.vala', 'ShellClients/HideTracker.vala', 'ShellClients/ManagedClient.vala', 'ShellClients/NotificationsClient.vala',