diff --git a/src/select.rs b/src/select.rs index f94ecd7..1b5afff 100644 --- a/src/select.rs +++ b/src/select.rs @@ -19,6 +19,10 @@ use wayland_protocols::wp::cursor_shape::v1::client::{ wp_cursor_shape_device_v1::{Shape as CursorShape, WpCursorShapeDeviceV1}, 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::{ zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1}, @@ -67,6 +71,7 @@ pub struct HintBox { struct PendingOut { wl: wl_output::WlOutput, name: Option, + wl_name: u32, ox: i32, oy: i32, scale: i32, @@ -109,6 +114,8 @@ struct St { keyboard: Option, cursor_shape: Option, + _xdg_outputs: Vec, + pending: Vec, surfs: Vec, configured: usize, // count of configure events received @@ -750,6 +757,29 @@ impl Dispatch for St { } } +impl Dispatch for St { + fn event( + state: &mut Self, + _: &ZxdgOutputV1, + event: zxdg_output_v1::Event, + wl_name: &u32, + _: &Connection, + _: &QueueHandle, + ) { + 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_surface::WlSurface); delegate_noop!(St: ignore wl_shm_pool::WlShmPool); @@ -783,19 +813,25 @@ pub fn select_region( .bind::(&qh, 1..=8, ()) .map_err(|e| BlastError::Selection(format!("seat: {e}")))?; - let pending: Vec = globals + let out_globals: Vec<(u32, u32)> = globals // (wl_global_name, version) .contents() .clone_list() .iter() .filter(|g| g.interface == "wl_output") - .map(|g| PendingOut { + .map(|g| (g.name, g.version)) + .collect(); + + let pending: Vec = out_globals + .iter() + .map(|&(gname, ver)| PendingOut { wl: globals.registry().bind::( - g.name, - g.version.min(4), + gname, + ver.min(4), &qh, (), ), name: None, + wl_name: gname, ox: 0, oy: 0, scale: 1, @@ -822,6 +858,7 @@ pub fn select_region( pointer: None, keyboard: None, cursor_shape, + _xdg_outputs: Vec::new(), pending, surfs: Vec::new(), configured: 0, @@ -843,6 +880,21 @@ pub fn select_region( st.roundtrip()?; 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::(&st.qh, 2..=3, ()) { + let xdg_outputs: Vec = 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()?; let expected = st.surfs.len();