Add ShellComponentManager

This commit is contained in:
Leonhard 2024-02-29 11:57:22 +01:00
parent 053ab0ead4
commit a503516bb0
4 changed files with 186 additions and 1 deletions

69
src/ManagedClient.vala Normal file
View File

@ -0,0 +1,69 @@
/**
* 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 shown
* On X this just launches a normal subprocess
*/
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

@ -38,6 +38,7 @@ namespace Gala {
private static Pantheon.Desktop.WidgetInterface wayland_pantheon_widget_interface;
private static Pantheon.Desktop.ExtendedBehaviorInterface wayland_pantheon_extended_behavior_interface;
private static Wl.Global shell_global;
private static ShellComponentManager component_manager;
public void init_pantheon_shell (Meta.Context context) {
unowned Wl.Display? wl_disp = get_display_from_context (context);
@ -72,6 +73,9 @@ namespace Gala {
var resource = new Wl.Resource (client, ref Pantheon.Desktop.ShellInterface.iface, (int) version, id);
resource.set_implementation (&wayland_pantheon_shell_interface, null, (res) => {});
});
component_manager = new ShellComponentManager ();
component_manager.init (context.get_display ());
}
public class PanelSurface : GLib.Object {
@ -243,7 +247,7 @@ namespace Gala {
return;
}
// TODO
component_manager.set_anchor (window, TOP);
}
internal static void set_keep_above (Wl.Client client, Wl.Resource resource) {

View File

@ -0,0 +1,110 @@
public class Gala.ShellComponentManager : Object {
private GLib.HashTable<Meta.Window, Meta.Side> window_anchors;
private GLib.HashTable<Meta.Window, Meta.Strut?> window_struts;
private GLib.HashTable<Meta.Window, ManagedClient> window_to_client;
private Meta.Display display;
private string[] components = {"io.elementary.dock"};
private ManagedClient[] clients = {};
construct {
window_anchors = new GLib.HashTable<Meta.Window, Meta.Side> (null, null);
window_struts = new GLib.HashTable<Meta.Window, Meta.Strut?> (null, null);
window_to_client = new GLib.HashTable<Meta.Window, ManagedClient> (null, null);
}
public void init (Meta.Display display) {
this.display = display;
foreach (var args in components) {
var client = new ManagedClient (display, components);
client.window_created.connect ((window) => {
window_to_client[window] = client;
window.unmanaged.connect (() => window_to_client.remove (window));
});
clients += client;
}
}
public void set_anchor (Meta.Window window, Meta.Side side) {
#if !HAS_MUTTER46
critical ("Mutter 46 required for wayland docks");
return;
#else
var client = window_to_client[window];
if (client == null) {
critical ("Window doesn't belong to a known client, can't set anchor.");
return;
}
client.make_dock (window);
position_window (window, side);
window_anchors[window] = side;
window.unmanaged.connect (() => {
window_anchors.remove (window);
if (window_struts.remove (window)) {
update_struts ();
}
});
#endif
}
public void make_exclusive (Meta.Window window) {
if (!(window in window_anchors)) {
warning ("Set an anchor before making a window area exclusive.");
return;
}
if (window in window_struts) {
warning ("Window is already exclusive.");
return;
}
window.size_changed.connect (update_strut);
update_strut (window);
}
private void update_strut (Meta.Window window) {
var rect = window.get_frame_rect ();
Meta.Strut strut = {
rect,
window_anchors[window]
};
window_struts[window] = strut;
update_struts ();
}
private void update_struts () {
var list = new SList<Meta.Strut?> ();
foreach (var window_strut in window_struts.get_values ()) {
list.append (window_strut);
}
foreach (var workspace in display.get_workspace_manager ().get_workspaces ()) {
workspace.set_builtin_struts (list);
}
}
public void unmake_exclusive (Meta.Window window) {
if (window in window_struts) {
window.size_changed.disconnect (update_strut);
window_struts.remove (window);
}
}
private void position_window (Meta.Window window, Meta.Side side) {
switch (side) {
case TOP:
break;
default:
break;
}
}
}

View File

@ -6,6 +6,7 @@ gala_bin_sources = files(
'InternalUtils.vala',
'KeyboardManager.vala',
'Main.vala',
'ManagedClient.vala',
'MediaFeedback.vala',
'NotificationStack.vala',
'PantheonShell.vala',
@ -13,6 +14,7 @@ gala_bin_sources = files(
'ScreenSaverManager.vala',
'ScreenshotManager.vala',
'SessionManager.vala',
'ShellComponentManager.vala',
'WindowListener.vala',
'WindowManager.vala',
'WindowStateSaver.vala',