Handle clients that support fractional scaling but don't set geometry

Goxel is a great example
This commit is contained in:
Val Packett 2026-03-20 03:23:42 -03:00
parent 207529a185
commit 049eb03387
2 changed files with 116 additions and 27 deletions

View file

@ -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<WlRegistry>, name: u32, id: Rc<dyn Object>) {
match id.interface() {
WlCompositor::INTERFACE => id
.downcast::<WlCompositor>()
.set_handler(xdg::ClientWlCompositor),
XdgWmBase::INTERFACE => id
.downcast::<XdgWmBase>()
.set_handler(xdg::ClientXdgWmBase {
globals: self.globals.clone(),
}),
WpViewporter::INTERFACE => id
.downcast::<WpViewporter>()
.set_handler(xdg::ClientWpViewporter),
ZxdgDecorationManagerV1::INTERFACE => {
id.set_forward_to_server(false);
id.downcast::<ZxdgDecorationManagerV1>()

View file

@ -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<WlCompositor>, id: &Rc<WlSurface>) {
id.set_handler(ClientWlSurface::default());
slf.send_create_surface(id);
}
}
#[derive(Default)]
pub struct ClientWlSurface {
pub xdg_surface: Rc<XdgSurface>,
pub xdg_surface: Option<Rc<XdgSurface>>,
// All of this is buffered next state, we don't reference past state here
pub transform: Option<WlOutputTransform>,
pub scale: Option<i32>,
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::<ClientXdgSurface>();
if let Some(buffer) = buffer {
if let Ok(buf_handler) = buffer.try_get_handler_ref::<client_buffer::ClientWlBuffer>() {
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<WlSurface>) {
log::debug!("commit {slf:?}");
let surf_handler = self.xdg_surface.get_handler_ref::<ClientXdgSurface>();
let Some(ref xdg_surface) = self.xdg_surface else {
// probably a cursor
slf.send_commit();
return;
};
let surf_handler = xdg_surface.get_handler_ref::<ClientXdgSurface>();
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<WpViewporter>,
id: &Rc<WpViewport>,
surface: &Rc<WlSurface>,
) {
id.set_handler(ClientWpViewport {
surface: surface.clone(),
});
slf.send_get_viewport(id, surface);
}
}
pub struct ClientWpViewport {
surface: Rc<WlSurface>,
}
impl WpViewportHandler for ClientWpViewport {
fn handle_destroy(&mut self, slf: &Rc<WpViewport>) {
let mut surf_handler = self.surface.get_handler_mut::<ClientWlSurface>();
surf_handler.wp_src_size = None;
surf_handler.wp_dest_size = None;
slf.send_destroy();
}
fn handle_set_source(
&mut self,
slf: &Rc<WpViewport>,
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::<ClientWlSurface>();
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<WpViewport>, width: i32, height: i32) {
log::debug!("surface {:?} WP dest {width}x{height}", self.surface);
let mut surf_handler = self.surface.get_handler_mut::<ClientWlSurface>();
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<XdgSurface>,
surface: &Rc<WlSurface>,
) {
surface.set_handler(ClientWlSurface {
xdg_surface: id.clone(),
scale: None,
transform: None,
});
let mut surf_handler = surface.get_handler_mut::<ClientWlSurface>();
surf_handler.xdg_surface = Some(id.clone());
id.set_handler(ClientXdgSurface {
deco: None,
globals: self.globals.clone(),