Don't set viewport for 1x1 windows with scaling

This commit is contained in:
Shawn Wallace 2025-04-13 23:50:13 -04:00
parent 555f9492ad
commit 0cd5059c42
5 changed files with 106 additions and 49 deletions

View file

@ -170,6 +170,10 @@ impl<C: XConnection> Dispatch<WlSurface, ObjectKey> for ServerState<C> {
} }
surface.destroy_role(); surface.destroy_role();
surface.client.destroy(); surface.client.destroy();
surface.viewport.destroy();
if let Some(f) = &mut surface.fractional {
f.destroy();
}
debug!( debug!(
"deleting key: {key:?} (surface {:?})", "deleting key: {key:?} (surface {:?})",
surface.server.id().protocol_id() surface.server.id().protocol_id()

View file

@ -128,7 +128,9 @@ impl SurfaceData {
fn update_viewport(&self, dims: WindowDims, size_hints: Option<WmNormalHints>) { fn update_viewport(&self, dims: WindowDims, size_hints: Option<WmNormalHints>) {
let width = (dims.width as f64 / self.scale_factor) as i32; let width = (dims.width as f64 / self.scale_factor) as i32;
let height = (dims.height as f64 / self.scale_factor) as i32; let height = (dims.height as f64 / self.scale_factor) as i32;
if width > 0 && height > 0 {
self.viewport.set_destination(width, height); self.viewport.set_destination(width, height);
}
debug!("{} viewport: {width}x{height}", self.server.id()); debug!("{} viewport: {width}x{height}", self.server.id());
if let Some(hints) = size_hints { if let Some(hints) = size_hints {
let Some(SurfaceRole::Toplevel(Some(data))) = &self.role else { let Some(SurfaceRole::Toplevel(Some(data))) = &self.role else {

View file

@ -1,7 +1,6 @@
use super::{ServerState, WindowDims}; use super::{ServerState, WindowDims};
use crate::xstate::{SetState, WinSize, WmName}; use crate::xstate::{SetState, WinSize, WmName};
use rustix::event::{poll, PollFd, PollFlags}; use rustix::event::{poll, PollFd, PollFlags};
use smithay_client_toolkit::compositor::Surface;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Write; use std::io::Write;
use std::os::fd::{AsRawFd, BorrowedFd}; use std::os::fd::{AsRawFd, BorrowedFd};
@ -24,8 +23,6 @@ use wayland_client::{
}, },
Connection, Proxy, WEnum, Connection, Proxy, WEnum,
}; };
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1;
use wayland_protocols::{ use wayland_protocols::{
wp::{ wp::{
linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
@ -519,39 +516,6 @@ impl TestFixture {
(output, self.testwl.last_created_output()) (output, self.testwl.last_created_output())
} }
fn enable_fractional_scale(&mut self) -> TestObject<WpFractionalScaleManagerV1> {
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::<WlRegistry>::Global {
name,
interface,
version,
} = event
else {
panic!("Unexpected event: {event:?}");
};
assert_eq!(interface, WpFractionalScaleManagerV1::interface().name);
let man = TestObject::<WpFractionalScaleManagerV1>::from_request(
&self.registry.obj,
Req::<WlRegistry>::Bind {
name,
id: (WpFractionalScaleManagerV1::interface(), version),
},
);
self.run();
man
}
fn enable_xdg_output(&mut self) -> TestObject<ZxdgOutputManagerV1> { fn enable_xdg_output(&mut self) -> TestObject<ZxdgOutputManagerV1> {
self.testwl.enable_xdg_output_manager(); self.testwl.enable_xdg_output_manager();
self.run(); self.run();
@ -2055,7 +2019,6 @@ fn scaled_output_small_popup() {
.y(50) .y(50)
.width(1) .width(1)
.height(1) .height(1)
.scale(2)
.check_size_and_pos(false); .check_size_and_pos(false);
let (_, popup_id) = f.create_popup(&comp, builder); let (_, popup_id) = f.create_popup(&comp, builder);
@ -2068,6 +2031,50 @@ fn scaled_output_small_popup() {
assert!(dims.height > 0); 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] #[test]
fn toplevel_size_limits_scaled() { fn toplevel_size_limits_scaled() {
let (mut f, comp) = TestFixture::new_with_compositor(); let (mut f, comp) = TestFixture::new_with_compositor();

View file

@ -783,7 +783,8 @@ fn input_focus() {
long_offset: 0, long_offset: 0,
long_length: 1, long_length: 1,
}) })
.value::<u32>().first() .value::<u32>()
.first()
.and_then(|state| WmState::try_from(*state).ok()), .and_then(|state| WmState::try_from(*state).ok()),
Some(WmState::Normal) Some(WmState::Normal)
); );

View file

@ -25,7 +25,7 @@ use wayland_protocols::{
zwp_tablet_v2::ZwpTabletV2, zwp_tablet_v2::ZwpTabletV2,
}, },
viewporter::server::{ viewporter::server::{
wp_viewport::WpViewport, wp_viewport::{self, WpViewport},
wp_viewporter::{self, WpViewporter}, wp_viewporter::{self, WpViewporter},
}, },
}, },
@ -85,6 +85,13 @@ pub struct BufferDamage {
pub height: i32, pub height: i32,
} }
#[derive(Debug, PartialEq, Eq)]
pub struct Viewport {
pub width: i32,
pub height: i32,
viewport: WpViewport,
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct SurfaceData { pub struct SurfaceData {
pub surface: WlSurface, pub surface: WlSurface,
@ -93,6 +100,7 @@ pub struct SurfaceData {
pub role: Option<SurfaceRole>, pub role: Option<SurfaceRole>,
pub last_enter_serial: Option<u32>, pub last_enter_serial: Option<u32>,
pub fractional: Option<WpFractionalScaleV1>, pub fractional: Option<WpFractionalScaleV1>,
pub viewport: Option<Viewport>,
} }
impl SurfaceData { impl SurfaceData {
@ -1519,6 +1527,7 @@ impl Dispatch<WlCompositor, ()> for State {
role: None, role: None,
last_enter_serial: None, last_enter_serial: None,
fractional: None, fractional: None,
viewport: None,
}, },
); );
state.last_surface_id = Some(SurfaceId(id)); state.last_surface_id = Some(SurfaceId(id));
@ -1828,7 +1837,7 @@ impl Dispatch<ZxdgToplevelDecorationV1, SurfaceId> for State {
impl Dispatch<WpViewporter, ()> for State { impl Dispatch<WpViewporter, ()> for State {
fn request( fn request(
_: &mut Self, state: &mut Self,
_: &Client, _: &Client,
_: &WpViewporter, _: &WpViewporter,
request: <WpViewporter as Resource>::Request, request: <WpViewporter as Resource>::Request,
@ -1837,8 +1846,18 @@ impl Dispatch<WpViewporter, ()> for State {
data_init: &mut wayland_server::DataInit<'_, Self>, data_init: &mut wayland_server::DataInit<'_, Self>,
) { ) {
match request { match request {
wp_viewporter::Request::GetViewport { surface: _, id } => { wp_viewporter::Request::GetViewport { surface, id } => {
data_init.init(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 => {} wp_viewporter::Request::Destroy => {}
_ => unreachable!(), _ => unreachable!(),
@ -1846,17 +1865,41 @@ impl Dispatch<WpViewporter, ()> for State {
} }
} }
impl Dispatch<WpViewport, ()> for State { impl Dispatch<WpViewport, SurfaceId> for State {
fn request( fn request(
_: &mut Self, state: &mut Self,
_: &Client, _: &Client,
_: &WpViewport, viewport: &WpViewport,
_: <WpViewport as Resource>::Request, request: <WpViewport as Resource>::Request,
_: &(), surface_id: &SurfaceId,
_: &DisplayHandle, _: &DisplayHandle,
_: &mut wayland_server::DataInit<'_, Self>, _: &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:?}"),
}
} }
} }