server: check if connected to x server when output offset updates

Caused by the compositor reporting negative output coordinates to start.

Fixes #187
This commit is contained in:
Shawn Wallace 2025-06-24 23:18:32 -04:00
parent 117af56a83
commit 2e7c318ac2
3 changed files with 94 additions and 26 deletions

View file

@ -151,7 +151,7 @@ impl SurfaceEvents {
x: dimensions.x - state.global_output_offset.x.value, x: dimensions.x - state.global_output_offset.x.value,
y: dimensions.y - state.global_output_offset.y.value, y: dimensions.y - state.global_output_offset.y.value,
}, },
state.connection.as_mut().unwrap(), &mut state.connection,
); );
if state.last_focused_toplevel == Some(*window) { if state.last_focused_toplevel == Some(*window) {
let output = get_output_name(Some(&on_output), &state.world); let output = get_output_name(Some(&on_output), &state.world);
@ -889,21 +889,19 @@ fn update_output_offset(
); );
} }
if let Some(connection) = state.connection.as_mut() {
update_window_output_offsets( update_window_output_offsets(
output, output,
&state.global_output_offset, &state.global_output_offset,
&state.world, &state.world,
connection, &mut state.connection,
); );
} }
}
fn update_window_output_offsets( fn update_window_output_offsets(
output: Entity, output: Entity,
global_output_offset: &GlobalOutputOffset, global_output_offset: &GlobalOutputOffset,
world: &World, world: &World,
connection: &mut impl XConnection, connection: &mut Option<impl XConnection>,
) { ) {
let dimensions = world.get::<&OutputDimensions>(output).unwrap(); let dimensions = world.get::<&OutputDimensions>(output).unwrap();
let mut query = world.query::<(&x::Window, &mut WindowData, &OnOutput)>(); let mut query = world.query::<(&x::Window, &mut WindowData, &OnOutput)>();
@ -927,7 +925,7 @@ pub(super) fn update_global_output_offset(
output: Entity, output: Entity,
global_output_offset: &GlobalOutputOffset, global_output_offset: &GlobalOutputOffset,
world: &World, world: &World,
connection: &mut impl XConnection, connection: &mut Option<impl XConnection>,
) { ) {
let entity = world.entity(output).unwrap(); let entity = world.entity(output).unwrap();
let mut query = entity.query::<(&OutputDimensions, &WlOutput)>(); let mut query = entity.query::<(&OutputDimensions, &WlOutput)>();

View file

@ -138,7 +138,7 @@ impl WindowData {
&mut self, &mut self,
window: x::Window, window: x::Window,
offset: WindowOutputOffset, offset: WindowOutputOffset,
connection: &mut C, connection: &mut Option<C>,
) { ) {
log::trace!("offset: {offset:?}"); log::trace!("offset: {offset:?}");
if offset == self.output_offset { if offset == self.output_offset {
@ -150,6 +150,7 @@ impl WindowData {
dims.y += (offset.y - self.output_offset.y) as i16; dims.y += (offset.y - self.output_offset.y) as i16;
self.output_offset = offset; self.output_offset = offset;
if let Some(connection) = connection.as_mut() {
connection.set_window_dims( connection.set_window_dims(
window, window,
PendingSurfaceState { PendingSurfaceState {
@ -159,6 +160,7 @@ impl WindowData {
height: self.attrs.dims.height as _, height: self.attrs.dims.height as _,
}, },
); );
}
debug!("set {:?} offset to {:?}", window, self.output_offset); debug!("set {:?} offset to {:?}", window, self.output_offset);
} }
@ -989,7 +991,7 @@ impl<C: XConnection> ServerState<C> {
e, e,
&self.global_output_offset, &self.global_output_offset,
&self.world, &self.world,
self.connection.as_mut().unwrap(), &mut self.connection,
); );
} }
self.global_offset_updated = false; self.global_offset_updated = false;

View file

@ -321,8 +321,50 @@ impl PopupBuilder {
} }
} }
trait PreConnectFn: Sized {
fn call(self, _: &mut testwl::Server) {}
}
impl<F: FnOnce(&mut testwl::Server)> PreConnectFn for F {
fn call(self, server: &mut testwl::Server) {
self(server);
}
}
impl PreConnectFn for () {}
struct SetupOptions<F> {
pre_connect: Option<F>,
connect_x: bool,
}
impl SetupOptions<()> {
fn dont_connect_x() -> Self {
Self {
pre_connect: None,
connect_x: false,
}
}
}
impl<F: PreConnectFn> SetupOptions<F> {
fn pre_connect(pre_connect: F) -> Self {
Self {
pre_connect: Some(pre_connect),
connect_x: true,
}
}
}
impl Default for SetupOptions<()> {
fn default() -> Self {
Self {
pre_connect: None,
connect_x: true,
}
}
}
impl TestFixture { impl TestFixture {
fn new_pre_connect(pre_connect: impl FnOnce(&mut testwl::Server)) -> Self { fn new_with_options<F: PreConnectFn>(options: SetupOptions<F>) -> Self {
INIT.call_once(|| { INIT.call_once(|| {
env_logger::builder() env_logger::builder()
.is_test(true) .is_test(true)
@ -332,7 +374,9 @@ impl TestFixture {
let (client_s, server_s) = UnixStream::pair().unwrap(); let (client_s, server_s) = UnixStream::pair().unwrap();
let mut testwl = testwl::Server::new(true); let mut testwl = testwl::Server::new(true);
pre_connect(&mut testwl); if let Some(pre_connect) = options.pre_connect {
pre_connect.call(&mut testwl);
}
let display = Display::<FakeServerState>::new().unwrap(); let display = Display::<FakeServerState>::new().unwrap();
testwl.connect(server_s); testwl.connect(server_s);
// Handle initial globals roundtrip setup requirement // Handle initial globals roundtrip setup requirement
@ -350,7 +394,9 @@ impl TestFixture {
let (fake_client, xwls_server) = UnixStream::pair().unwrap(); let (fake_client, xwls_server) = UnixStream::pair().unwrap();
satellite.connect(xwls_server); satellite.connect(xwls_server);
if options.connect_x {
satellite.set_x_connection(FakeXConnection::default()); satellite.set_x_connection(FakeXConnection::default());
}
let xwls_connection = Connection::from_socket(fake_client).unwrap(); let xwls_connection = Connection::from_socket(fake_client).unwrap();
let registry = TestObject::<WlRegistry>::from_request( let registry = TestObject::<WlRegistry>::from_request(
@ -371,7 +417,11 @@ impl TestFixture {
} }
fn new() -> Self { fn new() -> Self {
Self::new_pre_connect(|_| {}) Self::new_with_options(SetupOptions::default())
}
fn new_pre_connect(pre_connect: impl FnOnce(&mut testwl::Server)) -> Self {
Self::new_with_options(SetupOptions::pre_connect(pre_connect))
} }
fn new_with_compositor() -> (Self, Compositor) { fn new_with_compositor() -> (Self, Compositor) {
@ -514,6 +564,7 @@ impl TestFixture {
}, },
); );
self.run(); self.run();
self.run();
(output, self.testwl.last_created_output()) (output, self.testwl.last_created_output())
} }
@ -2359,6 +2410,23 @@ fn tablet_tool_fractional_scale() {
assert_eq!(y, 40.0 * 1.5); assert_eq!(y, 40.0 * 1.5);
} }
#[test]
fn output_updated_before_x_connection() {
let mut f = TestFixture::new_with_options(SetupOptions::dont_connect_x());
let comp = f.compositor();
let (_, output) = f.new_output(-20, -20);
f.satellite.set_x_connection(FakeXConnection::default());
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();
f.run();
let data = &f.connection().windows[&window];
assert_eq!(data.dims.x, 0);
assert_eq!(data.dims.y, 0);
}
/// See Pointer::handle_event for an explanation. /// See Pointer::handle_event for an explanation.
#[test] #[test]
fn popup_pointer_motion_workaround() {} fn popup_pointer_motion_workaround() {}