diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index d5a901df4c..08c5b609a2 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -36,6 +36,7 @@ env_logger = { version = "0.9", optional = true } etagere = "0.2" futures.workspace = true gpui_macros = { path = "../gpui_macros" } +font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "d97147f" } image = "0.23" itertools = "0.10" lazy_static.workspace = true @@ -48,7 +49,7 @@ parking_lot.workspace = true pathfinder_geometry = "0.5" postage.workspace = true rand.workspace = true -raw-window-handle = "0.6.0" +raw-window-handle = "0.5.0" refineable.workspace = true resvg = "0.14" schemars.workspace = true @@ -96,4 +97,5 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" -x11rb = "0.13" +xcb = { version = "1.3", features = ["as-raw-xcb-connection"] } +as-raw-xcb-connection = "1" diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 2ee2b6d548..b81b282925 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -5,7 +5,6 @@ use crate::{ }; use anyhow::Result; use collections::FxHashMap; -use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use std::{borrow::Cow, sync::Arc}; diff --git a/crates/gpui/src/platform/linux/display.rs b/crates/gpui/src/platform/linux/display.rs index 7941daf6d0..cdca59c435 100644 --- a/crates/gpui/src/platform/linux/display.rs +++ b/crates/gpui/src/platform/linux/display.rs @@ -1,25 +1,24 @@ -use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; +use crate::{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 { - x11_screen_index: usize, + x_screen_index: i32, bounds: Bounds, uuid: Uuid, } impl LinuxDisplay { - pub(crate) fn new(xc: &RustConnection, x11_screen_index: usize) -> Self { - let screen = &xc.setup().roots[x11_screen_index]; + pub(crate) fn new(xc: &xcb::Connection, x_screen_index: i32) -> Self { + let screen = xc.get_setup().roots().nth(x_screen_index as usize).unwrap(); Self { - x11_screen_index, + x_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), + width: GlobalPixels(screen.width_in_pixels() as f32), + height: GlobalPixels(screen.height_in_pixels() as f32), }, }, uuid: Uuid::from_bytes([0; 16]), @@ -29,7 +28,7 @@ impl LinuxDisplay { impl PlatformDisplay for LinuxDisplay { fn id(&self) -> DisplayId { - DisplayId(self.x11_screen_index as u32) + DisplayId(self.x_screen_index as u32) } fn uuid(&self) -> Result { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 3817eeff34..91eacc9d42 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -19,48 +19,28 @@ use std::{ time::Duration, }; use time::UtcOffset; -use x11rb::{ - connection::Connection as _, - protocol::{ - xproto::{Atom, ConnectionExt as _}, - Event, - }, - rust_connection::RustConnection, -}; +use xcb::{x, Xid as _}; -pub(crate) struct LinuxPlatform(Mutex); - -pub(crate) struct WmAtoms { - pub protocols: Atom, - pub delete_window: Atom, -} - -impl WmAtoms { - fn new(x11_connection: &RustConnection) -> Self { - Self { - protocols: x11_connection - .intern_atom(false, b"WM_PROTOCOLS") - .unwrap() - .reply() - .unwrap() - .atom, - delete_window: x11_connection - .intern_atom(false, b"WM_DELETE_WINDOW") - .unwrap() - .reply() - .unwrap() - .atom, - } +xcb::atoms_struct! { + #[derive(Debug)] + pub(crate) struct XcbAtoms { + pub wm_protocols => b"WM_PROTOCOLS", + pub wm_del_window => b"WM_DELETE_WINDOW", + wm_state => b"_NET_WM_STATE", + wm_state_maxv => b"_NET_WM_STATE_MAXIMIZED_VERT", + wm_state_maxh => b"_NET_WM_STATE_MAXIMIZED_HORZ", } } +pub(crate) struct LinuxPlatform(Mutex); + pub(crate) struct LinuxPlatformState { - x11_connection: RustConnection, - x11_root_index: usize, - atoms: WmAtoms, + xcb_connection: xcb::Connection, + x_root_index: i32, + atoms: XcbAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, - windows: HashMap, + windows: HashMap, text_system: Arc, } @@ -72,14 +52,14 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { - let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap(); - let atoms = WmAtoms::new(&x11_connection); + let (xcb_connection, x_root_index) = xcb::Connection::connect(None).unwrap(); + let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap(); let dispatcher = Arc::new(LinuxDispatcher::new()); Self(Mutex::new(LinuxPlatformState { - x11_connection, - x11_root_index, + xcb_connection, + x_root_index, atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), @@ -105,54 +85,44 @@ impl Platform for LinuxPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - let mut need_repaint = HashSet::::default(); + let mut need_repaint = HashSet::::default(); while !self.0.lock().windows.is_empty() { - let event = self.0.lock().x11_connection.wait_for_event().unwrap(); - let mut event_option = Some(event); - while let Some(event) = event_option { - match event { - Event::Expose(event) => { - if event.count == 0 { - need_repaint.insert(event.window); - } - } - Event::ConfigureNotify(event) => { - let lock = self.0.lock(); - let mut window = lock.windows[&event.window].lock(); - window.resize(event.width, event.height); - } - Event::MotionNotify(_event) => { - //mouse_position = (event.event_x, event.event_y); - //need_repaint.insert(event.window); - } - Event::MapNotify(_) => {} - Event::ClientMessage(event) => { + let event = self.0.lock().xcb_connection.wait_for_event().unwrap(); + match event { + xcb::Event::X(x::Event::ClientMessage(ev)) => { + if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { let mut lock = self.0.lock(); - let data = event.data.as_data32(); - if data[0] == lock.atoms.delete_window { + if atom == lock.atoms.wm_del_window.resource_id() { + // window "x" button clicked by user, we gracefully exit { - let mut window = lock.windows[&event.window].lock(); + let mut window = lock.windows[&ev.window()].lock(); window.destroy(); } - lock.windows.remove(&event.window); + lock.windows.remove(&ev.window()); + break; } } - Event::Error(error) => { - log::error!("X11 error {:?}", error); - } - _ => {} } - - let lock = self.0.lock(); - event_option = lock.x11_connection.poll_for_event().unwrap(); + _ => {} /* + Event::Expose(event) => { + if event.count == 0 { + need_repaint.insert(event.window); + } + } + Event::ConfigureNotify(event) => { + let lock = self.0.lock(); + let mut window = lock.windows[&event.window].lock(); + window.resize(event.width, event.height); + } + _ => {}*/ } - for x11_window in need_repaint.drain() { + for x_window in need_repaint.drain() { let lock = self.0.lock(); - let mut window = lock.windows[&x11_window].lock(); + let mut window = lock.windows[&x_window].lock(); window.paint(); - lock.x11_connection.flush().unwrap(); + lock.xcb_connection.flush(); } } } @@ -171,10 +141,13 @@ impl Platform for LinuxPlatform { fn displays(&self) -> Vec> { 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 + let setup = lock.xcb_connection.get_setup(); + setup + .roots() + .enumerate() + .map(|(root_id, _)| { + Rc::new(LinuxDisplay::new(&lock.xcb_connection, root_id as i32)) + as Rc }) .collect() } @@ -182,8 +155,8 @@ impl Platform for LinuxPlatform { fn display(&self, id: DisplayId) -> Option> { let lock = self.0.lock(); Some(Rc::new(LinuxDisplay::new( - &lock.x11_connection, - id.0 as usize, + &lock.xcb_connection, + id.0 as i32, ))) } @@ -197,17 +170,17 @@ impl Platform for LinuxPlatform { options: WindowOptions, ) -> Box { let mut lock = self.0.lock(); - let win_id = lock.x11_connection.generate_id().unwrap(); + let x_window = lock.xcb_connection.generate_id(); let window_ptr = LinuxWindowState::new_ptr( options, handle, - &lock.x11_connection, - lock.x11_root_index, - win_id, + &lock.xcb_connection, + lock.x_root_index, + x_window, &lock.atoms, ); - lock.windows.insert(win_id, window_ptr.clone()); + lock.windows.insert(x_window, window_ptr.clone()); Box::new(LinuxWindow(window_ptr)) } diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 09913b7077..c21c8adfd1 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -15,7 +15,7 @@ use font_kit::{ }; use parking_lot::RwLock; use smallvec::SmallVec; -use std::sync::Arc; +use std::{borrow::Cow}; pub(crate) struct LinuxTextSystem(RwLock); @@ -54,7 +54,7 @@ impl Default for LinuxTextSystem { #[allow(unused)] impl PlatformTextSystem for LinuxTextSystem { - fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { + fn add_fonts(&self, fonts: Vec>) -> Result<()> { Ok(()) //TODO } fn all_font_names(&self) -> Vec { diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 63cbb15e5a..9f6851ca93 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,28 +1,19 @@ use super::BladeRenderer; use crate::{ - px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent, - Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, - WindowOptions, WmAtoms, + AnyWindowHandle, BladeAtlas, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, + PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, }; -use collections::HashMap; use parking_lot::Mutex; use std::{ - rc::{Rc, Weak}, + ffi::c_void, + rc::Rc, sync::{self, Arc}, }; -use x11rb::{ - connection::Connection as _, - protocol::xproto::{ - AtomEnum, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, WindowClass, - }, - rust_connection::RustConnection, - wrapper::ConnectionExt as _, -}; +use xcb::{x, Xid as _}; pub(crate) struct LinuxWindowState { display: Rc, - x11_window: u32, + x_window: x::Window, window_bounds: WindowBounds, content_size: Size, sprite_atlas: Arc, @@ -33,29 +24,53 @@ pub(crate) type LinuxWindowStatePtr = Arc>; #[derive(Clone)] pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr); +struct RawWindow { + connection: *mut c_void, + screen_id: i32, + window_id: u32, +} +unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow { + fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + let mut wh = raw_window_handle::XcbWindowHandle::empty(); + wh.window = self.window_id; + wh.into() + } +} +unsafe impl raw_window_handle::HasRawDisplayHandle for RawWindow { + fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { + let mut dh = raw_window_handle::XcbDisplayHandle::empty(); + dh.connection = self.connection; + dh.screen = self.screen_id; + dh.into() + } +} + impl LinuxWindowState { pub fn new_ptr( options: WindowOptions, handle: AnyWindowHandle, - x11_connection: &RustConnection, - x11_main_screen_index: usize, - x11_window: u32, - atoms: &WmAtoms, + xcb_connection: &xcb::Connection, + x_main_screen_index: i32, + x_window: x::Window, + atoms: &XcbAtoms, ) -> LinuxWindowStatePtr { - let x11_screen_index = options + let x_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]; + .map_or(x_main_screen_index, |did| did.0 as i32); + let screen = xcb_connection + .get_setup() + .roots() + .nth(x_screen_index as usize) + .unwrap(); - let win_aux = CreateWindowAux::new() - .event_mask( - EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION, - ) - .background_pixel(screen.white_pixel); + let xcb_values = [ + x::Cw::BackPixel(screen.white_pixel()), + x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), + ]; 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) + (0, 0, screen.width_in_pixels(), screen.height_in_pixels()) } WindowBounds::Fixed(bounds) => ( bounds.origin.x.0 as i16, @@ -65,61 +80,67 @@ impl LinuxWindowState { ), }; - x11_connection - .create_window( - x11rb::COPY_DEPTH_FROM_PARENT, - x11_window, - screen.root, - bound_x, - bound_y, - bound_width, - bound_height, - 0, - WindowClass::INPUT_OUTPUT, - 0, - &win_aux, - ) - .unwrap(); + xcb_connection.send_request(&x::CreateWindow { + depth: x::COPY_FROM_PARENT as u8, + wid: x_window, + parent: screen.root(), + x: bound_x, + y: bound_y, + width: bound_width, + height: bound_height, + border_width: 0, + class: x::WindowClass::InputOutput, + visual: screen.root_visual(), + value_list: &xcb_values, + }); if let Some(titlebar) = options.titlebar { if let Some(title) = titlebar.title { - x11_connection - .change_property8( - PropMode::REPLACE, - x11_window, - AtomEnum::WM_NAME, - AtomEnum::STRING, - title.as_bytes(), - ) - .unwrap(); + xcb_connection.send_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: x_window, + property: x::ATOM_WM_NAME, + r#type: x::ATOM_STRING, + data: title.as_bytes(), + }); } } - x11_connection - .change_property32( - PropMode::REPLACE, - x11_window, - atoms.protocols, - AtomEnum::ATOM, - &[atoms.delete_window], - ) + xcb_connection + .send_and_check_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: x_window, + property: atoms.wm_protocols, + r#type: x::ATOM_ATOM, + data: &[atoms.wm_del_window], + }) .unwrap(); - x11_connection.map_window(x11_window).unwrap(); - x11_connection.flush().unwrap(); + xcb_connection.send_request(&x::MapWindow { window: x_window }); + xcb_connection.flush().unwrap(); + let raw_window = RawWindow { + connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection( + xcb_connection, + ) as *mut _, + screen_id: x_screen_index, + window_id: x_window.resource_id(), + }; let gpu = Arc::new( unsafe { - blade::Context::init(blade::ContextDesc { - validation: cfg!(debug_assertions), - capture: false, - }) + blade::Context::init_windowed( + &raw_window, + blade::ContextDesc { + validation: cfg!(debug_assertions), + capture: false, + }, + ) } .unwrap(), ); Arc::new(Mutex::new(Self { - display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)), - x11_window, + display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), + x_window, window_bounds: options.bounds, content_size: Size { width: Pixels(bound_width as f32),