From a04c18819b5f61f48588f4816b6521a3b262f0ff Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 20 Feb 2024 23:06:55 +0100 Subject: [PATCH] Root: fix crash on output disable If there is a transaction inflight when an output is disabled then we must call View.commitTransaction() for any views evacuated from the disabled output to ensure transaction state remains consistent. Root.commitTransaction() will not call View.commitTransaction() for these evacuated views as their output is no longer around. --- river/Root.zig | 11 +++++++++-- river/View.zig | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/river/Root.zig b/river/Root.zig index 5743d30..7399408 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -280,6 +280,10 @@ pub fn deactivateOutput(root: *Self, output: *Output) void { view.inflight_wm_stack_link.remove(); view.inflight_wm_stack_link.init(); + + if (view.inflight_transaction) { + view.commitTransaction(); + } } } // Use the first output in the list as fallback. If the last real output @@ -552,11 +556,16 @@ fn sendConfigures(root: *Self) void { while (output_it.next()) |output| { var focus_stack_it = output.inflight.focus_stack.iterator(.forward); while (focus_stack_it.next()) |view| { + assert(!view.inflight_transaction); + view.inflight_transaction = true; + // This can happen if a view is unmapped while a layout demand including it is inflight + // If a view has been unmapped, don't send it a configure. if (!view.mapped) continue; if (view.configure()) { root.inflight_configures += 1; + view.saveSurfaceTree(); view.sendFrameDone(); } @@ -617,8 +626,6 @@ fn commitTransaction(root: *Self) void { view.tree.node.reparent(root.hidden.tree); view.popup_tree.node.reparent(root.hidden.tree); - - view.commitTransaction(); } } diff --git a/river/View.zig b/river/View.zig index 4de5fc1..b5b418e 100644 --- a/river/View.zig +++ b/river/View.zig @@ -131,6 +131,8 @@ popup_tree: *wlr.SceneTree, constraints: Constraints = .{}, mapped: bool = false, +/// This is true if the View is involved in the currently inflight transaction. +inflight_transaction: bool = false, /// This indicates that the view should be destroyed when the current /// transaction completes. See View.destroy() destroying: bool = false, @@ -273,6 +275,9 @@ pub fn resizeUpdatePosition(view: *Self, width: i32, height: i32) void { } pub fn commitTransaction(view: *Self) void { + assert(view.inflight_transaction); + view.inflight_transaction = false; + view.foreign_toplevel_handle.update(); // Tag and output changes must be applied immediately even if the configure sequence times out.