fix(wayland): proper frame callback handling

This commit is contained in:
Nova
2025-09-14 00:40:07 -07:00
parent 1bfd9c95f0
commit 2632a0c5f3
3 changed files with 26 additions and 20 deletions

View File

@@ -57,7 +57,7 @@ pub struct SurfaceState {
pub geometry: Option<Geometry>, pub geometry: Option<Geometry>,
pub min_size: Option<Vector2<u32>>, pub min_size: Option<Vector2<u32>>,
pub max_size: Option<Vector2<u32>>, pub max_size: Option<Vector2<u32>>,
frame_callbacks: Vec<Arc<Callback>>, frame_callbacks: Registry<Callback>,
} }
impl Default for SurfaceState { impl Default for SurfaceState {
fn default() -> Self { fn default() -> Self {
@@ -67,7 +67,7 @@ impl Default for SurfaceState {
geometry: None, geometry: None,
min_size: None, min_size: None,
max_size: None, max_size: None,
frame_callbacks: Vec::new(), frame_callbacks: Registry::new(),
} }
} }
} }
@@ -90,6 +90,7 @@ pub struct Surface {
pub role: OnceLock<SurfaceRole>, pub role: OnceLock<SurfaceRole>,
pub panel_item: Mutex<Weak<PanelItem<XdgBackend>>>, pub panel_item: Mutex<Weak<PanelItem<XdgBackend>>>,
on_commit_handlers: Mutex<Vec<OnCommitCallback>>, on_commit_handlers: Mutex<Vec<OnCommitCallback>>,
frame_callbacks: Registry<Callback>,
material: OnceLock<Handle<BevyMaterial>>, material: OnceLock<Handle<BevyMaterial>>,
pending_material_applications: Registry<ModelPart>, pending_material_applications: Registry<ModelPart>,
presentation_feedback: Mutex<Vec<Arc<PresentationFeedback>>>, presentation_feedback: Mutex<Vec<Arc<PresentationFeedback>>>,
@@ -122,6 +123,7 @@ impl Surface {
role: OnceLock::new(), role: OnceLock::new(),
panel_item: Mutex::new(Weak::default()), panel_item: Mutex::new(Weak::default()),
on_commit_handlers: Mutex::new(Vec::new()), on_commit_handlers: Mutex::new(Vec::new()),
frame_callbacks: Registry::new(),
material: OnceLock::new(), material: OnceLock::new(),
pending_material_applications: Registry::new(), pending_material_applications: Registry::new(),
presentation_feedback: Mutex::default(), presentation_feedback: Mutex::default(),
@@ -156,7 +158,7 @@ impl Surface {
} }
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
pub fn pending_state(&self) -> parking_lot::MutexGuard<'_, DoubleBuffer<SurfaceState>> { pub fn state_lock(&self) -> parking_lot::MutexGuard<'_, DoubleBuffer<SurfaceState>> {
self.state.lock() self.state.lock()
} }
@@ -232,8 +234,9 @@ impl Surface {
} }
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
pub fn frame_event(&self) { pub fn frame_event(&self) {
for callback in self.state.lock().current.frame_callbacks.drain(..) { let callbacks = self.frame_callbacks.take_valid_contents();
let _ = self.message_sink.send(Message::Frame(callback)); if !callbacks.is_empty() {
let _ = self.message_sink.send(Message::Frame(callbacks));
} }
} }
// pub fn size(&self) -> Option<Vector2<u32>> { // pub fn size(&self) -> Option<Vector2<u32>> {
@@ -366,7 +369,7 @@ impl WlSurface for Surface {
callback_id: ObjectId, callback_id: ObjectId,
) -> Result<()> { ) -> Result<()> {
let callback = client.insert(callback_id, Callback(callback_id)); let callback = client.insert(callback_id, Callback(callback_id));
self.state.lock().pending.frame_callbacks.push(callback); self.state.lock().pending.frame_callbacks.add_raw(&callback);
Ok(()) Ok(())
} }
@@ -399,8 +402,7 @@ impl WlSurface for Surface {
async fn commit(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> { async fn commit(&self, _client: &mut Client, _sender_id: ObjectId) -> Result<()> {
// we want the upload to complete before we give the image to bevy // we want the upload to complete before we give the image to bevy
let buffer_option = self let buffer_option = self
.state .state_lock()
.lock()
.pending .pending
.buffer .buffer
.as_ref() .as_ref()
@@ -411,7 +413,9 @@ impl WlSurface for Surface {
.unwrap(); .unwrap();
} }
self.state.lock().apply(); self.state.lock().apply();
self.state.lock().pending.frame_callbacks.clear(); for callback in self.current_state().frame_callbacks.get_valid_contents() {
self.frame_callbacks.add_raw(&callback)
}
let current_state = self.current_state(); let current_state = self.current_state();
let mut handlers = self.on_commit_handlers.lock(); let mut handlers = self.on_commit_handlers.lock();
handlers.retain(|f| (f)(self, &current_state)); handlers.retain(|f| (f)(self, &current_state));

View File

@@ -119,7 +119,7 @@ pub fn get_free_wayland_socket_path() -> Option<(PathBuf, FlockLock<File>)> {
pub enum Message { pub enum Message {
Disconnect, Disconnect,
Frame(Arc<Callback>), Frame(Vec<Arc<Callback>>),
ReleaseBuffer(Arc<Buffer>), ReleaseBuffer(Arc<Buffer>),
CloseToplevel(Arc<Toplevel>), CloseToplevel(Arc<Toplevel>),
ResizeToplevel { ResizeToplevel {
@@ -215,17 +215,19 @@ impl WaylandClient {
) -> Result<bool, waynest::server::Error> { ) -> Result<bool, waynest::server::Error> {
match message { match message {
Message::Disconnect => return Ok(true), Message::Disconnect => return Ok(true),
Message::Frame(callback) => { Message::Frame(callbacks) => {
let now = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic); let now = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic);
let now = Duration::new(now.tv_sec as u64, now.tv_nsec as u32); let now = Duration::new(now.tv_sec as u64, now.tv_nsec as u32);
let ms = (now.as_millis() % (u32::MAX as u128)) as u32; let ms = (now.as_millis() % (u32::MAX as u128)) as u32;
callback.done(client, callback.0, ms).await?; for callback in callbacks {
client callback.done(client, callback.0, ms).await?;
.get::<Display>(ObjectId::DISPLAY) client
.unwrap() .get::<Display>(ObjectId::DISPLAY)
.delete_id(client, ObjectId::DISPLAY, callback.0.as_raw()) .unwrap()
.await?; .delete_id(client, ObjectId::DISPLAY, callback.0.as_raw())
client.remove(callback.0); .await?;
client.remove(callback.0);
}
} }
Message::ReleaseBuffer(buffer) => { Message::ReleaseBuffer(buffer) => {
buffer.release(client, buffer.id).await?; buffer.release(client, buffer.id).await?;

View File

@@ -246,7 +246,7 @@ impl XdgToplevel for Toplevel {
width: i32, width: i32,
height: i32, height: i32,
) -> Result<()> { ) -> Result<()> {
self.wl_surface().pending_state().pending.max_size = if width == 0 && height == 0 { self.wl_surface().state_lock().pending.max_size = if width == 0 && height == 0 {
None None
} else { } else {
Some([width as u32, height as u32].into()) Some([width as u32, height as u32].into())
@@ -261,7 +261,7 @@ impl XdgToplevel for Toplevel {
width: i32, width: i32,
height: i32, height: i32,
) -> Result<()> { ) -> Result<()> {
self.xdg_surface.wl_surface.pending_state().pending.min_size = if width == 0 && height == 0 self.xdg_surface.wl_surface.state_lock().pending.min_size = if width == 0 && height == 0
{ {
None None
} else { } else {