use bevy::ecs::system::NonSendMarker;
use bevy::prelude::*;
use bevy::winit::WINIT_WINDOWS;
use bevy_kana::ToI32;
use bevy_kana::ToU32;
use raw_window_handle::HasWindowHandle;
use raw_window_handle::RawWindowHandle;
use x11rb::protocol::xproto::AtomEnum;
use x11rb::protocol::xproto::ConnectionExt;
use x11rb::xcb_ffi::XCBConnection;
use super::types::TargetPosition;
use super::types::X11FrameCompensated;
fn query_frame_top(window_id: u32) -> Option<i32> {
let (conn, _screen_num) = XCBConnection::connect(None).ok()?;
let atom_cookie = conn.intern_atom(false, b"_NET_FRAME_EXTENTS").ok()?;
let atom = atom_cookie.reply().ok()?.atom;
let property_cookie = conn
.get_property(false, window_id, atom, AtomEnum::CARDINAL, 0, 4)
.ok()?;
let property = property_cookie.reply().ok()?;
let values: Vec<u32> = property.value32()?.collect();
if values.len() >= 4 {
Some(values[2].to_i32()) } else {
None
}
}
fn get_x11_window_id<W: HasWindowHandle>(window: &W) -> Option<u32> {
let handle = window.window_handle().ok()?;
match handle.as_raw() {
RawWindowHandle::Xlib(h) => Some(h.window.to_u32()),
RawWindowHandle::Xcb(h) => Some(h.window.get()),
_ => None,
}
}
pub(crate) fn compensate_target_position(
mut commands: Commands,
mut windows: Query<(Entity, &mut TargetPosition), Without<X11FrameCompensated>>,
_non_send: NonSendMarker,
) {
for (entity, mut target) in &mut windows {
let Some(pos) = target.position else {
commands.entity(entity).insert(X11FrameCompensated);
continue;
};
let frame_top = WINIT_WINDOWS.with(|ww| {
let ww = ww.borrow();
ww.get_window(entity).and_then(|winit_window| {
let window_id = get_x11_window_id(&**winit_window)?;
query_frame_top(window_id)
})
});
let Some(frame_top) = frame_top else {
continue;
};
let compensated = IVec2::new(pos.x, pos.y - frame_top);
info!(
"[W6] Compensating position: {:?} -> {:?} (frame_top={})",
pos, compensated, frame_top
);
target.position = Some(compensated);
commands.entity(entity).insert(X11FrameCompensated);
}
}