From 049eb0338742d6662eebcc8c448d8812d0dfa48d Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 20 Mar 2026 03:23:42 -0300 Subject: [PATCH] Handle clients that support fractional scaling but don't set geometry Goxel is a great example --- src/main.rs | 8 ++++ src/xdg.rs | 135 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 116 insertions(+), 27 deletions(-) diff --git a/src/main.rs b/src/main.rs index 99fdb97..8883c99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,9 @@ use wl_proxy::{ protocols::{ ObjectInterface, linux_dmabuf_v1::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, + viewporter::wp_viewporter::WpViewporter, wayland::{ + wl_compositor::WlCompositor, wl_registry::{WlRegistry, WlRegistryHandler}, wl_shm::WlShm, }, @@ -47,11 +49,17 @@ impl ClientWlRegistry { impl WlRegistryHandler for ClientWlRegistry { fn handle_bind(&mut self, slf: &Rc, name: u32, id: Rc) { match id.interface() { + WlCompositor::INTERFACE => id + .downcast::() + .set_handler(xdg::ClientWlCompositor), XdgWmBase::INTERFACE => id .downcast::() .set_handler(xdg::ClientXdgWmBase { globals: self.globals.clone(), }), + WpViewporter::INTERFACE => id + .downcast::() + .set_handler(xdg::ClientWpViewporter), ZxdgDecorationManagerV1::INTERFACE => { id.set_forward_to_server(false); id.downcast::() diff --git a/src/xdg.rs b/src/xdg.rs index 20b519d..a8078ad 100644 --- a/src/xdg.rs +++ b/src/xdg.rs @@ -1,10 +1,16 @@ use std::rc::Rc; use wl_proxy::{ + fixed::Fixed, object::{ObjectCoreApi, ObjectUtils}, protocols::{ + viewporter::{ + wp_viewport::{WpViewport, WpViewportHandler}, + wp_viewporter::{WpViewporter, WpViewporterHandler}, + }, wayland::{ wl_buffer::WlBuffer, + wl_compositor::{WlCompositor, WlCompositorHandler}, wl_output::{WlOutput, WlOutputTransform}, wl_surface::{WlSurface, WlSurfaceHandler}, }, @@ -32,10 +38,25 @@ use crate::{ util::{Bounds, Edges}, }; +pub struct ClientWlCompositor; + +impl WlCompositorHandler for ClientWlCompositor { + fn handle_create_surface(&mut self, slf: &Rc, id: &Rc) { + id.set_handler(ClientWlSurface::default()); + slf.send_create_surface(id); + } +} + +#[derive(Default)] pub struct ClientWlSurface { - pub xdg_surface: Rc, + pub xdg_surface: Option>, + + // All of this is buffered next state, we don't reference past state here pub transform: Option, pub scale: Option, + pub buffer_size: Option<(i32, i32)>, + pub wp_src_size: Option<(i32, i32)>, + pub wp_dest_size: Option<(i32, i32)>, } impl WlSurfaceHandler for ClientWlSurface { @@ -56,28 +77,9 @@ impl WlSurfaceHandler for ClientWlSurface { x: i32, y: i32, ) { - let surf_handler = self.xdg_surface.get_handler_ref::(); if let Some(buffer) = buffer { if let Ok(buf_handler) = buffer.try_get_handler_ref::() { - if let Some(deco) = surf_handler.deco.as_ref() { - let mut width = buf_handler.width; - let mut height = buf_handler.height; - if let Some(scale) = self.scale { - width /= scale; - height /= scale; - } - if let Some(transform) = self.transform - && (transform == WlOutputTransform::_90 - || transform == WlOutputTransform::_270 - || transform == WlOutputTransform::FLIPPED_270 - || transform == WlOutputTransform::FLIPPED_270) - { - std::mem::swap(&mut width, &mut height); - } - deco.handle_intrinsic_size(width, height); - } else { - log::debug!("surface {slf:?} buffer attach before deco"); - } + self.buffer_size = Some((buf_handler.width, buf_handler.height)); } else { log::warn!("surface {slf:?} buffer with unknown size {buffer:?}"); } @@ -89,14 +91,96 @@ impl WlSurfaceHandler for ClientWlSurface { fn handle_commit(&mut self, slf: &Rc) { log::debug!("commit {slf:?}"); - let surf_handler = self.xdg_surface.get_handler_ref::(); + let Some(ref xdg_surface) = self.xdg_surface else { + // probably a cursor + slf.send_commit(); + return; + }; + let surf_handler = xdg_surface.get_handler_ref::(); if let Some(deco) = surf_handler.deco.as_ref() { + if let Some((width, height)) = self.wp_dest_size { + deco.handle_intrinsic_size(width, height); + } else if let Some((width, height)) = self.wp_src_size { + // "If the source rectangle is set and the destination size is not set, + // [..] the surface size becomes the source rectangle size" + deco.handle_intrinsic_size(width, height); + } else if let Some((mut width, mut height)) = self.buffer_size { + if let Some(transform) = self.transform + && (transform == WlOutputTransform::_90 + || transform == WlOutputTransform::_270 + || transform == WlOutputTransform::FLIPPED_270 + || transform == WlOutputTransform::FLIPPED_270) + { + std::mem::swap(&mut width, &mut height); + } + if let Some(scale) = self.scale { + width /= scale; + height /= scale; + } + deco.handle_intrinsic_size(width, height); + } deco.handle_commit().unwrap(); } slf.send_commit(); } } +pub struct ClientWpViewporter; + +impl WpViewporterHandler for ClientWpViewporter { + fn handle_get_viewport( + &mut self, + slf: &Rc, + id: &Rc, + surface: &Rc, + ) { + id.set_handler(ClientWpViewport { + surface: surface.clone(), + }); + slf.send_get_viewport(id, surface); + } +} + +pub struct ClientWpViewport { + surface: Rc, +} + +impl WpViewportHandler for ClientWpViewport { + fn handle_destroy(&mut self, slf: &Rc) { + let mut surf_handler = self.surface.get_handler_mut::(); + surf_handler.wp_src_size = None; + surf_handler.wp_dest_size = None; + slf.send_destroy(); + } + + fn handle_set_source( + &mut self, + slf: &Rc, + x: Fixed, + y: Fixed, + width: Fixed, + height: Fixed, + ) { + log::debug!( + "surface {:?} WP src {width}x{height} @ {x},{y}", + self.surface + ); + let mut surf_handler = self.surface.get_handler_mut::(); + surf_handler.wp_src_size = Some(( + width.to_i32_round_towards_nearest(), + height.to_i32_round_towards_nearest(), + )); + slf.send_set_source(x, y, width, height); + } + + fn handle_set_destination(&mut self, slf: &Rc, width: i32, height: i32) { + log::debug!("surface {:?} WP dest {width}x{height}", self.surface); + let mut surf_handler = self.surface.get_handler_mut::(); + surf_handler.wp_dest_size = Some((width, height)); + slf.send_set_destination(width, height); + } +} + pub struct ClientZxdgDecorationManagerV1; impl ZxdgDecorationManagerV1Handler for ClientZxdgDecorationManagerV1 { @@ -156,11 +240,8 @@ impl XdgWmBaseHandler for ClientXdgWmBase { id: &Rc, surface: &Rc, ) { - surface.set_handler(ClientWlSurface { - xdg_surface: id.clone(), - scale: None, - transform: None, - }); + let mut surf_handler = surface.get_handler_mut::(); + surf_handler.xdg_surface = Some(id.clone()); id.set_handler(ClientXdgSurface { deco: None, globals: self.globals.clone(),