Offset output positions to always have positive coordinates

Honestly, this is something that should probably be fixed in Xwayland itself,
but they don't seem interested in fixing it:
https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/395#note_555613

Fixes #15
This commit is contained in:
Shawn Wallace 2025-03-16 15:55:56 -04:00
parent f9ec97b007
commit beb7c3ebe0
4 changed files with 397 additions and 41 deletions

View file

@ -14,7 +14,7 @@ use wayland_client::{
wl_compositor::WlCompositor,
wl_display::WlDisplay,
wl_keyboard::WlKeyboard,
wl_output::WlOutput,
wl_output::{self, WlOutput},
wl_pointer::WlPointer,
wl_registry::WlRegistry,
wl_seat::{self, WlSeat},
@ -51,7 +51,7 @@ use wayland_protocols::{
shell::server::{xdg_positioner, xdg_toplevel},
xdg_output::zv1::client::{
zxdg_output_manager_v1::{self, ZxdgOutputManagerV1},
zxdg_output_v1::ZxdgOutputV1,
zxdg_output_v1::{self, ZxdgOutputV1},
},
},
xwayland::shell::v1::client::{
@ -487,13 +487,18 @@ impl TestFixture {
man
}
fn create_xdg_output(&mut self, man: &TestObject<ZxdgOutputManagerV1>, output: WlOutput) {
TestObject::<ZxdgOutputV1>::from_request(
fn create_xdg_output(
&mut self,
man: &TestObject<ZxdgOutputManagerV1>,
output: WlOutput,
) -> TestObject<ZxdgOutputV1> {
let xdg = TestObject::<ZxdgOutputV1>::from_request(
&man.obj,
zxdg_output_manager_v1::Request::GetXdgOutput { output },
);
self.run();
self.run();
xdg
}
fn register_window(&mut self, window: Window, data: WindowData) {
@ -1704,6 +1709,140 @@ fn fullscreen_heuristic() {
check_fullscreen(3, true);
}
#[track_caller]
fn check_output_position_event(output: &TestObject<WlOutput>, x: i32, y: i32) {
let events = std::mem::take(&mut *output.data.events.lock().unwrap());
assert!(!events.is_empty());
let mut done = false;
let mut geo = false;
for event in events {
match event {
wl_output::Event::Geometry {
x: geo_x, y: geo_y, ..
} => {
assert_eq!(geo_x, x);
assert_eq!(geo_y, y);
geo = true;
}
wl_output::Event::Done => {
done = true;
}
_ => {}
}
}
assert!(geo, "Didn't get geometry event");
assert!(done, "Didn't get done event");
}
#[test]
fn negative_output_position() {
let mut f = TestFixture::new();
std::mem::take(&mut *f.registry.data.events.lock().unwrap());
let (output, _) = f.new_output(-500, -500);
f.run();
f.run();
check_output_position_event(&output, 0, 0);
let (output2, _) = f.new_output(0, 0);
f.run();
f.run();
check_output_position_event(&output2, 500, 500);
assert!(output.data.events.lock().unwrap().is_empty());
let (output3, _) = f.new_output(500, 500);
f.run();
f.run();
check_output_position_event(&output3, 1000, 1000);
assert!(output.data.events.lock().unwrap().is_empty());
assert!(output2.data.events.lock().unwrap().is_empty());
}
#[test]
fn negative_output_position_update_offset() {
let mut f = TestFixture::new();
std::mem::take(&mut *f.registry.data.events.lock().unwrap());
let (output, _) = f.new_output(-500, -500);
f.run();
f.run();
check_output_position_event(&output, 0, 0);
let (output2, _) = f.new_output(0, -1000);
f.run();
f.run();
check_output_position_event(&output, 0, 500);
check_output_position_event(&output2, 500, 0);
let (output3, _) = f.new_output(-1000, 0);
f.run();
f.run();
check_output_position_event(&output, 500, 500);
check_output_position_event(&output2, 1000, 0);
check_output_position_event(&output3, 0, 1000);
}
#[test]
fn negative_output_xdg_position_update_offset() {
let mut f = TestFixture::new();
std::mem::take(&mut *f.registry.data.events.lock().unwrap());
let xdg = f.enable_xdg_output();
let (output, _) = f.new_output(-500, -500);
f.run();
f.run();
check_output_position_event(&output, 0, 0);
let (output2, output_s) = f.new_output(0, 0);
let xdg_output = f.create_xdg_output(&xdg, output2.obj);
f.testwl.move_xdg_output(&output_s, 0, -1000);
f.run();
f.run();
check_output_position_event(&output, 0, 500);
let mut found = false;
let mut first = false;
for event in std::mem::take(&mut *xdg_output.data.events.lock().unwrap()) {
if let zxdg_output_v1::Event::LogicalPosition { x, y } = event {
// Testwl sends a logical positon event when the output is first created
// We are interested in the second one generated by satellite
if !first {
first = true;
continue;
}
assert_eq!(x, 500);
assert_eq!(y, 0);
found = true;
break;
}
}
assert!(found, "Did not get xdg output logical position");
found = false;
for event in std::mem::take(&mut *output2.data.events.lock().unwrap()) {
if let wl_output::Event::Done = event {
found = true;
break;
}
}
assert!(found, "Did not get done event");
}
#[test]
fn negative_output_position_remove_offset() {
let mut f = TestFixture::new();
std::mem::take(&mut *f.registry.data.events.lock().unwrap());
let (c_output, s_output) = f.new_output(-500, -500);
f.run();
f.run();
check_output_position_event(&c_output, 0, 0);
f.testwl.move_output(&s_output, 500, 500);
f.run();
f.run();
check_output_position_event(&c_output, 500, 500);
}
/// See Pointer::handle_event for an explanation.
#[test]
fn popup_pointer_motion_workaround() {}