From 4fe557c536e6c052586553ccc703f8c37f858ee2 Mon Sep 17 00:00:00 2001 From: Stephen Hellings Date: Mon, 9 Mar 2026 19:24:33 -0400 Subject: [PATCH] fix: register Wayland display/socket fds with calloop to fix DRM rendering stall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DRM event loop only woke for registered calloop sources (VBlank, libinput, session). When the VBlank rendering cycle broke (transient page-flip failure, timing edge), nothing woke the loop to restart it — only input events (mouse movement) would un-stall rendering. Three changes: 1. Register the Wayland display poll_fd as a calloop source so client surface commits immediately wake the event loop and trigger render_frame via the main callback. 2. Register the listening socket fd so new Wayland client connections also wake the loop promptly. 3. Add a periodic timer (fires every frame_time ≈ 16ms) as a safety net to restart stalled VBlank cycles even with no input or client activity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/main.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/main.rs b/src/main.rs index d1ac52d..4bcf9c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1585,6 +1585,60 @@ fn run_drm(mut config: Config) -> Result<(), anyhow::Error> { ListeningSocket::bind(&config.compositor.socket_name).context("binding Wayland socket")?; info!(socket = %config.compositor.socket_name, "Wayland socket bound"); + // Register the Wayland display fd with calloop so that client messages + // (surface commits, etc.) wake the event loop. Without this, the loop + // only wakes on DRM VBlank / libinput / session events and rendering + // stalls when the VBlank cycle breaks. + { + use std::os::unix::io::AsFd; + let display_fd = display.as_fd().try_clone_to_owned() + .context("duping Wayland display fd for calloop")?; + let wayland_display_source = calloop::generic::Generic::new( + display_fd, + calloop::Interest::READ, + calloop::Mode::Level, + ); + loop_handle + .insert_source(wayland_display_source, |_, _, _: &mut DrmLoopData| { + Ok(calloop::PostAction::Continue) + }) + .map_err(|e| anyhow::anyhow!("Error inserting Wayland display source: {e:?}"))?; + info!("Wayland display fd registered with calloop"); + } + + // Register the listening socket fd so new client connections also wake + // the event loop promptly. + { + use std::os::unix::io::AsFd; + let listen_fd = wayland_socket.as_fd().try_clone_to_owned() + .context("duping Wayland listening socket fd for calloop")?; + let listen_source = calloop::generic::Generic::new( + listen_fd, + calloop::Interest::READ, + calloop::Mode::Level, + ); + loop_handle + .insert_source(listen_source, |_, _, _: &mut DrmLoopData| { + Ok(calloop::PostAction::Continue) + }) + .map_err(|e| anyhow::anyhow!("Error inserting Wayland listen source: {e:?}"))?; + info!("Wayland listening socket registered with calloop"); + } + + // Periodic render timer: safety net to restart stalled VBlank cycles. + // If the VBlank rendering cycle breaks (e.g. transient page-flip failure), + // this timer ensures the event loop wakes up and render_frame gets a + // chance to restart the cycle, even with no input or client activity. + { + let render_timer = calloop::timer::Timer::from_duration(frame_time); + loop_handle + .insert_source(render_timer, move |_deadline, _, _: &mut DrmLoopData| { + calloop::timer::TimeoutAction::ToDuration(frame_time) + }) + .map_err(|e| anyhow::anyhow!("Error inserting render timer: {e:?}"))?; + info!(?frame_time, "Periodic render timer registered"); + } + // Move all state into EventLoopData for access in event loop callbacks let mut loop_data = EventLoopData { display,