From cefc98258fe2d2f0963aac2321ba14ea7ff707b1 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 27 Jan 2024 22:59:30 -0800 Subject: [PATCH] linux: hook up X11rb for Window creation --- Cargo.lock | 28 ++++++ crates/gpui/Cargo.toml | 1 + crates/gpui/src/platform/linux/display.rs | 49 +++++++--- crates/gpui/src/platform/linux/platform.rs | 27 +++++- crates/gpui/src/platform/linux/window.rs | 101 +++++++++++++++++++-- 5 files changed, 181 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9644e2b619..291e86f0b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3088,6 +3088,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -3277,6 +3287,7 @@ dependencies = [ "util", "uuid 1.4.1", "waker-fn", + "x11rb", ] [[package]] @@ -10273,6 +10284,23 @@ dependencies = [ "tap", ] +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "gethostname", + "rustix 0.38.30", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + [[package]] name = "xattr" version = "0.2.3" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 42978e203b..d5a901df4c 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -96,3 +96,4 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" +x11rb = "0.13" diff --git a/crates/gpui/src/platform/linux/display.rs b/crates/gpui/src/platform/linux/display.rs index 507edad3b0..7941daf6d0 100644 --- a/crates/gpui/src/platform/linux/display.rs +++ b/crates/gpui/src/platform/linux/display.rs @@ -1,23 +1,42 @@ -use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay}; +use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; use anyhow::Result; use uuid::Uuid; +use x11rb::{connection::Connection as _, rust_connection::RustConnection}; #[derive(Debug)] -pub(crate) struct LinuxDisplay; +pub(crate) struct LinuxDisplay { + x11_screen_index: usize, + bounds: Bounds, + uuid: Uuid, +} -impl PlatformDisplay for LinuxDisplay { - fn id(&self) -> DisplayId { - DisplayId(0) - } - - fn uuid(&self) -> Result { - Ok(Uuid::from_bytes([0; 16])) - } - - fn bounds(&self) -> Bounds { - Bounds { - origin: point(GlobalPixels(0.0), GlobalPixels(0.0)), - size: size(GlobalPixels(100.0), GlobalPixels(100.0)), +impl LinuxDisplay { + pub(crate) fn new(xc: &RustConnection, x11_screen_index: usize) -> Self { + let screen = &xc.setup().roots[x11_screen_index]; + Self { + x11_screen_index, + bounds: Bounds { + origin: Default::default(), + size: Size { + width: GlobalPixels(screen.width_in_pixels as f32), + height: GlobalPixels(screen.height_in_pixels as f32), + }, + }, + uuid: Uuid::from_bytes([0; 16]), } } } + +impl PlatformDisplay for LinuxDisplay { + fn id(&self) -> DisplayId { + DisplayId(self.x11_screen_index as u32) + } + + fn uuid(&self) -> Result { + Ok(self.uuid) + } + + fn bounds(&self) -> Bounds { + self.bounds + } +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 8136b77292..18016358e6 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -17,10 +17,13 @@ use std::{ time::Duration, }; use time::UtcOffset; +use x11rb::{connection::Connection as _, rust_connection::RustConnection}; pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { + x11_connection: RustConnection, + x11_root_index: usize, gpu: Arc, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, @@ -35,17 +38,22 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { + let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap(); + let dispatcher = Arc::new(LinuxDispatcher::new()); let gpu = Arc::new( unsafe { blade::Context::init(blade::ContextDesc { - validation: true, //FIXME + validation: cfg!(debug_assertions), capture: false, }) } .unwrap(), ); + Self(Mutex::new(LinuxPlatformState { + x11_connection, + x11_root_index, gpu, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), @@ -84,11 +92,21 @@ impl Platform for LinuxPlatform { fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - Vec::new() + let lock = self.0.lock(); + let setup = lock.x11_connection.setup(); + (0..setup.roots.len()) + .map(|id| { + Rc::new(LinuxDisplay::new(&lock.x11_connection, id)) as Rc + }) + .collect() } fn display(&self, id: DisplayId) -> Option> { - None + let lock = self.0.lock(); + Some(Rc::new(LinuxDisplay::new( + &lock.x11_connection, + id.0 as usize, + ))) } fn active_window(&self) -> Option { @@ -104,7 +122,8 @@ impl Platform for LinuxPlatform { Box::new(LinuxWindow::new( options, handle, - Rc::new(LinuxDisplay), + &lock.x11_connection, + lock.x11_root_index, &lock.gpu, )) } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 72155dacab..ffc8372dbd 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,7 +1,8 @@ use crate::{ px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent, - Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, - PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, WindowOptions, + Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, + PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, + WindowOptions, }; use collections::HashMap; use parking_lot::Mutex; @@ -9,9 +10,18 @@ use std::{ rc::{Rc, Weak}, sync::{self, Arc}, }; +use x11rb::{ + connection::Connection as _, + protocol::xproto::{ + AtomEnum, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, WindowClass, + }, + rust_connection::RustConnection, + wrapper::ConnectionExt as _, +}; pub(crate) struct LinuxWindowState { - display: Rc, + display: Rc, + win_id: u32, sprite_atlas: Arc, } @@ -22,11 +32,90 @@ impl LinuxWindow { pub fn new( options: WindowOptions, handle: AnyWindowHandle, - display: Rc, + x11_connection: &RustConnection, + x11_main_screen_index: usize, gpu: &Arc, ) -> Self { + let x11_screen_index = options + .display_id + .map_or(x11_main_screen_index, |did| did.0 as usize); + let screen = &x11_connection.setup().roots[x11_screen_index]; + + let win_id = x11_connection.generate_id().unwrap(); + let win_aux = CreateWindowAux::new() + .event_mask( + EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION, + ) + .background_pixel(screen.white_pixel); + + let wm_protocols = x11_connection + .intern_atom(false, b"WM_PROTOCOLS") + .unwrap() + .reply() + .unwrap() + .atom; + let wm_delete_window = x11_connection + .intern_atom(false, b"WM_DELETE_WINDOW") + .unwrap() + .reply() + .unwrap() + .atom; + let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { + WindowBounds::Fullscreen | WindowBounds::Maximized => { + (0, 0, screen.width_in_pixels, screen.height_in_pixels) + } + WindowBounds::Fixed(bounds) => ( + bounds.origin.x.0 as i16, + bounds.origin.y.0 as i16, + bounds.size.width.0 as u16, + bounds.size.height.0 as u16, + ), + }; + + x11_connection + .create_window( + x11rb::COPY_DEPTH_FROM_PARENT, + win_id, + screen.root, + bound_x, + bound_y, + bound_width, + bound_height, + 0, + WindowClass::INPUT_OUTPUT, + 0, + &win_aux, + ) + .unwrap(); + + if let Some(titlebar) = options.titlebar { + if let Some(title) = titlebar.title { + x11_connection + .change_property8( + PropMode::REPLACE, + win_id, + AtomEnum::WM_NAME, + AtomEnum::STRING, + title.as_bytes(), + ) + .unwrap(); + } + } + x11_connection + .change_property32( + PropMode::REPLACE, + win_id, + wm_protocols, + AtomEnum::ATOM, + &[wm_delete_window], + ) + .unwrap(); + + x11_connection.map_window(win_id).unwrap(); + Self(Arc::new(Mutex::new(LinuxWindowState { - display, + display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)), + win_id, sprite_atlas: Arc::new(BladeAtlas::new(gpu)), }))) } @@ -53,7 +142,7 @@ impl PlatformWindow for LinuxWindow { unimplemented!() } - fn display(&self) -> Rc { + fn display(&self) -> Rc { Rc::clone(&self.0.lock().display) }