wayland: Launch notifications server as a wayland client (#1882)

This commit is contained in:
Leonhard 2024-04-02 12:44:16 +02:00 committed by GitHub
parent 4c6f503b31
commit 0e88796b96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 173 additions and 34 deletions

View File

@ -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<bool> 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";
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/
/**
* 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);
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/
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
});
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/
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);
}
}

View File

@ -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);

View File

@ -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',