From 6ab9c3c3ab15cb2ad8ac43022191e039780ba903 Mon Sep 17 00:00:00 2001 From: apricotbucket28 <71973804+apricotbucket28@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:07:54 -0300 Subject: [PATCH] x11: HiDPI support (#11140) Fixes https://github.com/zed-industries/zed/issues/11121 Release Notes: - N/A --- crates/gpui/Cargo.toml | 8 ++- crates/gpui/src/platform/linux/x11/client.rs | 75 ++++++++++++++++---- crates/gpui/src/platform/linux/x11/window.rs | 20 ++++-- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 71c877c458ebc40acc6154a4f015af92f75ce86e..7a727e3793420526656e837606273f4f771d99a5 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -114,7 +114,13 @@ wayland-protocols = { version = "0.31.2", features = [ oo7 = "0.3.0" open = "5.1.2" filedescriptor = "0.8.2" -x11rb = { version = "0.13.0", features = ["allow-unsafe-code", "xkb", "randr", "xinput"] } +x11rb = { version = "0.13.0", features = [ + "allow-unsafe-code", + "xkb", + "randr", + "xinput", + "resource_manager", +] } xkbcommon = { version = "0.7", features = ["wayland", "x11"] } [target.'cfg(windows)'.dependencies] diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 7b440b5f60c4b3fddd7c25bfd67d71ccab5c7e4e..90fc094a49a8a9fdfc9b73efb3b8cb1a940a0f8d 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -16,6 +16,7 @@ use x11rb::protocol::xinput::{ConnectionExt, ScrollClass}; use x11rb::protocol::xkb::ConnectionExt as _; use x11rb::protocol::xproto::ConnectionExt as _; use x11rb::protocol::{randr, xinput, xkb, xproto, Event}; +use x11rb::resource_manager::Database; use x11rb::xcb_ffi::XCBConnection; use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION}; use xkbcommon::xkb as xkbc; @@ -23,9 +24,9 @@ use xkbcommon::xkb as xkbc; use crate::platform::linux::LinuxClient; use crate::platform::{LinuxCommon, PlatformWindow}; use crate::{ - modifiers_from_xinput_info, px, AnyWindowHandle, Bounds, CursorStyle, DisplayId, Modifiers, - ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta, Size, - TouchPhase, WindowParams, X11Window, + modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, CursorStyle, DisplayId, + Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta, + Size, TouchPhase, WindowParams, X11Window, }; use super::{super::SCROLL_LINES, X11Display, X11WindowStatePtr, XcbAtoms}; @@ -58,8 +59,11 @@ pub struct X11ClientState { pub(crate) last_location: Point, pub(crate) current_count: usize, + pub(crate) scale_factor: f32, + pub(crate) xcb_connection: Rc, pub(crate) x_root_index: usize, + pub(crate) resource_database: Database, pub(crate) atoms: XcbAtoms, pub(crate) windows: HashMap, pub(crate) focused_window: Option, @@ -178,6 +182,31 @@ impl X11Client { xkbc::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id) }; + let screen = xcb_connection.setup().roots.get(x_root_index).unwrap(); + + // Values from `Database::GET_RESOURCE_DATABASE` + let resource_manager = xcb_connection + .get_property( + false, + screen.root, + xproto::AtomEnum::RESOURCE_MANAGER, + xproto::AtomEnum::STRING, + 0, + 100_000_000, + ) + .unwrap(); + let resource_manager = resource_manager.reply().unwrap(); + + // todo(linux): read hostname + let resource_database = Database::new_from_default(&resource_manager, "HOSTNAME".into()); + + let scale_factor = resource_database + .get_value("Xft.dpi", "Xft.dpi") + .ok() + .flatten() + .map(|dpi: f32| dpi / 96.0) + .unwrap_or(1.0); + let clipboard = X11ClipboardContext::::new().unwrap(); let primary = X11ClipboardContext::::new().unwrap(); @@ -212,9 +241,11 @@ impl X11Client { last_click: Instant::now(), last_location: Point::new(px(0.0), px(0.0)), current_count: 0, + scale_factor, xcb_connection, x_root_index, + resource_database, atoms, windows: HashMap::default(), focused_window: None, @@ -345,8 +376,10 @@ impl X11Client { let mut state = self.0.borrow_mut(); let modifiers = modifiers_from_state(event.state); - let position = - Point::new((event.event_x as f32).into(), (event.event_y as f32).into()); + let position = point( + px(event.event_x as f32 / state.scale_factor), + px(event.event_y as f32 / state.scale_factor), + ); if let Some(button) = button_of_key(event.detail) { let click_elapsed = state.last_click.elapsed(); @@ -378,8 +411,10 @@ impl X11Client { let window = self.get_window(event.event)?; let state = self.0.borrow(); let modifiers = modifiers_from_state(event.state); - let position = - Point::new((event.event_x as f32).into(), (event.event_y as f32).into()); + let position = point( + px(event.event_x as f32 / state.scale_factor), + px(event.event_y as f32 / state.scale_factor), + ); if let Some(button) = button_of_key(event.detail) { let click_count = state.current_count; drop(state); @@ -393,11 +428,12 @@ impl X11Client { } Event::XinputMotion(event) => { let window = self.get_window(event.event)?; - - let position = Point::new( - (event.event_x as f32 / u16::MAX as f32).into(), - (event.event_y as f32 / u16::MAX as f32).into(), + let state = self.0.borrow(); + let position = point( + px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), + px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); + drop(state); let modifiers = modifiers_from_xinput_info(event.mods); let axisvalues = event @@ -471,10 +507,14 @@ impl X11Client { } Event::MotionNotify(event) => { let window = self.get_window(event.event)?; + let state = self.0.borrow(); let pressed_button = super::button_from_state(event.state); - let position = - Point::new((event.event_x as f32).into(), (event.event_y as f32).into()); + let position = point( + px(event.event_x as f32 / state.scale_factor), + px(event.event_y as f32 / state.scale_factor), + ); let modifiers = modifiers_from_state(event.state); + drop(state); window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent { pressed_button, position, @@ -483,10 +523,14 @@ impl X11Client { } Event::LeaveNotify(event) => { let window = self.get_window(event.event)?; + let state = self.0.borrow(); let pressed_button = super::button_from_state(event.state); - let position = - Point::new((event.event_x as f32).into(), (event.event_y as f32).into()); + let position = point( + px(event.event_x as f32 / state.scale_factor), + px(event.event_y as f32 / state.scale_factor), + ); let modifiers = modifiers_from_state(event.state); + drop(state); window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent { pressed_button, position, @@ -553,6 +597,7 @@ impl LinuxClient for X11Client { state.x_root_index, x_window, &state.atoms, + state.scale_factor, ); let screen_resources = state diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 88b0d0a70acee4b0467255f65fc5ed311aef92b0..33b8f594d6c8405d6bc48f049bc6990ae28dc43d 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -17,6 +17,7 @@ use x11rb::{ xinput, xproto::{self, ConnectionExt as _, CreateWindowAux}, }, + resource_manager::Database, wrapper::ConnectionExt, xcb_ffi::XCBConnection, }; @@ -27,6 +28,7 @@ use std::{ iter::Zip, mem, num::NonZeroU32, + ops::Div, ptr::NonNull, rc::Rc, sync::{self, Arc}, @@ -128,6 +130,7 @@ impl rwh::HasDisplayHandle for X11Window { } impl X11WindowState { + #[allow(clippy::too_many_arguments)] pub fn new( client: X11ClientStatePtr, executor: ForegroundExecutor, @@ -136,6 +139,7 @@ impl X11WindowState { x_main_screen_index: usize, x_window: xproto::Window, atoms: &XcbAtoms, + scale_factor: f32, ) -> Self { let x_screen_index = params .display_id @@ -246,7 +250,7 @@ impl X11WindowState { display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()), raw, bounds: params.bounds.map(|v| v.0), - scale_factor: 1.0, + scale_factor, renderer: BladeRenderer::new(gpu, gpu_extent), atoms: *atoms, @@ -291,6 +295,7 @@ impl Drop for X11Window { } impl X11Window { + #[allow(clippy::too_many_arguments)] pub fn new( client: X11ClientStatePtr, executor: ForegroundExecutor, @@ -299,6 +304,7 @@ impl X11Window { x_main_screen_index: usize, x_window: xproto::Window, atoms: &XcbAtoms, + scale_factor: f32, ) -> Self { Self(X11WindowStatePtr { state: Rc::new(RefCell::new(X11WindowState::new( @@ -309,6 +315,7 @@ impl X11Window { x_main_screen_index, x_window, atoms, + scale_factor, ))), callbacks: Rc::new(RefCell::new(Callbacks::default())), xcb_connection: xcb_connection.clone(), @@ -402,7 +409,7 @@ impl X11WindowStatePtr { impl PlatformWindow for X11Window { fn bounds(&self) -> Bounds { - self.0.state.borrow_mut().bounds.map(|v| v.into()) + self.0.state.borrow().bounds.map(|v| v.into()) } // todo(linux) @@ -416,11 +423,16 @@ impl PlatformWindow for X11Window { } fn content_size(&self) -> Size { - self.0.state.borrow_mut().content_size() + // We divide by the scale factor here because this value is queried to determine how much to draw, + // but it will be multiplied later by the scale to adjust for scaling. + let state = self.0.state.borrow(); + state + .content_size() + .map(|size| size.div(state.scale_factor)) } fn scale_factor(&self) -> f32 { - self.0.state.borrow_mut().scale_factor + self.0.state.borrow().scale_factor } // todo(linux)