From 0e88796b96e16319fd82dc0625f501df7ca20150 Mon Sep 17 00:00:00 2001 From: Leonhard <106322251+leolost2605@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:44:16 +0200 Subject: [PATCH] wayland: Launch notifications server as a wayland client (#1882) --- lib/Constants.vala | 6 ++ src/ShellClients/ManagedClient.vala | 76 +++++++++++++++++++++++ src/ShellClients/NotificationsClient.vala | 28 +++++++++ src/ShellClients/ShellClientsManager.vala | 20 ++++++ src/WindowManager.vala | 74 ++++++++++++---------- src/meson.build | 3 + 6 files changed, 173 insertions(+), 34 deletions(-) create mode 100644 src/ShellClients/ManagedClient.vala create mode 100644 src/ShellClients/NotificationsClient.vala create mode 100644 src/ShellClients/ShellClientsManager.vala diff --git a/lib/Constants.vala b/lib/Constants.vala index 37868e28..71e21681 100644 --- a/lib/Constants.vala +++ b/lib/Constants.vala @@ -34,4 +34,10 @@ namespace Gala { // Duration of the nudge animation when trying to switch to at the end of the workspace list NUDGE = 360, } + + /** + * Used as a key for Object.set_data on Meta.Windows that should be + * treated as notifications. Has to be set before the window is mapped. + */ + public const string NOTIFICATION_DATA_KEY = "elementary-notification"; } diff --git a/src/ShellClients/ManagedClient.vala b/src/ShellClients/ManagedClient.vala new file mode 100644 index 00000000..d354fc46 --- /dev/null +++ b/src/ShellClients/ManagedClient.vala @@ -0,0 +1,76 @@ +/* + * Copyright 2024 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +/** + * Utility class that takes care of launching and restarting a subprocess. + * On wayland this uses a WaylandClient and emits window_created if a window for the client was created. + * On X this just launches a normal subprocess and never emits window_created. + */ +public class Gala.ManagedClient : Object { + public signal void window_created (Meta.Window window); + + public Meta.Display display { get; construct; } + public string[] args { get; construct; } + + public Meta.WaylandClient? wayland_client { get; private set; } + + public ManagedClient (Meta.Display display, string[] args) { + Object (display: display, args: args); + } + + construct { + if (Meta.Util.is_wayland_compositor ()) { + start_wayland.begin (); + + display.window_created.connect ((window) => { + if (wayland_client != null && wayland_client.owns_window (window)) { + window_created (window); + } + }); + } else { + start_x.begin (); + } + } + + private async void start_wayland () { + var subprocess_launcher = new GLib.SubprocessLauncher (STDERR_PIPE | STDOUT_PIPE); + try { +#if HAS_MUTTER44 + wayland_client = new Meta.WaylandClient (display.get_context (), subprocess_launcher); +#else + wayland_client = new Meta.WaylandClient (subprocess_launcher); +#endif + var subprocess = wayland_client.spawnv (display, args); + + yield subprocess.wait_async (); + + //Restart the daemon if it crashes + Timeout.add_seconds (1, () => { + start_wayland.begin (); + return Source.REMOVE; + }); + } catch (Error e) { + warning ("Failed to create dock client: %s", e.message); + return; + } + } + + private async void start_x () { + try { + var subprocess = new Subprocess.newv (args, NONE); + yield subprocess.wait_async (); + + //Restart the daemon if it crashes + Timeout.add_seconds (1, () => { + start_x.begin (); + return Source.REMOVE; + }); + } catch (Error e) { + warning ("Failed to create daemon subprocess with x: %s", e.message); + } + } +} diff --git a/src/ShellClients/NotificationsClient.vala b/src/ShellClients/NotificationsClient.vala new file mode 100644 index 00000000..fbe8e8c3 --- /dev/null +++ b/src/ShellClients/NotificationsClient.vala @@ -0,0 +1,28 @@ +/* + * Copyright 2024 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.NotificationsClient : Object { + public Meta.Display display { get; construct; } + + private ManagedClient client; + + public NotificationsClient (Meta.Display display) { + Object (display: display); + } + + construct { + client = new ManagedClient (display, { "io.elementary.notifications" }); + + client.window_created.connect ((window) => { + window.set_data (NOTIFICATION_DATA_KEY, true); + window.make_above (); +#if HAS_MUTTER_46 + client.wayland_client.make_dock (window); +#endif + }); + } +} diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala new file mode 100644 index 00000000..74f26ac5 --- /dev/null +++ b/src/ShellClients/ShellClientsManager.vala @@ -0,0 +1,20 @@ +/* + * Copyright 2024 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class Gala.ShellClientsManager : Object { + public Meta.Display display { get; construct; } + + private NotificationsClient notifications_client; + + public ShellClientsManager (Meta.Display display) { + Object (display: display); + } + + construct { + notifications_client = new NotificationsClient (display); + } +} diff --git a/src/WindowManager.vala b/src/WindowManager.vala index 9c12d923..1e2a4478 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -83,6 +83,8 @@ namespace Gala { private DaemonManager daemon_manager; + private ShellClientsManager shell_clients_manager; + private WindowGrabTracker window_grab_tracker; private NotificationStack notification_stack; @@ -143,6 +145,7 @@ namespace Gala { } public override void start () { + shell_clients_manager = new ShellClientsManager (get_display ()); daemon_manager = new DaemonManager (get_display ()); window_grab_tracker = new WindowGrabTracker (get_display ()); @@ -1446,10 +1449,20 @@ namespace Gala { move_window_to_next_ws (window); } + actor.remove_all_transitions (); + actor.show (); + // Notifications are a special case and have to be always be handled - // regardless of the animation setting - if (!enable_animations && window.window_type != Meta.WindowType.NOTIFICATION) { - actor.show (); + // (also regardless of the animation setting) + if (window.get_data (NOTIFICATION_DATA_KEY) || window.window_type == NOTIFICATION) { + clutter_actor_reparent (actor, notification_group); + notification_stack.show_notification (actor, enable_animations); + + map_completed (actor); + return; + } + + if (!enable_animations) { map_completed (actor); if (InternalUtils.get_window_is_normal (window) && window.get_layer () == Meta.StackLayer.BOTTOM) { @@ -1459,9 +1472,6 @@ namespace Gala { return; } - actor.remove_all_transitions (); - actor.show (); - switch (window.window_type) { case Meta.WindowType.NORMAL: var duration = AnimationDuration.HIDE; @@ -1558,12 +1568,6 @@ namespace Gala { dim_parent_window (window); - break; - case Meta.WindowType.NOTIFICATION: - clutter_actor_reparent (actor, notification_group); - notification_stack.show_notification (actor, enable_animations); - map_completed (actor); - break; default: map_completed (actor); @@ -1576,7 +1580,30 @@ namespace Gala { ws_assoc.remove (window); - if (!enable_animations && window.window_type != Meta.WindowType.NOTIFICATION) { + actor.remove_all_transitions (); + + if (window.get_data (NOTIFICATION_DATA_KEY) || window.window_type == NOTIFICATION) { + if (enable_animations) { + destroying.add (actor); + } + + notification_stack.destroy_notification (actor, enable_animations); + + if (enable_animations) { + ulong destroy_handler_id = 0UL; + destroy_handler_id = actor.transitions_completed.connect (() => { + actor.disconnect (destroy_handler_id); + destroying.remove (actor); + destroy_completed (actor); + }); + } else { + destroy_completed (actor); + } + + return; + } + + if (!enable_animations) { destroy_completed (actor); if (window.window_type == Meta.WindowType.NORMAL) { @@ -1586,8 +1613,6 @@ namespace Gala { return; } - actor.remove_all_transitions (); - switch (window.window_type) { case Meta.WindowType.NORMAL: var duration = AnimationDuration.CLOSE; @@ -1659,25 +1684,6 @@ namespace Gala { destroying.remove (actor); destroy_completed (actor); }); - break; - case Meta.WindowType.NOTIFICATION: - if (enable_animations) { - destroying.add (actor); - } - - notification_stack.destroy_notification (actor, enable_animations); - - if (enable_animations) { - ulong destroy_handler_id = 0UL; - destroy_handler_id = actor.transitions_completed.connect (() => { - actor.disconnect (destroy_handler_id); - destroying.remove (actor); - destroy_completed (actor); - }); - } else { - destroy_completed (actor); - } - break; default: destroy_completed (actor); diff --git a/src/meson.build b/src/meson.build index 201ed842..4c23aa2b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -39,6 +39,9 @@ gala_bin_sources = files( 'HotCorners/Barrier.vala', 'HotCorners/HotCorner.vala', 'HotCorners/HotCornerManager.vala', + 'ShellClients/ManagedClient.vala', + 'ShellClients/NotificationsClient.vala', + 'ShellClients/ShellClientsManager.vala', 'Widgets/DwellClickTimer.vala', 'Widgets/IconGroup.vala', 'Widgets/IconGroupContainer.vala',