git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit516c7b61e83f2875c8e8cc1573a4ae90aa9c6faf
parentf8d8e5da5d
authorLeon Henrik Plickat <[email protected]>
date2023-11-10 01:01
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();
     }
 }