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

commitbf8d9e47a78d2f13e736d7be8d74e1950b3b2b92
parent908c709d9e
authorLucas Galante <[email protected]>
date2026-08-10 00:30
fix: per-client line dedup in the status server

A StatusUpdate is one struct, so any field change re-broadcast EVERY
topic: a camera animation (the viewport text embeds live pan/zoom)
re-sent identical layout/title lines at frame rate and every bar
segment rebuilt per frame — the intermittent Mode-text flicker that
lasted until the camera settled. Each client now only receives lines
that changed for its own topic; a fresh subscriber still gets its
first line.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/server/status_server.rs | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/server/status_server.rs b/src/server/status_server.rs
index 6ceab64..05cb149 100644
--- a/src/server/status_server.rs
+++ b/src/server/status_server.rs
@@ -64,6 +64,12 @@ impl Subscription {
 struct Client {
     subscription: Subscription,
     stream: UnixStream,
+    /// The last line actually written to this client. A state push only
+    /// re-sends a topic whose formatted line CHANGED — a StatusUpdate is
+    /// one struct, so e.g. a camera animation (viewport text embeds pan/
+    /// zoom) used to re-broadcast identical layout/title lines at frame
+    /// rate, and every subscriber rebuilt its segment per frame.
+    last_line: Option<String>,
 }
 
 /// Handle to the status server for sending updates from the main loop.
@@ -150,6 +156,7 @@ fn status_server_main(rx: mpsc::Receiver<StatusMsg>, display_socket: Option<Stri
                         let client = Client {
                             subscription: sub,
                             stream,
+                            last_line: None,
                         };
                         clients.push(client);
                         activity = true;
@@ -229,12 +236,19 @@ fn status_server_main(rx: mpsc::Receiver<StatusMsg>, display_socket: Option<Stri
                         continue;
                     }
                     let msg = format_for_subscription(client.subscription, update);
+                    // Only lines that changed for THIS topic go out (see
+                    // Client::last_line); a fresh client always gets one.
+                    if client.last_line.as_deref() == Some(msg.as_str()) {
+                        continue;
+                    }
                     match client
                         .stream
                         .write_all(msg.as_bytes())
                         .and_then(|_| client.stream.write_all(b"\n"))
                     {
-                        Ok(_) => {}
+                        Ok(_) => {
+                            client.last_line = Some(msg);
+                        }
                         Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
                             // Client not ready to receive — skip for now
                         }