From da634338c83b2cc42511f72d00dbd256d624ce9f Mon Sep 17 00:00:00 2001 From: Leastrio Date: Fri, 29 May 2026 17:15:47 -0600 Subject: [PATCH] use xdg-output logical geometry for display size & position --- crates/gpui_linux/src/linux/wayland/client.rs | 95 ++++++++++++++++--- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/crates/gpui_linux/src/linux/wayland/client.rs b/crates/gpui_linux/src/linux/wayland/client.rs index 6a7c4c76e4d..4375795e36b 100644 --- a/crates/gpui_linux/src/linux/wayland/client.rs +++ b/crates/gpui_linux/src/linux/wayland/client.rs @@ -59,6 +59,7 @@ use wayland_protocols::xdg::decoration::zv1::client::{ }; use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}; use wayland_protocols::xdg::system_bell::v1::client::xdg_system_bell_v1; +use wayland_protocols::xdg::xdg_output::zv1::client::{zxdg_output_manager_v1, zxdg_output_v1}; use wayland_protocols::{ wp::cursor_shape::v1::client::{wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1}, xdg::dialog::v1::client::xdg_wm_dialog_v1::{self, XdgWmDialogV1}, @@ -126,6 +127,7 @@ pub struct Globals { Option, pub decoration_manager: Option, pub layer_shell: Option, + pub xdg_output_manager: Option, pub blur_manager: Option, pub text_input_manager: Option, pub gesture_manager: Option, @@ -168,6 +170,7 @@ impl Globals { fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(), decoration_manager: globals.bind(&qh, 1..=1, ()).ok(), layer_shell: globals.bind(&qh, 1..=5, ()).ok(), + xdg_output_manager: globals.bind(&qh, 1..=3, ()).ok(), blur_manager: globals.bind(&qh, 1..=1, ()).ok(), text_input_manager: globals.bind(&qh, 1..=1, ()).ok(), gesture_manager: globals.bind(&qh, 1..=3, ()).ok(), @@ -185,22 +188,24 @@ pub struct InProgressOutput { scale: Option, position: Option>, size: Option>, + logical_position: Option>, + logical_size: Option>, subpixel: Option, } impl InProgressOutput { fn complete(&self) -> Option { - if let Some((position, size)) = self.position.zip(self.size) { - let scale = self.scale.unwrap_or(1); - Some(Output { - name: self.name.clone(), - scale, - bounds: Bounds::new(position, size), - subpixel: self.subpixel, - }) - } else { - None - } + let position = self.logical_position.or(self.position)?; + let size = self.logical_size.or(self.size)?; + + let scale = self.scale.unwrap_or(1); + + Some(Output { + name: self.name.clone(), + scale, + bounds: Bounds::new(position, size), + subpixel: self.subpixel, + }) } } @@ -233,8 +238,10 @@ pub(crate) struct WaylandClientState { windows: HashMap, // Output to scale mapping outputs: HashMap, + registry_name_to_output: HashMap, in_progress_outputs: HashMap, wl_outputs: HashMap, + xdg_outputs: HashMap, keyboard_layout: LinuxKeyboardLayout, keymap_state: Option, compose_state: Option, @@ -539,6 +546,7 @@ impl WaylandClient { let mut in_progress_outputs = HashMap::default(); #[allow(clippy::mutable_key_type)] let mut wl_outputs: HashMap = HashMap::default(); + let mut registry_name_to_output: HashMap = HashMap::default(); globals.contents().with_list(|list| { for global in list { match &global.interface[..] { @@ -557,6 +565,9 @@ impl WaylandClient { &qh, (), ); + + registry_name_to_output.insert(global.name, output.id()); + in_progress_outputs.insert(output.id(), InProgressOutput::default()); wl_outputs.insert(output.id(), output); } @@ -607,6 +618,15 @@ impl WaylandClient { seat.clone(), ); + let mut xdg_outputs = HashMap::default(); + + if let Some(manager) = globals.xdg_output_manager.as_ref() { + for output in wl_outputs.values() { + let xdg_output = manager.get_xdg_output(output, &qh, output.id()); + xdg_outputs.insert(output.id(), xdg_output); + } + } + let data_device = globals .data_device_manager .as_ref() @@ -680,8 +700,10 @@ impl WaylandClient { ime_pre_edit: None, composing: false, outputs: HashMap::default(), + registry_name_to_output, in_progress_outputs, wl_outputs, + xdg_outputs, windows: HashMap::default(), common, keyboard_layout: LinuxKeyboardLayout::new(UNKNOWN_KEYBOARD_LAYOUT_NAME), @@ -1128,11 +1150,25 @@ impl Dispatch for WaylandClientStat state .in_progress_outputs .insert(output.id(), InProgressOutput::default()); + + state.registry_name_to_output.insert(name, output.id()); + + if let Some(manager) = state.globals.xdg_output_manager.as_ref() { + let xdg_output = manager.get_xdg_output(&output, qh, output.id()); + state.xdg_outputs.insert(output.id(), xdg_output); + } + state.wl_outputs.insert(output.id(), output); } _ => {} }, - wl_registry::Event::GlobalRemove { name: _ } => { + wl_registry::Event::GlobalRemove { name } => { + if let Some(id) = state.registry_name_to_output.remove(&name) { + state.wl_outputs.remove(&id); + state.outputs.remove(&id); + state.in_progress_outputs.remove(&id); + state.xdg_outputs.remove(&id); + } // TODO: handle global removal } _ => {} @@ -1159,6 +1195,7 @@ delegate_noop!(WaylandClientStatePtr: ignore zwp_text_input_manager_v3::ZwpTextI delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur::OrgKdeKwinBlur); delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter); delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport); +delegate_noop!(WaylandClientStatePtr: ignore zxdg_output_manager_v1::ZxdgOutputManagerV1); impl Dispatch for WaylandClientStatePtr { fn event( @@ -1248,7 +1285,39 @@ impl Dispatch for WaylandClientStatePtr { if let Some(complete) = in_progress_output.complete() { state.outputs.insert(output.id(), complete); } - state.in_progress_outputs.remove(&output.id()); + } + _ => {} + } + } +} + +impl Dispatch for WaylandClientStatePtr { + fn event( + state: &mut Self, + _: &zxdg_output_v1::ZxdgOutputV1, + event: zxdg_output_v1::Event, + output_id: &ObjectId, + _: &Connection, + _: &QueueHandle, + ) { + let client = state.get_client(); + let mut state = client.borrow_mut(); + + let Some(in_progress) = state.in_progress_outputs.get_mut(output_id) else { + return; + }; + + match event { + zxdg_output_v1::Event::LogicalPosition { x, y } => { + in_progress.logical_position = Some(point(DevicePixels(x), DevicePixels(y))); + } + zxdg_output_v1::Event::LogicalSize { width, height } => { + in_progress.logical_size = Some(size(DevicePixels(width), DevicePixels(height))); + } + zxdg_output_v1::Event::Done => { + if let Some(complete) = in_progress.complete() { + state.outputs.insert(output_id.clone(), complete); + } } _ => {} }