From 266988adea954cd759525cc7222e261536c25fd1 Mon Sep 17 00:00:00 2001 From: Roman <40907255+witelokk@users.noreply.github.com> Date: Mon, 12 Feb 2024 21:44:34 +0300 Subject: [PATCH] gpui: Decouple X11 logic from LinuxPlatform (#7598) Release Notes: - Separated Linux platform and X11-specific code, so that we can add Wayland support now. --------- Co-authored-by: Mikayla --- crates/gpui/src/platform/linux.rs | 10 +- crates/gpui/src/platform/linux/client.rs | 15 ++ .../src/platform/linux/client_dispatcher.rs | 3 + crates/gpui/src/platform/linux/dispatcher.rs | 54 +---- crates/gpui/src/platform/linux/platform.rs | 205 ++++++------------ crates/gpui/src/platform/linux/x11.rs | 9 + crates/gpui/src/platform/linux/x11/client.rs | 139 ++++++++++++ .../platform/linux/x11/client_dispatcher.rs | 64 ++++++ .../src/platform/linux/{ => x11}/display.rs | 9 +- .../src/platform/linux/{ => x11}/window.rs | 43 ++-- crates/gpui/src/platform/mac/text_system.rs | 1 + 11 files changed, 337 insertions(+), 215 deletions(-) create mode 100644 crates/gpui/src/platform/linux/client.rs create mode 100644 crates/gpui/src/platform/linux/client_dispatcher.rs create mode 100644 crates/gpui/src/platform/linux/x11.rs create mode 100644 crates/gpui/src/platform/linux/x11/client.rs create mode 100644 crates/gpui/src/platform/linux/x11/client_dispatcher.rs rename crates/gpui/src/platform/linux/{ => x11}/display.rs (91%) rename crates/gpui/src/platform/linux/{ => x11}/window.rs (93%) diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index f76e791a56..fb81f7da8c 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,18 +1,18 @@ mod blade_atlas; mod blade_belt; mod blade_renderer; +mod client; +mod client_dispatcher; mod dispatcher; -mod display; mod platform; mod text_system; -mod window; +mod x11; pub(crate) use blade_atlas::*; pub(crate) use dispatcher::*; -pub(crate) use display::*; pub(crate) use platform::*; pub(crate) use text_system::*; -pub(crate) use window::*; +pub(crate) use x11::display::*; +pub(crate) use x11::*; use blade_belt::*; -use blade_renderer::*; diff --git a/crates/gpui/src/platform/linux/client.rs b/crates/gpui/src/platform/linux/client.rs new file mode 100644 index 0000000000..d314202972 --- /dev/null +++ b/crates/gpui/src/platform/linux/client.rs @@ -0,0 +1,15 @@ +use std::rc::Rc; + +use crate::platform::PlatformWindow; +use crate::{AnyWindowHandle, DisplayId, PlatformDisplay, WindowOptions}; + +pub trait Client { + fn run(&self, on_finish_launching: Box); + fn displays(&self) -> Vec>; + fn display(&self, id: DisplayId) -> Option>; + fn open_window( + &self, + handle: AnyWindowHandle, + options: WindowOptions, + ) -> Box; +} diff --git a/crates/gpui/src/platform/linux/client_dispatcher.rs b/crates/gpui/src/platform/linux/client_dispatcher.rs new file mode 100644 index 0000000000..823e2df0b7 --- /dev/null +++ b/crates/gpui/src/platform/linux/client_dispatcher.rs @@ -0,0 +1,3 @@ +pub trait ClientDispatcher: Send + Sync { + fn dispatch_on_main_thread(&self); +} diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index fad7d01c87..bb96da6a8b 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -4,6 +4,7 @@ //todo!(linux): remove #![allow(unused_variables)] +use crate::platform::linux::client_dispatcher::ClientDispatcher; use crate::{PlatformDispatcher, TaskLabel}; use async_task::Runnable; use parking::{Parker, Unparker}; @@ -14,11 +15,9 @@ use std::{ thread, time::{Duration, Instant}, }; -use xcb::x; pub(crate) struct LinuxDispatcher { - xcb_connection: Arc, - x_listener_window: x::Window, + client_dispatcher: Arc, parker: Mutex, timed_tasks: Mutex>, main_sender: flume::Sender, @@ -30,38 +29,16 @@ pub(crate) struct LinuxDispatcher { impl LinuxDispatcher { pub fn new( main_sender: flume::Sender, - xcb_connection: &Arc, - x_root_index: i32, + client_dispatcher: &Arc, ) -> Self { - let x_listener_window = xcb_connection.generate_id(); - let screen = xcb_connection - .get_setup() - .roots() - .nth(x_root_index as usize) - .unwrap(); - xcb_connection.send_request(&x::CreateWindow { - depth: 0, - wid: x_listener_window, - parent: screen.root(), - x: 0, - y: 0, - width: 1, - height: 1, - border_width: 0, - class: x::WindowClass::InputOnly, - visual: screen.root_visual(), - value_list: &[], - }); - let (background_sender, background_receiver) = flume::unbounded::(); let background_thread = thread::spawn(move || { for runnable in background_receiver { let _ignore_panic = panic::catch_unwind(|| runnable.run()); } }); - LinuxDispatcher { - xcb_connection: Arc::clone(xcb_connection), - x_listener_window, + Self { + client_dispatcher: Arc::clone(client_dispatcher), parker: Mutex::new(Parker::new()), timed_tasks: Mutex::new(Vec::new()), main_sender, @@ -72,14 +49,6 @@ impl LinuxDispatcher { } } -impl Drop for LinuxDispatcher { - fn drop(&mut self) { - self.xcb_connection.send_request(&x::DestroyWindow { - window: self.x_listener_window, - }); - } -} - impl PlatformDispatcher for LinuxDispatcher { fn is_main_thread(&self) -> bool { thread::current().id() == self.main_thread_id @@ -91,18 +60,7 @@ impl PlatformDispatcher for LinuxDispatcher { fn dispatch_on_main_thread(&self, runnable: Runnable) { self.main_sender.send(runnable).unwrap(); - // Send a message to the invisible window, forcing - // the main loop to wake up and dispatch the runnable. - self.xcb_connection.send_request(&x::SendEvent { - propagate: false, - destination: x::SendEventDest::Window(self.x_listener_window), - event_mask: x::EventMask::NO_EVENT, - event: &x::VisibilityNotifyEvent::new( - self.x_listener_window, - x::Visibility::Unobscured, - ), - }); - self.xcb_connection.flush().unwrap(); + self.client_dispatcher.dispatch_on_main_thread(); } fn dispatch_after(&self, duration: Duration, runnable: Runnable) { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index dfc8f1163f..b83d0b9dd5 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -1,43 +1,36 @@ #![allow(unused)] -use crate::{ - Action, AnyWindowHandle, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, - LinuxWindowState, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, - PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, WindowOptions, -}; - -use async_task::Runnable; -use collections::{HashMap, HashSet}; -use futures::channel::oneshot; -use parking_lot::Mutex; - use std::{ path::{Path, PathBuf}, rc::Rc, sync::Arc, time::Duration, }; -use time::UtcOffset; -use xcb::{x, Xid as _}; -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", - } -} +use async_task::Runnable; +use futures::channel::oneshot; +use parking_lot::Mutex; +use time::UtcOffset; + +use collections::{HashMap, HashSet}; + +use crate::platform::linux::client::Client; +use crate::platform::linux::client_dispatcher::ClientDispatcher; +use crate::platform::{X11Client, X11ClientDispatcher, XcbAtoms}; +use crate::{ + Action, AnyWindowHandle, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DisplayId, + ForegroundExecutor, Keymap, LinuxDispatcher, LinuxTextSystem, Menu, PathPromptOptions, + Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Point, Result, + SemanticVersion, Size, Task, WindowAppearance, WindowOptions, X11Display, X11Window, + X11WindowState, +}; #[derive(Default)] -struct Callbacks { +pub(crate) struct Callbacks { open_urls: Option)>>, become_active: Option>, resign_active: Option>, - quit: Option>, + pub(crate) quit: Option>, reopen: Option>, event: Option bool>>, app_menu_action: Option>, @@ -45,21 +38,22 @@ struct Callbacks { validate_app_menu_command: Option bool>>, } +pub(crate) struct LinuxPlatformInner { + pub(crate) background_executor: BackgroundExecutor, + pub(crate) foreground_executor: ForegroundExecutor, + pub(crate) main_receiver: flume::Receiver, + pub(crate) text_system: Arc, + pub(crate) callbacks: Mutex, + pub(crate) state: Mutex, +} + pub(crate) struct LinuxPlatform { - xcb_connection: Arc, - x_root_index: i32, - atoms: XcbAtoms, - background_executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - main_receiver: flume::Receiver, - text_system: Arc, - callbacks: Mutex, - state: Mutex, + client: Arc, + inner: Arc, } pub(crate) struct LinuxPlatformState { - quit_requested: bool, - windows: HashMap>, + pub(crate) quit_requested: bool, } impl Default for LinuxPlatform { @@ -75,16 +69,12 @@ impl LinuxPlatform { let xcb_connection = Arc::new(xcb_connection); let (main_sender, main_receiver) = flume::unbounded::(); - let dispatcher = Arc::new(LinuxDispatcher::new( - main_sender, - &xcb_connection, - x_root_index, - )); + let client_dispatcher: Arc = + Arc::new(X11ClientDispatcher::new(&xcb_connection, x_root_index)); + let dispatcher = LinuxDispatcher::new(main_sender, &client_dispatcher); + let dispatcher = Arc::new(dispatcher); - Self { - xcb_connection, - x_root_index, - atoms, + let inner = LinuxPlatformInner { background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher.clone()), main_receiver, @@ -92,83 +82,39 @@ impl LinuxPlatform { callbacks: Mutex::new(Callbacks::default()), state: Mutex::new(LinuxPlatformState { quit_requested: false, - windows: HashMap::default(), }), + }; + let inner = Arc::new(inner); + + let x11client = X11Client::new(Arc::clone(&inner), xcb_connection, x_root_index, atoms); + let x11client = Arc::new(x11client); + + Self { + client: x11client, + inner: Arc::clone(&inner), } } } impl Platform for LinuxPlatform { fn background_executor(&self) -> BackgroundExecutor { - self.background_executor.clone() + self.inner.background_executor.clone() } fn foreground_executor(&self) -> ForegroundExecutor { - self.foreground_executor.clone() + self.inner.foreground_executor.clone() } fn text_system(&self) -> Arc { - self.text_system.clone() + self.inner.text_system.clone() } fn run(&self, on_finish_launching: Box) { - on_finish_launching(); - //Note: here and below, don't keep the lock() open when calling - // into window functions as they may invoke callbacks that need - // to immediately access the platform (self). - while !self.state.lock().quit_requested { - let event = self.xcb_connection.wait_for_event().unwrap(); - match event { - xcb::Event::X(x::Event::ClientMessage(ev)) => { - if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { - if atom == self.atoms.wm_del_window.resource_id() { - // window "x" button clicked by user, we gracefully exit - let window = self.state.lock().windows.remove(&ev.window()).unwrap(); - window.destroy(); - let mut state = self.state.lock(); - state.quit_requested |= state.windows.is_empty(); - } - } - } - xcb::Event::X(x::Event::Expose(ev)) => { - let window = { - let state = self.state.lock(); - Rc::clone(&state.windows[&ev.window()]) - }; - window.expose(); - } - xcb::Event::X(x::Event::ConfigureNotify(ev)) => { - let bounds = Bounds { - origin: Point { - x: ev.x().into(), - y: ev.y().into(), - }, - size: Size { - width: ev.width().into(), - height: ev.height().into(), - }, - }; - let window = { - let state = self.state.lock(); - Rc::clone(&state.windows[&ev.window()]) - }; - window.configure(bounds) - } - _ => {} - } - - if let Ok(runnable) = self.main_receiver.try_recv() { - runnable.run(); - } - } - - if let Some(ref mut fun) = self.callbacks.lock().quit { - fun(); - } + self.client.run(on_finish_launching) } fn quit(&self) { - self.state.lock().quit_requested = true; + self.inner.state.lock().quit_requested = true; } //todo!(linux) @@ -187,22 +133,11 @@ impl Platform for LinuxPlatform { fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - let setup = self.xcb_connection.get_setup(); - setup - .roots() - .enumerate() - .map(|(root_id, _)| { - Rc::new(LinuxDisplay::new(&self.xcb_connection, root_id as i32)) - as Rc - }) - .collect() + self.client.displays() } fn display(&self, id: DisplayId) -> Option> { - Some(Rc::new(LinuxDisplay::new( - &self.xcb_connection, - id.0 as i32, - ))) + self.client.display(id) } //todo!(linux) @@ -215,21 +150,7 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let x_window = self.xcb_connection.generate_id(); - - let window_ptr = Rc::new(LinuxWindowState::new( - options, - &self.xcb_connection, - self.x_root_index, - x_window, - &self.atoms, - )); - - self.state - .lock() - .windows - .insert(x_window, Rc::clone(&window_ptr)); - Box::new(LinuxWindow(window_ptr)) + self.client.open_window(handle, options) } fn open_url(&self, url: &str) { @@ -237,7 +158,7 @@ impl Platform for LinuxPlatform { } fn on_open_urls(&self, callback: Box)>) { - self.callbacks.lock().open_urls = Some(callback); + self.inner.callbacks.lock().open_urls = Some(callback); } fn prompt_for_paths( @@ -256,35 +177,35 @@ impl Platform for LinuxPlatform { } fn on_become_active(&self, callback: Box) { - self.callbacks.lock().become_active = Some(callback); + self.inner.callbacks.lock().become_active = Some(callback); } fn on_resign_active(&self, callback: Box) { - self.callbacks.lock().resign_active = Some(callback); + self.inner.callbacks.lock().resign_active = Some(callback); } fn on_quit(&self, callback: Box) { - self.callbacks.lock().quit = Some(callback); + self.inner.callbacks.lock().quit = Some(callback); } fn on_reopen(&self, callback: Box) { - self.callbacks.lock().reopen = Some(callback); + self.inner.callbacks.lock().reopen = Some(callback); } fn on_event(&self, callback: Box bool>) { - self.callbacks.lock().event = Some(callback); + self.inner.callbacks.lock().event = Some(callback); } fn on_app_menu_action(&self, callback: Box) { - self.callbacks.lock().app_menu_action = Some(callback); + self.inner.callbacks.lock().app_menu_action = Some(callback); } fn on_will_open_app_menu(&self, callback: Box) { - self.callbacks.lock().will_open_app_menu = Some(callback); + self.inner.callbacks.lock().will_open_app_menu = Some(callback); } fn on_validate_app_menu_command(&self, callback: Box bool>) { - self.callbacks.lock().validate_app_menu_command = Some(callback); + self.inner.callbacks.lock().validate_app_menu_command = Some(callback); } fn os_name(&self) -> &'static str { @@ -361,8 +282,6 @@ impl Platform for LinuxPlatform { #[cfg(test)] mod tests { - use crate::ClipboardItem; - use super::*; fn build_platform() -> LinuxPlatform { diff --git a/crates/gpui/src/platform/linux/x11.rs b/crates/gpui/src/platform/linux/x11.rs new file mode 100644 index 0000000000..8bbbdc795f --- /dev/null +++ b/crates/gpui/src/platform/linux/x11.rs @@ -0,0 +1,9 @@ +mod client; +mod client_dispatcher; +pub mod display; +mod window; + +pub(crate) use client::*; +pub(crate) use client_dispatcher::*; +pub(crate) use display::*; +pub(crate) use window::*; diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs new file mode 100644 index 0000000000..0bfcfa9157 --- /dev/null +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -0,0 +1,139 @@ +use std::rc::Rc; +use std::sync::Arc; + +use parking_lot::Mutex; +use xcb::{x, Xid}; + +use collections::HashMap; + +use crate::platform::linux::client::Client; +use crate::platform::{ + LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms, +}; +use crate::{AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, Point, Size, WindowOptions}; + +pub(crate) struct X11ClientState { + pub(crate) windows: HashMap>, +} + +pub(crate) struct X11Client { + platform_inner: Arc, + xcb_connection: Arc, + x_root_index: i32, + atoms: XcbAtoms, + state: Mutex, +} + +impl X11Client { + pub(crate) fn new( + inner: Arc, + xcb_connection: Arc, + x_root_index: i32, + atoms: XcbAtoms, + ) -> Self { + Self { + platform_inner: inner, + xcb_connection, + x_root_index, + atoms, + state: Mutex::new(X11ClientState { + windows: HashMap::default(), + }), + } + } +} + +impl Client for X11Client { + fn run(&self, on_finish_launching: Box) { + on_finish_launching(); + //Note: here and below, don't keep the lock() open when calling + // into window functions as they may invoke callbacks that need + // to immediately access the platform (self). + while !self.platform_inner.state.lock().quit_requested { + let event = self.xcb_connection.wait_for_event().unwrap(); + match event { + xcb::Event::X(x::Event::ClientMessage(ev)) => { + if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { + if atom == self.atoms.wm_del_window.resource_id() { + // window "x" button clicked by user, we gracefully exit + let window = self.state.lock().windows.remove(&ev.window()).unwrap(); + window.destroy(); + let mut state = self.state.lock(); + self.platform_inner.state.lock().quit_requested |= + state.windows.is_empty(); + } + } + } + xcb::Event::X(x::Event::Expose(ev)) => { + let window = { + let state = self.state.lock(); + Rc::clone(&state.windows[&ev.window()]) + }; + window.expose(); + } + xcb::Event::X(x::Event::ConfigureNotify(ev)) => { + let bounds = Bounds { + origin: Point { + x: ev.x().into(), + y: ev.y().into(), + }, + size: Size { + width: ev.width().into(), + height: ev.height().into(), + }, + }; + let window = { + let state = self.state.lock(); + Rc::clone(&state.windows[&ev.window()]) + }; + window.configure(bounds) + } + _ => {} + } + + if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() { + runnable.run(); + } + } + + if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit { + fun(); + } + } + fn displays(&self) -> Vec> { + let setup = self.xcb_connection.get_setup(); + setup + .roots() + .enumerate() + .map(|(root_id, _)| { + Rc::new(X11Display::new(&self.xcb_connection, root_id as i32)) + as Rc + }) + .collect() + } + fn display(&self, id: DisplayId) -> Option> { + Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32))) + } + + fn open_window( + &self, + handle: AnyWindowHandle, + options: WindowOptions, + ) -> Box { + let x_window = self.xcb_connection.generate_id(); + + let window_ptr = Rc::new(X11WindowState::new( + options, + &self.xcb_connection, + self.x_root_index, + x_window, + &self.atoms, + )); + + self.state + .lock() + .windows + .insert(x_window, Rc::clone(&window_ptr)); + Box::new(X11Window(window_ptr)) + } +} diff --git a/crates/gpui/src/platform/linux/x11/client_dispatcher.rs b/crates/gpui/src/platform/linux/x11/client_dispatcher.rs new file mode 100644 index 0000000000..07f67af99f --- /dev/null +++ b/crates/gpui/src/platform/linux/x11/client_dispatcher.rs @@ -0,0 +1,64 @@ +use std::sync::Arc; + +use xcb::x; + +use crate::platform::linux::client_dispatcher::ClientDispatcher; + +pub(crate) struct X11ClientDispatcher { + xcb_connection: Arc, + x_listener_window: x::Window, +} + +impl X11ClientDispatcher { + pub fn new(xcb_connection: &Arc, x_root_index: i32) -> Self { + let x_listener_window = xcb_connection.generate_id(); + let screen = xcb_connection + .get_setup() + .roots() + .nth(x_root_index as usize) + .unwrap(); + xcb_connection.send_request(&x::CreateWindow { + depth: 0, + wid: x_listener_window, + parent: screen.root(), + x: 0, + y: 0, + width: 1, + height: 1, + border_width: 0, + class: x::WindowClass::InputOnly, + visual: screen.root_visual(), + value_list: &[], + }); + + Self { + xcb_connection: Arc::clone(xcb_connection), + x_listener_window, + } + } +} + +impl Drop for X11ClientDispatcher { + fn drop(&mut self) { + self.xcb_connection.send_request(&x::DestroyWindow { + window: self.x_listener_window, + }); + } +} + +impl ClientDispatcher for X11ClientDispatcher { + fn dispatch_on_main_thread(&self) { + // Send a message to the invisible window, forcing + // the main loop to wake up and dispatch the runnable. + self.xcb_connection.send_request(&x::SendEvent { + propagate: false, + destination: x::SendEventDest::Window(self.x_listener_window), + event_mask: x::EventMask::NO_EVENT, + event: &x::VisibilityNotifyEvent::new( + self.x_listener_window, + x::Visibility::Unobscured, + ), + }); + self.xcb_connection.flush().unwrap(); + } +} diff --git a/crates/gpui/src/platform/linux/display.rs b/crates/gpui/src/platform/linux/x11/display.rs similarity index 91% rename from crates/gpui/src/platform/linux/display.rs rename to crates/gpui/src/platform/linux/x11/display.rs index cdca59c435..85ae754192 100644 --- a/crates/gpui/src/platform/linux/display.rs +++ b/crates/gpui/src/platform/linux/x11/display.rs @@ -1,15 +1,16 @@ -use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; use anyhow::Result; use uuid::Uuid; +use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; + #[derive(Debug)] -pub(crate) struct LinuxDisplay { +pub(crate) struct X11Display { x_screen_index: i32, bounds: Bounds, uuid: Uuid, } -impl LinuxDisplay { +impl X11Display { 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 { @@ -26,7 +27,7 @@ impl LinuxDisplay { } } -impl PlatformDisplay for LinuxDisplay { +impl PlatformDisplay for X11Display { fn id(&self) -> DisplayId { DisplayId(self.x_screen_index as u32) } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/x11/window.rs similarity index 93% rename from crates/gpui/src/platform/linux/window.rs rename to crates/gpui/src/platform/linux/x11/window.rs index 774c4a537c..e8bf2e0102 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -1,14 +1,6 @@ //todo!(linux): remove #![allow(unused)] -use super::BladeRenderer; -use crate::{ - Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, - PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, -}; -use blade_graphics as gpu; -use parking_lot::Mutex; -use raw_window_handle as rwh; use std::{ ffi::c_void, mem, @@ -17,11 +9,21 @@ use std::{ rc::Rc, sync::{self, Arc}, }; + +use blade_graphics as gpu; +use parking_lot::Mutex; +use raw_window_handle as rwh; use xcb::{ x::{self, StackMode}, Xid as _, }; +use crate::platform::linux::blade_renderer::BladeRenderer; +use crate::{ + Bounds, GlobalPixels, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, + Size, WindowAppearance, WindowBounds, WindowOptions, X11Display, +}; + #[derive(Default)] struct Callbacks { request_frame: Option>, @@ -35,6 +37,17 @@ struct Callbacks { appearance_changed: Option>, } +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", + } +} + struct LinuxWindowInner { bounds: Bounds, scale_factor: f32, @@ -70,7 +83,7 @@ struct RawWindow { visual_id: u32, } -pub(crate) struct LinuxWindowState { +pub(crate) struct X11WindowState { xcb_connection: Arc, display: Rc, raw: RawWindow, @@ -80,7 +93,7 @@ pub(crate) struct LinuxWindowState { } #[derive(Clone)] -pub(crate) struct LinuxWindow(pub(crate) Rc); +pub(crate) struct X11Window(pub(crate) Rc); //todo!(linux): Remove other RawWindowHandle implementation unsafe impl blade_rwh::HasRawWindowHandle for RawWindow { @@ -100,7 +113,7 @@ unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow { } } -impl rwh::HasWindowHandle for LinuxWindow { +impl rwh::HasWindowHandle for X11Window { fn window_handle(&self) -> Result { Ok(unsafe { let non_zero = NonZeroU32::new(self.0.raw.window_id).unwrap(); @@ -109,7 +122,7 @@ impl rwh::HasWindowHandle for LinuxWindow { }) } } -impl rwh::HasDisplayHandle for LinuxWindow { +impl rwh::HasDisplayHandle for X11Window { fn display_handle(&self) -> Result { Ok(unsafe { let non_zero = NonNull::new(self.0.raw.connection).unwrap(); @@ -119,7 +132,7 @@ impl rwh::HasDisplayHandle for LinuxWindow { } } -impl LinuxWindowState { +impl X11WindowState { pub fn new( options: WindowOptions, xcb_connection: &Arc, @@ -219,7 +232,7 @@ impl LinuxWindowState { Self { xcb_connection: Arc::clone(xcb_connection), - display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), + display: Rc::new(X11Display::new(xcb_connection, x_screen_index)), raw, x_window, callbacks: Mutex::new(Callbacks::default()), @@ -280,7 +293,7 @@ impl LinuxWindowState { } } -impl PlatformWindow for LinuxWindow { +impl PlatformWindow for X11Window { fn bounds(&self) -> WindowBounds { WindowBounds::Fixed(self.0.inner.lock().bounds.map(|v| GlobalPixels(v as f32))) } diff --git a/crates/gpui/src/platform/mac/text_system.rs b/crates/gpui/src/platform/mac/text_system.rs index 24ab8346f4..83d4fce909 100644 --- a/crates/gpui/src/platform/mac/text_system.rs +++ b/crates/gpui/src/platform/mac/text_system.rs @@ -223,6 +223,7 @@ impl MacTextSystemState { .or_else(|_| self.system_source.select_family_by_name(name.as_ref()))?; for font in family.fonts() { let mut font = font.load()?; + open_type::apply_features(&mut font, features); let Some(_) = font.glyph_for_char('m') else { continue;