git.lucas.co / cce-gallery
widget gallery and compositor test bench

commitf04151d0bdba42b892394e90b166e232d6885c98
parent19dfca0bd7
authorLucas Galante <[email protected]>
date2026-07-05 01:11
Implement Wayland frame callback throttling to resolve background blur flickering when panning

 src/main.rs | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index e80af6e..2865f48 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -32,7 +32,7 @@ use smithay_client_toolkit::{
 };
 use wayland_client::{
     globals::registry_queue_init,
-    protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_surface},
+    protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_surface, wl_callback},
     Connection, QueueHandle, Proxy,
 };
 use calloop::EventLoop;
@@ -1712,6 +1712,7 @@ struct AppState {
     state: Option<State>,
     exit: bool,
     redraw: bool,
+    frame_callback_pending: bool,
 
     cursor_shape_manager: Option<smithay_client_toolkit::seat::pointer::cursor_shape::CursorShapeManager>,
     cursor_shape_device: Option<smithay_client_toolkit::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
@@ -2514,6 +2515,7 @@ impl WindowHandler for AppState {
             state.resize(pw, ph);
         }
         self.redraw = true;
+        self.frame_callback_pending = false;
     }
 
     fn request_close(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _window: &XdgWindow) {
@@ -2544,6 +2546,21 @@ impl ProvidesRegistryState for AppState {
     ) {}
 }
 
+impl wayland_client::Dispatch<wl_callback::WlCallback, ()> for AppState {
+    fn event(
+        state: &mut Self,
+        _proxy: &wl_callback::WlCallback,
+        event: wl_callback::Event,
+        _data: &(),
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {
+        if let wl_callback::Event::Done { .. } = event {
+            state.frame_callback_pending = false;
+        }
+    }
+}
+
 delegate_compositor!(AppState);
 delegate_xdg_shell!(AppState);
 delegate_xdg_window!(AppState);
@@ -2643,6 +2660,7 @@ fn main() {
         state: None,
         exit: false,
         redraw: true,
+        frame_callback_pending: false,
         cursor_shape_manager,
         cursor_shape_device: None,
         ctrl_pressed: false,
@@ -2764,10 +2782,14 @@ fn main() {
             }
         }
 
-        if app.redraw {
+        if app.redraw && !app.frame_callback_pending {
             app.redraw = false;
             if let Some(state) = &mut app.state {
                 state.render();
+                if let Some(ref surface) = app.surface {
+                    let _callback = surface.frame(&qh, ());
+                    app.frame_callback_pending = true;
+                }
             }
         }
     }