From 3794c4b94569b0623448dbd2625534406b8d1ad6 Mon Sep 17 00:00:00 2001 From: Shawn Wallace Date: Tue, 2 Jul 2024 00:16:01 -0400 Subject: [PATCH] Properly offset popups on offset outputs --- src/server/event.rs | 8 ++-- src/server/mod.rs | 6 ++- src/server/tests.rs | 113 +++++++++++++++++++++++++++++++++----------- 3 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/server/event.rs b/src/server/event.rs index 934ea52..ac756ba 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -157,8 +157,8 @@ impl SurfaceData { }; let output: &mut Output = object.as_mut(); if let Some(win) = self.window { - if let Some(data) = state.windows.get(&win) { - output.add_surface(self, data.attrs.dims, state.connection.as_mut().unwrap()); + if let Some(data) = state.windows.get_mut(&win) { + output.add_surface(self, &mut data.attrs.dims, state.connection.as_mut().unwrap()); } } &output.server @@ -661,11 +661,13 @@ impl Output { fn add_surface( &mut self, surface: &SurfaceData, - dims: WindowDims, + dims: &mut WindowDims, connection: &mut C, ) { self.surfaces.insert(surface.client.clone()); let window = surface.window.unwrap(); + dims.x = self.x as _; + dims.y = self.y as _; debug!("moving surface to {}x{}", self.x, self.y); connection.set_window_dims( diff --git a/src/server/mod.rs b/src/server/mod.rs index 2565836..842b00c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -817,10 +817,14 @@ impl ServerState { let parent_window = self.windows.get(&parent).unwrap(); let parent_surface: &SurfaceData = self.objects[parent_window.surface_key.unwrap()].as_ref(); + let parent_dims = parent_window.attrs.dims; + + let x = window.attrs.dims.x - parent_dims.x; + let y = window.attrs.dims.y - parent_dims.y; let positioner = self.xdg_wm_base.create_positioner(&self.qh, ()); positioner.set_size(window.attrs.dims.width as _, window.attrs.dims.height as _); - positioner.set_offset(window.attrs.dims.x as i32, window.attrs.dims.y as i32); + positioner.set_offset(x as i32, y as i32); positioner.set_anchor(Anchor::TopLeft); positioner.set_gravity(Gravity::BottomRight); positioner.set_anchor_rect( diff --git a/src/server/tests.rs b/src/server/tests.rs index 52fb17f..9c2481d 100644 --- a/src/server/tests.rs +++ b/src/server/tests.rs @@ -13,6 +13,7 @@ use wayland_client::{ wl_compositor::WlCompositor, wl_display::WlDisplay, wl_keyboard::WlKeyboard, + wl_output::WlOutput, wl_pointer::WlPointer, wl_registry::WlRegistry, wl_seat::{self, WlSeat}, @@ -226,6 +227,7 @@ struct TestFixture { /// Exwayland's display - must dispatch this for our server state to advance exwl_display: Display, surface_serial: u64, + registry: TestObject, } static INIT: std::sync::Once = std::sync::Once::new(); @@ -259,12 +261,20 @@ impl TestFixture { exwayland.connect(ex_server); exwayland.set_x_connection(FakeXConnection::default()); + + let exwl_connection = Connection::from_socket(fake_client).unwrap(); + let registry = TestObject::::from_request( + &exwl_connection.display(), + Req::::GetRegistry {}, + ); + let mut f = TestFixture { testwl, exwayland, - exwl_connection: Connection::from_socket(fake_client).unwrap().into(), + exwl_connection: exwl_connection.into(), exwl_display: display, surface_serial: 1, + registry, }; f.run(); f @@ -282,13 +292,7 @@ impl TestFixture { fn compositor(&mut self) -> Compositor { let mut ret = CompositorOptional::default(); - let wl_display = self.exwl_connection.display(); - - let registry = - TestObject::::from_request(&wl_display, Req::::GetRegistry {}); - self.run(); - - let events = std::mem::take(&mut *registry.data.events.lock().unwrap()); + let events = std::mem::take(&mut *self.registry.data.events.lock().unwrap()); assert!(!events.is_empty()); for event in events { @@ -306,25 +310,25 @@ impl TestFixture { match interface { x if x == WlCompositor::interface().name => { ret.compositor = Some(TestObject::from_request( - ®istry.obj, + &self.registry.obj, bind_req(WlCompositor::interface()), )); } x if x == WlShm::interface().name => { ret.shm = Some(TestObject::from_request( - ®istry.obj, + &self.registry.obj, bind_req(WlShm::interface()), )); } x if x == XwaylandShellV1::interface().name => { ret.shell = Some(TestObject::from_request( - ®istry.obj, + &self.registry.obj, bind_req(XwaylandShellV1::interface()), )); } x if x == WlSeat::interface().name => { ret.seat = Some(TestObject::from_request( - ®istry.obj, + &self.registry.obj, bind_req(WlSeat::interface()), )); } @@ -361,12 +365,40 @@ impl TestFixture { // Get our events let res = self.exwl_connection.prepare_read().unwrap().read(); if res.is_err() - && !matches!(res, Err(WaylandError::Io(ref e)) if e.kind() == std::io::ErrorKind::WouldBlock) + && matches!(res, Err(WaylandError::Io(ref e)) if e.kind() != std::io::ErrorKind::WouldBlock) { panic!("Read failed: {res:?}") } } + fn new_output(&mut self, x: i32, y: i32) -> wayland_server::protocol::wl_output::WlOutput { + self.testwl.new_output(x, y); + self.run(); + self.run(); + let mut events = std::mem::take(&mut *self.registry.data.events.lock().unwrap()); + assert_eq!(events.len(), 1); + let event = events.pop().unwrap(); + let Ev::::Global { + name, + interface, + version, + } = event + else { + panic!("Unexpected event: {event:?}"); + }; + + assert_eq!(interface, WlOutput::interface().name); + TestObject::::from_request( + &self.registry.obj, + Req::::Bind { + name, + id: (WlOutput::interface(), version), + }, + ); + self.run(); + self.testwl.last_created_output() + } + fn register_window(&mut self, window: Window, data: WindowData) { self.exwayland .connection @@ -510,14 +542,17 @@ impl TestFixture { &mut self, comp: &Compositor, window: Window, - parent_id: testwl::SurfaceId, + parent_window: Window, + parent_surface: testwl::SurfaceId, + x: i16, + y: i16, ) -> (TestObject, testwl::SurfaceId) { let (buffer, surface) = comp.create_surface(); let data = WindowData { mapped: true, dims: WindowDims { - x: 10, - y: 10, + x, + y, width: 50, height: 50, }, @@ -529,7 +564,7 @@ impl TestFixture { self.run(); let popup_id = self.check_new_surface(); - assert_ne!(popup_id, parent_id); + assert_ne!(popup_id, parent_surface); { let surface_data = self.testwl.get_surface_data(popup_id).unwrap(); @@ -549,7 +584,7 @@ impl TestFixture { let toplevel_xdg = &self .testwl - .get_surface_data(parent_id) + .get_surface_data(parent_surface) .unwrap() .xdg() .surface; @@ -557,18 +592,23 @@ impl TestFixture { let pos = &surface_data.popup().positioner_state; assert_eq!(pos.size.as_ref().unwrap(), &testwl::Vec2 { x: 50, y: 50 }); + + let parent_win = &self.connection().windows[&parent_window]; assert_eq!( pos.anchor_rect.as_ref().unwrap(), &testwl::Rect { - size: testwl::Vec2 { x: 100, y: 100 }, + size: testwl::Vec2 { + x: parent_win.dims.width as _, + y: parent_win.dims.height as _ + }, offset: testwl::Vec2::default() } ); assert_eq!( pos.offset, testwl::Vec2 { - x: dims.x as _, - y: dims.y as _ + x: (dims.x - parent_win.dims.x) as _, + y: (dims.y - parent_win.dims.y) as _ } ); assert_eq!(pos.anchor, xdg_positioner::Anchor::TopLeft); @@ -692,7 +732,7 @@ fn popup_flow_simple() { let (_, toplevel_id) = f.create_toplevel(&compositor, win_toplevel); let win_popup = unsafe { Window::new(2) }; - let (popup_surface, popup_id) = f.create_popup(&compositor, win_popup, toplevel_id); + let (popup_surface, popup_id) = f.create_popup(&compositor, win_popup, win_toplevel, toplevel_id, 10, 10); f.exwayland.unmap_window(win_popup); f.exwayland.destroy_window(win_popup); @@ -756,11 +796,8 @@ fn pass_through_globals() { } let mut globals = SupportedGlobals::default(); - let display = f.exwl_connection.display(); - let registry = - TestObject::::from_request(&display, Req::::GetRegistry {}); f.run(); - let events = std::mem::take(&mut *registry.data.events.lock().unwrap()); + let events = std::mem::take(&mut *f.registry.data.events.lock().unwrap()); assert!(!events.is_empty()); for event in events { let Ev::::Global { interface, .. } = event else { @@ -809,7 +846,7 @@ fn popup_window_changes_surface() { let (_, toplevel_id) = f.create_toplevel(&comp, t_win); let win = unsafe { Window::new(2) }; - let (surface, old_id) = f.create_popup(&comp, win, toplevel_id); + let (surface, old_id) = f.create_popup(&comp, win, t_win, toplevel_id, 0, 0); f.exwayland.unmap_window(win); surface.obj.destroy(); @@ -1178,6 +1215,28 @@ fn override_redirect_choose_hover_window() { assert_eq!(&popup_data.popup().parent, win1_xdg); } +#[test] +fn offset_output() { + let (mut f, comp) = TestFixture::new_with_compositor(); + let output = f.new_output(500, 100); + + let window = unsafe { Window::new(1) }; + let (_, surface_id) = f.create_toplevel(&comp, window); + f.testwl.move_surface_to_output(surface_id, output); + f.run(); + let data = &f.connection().windows[&window]; + assert_eq!(data.dims.x, 500); + assert_eq!(data.dims.y, 100); + + let popup = unsafe { Window::new(2) }; + let (_, p_id) = f.create_popup(&comp, popup, window, surface_id, 510, 110); + let data = f.testwl.get_surface_data(p_id).unwrap(); + assert_eq!( + data.popup().positioner_state.offset, + testwl::Vec2 { x: 10, y: 10 } + ); +} + /// See Pointer::handle_event for an explanation. #[test] fn popup_pointer_motion_workaround() {}