diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index ba70c6fe67..f76e791a56 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,5 +1,6 @@ mod blade_atlas; mod blade_belt; +mod blade_renderer; mod dispatcher; mod display; mod platform; @@ -14,3 +15,4 @@ pub(crate) use text_system::*; pub(crate) use window::*; use blade_belt::*; +use blade_renderer::*; diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 24a9bbde8d..2ee2b6d548 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -22,6 +22,22 @@ struct BladeAtlasState { tiles_by_key: FxHashMap, } +impl BladeAtlasState { + fn destroy(&mut self) { + for texture in self.monochrome_textures.drain(..) { + self.gpu.destroy_texture(texture.raw); + } + for texture in self.polychrome_textures.drain(..) { + self.gpu.destroy_texture(texture.raw); + } + for texture in self.path_textures.drain(..) { + self.gpu.destroy_texture(texture.raw); + } + self.gpu.destroy_command_encoder(&mut self.gpu_encoder); + self.upload_belt.destroy(&self.gpu); + } +} + impl BladeAtlas { pub(crate) fn new(gpu: &Arc) -> Self { BladeAtlas(Mutex::new(BladeAtlasState { @@ -41,6 +57,10 @@ impl BladeAtlas { })) } + pub(crate) fn destroy(&self) { + self.0.lock().destroy(); + } + pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) { let mut lock = self.0.lock(); let textures = match texture_kind { diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs new file mode 100644 index 0000000000..1f87b30efe --- /dev/null +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -0,0 +1,11 @@ +use std::sync::Arc; + +pub struct BladeRenderer { + gpu: Arc, +} + +impl BladeRenderer { + pub fn new(gpu: Arc) -> Self { + Self { gpu } + } +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 18016358e6..3817eeff34 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -2,11 +2,13 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, Menu, - PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, - PlatformWindow, Result, SemanticVersion, Task, WindowOptions, + ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, + LinuxWindowState, LinuxWindowStatePtr, Menu, PathPromptOptions, Platform, PlatformDisplay, + PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, + WindowOptions, }; +use collections::{HashMap, HashSet}; use futures::channel::oneshot; use parking_lot::Mutex; @@ -17,16 +19,48 @@ use std::{ time::Duration, }; use time::UtcOffset; -use x11rb::{connection::Connection as _, rust_connection::RustConnection}; +use x11rb::{ + connection::Connection as _, + protocol::{ + xproto::{Atom, ConnectionExt as _}, + Event, + }, + rust_connection::RustConnection, +}; 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, + } + } +} + pub(crate) struct LinuxPlatformState { x11_connection: RustConnection, x11_root_index: usize, - gpu: Arc, + atoms: WmAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, + windows: HashMap, text_system: Arc, } @@ -39,24 +73,17 @@ 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 dispatcher = Arc::new(LinuxDispatcher::new()); - let gpu = Arc::new( - unsafe { - blade::Context::init(blade::ContextDesc { - validation: cfg!(debug_assertions), - capture: false, - }) - } - .unwrap(), - ); Self(Mutex::new(LinuxPlatformState { x11_connection, x11_root_index, - gpu, + atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), + windows: HashMap::default(), text_system: Arc::new(LinuxTextSystem::new()), })) } @@ -76,7 +103,58 @@ impl Platform for LinuxPlatform { } fn run(&self, on_finish_launching: Box) { - on_finish_launching() + on_finish_launching(); + + 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 mut lock = self.0.lock(); + let data = event.data.as_data32(); + if data[0] == lock.atoms.delete_window { + { + let mut window = lock.windows[&event.window].lock(); + window.destroy(); + } + lock.windows.remove(&event.window); + } + } + Event::Error(error) => { + log::error!("X11 error {:?}", error); + } + _ => {} + } + + let lock = self.0.lock(); + event_option = lock.x11_connection.poll_for_event().unwrap(); + } + + for x11_window in need_repaint.drain() { + let lock = self.0.lock(); + let mut window = lock.windows[&x11_window].lock(); + window.paint(); + lock.x11_connection.flush().unwrap(); + } + } } fn quit(&self) {} @@ -118,14 +196,19 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let lock = self.0.lock(); - Box::new(LinuxWindow::new( + let mut lock = self.0.lock(); + let win_id = lock.x11_connection.generate_id().unwrap(); + + let window_ptr = LinuxWindowState::new_ptr( options, handle, &lock.x11_connection, lock.x11_root_index, - &lock.gpu, - )) + win_id, + &lock.atoms, + ); + lock.windows.insert(win_id, window_ptr.clone()); + Box::new(LinuxWindow(window_ptr)) } fn set_display_link_output_callback( diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index ffc8372dbd..63cbb15e5a 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,8 +1,9 @@ +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, + WindowOptions, WmAtoms, }; use collections::HashMap; use parking_lot::Mutex; @@ -21,45 +22,37 @@ use x11rb::{ pub(crate) struct LinuxWindowState { display: Rc, - win_id: u32, + x11_window: u32, + window_bounds: WindowBounds, + content_size: Size, sprite_atlas: Arc, + renderer: BladeRenderer, } +pub(crate) type LinuxWindowStatePtr = Arc>; #[derive(Clone)] -pub(crate) struct LinuxWindow(pub(crate) Arc>); +pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr); -impl LinuxWindow { - pub fn new( +impl LinuxWindowState { + pub fn new_ptr( options: WindowOptions, handle: AnyWindowHandle, x11_connection: &RustConnection, x11_main_screen_index: usize, - gpu: &Arc, - ) -> Self { + x11_window: u32, + atoms: &WmAtoms, + ) -> LinuxWindowStatePtr { 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) @@ -75,7 +68,7 @@ impl LinuxWindow { x11_connection .create_window( x11rb::COPY_DEPTH_FROM_PARENT, - win_id, + x11_window, screen.root, bound_x, bound_y, @@ -93,7 +86,7 @@ impl LinuxWindow { x11_connection .change_property8( PropMode::REPLACE, - win_id, + x11_window, AtomEnum::WM_NAME, AtomEnum::STRING, title.as_bytes(), @@ -104,30 +97,63 @@ impl LinuxWindow { x11_connection .change_property32( PropMode::REPLACE, - win_id, - wm_protocols, + x11_window, + atoms.protocols, AtomEnum::ATOM, - &[wm_delete_window], + &[atoms.delete_window], ) .unwrap(); - x11_connection.map_window(win_id).unwrap(); + x11_connection.map_window(x11_window).unwrap(); + x11_connection.flush().unwrap(); - Self(Arc::new(Mutex::new(LinuxWindowState { + let gpu = Arc::new( + unsafe { + blade::Context::init(blade::ContextDesc { + validation: cfg!(debug_assertions), + capture: false, + }) + } + .unwrap(), + ); + + Arc::new(Mutex::new(Self { display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)), - win_id, - sprite_atlas: Arc::new(BladeAtlas::new(gpu)), - }))) + x11_window, + window_bounds: options.bounds, + content_size: Size { + width: Pixels(bound_width as f32), + height: Pixels(bound_height as f32), + }, + sprite_atlas: Arc::new(BladeAtlas::new(&gpu)), + renderer: BladeRenderer::new(gpu), + })) + } + + pub fn resize(&mut self, width: u16, height: u16) { + self.content_size = Size { + width: Pixels(width as f32), + height: Pixels(height as f32), + }; + } + + pub fn destroy(&mut self) { + self.sprite_atlas.destroy(); + } + + pub fn paint(&mut self) { + //TODO } } impl PlatformWindow for LinuxWindow { fn bounds(&self) -> WindowBounds { - unimplemented!() + //TODO: update when window moves + self.0.lock().window_bounds } fn content_size(&self) -> Size { - unimplemented!() + self.0.lock().content_size } fn scale_factor(&self) -> f32 {