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

commit3dcd91e07946369795f083b81d6b2242d9c5b6fd
parent86baf0d797
authorIsaac Freund <[email protected]>
date2023-03-14 12:30
XdgToplevel: ensure view dimensions match geometry

Some clients (e.g. mpv) do not respond to configures by committing
buffers of the exact size requested. Instead they may commit a buffer of
a smaller size in order to maintain an aspect ratio or not commit a
buffer at all.

 river/View.zig        | 18 +++++++++++++++---
 river/XdgToplevel.zig | 14 +++++++++++---
 2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/river/View.zig b/river/View.zig
index 24934dd..9eec85e 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -247,13 +247,25 @@ pub fn destroy(view: *Self) void {
 pub fn updateCurrent(view: *Self) void {
     const config = &server.config;
 
+    if (view.impl == .xdg_toplevel) {
+        switch (view.impl.xdg_toplevel.configure_state) {
+            // If the configure timed out, don't update current to dimensions
+            // that have not been committed by the client.
+            .inflight, .acked => {
+                view.inflight.box.width = view.current.box.width;
+                view.inflight.box.height = view.current.box.height;
+                view.pending.box.width = view.current.box.width;
+                view.pending.box.height = view.current.box.height;
+            },
+            .idle, .committed => {},
+        }
+        view.impl.xdg_toplevel.configure_state = .idle;
+    }
+
     view.foreign_toplevel_handle.update();
 
     view.current = view.inflight;
     view.dropSavedSurfaceTree();
-    if (view.impl == .xdg_toplevel) {
-        view.impl.xdg_toplevel.configure_state = .idle;
-    }
 
     const color = blk: {
         if (view.current.urgent) break :blk &config.border_color_urgent;
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 188be4b..734e116 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -50,6 +50,8 @@ configure_state: union(enum) {
     inflight: u32,
     /// A configure was acked but the surface has not yet been committed.
     acked,
+    /// A configure was acked and the surface was committed.
+    committed,
 } = .idle,
 
 // Listeners that are always active over the view's lifetime
@@ -288,7 +290,7 @@ fn handleAckConfigure(
         .inflight => |serial| if (acked_configure.serial == serial) {
             self.configure_state = .acked;
         },
-        .acked, .idle => {},
+        .acked, .idle, .committed => {},
     }
 }
 
@@ -310,7 +312,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     self.xdg_toplevel.base.getGeometry(&self.geometry);
 
     switch (self.configure_state) {
-        .idle => {
+        .idle, .committed => {
             const size_changed = self.geometry.width != old_geometry.width or
                 self.geometry.height != old_geometry.height;
             const no_layout = view.current.output != null and view.current.output.?.layout == null;
@@ -334,7 +336,13 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
         // stashed buffer from when the transaction started.
         .inflight => view.sendFrameDone(),
         .acked => {
-            self.configure_state = .idle;
+            self.configure_state = .committed;
+
+            view.inflight.box.width = self.geometry.width;
+            view.inflight.box.height = self.geometry.height;
+            view.pending.box.width = self.geometry.width;
+            view.pending.box.height = self.geometry.height;
+
             server.root.notifyConfigured();
         },
     }