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

View file

@ -138,7 +138,7 @@ impl WindowData {
&mut self,
window: x::Window,
offset: WindowOutputOffset,
connection: &mut C,
connection: &mut Option<C>,
) {
log::trace!("offset: {offset:?}");
if offset == self.output_offset {
@ -150,15 +150,17 @@ impl WindowData {
dims.y += (offset.y - self.output_offset.y) as i16;
self.output_offset = offset;
connection.set_window_dims(
window,
PendingSurfaceState {
x: dims.x as i32,
y: dims.y as i32,
width: self.attrs.dims.width as _,
height: self.attrs.dims.height as _,
},
);
if let Some(connection) = connection.as_mut() {
connection.set_window_dims(
window,
PendingSurfaceState {
x: dims.x as i32,
y: dims.y as i32,
width: self.attrs.dims.width as _,
height: self.attrs.dims.height as _,
},
);
}
debug!("set {:?} offset to {:?}", window, self.output_offset);
}
@ -989,7 +991,7 @@ impl<C: XConnection> ServerState<C> {
e,
&self.global_output_offset,
&self.world,
self.connection.as_mut().unwrap(),
&mut self.connection,
);
}
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 {
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(|| {
env_logger::builder()
.is_test(true)
@ -332,7 +374,9 @@ impl TestFixture {
let (client_s, server_s) = UnixStream::pair().unwrap();
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();
testwl.connect(server_s);
// Handle initial globals roundtrip setup requirement
@ -350,7 +394,9 @@ impl TestFixture {
let (fake_client, xwls_server) = UnixStream::pair().unwrap();
satellite.connect(xwls_server);
satellite.set_x_connection(FakeXConnection::default());
if options.connect_x {
satellite.set_x_connection(FakeXConnection::default());
}
let xwls_connection = Connection::from_socket(fake_client).unwrap();
let registry = TestObject::<WlRegistry>::from_request(
@ -371,7 +417,11 @@ impl TestFixture {
}
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) {
@ -514,6 +564,7 @@ impl TestFixture {
},
);
self.run();
self.run();
(output, self.testwl.last_created_output())
}
@ -2359,6 +2410,23 @@ fn tablet_tool_fractional_scale() {
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.
#[test]
fn popup_pointer_motion_workaround() {}