From 0cd5059c42f410986056f6f892cfa5ef4d35d3c3 Mon Sep 17 00:00:00 2001 From: Shawn Wallace Date: Sun, 13 Apr 2025 23:50:13 -0400 Subject: [PATCH] Don't set viewport for 1x1 windows with scaling --- src/server/dispatch.rs | 4 +++ src/server/event.rs | 4 ++- src/server/tests.rs | 81 +++++++++++++++++++++++------------------- tests/integration.rs | 3 +- testwl/src/lib.rs | 63 ++++++++++++++++++++++++++------ 5 files changed, 106 insertions(+), 49 deletions(-) diff --git a/src/server/dispatch.rs b/src/server/dispatch.rs index 2c86717..fcecfe0 100644 --- a/src/server/dispatch.rs +++ b/src/server/dispatch.rs @@ -170,6 +170,10 @@ impl Dispatch for ServerState { } surface.destroy_role(); surface.client.destroy(); + surface.viewport.destroy(); + if let Some(f) = &mut surface.fractional { + f.destroy(); + } debug!( "deleting key: {key:?} (surface {:?})", surface.server.id().protocol_id() diff --git a/src/server/event.rs b/src/server/event.rs index 9f3b6f6..1ad6038 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -128,7 +128,9 @@ impl SurfaceData { fn update_viewport(&self, dims: WindowDims, size_hints: Option) { let width = (dims.width as f64 / self.scale_factor) as i32; let height = (dims.height as f64 / self.scale_factor) as i32; - self.viewport.set_destination(width, height); + if width > 0 && height > 0 { + self.viewport.set_destination(width, height); + } debug!("{} viewport: {width}x{height}", self.server.id()); if let Some(hints) = size_hints { let Some(SurfaceRole::Toplevel(Some(data))) = &self.role else { diff --git a/src/server/tests.rs b/src/server/tests.rs index 44bc07b..22887a2 100644 --- a/src/server/tests.rs +++ b/src/server/tests.rs @@ -1,7 +1,6 @@ use super::{ServerState, WindowDims}; use crate::xstate::{SetState, WinSize, WmName}; use rustix::event::{poll, PollFd, PollFlags}; -use smithay_client_toolkit::compositor::Surface; use std::collections::HashMap; use std::io::Write; use std::os::fd::{AsRawFd, BorrowedFd}; @@ -24,8 +23,6 @@ use wayland_client::{ }, Connection, Proxy, WEnum, }; -use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1; - use wayland_protocols::{ wp::{ linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, @@ -519,39 +516,6 @@ impl TestFixture { (output, self.testwl.last_created_output()) } - fn enable_fractional_scale(&mut self) -> TestObject { - self.testwl.enable_fractional_scale(); - self.run(); - self.run(); - - let mut events = std::mem::take(&mut *self.registry.data.events.lock().unwrap()); - assert_eq!( - events.len(), - 1, - "Unexpected number of global events after enabling fractional scale" - ); - let event = events.pop().unwrap(); - let Ev::::Global { - name, - interface, - version, - } = event - else { - panic!("Unexpected event: {event:?}"); - }; - - assert_eq!(interface, WpFractionalScaleManagerV1::interface().name); - let man = TestObject::::from_request( - &self.registry.obj, - Req::::Bind { - name, - id: (WpFractionalScaleManagerV1::interface(), version), - }, - ); - self.run(); - man - } - fn enable_xdg_output(&mut self) -> TestObject { self.testwl.enable_xdg_output_manager(); self.run(); @@ -2055,7 +2019,6 @@ fn scaled_output_small_popup() { .y(50) .width(1) .height(1) - .scale(2) .check_size_and_pos(false); let (_, popup_id) = f.create_popup(&comp, builder); @@ -2068,6 +2031,50 @@ fn scaled_output_small_popup() { assert!(dims.height > 0); } +#[test] +fn fractional_scale_small_popup() { + let mut f = TestFixture::new_pre_connect(|testwl| { + testwl.enable_fractional_scale(); + }); + let comp = f.compositor(); + + let toplevel = unsafe { Window::new(1) }; + let (_, toplevel_id) = f.create_toplevel(&comp, toplevel); + let data = f.testwl.get_surface_data(toplevel_id).unwrap(); + let fractional = data + .fractional + .as_ref() + .expect("Missing fracitonal scale data"); + fractional.preferred_scale(180); // 1.5 scale + f.run(); + f.run(); + + { + let data = f.testwl.get_surface_data(toplevel_id).unwrap(); + let viewport = data.viewport.as_ref().expect("Missing viewport"); + assert_eq!(viewport.width, 66); + assert_eq!(viewport.height, 66); + } + + let popup = unsafe { Window::new(2) }; + let builder = PopupBuilder::new(popup, toplevel, toplevel_id) + .width(1) + .height(1) + .check_size_and_pos(false); + + let (_, popup_id) = f.create_popup(&comp, builder); + let dims = f.connection().window(popup).dims; + assert!(dims.width > 0); + assert!(dims.height > 0); + + let data = f + .testwl + .get_surface_data(popup_id) + .expect("Missing popup data"); + let pos = &data.popup().positioner_state; + assert_eq!(pos.size.unwrap(), testwl::Vec2 { x: 1, y: 1 }); +} + #[test] fn toplevel_size_limits_scaled() { let (mut f, comp) = TestFixture::new_with_compositor(); diff --git a/tests/integration.rs b/tests/integration.rs index a86649c..c9fd3ec 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -783,7 +783,8 @@ fn input_focus() { long_offset: 0, long_length: 1, }) - .value::().first() + .value::() + .first() .and_then(|state| WmState::try_from(*state).ok()), Some(WmState::Normal) ); diff --git a/testwl/src/lib.rs b/testwl/src/lib.rs index d5aed16..42ba65a 100644 --- a/testwl/src/lib.rs +++ b/testwl/src/lib.rs @@ -25,7 +25,7 @@ use wayland_protocols::{ zwp_tablet_v2::ZwpTabletV2, }, viewporter::server::{ - wp_viewport::WpViewport, + wp_viewport::{self, WpViewport}, wp_viewporter::{self, WpViewporter}, }, }, @@ -85,6 +85,13 @@ pub struct BufferDamage { pub height: i32, } +#[derive(Debug, PartialEq, Eq)] +pub struct Viewport { + pub width: i32, + pub height: i32, + viewport: WpViewport, +} + #[derive(Debug, PartialEq, Eq)] pub struct SurfaceData { pub surface: WlSurface, @@ -93,6 +100,7 @@ pub struct SurfaceData { pub role: Option, pub last_enter_serial: Option, pub fractional: Option, + pub viewport: Option, } impl SurfaceData { @@ -1519,6 +1527,7 @@ impl Dispatch for State { role: None, last_enter_serial: None, fractional: None, + viewport: None, }, ); state.last_surface_id = Some(SurfaceId(id)); @@ -1828,7 +1837,7 @@ impl Dispatch for State { impl Dispatch for State { fn request( - _: &mut Self, + state: &mut Self, _: &Client, _: &WpViewporter, request: ::Request, @@ -1837,8 +1846,18 @@ impl Dispatch for State { data_init: &mut wayland_server::DataInit<'_, Self>, ) { match request { - wp_viewporter::Request::GetViewport { surface: _, id } => { - data_init.init(id, ()); + wp_viewporter::Request::GetViewport { surface, id } => { + let surface_id = SurfaceId(surface.id().protocol_id()); + let viewport = data_init.init(id, surface_id); + state + .surfaces + .get_mut(&surface_id) + .expect("Unknown surface") + .viewport = Some(Viewport { + viewport, + width: -1, + height: -1, + }) } wp_viewporter::Request::Destroy => {} _ => unreachable!(), @@ -1846,17 +1865,41 @@ impl Dispatch for State { } } -impl Dispatch for State { +impl Dispatch for State { fn request( - _: &mut Self, + state: &mut Self, _: &Client, - _: &WpViewport, - _: ::Request, - _: &(), + viewport: &WpViewport, + request: ::Request, + surface_id: &SurfaceId, _: &DisplayHandle, _: &mut wayland_server::DataInit<'_, Self>, ) { - //todo!() + match request { + wp_viewport::Request::SetDestination { width, height } => { + if width == 0 || width < -1 || height == 0 || height < -1 { + panic!( + "Bad viewport width/height ({width}x{height}) - {}", + viewport.id() + ); + } + let viewport = state + .surfaces + .get_mut(surface_id) + .unwrap_or_else(|| panic!("Missing surface id {surface_id:?}")) + .viewport + .as_mut() + .unwrap(); + viewport.width = width; + viewport.height = height; + } + wp_viewport::Request::Destroy => { + if let Some(surface) = state.surfaces.get_mut(surface_id) { + surface.viewport.take(); + } + } + _ => unimplemented!("{request:?}"), + } } }