Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
View: restore to previous output on reconnect
river/Root.zig | 23 +++++++++++++++++++++++
river/View.zig | 5 +++++
river/command/output.zig | 9 +++++++++
3 files changed, 37 insertions(+)
diff --git a/river/Root.zig b/river/Root.zig
index d0707c3..d6b86b9 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -286,6 +286,15 @@ pub fn deactivateOutput(root: *Self, output: *Output) void {
if (view.inflight_transaction) {
view.commitTransaction();
}
+
+ // Store outputs connector name so that views can be moved back to
+ // reconnecting outputs. Skip if there is already a connector name
+ // stored to better handle the case of multiple outputs being
+ // removed sequentially.
+ if (view.output_before_evac == null) {
+ const name = mem.span(output.wlr_output.name);
+ view.output_before_evac = util.gpa.dupe(u8, name) catch null;
+ }
}
}
// Use the first output in the list as fallback. If the last real output
@@ -386,6 +395,20 @@ pub fn activateOutput(root: *Self, output: *Output) void {
seat.focusOutput(output);
}
}
+ } else {
+ // Otherwise check if any views were previously evacuated from an output
+ // with the same (connector-)name and move them back.
+ var it = root.views.iterator(.forward);
+ while (it.next()) |view| {
+ const name = view.output_before_evac orelse continue;
+ if (mem.eql(u8, name, mem.span(output.wlr_output.name))) {
+ if (view.pending.output != output) {
+ view.setPendingOutput(output);
+ }
+ util.gpa.free(name);
+ view.output_before_evac = null;
+ }
+ }
}
assert(root.fallback_pending.focus_stack.empty());
assert(root.fallback_pending.wm_stack.empty());
diff --git a/river/View.zig b/river/View.zig
index 311b960..9a8f5ce 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -174,6 +174,9 @@ post_fullscreen_box: wlr.Box = undefined,
foreign_toplevel_handle: ForeignToplevelHandle = .{},
+/// Connector name of the output this view occupied before an evacuation.
+output_before_evac: ?[]const u8 = null,
+
pub fn create(impl: Impl) error{OutOfMemory}!*Self {
assert(impl != .none);
@@ -243,6 +246,8 @@ pub fn destroy(view: *Self) void {
view.inflight_focus_stack_link.remove();
view.inflight_wm_stack_link.remove();
+ if (view.output_before_evac) |name| util.gpa.free(name);
+
util.gpa.destroy(view);
}
}
diff --git a/river/command/output.zig b/river/command/output.zig
index 088aee8..3e10635 100644
--- a/river/command/output.zig
+++ b/river/command/output.zig
@@ -22,6 +22,7 @@ const wlr = @import("wlroots");
const flags = @import("flags");
const server = &@import("../main.zig").server;
+const util = @import("../util.zig");
const Direction = @import("../command.zig").Direction;
const PhysicalDirectionDirection = @import("../command.zig").PhysicalDirection;
@@ -79,6 +80,14 @@ pub fn sendToOutput(
seat.focused.view.setPendingOutput(destination_output);
+ // When explicitly sending a view to an output, the user likely
+ // does not expect a previously evacuated view moved back to a
+ // re-connecting output.
+ if (seat.focused.view.output_before_evac) |name| {
+ util.gpa.free(name);
+ seat.focused.view.output_before_evac = null;
+ }
+
server.root.applyPending();
}
}