select: use xdg_output logical_position for per-monitor surface origin

This commit is contained in:
entailz 2026-04-09 15:40:52 -07:00
parent fa28415c7a
commit 0e910d73cc

View file

@ -19,6 +19,10 @@ use wayland_protocols::wp::cursor_shape::v1::client::{
wp_cursor_shape_device_v1::{Shape as CursorShape, WpCursorShapeDeviceV1}, wp_cursor_shape_device_v1::{Shape as CursorShape, WpCursorShapeDeviceV1},
wp_cursor_shape_manager_v1::WpCursorShapeManagerV1, wp_cursor_shape_manager_v1::WpCursorShapeManagerV1,
}; };
use wayland_protocols::xdg::xdg_output::zv1::client::{
zxdg_output_manager_v1::ZxdgOutputManagerV1,
zxdg_output_v1::{self, ZxdgOutputV1},
};
use wayland_protocols_wlr::layer_shell::v1::client::{ use wayland_protocols_wlr::layer_shell::v1::client::{
zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_shell_v1::ZwlrLayerShellV1,
zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1}, zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1},
@ -67,6 +71,7 @@ pub struct HintBox {
struct PendingOut { struct PendingOut {
wl: wl_output::WlOutput, wl: wl_output::WlOutput,
name: Option<String>, name: Option<String>,
wl_name: u32,
ox: i32, ox: i32,
oy: i32, oy: i32,
scale: i32, scale: i32,
@ -109,6 +114,8 @@ struct St {
keyboard: Option<wl_keyboard::WlKeyboard>, keyboard: Option<wl_keyboard::WlKeyboard>,
cursor_shape: Option<WpCursorShapeManagerV1>, cursor_shape: Option<WpCursorShapeManagerV1>,
_xdg_outputs: Vec<ZxdgOutputV1>,
pending: Vec<PendingOut>, pending: Vec<PendingOut>,
surfs: Vec<Surf>, surfs: Vec<Surf>,
configured: usize, // count of configure events received configured: usize, // count of configure events received
@ -750,6 +757,29 @@ impl Dispatch<wl_shm::WlShm, ()> for St {
} }
} }
impl Dispatch<ZxdgOutputV1, u32> for St {
fn event(
state: &mut Self,
_: &ZxdgOutputV1,
event: zxdg_output_v1::Event,
wl_name: &u32,
_: &Connection,
_: &QueueHandle<Self>,
) {
let Some(p) = state.pending.iter_mut().find(|p| p.wl_name == *wl_name) else {
return;
};
match event {
zxdg_output_v1::Event::LogicalPosition { x, y } => {
p.ox = x;
p.oy = y;
}
_ => {}
}
}
}
delegate_noop!(St: ignore ZxdgOutputManagerV1);
delegate_noop!(St: ignore wl_compositor::WlCompositor); delegate_noop!(St: ignore wl_compositor::WlCompositor);
delegate_noop!(St: ignore wl_surface::WlSurface); delegate_noop!(St: ignore wl_surface::WlSurface);
delegate_noop!(St: ignore wl_shm_pool::WlShmPool); delegate_noop!(St: ignore wl_shm_pool::WlShmPool);
@ -783,19 +813,25 @@ pub fn select_region(
.bind::<wl_seat::WlSeat, _, _>(&qh, 1..=8, ()) .bind::<wl_seat::WlSeat, _, _>(&qh, 1..=8, ())
.map_err(|e| BlastError::Selection(format!("seat: {e}")))?; .map_err(|e| BlastError::Selection(format!("seat: {e}")))?;
let pending: Vec<PendingOut> = globals let out_globals: Vec<(u32, u32)> = globals // (wl_global_name, version)
.contents() .contents()
.clone_list() .clone_list()
.iter() .iter()
.filter(|g| g.interface == "wl_output") .filter(|g| g.interface == "wl_output")
.map(|g| PendingOut { .map(|g| (g.name, g.version))
.collect();
let pending: Vec<PendingOut> = out_globals
.iter()
.map(|&(gname, ver)| PendingOut {
wl: globals.registry().bind::<wl_output::WlOutput, _, _>( wl: globals.registry().bind::<wl_output::WlOutput, _, _>(
g.name, gname,
g.version.min(4), ver.min(4),
&qh, &qh,
(), (),
), ),
name: None, name: None,
wl_name: gname,
ox: 0, ox: 0,
oy: 0, oy: 0,
scale: 1, scale: 1,
@ -822,6 +858,7 @@ pub fn select_region(
pointer: None, pointer: None,
keyboard: None, keyboard: None,
cursor_shape, cursor_shape,
_xdg_outputs: Vec::new(),
pending, pending,
surfs: Vec::new(), surfs: Vec::new(),
configured: 0, configured: 0,
@ -843,6 +880,21 @@ pub fn select_region(
st.roundtrip()?; st.roundtrip()?;
st.roundtrip()?; // second pass to get pointer/keyboard from capabilities st.roundtrip()?; // second pass to get pointer/keyboard from capabilities
// Fetch logical positions from xdg_output so that surfaces on non-primary
// monitors get the correct (lx, ly) origin. wl_output.geometry is unreliable
// on compositors with fractional scaling — they may always report (0, 0) there
// and only set the real position via xdg_output.logical_position.
if let Ok(xdg_mgr) = globals.bind::<ZxdgOutputManagerV1, _, _>(&st.qh, 2..=3, ()) {
let xdg_outputs: Vec<ZxdgOutputV1> = st
.pending
.iter()
.map(|p| xdg_mgr.get_xdg_output(&p.wl, &st.qh, p.wl_name))
.collect();
st._xdg_outputs = xdg_outputs;
st.roundtrip()?; // receive logical_position events, updating p.ox / p.oy
drop(xdg_mgr);
}
st.create_surfaces()?; st.create_surfaces()?;
let expected = st.surfs.len(); let expected = st.surfs.len();