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

commit6d940b77109ee5b107b09189a1ec85a0c2ce38eb
parent3bfe7cda8b
authorIsaac Freund <[email protected]>
date2025-01-04 10:55
Seat: fix race on ending resize op

Currently, a race may cause a jump in window position when a resize
operation is ended. This is due to Window.pending.width/height remaining
modified when Window.pending.op is reset to null.

This commit fixes the race.

 river/Seat.zig          | 14 ++++++++++----
 river/WindowManager.zig |  5 +----
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/river/Seat.zig b/river/Seat.zig
index 9881a2f..90a10dc 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -272,10 +272,16 @@ pub fn queueEvent(seat: *Seat, event: Event) void {
 pub fn processEvents(seat: *Seat) void {
     assert(server.wm.state == .idle);
 
-    // Only process events while there is no pending state to be sent to the window manager.
+    // Only process events while there is no pending state to be sent to the window manager
+    // and no transaction in progress.
+    //
     // The window manager might decide to change focus or redefine keyboard/pointer bindings
-    // in response to the pending update.
-    while (!server.wm.pending.dirty) {
+    // in response to the pending update, which can affect further processing of events.
+    //
+    // Allowing event processing while there is a transaction in progress would require keeping
+    // track of additional state to differentiate pending state modified since the transaction
+    // was started. I don't see an advantage to that additional complexity.
+    while (server.wm.state == .idle and !server.wm.pending.dirty) {
         const event = seat.event_queue.readItem() orelse break;
 
         const pg = server.input_manager.pointer_gestures;
@@ -301,8 +307,8 @@ pub fn processEvents(seat: *Seat) void {
 
     if (seat.op) |*op| {
         if (op.dirty) {
-            server.wm.dirtyPending();
             op.dirty = false;
+            server.wm.sendConfigures();
         }
     }
 }
diff --git a/river/WindowManager.zig b/river/WindowManager.zig
index 43a859d..7056497 100644
--- a/river/WindowManager.zig
+++ b/river/WindowManager.zig
@@ -277,19 +277,16 @@ fn sendUpdate(wm: *WindowManager) void {
 
         wm.startTimeoutTimer();
     } else {
-        // Pretend that the non-existent wm client made an empty commit.
-        wm.committed.dirty = true;
         wm.sendConfigures();
     }
 }
 
-fn sendConfigures(wm: *WindowManager) void {
+pub fn sendConfigures(wm: *WindowManager) void {
     switch (wm.state) {
         .idle, .update_acked => {},
         .update_sent, .inflight_configures => unreachable,
     }
 
-    assert(wm.committed.dirty);
     wm.committed.dirty = false;
 
     {