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

@ -87,7 +87,7 @@ pub struct WindowAttributes {
pub group: Option<x::Window>,
}
#[derive(Debug, Default, PartialEq, Eq)]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
struct WindowOutputOffset {
x: i32,
y: i32,
@ -473,6 +473,18 @@ struct FocusData {
output_name: Option<String>,
}
#[derive(Copy, Clone, Default)]
struct GlobalOutputOffsetDimension {
owner: Option<ObjectKey>,
value: i32,
}
#[derive(Copy, Clone)]
struct GlobalOutputOffset {
x: GlobalOutputOffsetDimension,
y: GlobalOutputOffsetDimension,
}
pub struct ServerState<C: XConnection> {
dh: DisplayHandle,
clientside: ClientState,
@ -492,6 +504,8 @@ pub struct ServerState<C: XConnection> {
xdg_wm_base: XdgWmBase,
clipboard_data: Option<ClipboardData<C::X11Selection>>,
last_kb_serial: Option<u32>,
global_output_offset: GlobalOutputOffset,
global_offset_updated: bool,
}
impl<C: XConnection> ServerState<C> {
@ -542,6 +556,17 @@ impl<C: XConnection> ServerState<C> {
xdg_wm_base,
clipboard_data,
last_kb_serial: None,
global_output_offset: GlobalOutputOffset {
x: GlobalOutputOffsetDimension {
owner: None,
value: 0,
},
y: GlobalOutputOffsetDimension {
owner: None,
value: 0,
},
},
global_offset_updated: false,
}
}
@ -844,15 +869,43 @@ impl<C: XConnection> ServerState<C> {
for (key, event) in self.clientside.read_events() {
let Some(object) = &mut self.objects.get_mut(key) else {
warn!("could not handle clientside event: stale surface");
warn!("could not handle clientside event: stale object");
continue;
};
let mut object = object.0.take().unwrap();
object.handle_event(event, self);
let ret = self.objects[key].0.replace(object); // safe indexed access?
debug_assert!(ret.is_none());
}
if self.global_offset_updated {
if self.global_output_offset.x.owner.is_none()
|| self.global_output_offset.y.owner.is_none()
{
self.calc_global_output_offset();
}
debug!(
"updated global output offset: {}x{}",
self.global_output_offset.x.value, self.global_output_offset.y.value
);
for (key, _) in self.output_keys.clone() {
let Some(object) = &mut self.objects.get_mut(key) else {
continue;
};
let mut output: Output = object
.0
.take()
.expect("Output object missing?")
.try_into()
.expect("Not an output?");
output.global_offset_updated(self);
self.objects[key].0.replace(output.into());
}
self.global_offset_updated = false;
}
{
if let Some(FocusData {
window,
@ -918,6 +971,28 @@ impl<C: XConnection> ServerState<C> {
}
}
fn calc_global_output_offset(&mut self) {
for (key, _) in &self.output_keys {
let Some(object) = &self.objects.get(key) else {
continue;
};
let output: &Output = object.as_ref();
if output.dimensions.x < self.global_output_offset.x.value {
self.global_output_offset.x = GlobalOutputOffsetDimension {
owner: Some(key),
value: output.dimensions.x,
}
}
if output.dimensions.y < self.global_output_offset.y.value {
self.global_output_offset.y = GlobalOutputOffsetDimension {
owner: Some(key),
value: output.dimensions.y,
}
}
}
}
fn create_role_window(&mut self, window: x::Window, surface_key: ObjectKey) {
// Temporarily remove surface to placate borrow checker
let mut surface: SurfaceData = self.objects[surface_key]