diff --git a/Cargo.lock b/Cargo.lock index 4ae19e764cd6cdf4a9bd261d3615d767e7991db8..9644e2b61983ea8cb5ea8ea45449c00d6a40b825 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2732,6 +2732,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", + "nanorand", "spin 0.9.8", ] @@ -3105,8 +3106,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -3232,6 +3235,7 @@ dependencies = [ "dhat", "env_logger", "etagere", + "flume", "font-kit", "foreign-types 0.3.2", "futures 0.3.28", @@ -4644,6 +4648,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.10", +] + [[package]] name = "native-tls" version = "0.2.11" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 15e3e8d5c905cd3809ebd7a531feb6e7867a5a76..0a0a07382916cdc462adb4a466ebe66c7b15d23c 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -94,3 +94,6 @@ log.workspace = true media = { path = "../media" } metal = "0.21.0" objc = "0.2" + +[target.'cfg(target_os = "linux")'.dependencies] +flume = "0.11" diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 194dfe1c5464728a410c4ef9b3736fcba9618262..008baea33326d17cff345a0dff166ca8e6347935 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -1,9 +1,9 @@ mod app_menu; mod keystroke; -#[cfg(target_os = "macos")] -mod mac; #[cfg(target_os = "linux")] mod linux; +#[cfg(target_os = "macos")] +mod mac; #[cfg(any(test, feature = "test-support"))] mod test; @@ -35,10 +35,10 @@ use uuid::Uuid; pub use app_menu::*; pub use keystroke::*; -#[cfg(target_os = "macos")] -pub(crate) use mac::*; #[cfg(target_os = "linux")] pub(crate) use linux::*; +#[cfg(target_os = "macos")] +pub(crate) use mac::*; #[cfg(any(test, feature = "test-support"))] pub(crate) use test::*; use time::UtcOffset; diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index a979749edd667335bea75b4541a91ba955248d95..7a361c70343aca35d47a212cf337a080a629f4a6 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,3 +1,5 @@ +mod dispatcher; mod platform; +pub(crate) use dispatcher::*; pub(crate) use platform::*; diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs new file mode 100644 index 0000000000000000000000000000000000000000..14072f040a5e4e5a174a717be574b42330dc885f --- /dev/null +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -0,0 +1,97 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::{PlatformDispatcher, TaskLabel}; +use async_task::Runnable; +use parking::{Parker, Unparker}; +use parking_lot::Mutex; +use std::{ + panic, thread, + time::{Duration, Instant}, +}; + +pub(crate) struct LinuxDispatcher { + parker: Mutex, + timed_tasks: Mutex>, + main_sender: flume::Sender, + main_receiver: flume::Receiver, + background_sender: flume::Sender, + background_thread: thread::JoinHandle<()>, + main_thread_id: thread::ThreadId, +} + +impl Default for LinuxDispatcher { + fn default() -> Self { + Self::new() + } +} + +impl LinuxDispatcher { + pub fn new() -> Self { + let (main_sender, main_receiver) = flume::unbounded::(); + 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 { + parker: Mutex::new(Parker::new()), + timed_tasks: Mutex::new(Vec::new()), + main_sender, + main_receiver, + background_sender, + background_thread, + main_thread_id: thread::current().id(), + } + } +} + +impl PlatformDispatcher for LinuxDispatcher { + fn is_main_thread(&self) -> bool { + thread::current().id() == self.main_thread_id + } + + fn dispatch(&self, runnable: Runnable, _: Option) { + self.background_sender.send(runnable).unwrap(); + } + + fn dispatch_on_main_thread(&self, runnable: Runnable) { + self.main_sender.send(runnable).unwrap(); + } + + fn dispatch_after(&self, duration: Duration, runnable: Runnable) { + let moment = Instant::now() + duration; + let mut timed_tasks = self.timed_tasks.lock(); + timed_tasks.push((moment, runnable)); + timed_tasks.sort_unstable_by(|&(ref a, _), &(ref b, _)| b.cmp(a)); + } + + fn tick(&self, background_only: bool) -> bool { + let mut ran = false; + if self.is_main_thread() && !background_only { + for runnable in self.main_receiver.try_iter() { + runnable.run(); + ran = true; + } + } + let mut timed_tasks = self.timed_tasks.lock(); + while let Some(&(moment, _)) = timed_tasks.last() { + if moment <= Instant::now() { + let (_, runnable) = timed_tasks.pop().unwrap(); + runnable.run(); + ran = true; + } + } + ran + } + + fn park(&self) { + self.parker.lock().park() + } + + fn unparker(&self) -> Unparker { + self.parker.lock().unparker() + } +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 245cdbc11db514b16c37db569c5491b60104acc1..a2b87b7ea139db8db397ee677ccf63ab67cff8f4 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -2,8 +2,9 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, - PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowOptions, + ForegroundExecutor, Keymap, LinuxDispatcher, Menu, PathPromptOptions, Platform, + PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, + Task, WindowOptions, }; use futures::channel::oneshot; @@ -17,10 +18,11 @@ use std::{ }; use time::UtcOffset; - pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { + background_executor: BackgroundExecutor, + foreground_executor: ForegroundExecutor, } impl Default for LinuxPlatform { @@ -31,18 +33,21 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { + let dispatcher = Arc::new(LinuxDispatcher::new()); Self(Mutex::new(LinuxPlatformState { + background_executor: BackgroundExecutor::new(dispatcher.clone()), + foreground_executor: ForegroundExecutor::new(dispatcher), })) } } impl Platform for LinuxPlatform { fn background_executor(&self) -> BackgroundExecutor { - unimplemented!() + self.0.lock().background_executor.clone() } fn foreground_executor(&self) -> crate::ForegroundExecutor { - unimplemented!() + self.0.lock().foreground_executor.clone() } fn text_system(&self) -> Arc { diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index d17739239eede2e3b0aa2d5f91028dadd94d3bdd..6cd833b681b9db541ee86d75ba8777a52ae6cbba 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -1,9 +1,11 @@ mod dispatcher; mod display; mod platform; +mod text_system; mod window; pub(crate) use dispatcher::*; pub(crate) use display::*; pub(crate) use platform::*; +pub(crate) use text_system::*; pub(crate) use window::*; diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 5aadc4b760aeacf9bb03ee3bef4694a4d73fea4d..464e38b0c405dceb122aab61e15ea16daebfbe1a 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -1,7 +1,7 @@ use crate::{ AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, - Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow, - WindowOptions, + Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestTextSystem, + TestWindow, WindowOptions, }; use anyhow::{anyhow, Result}; use collections::VecDeque; @@ -118,7 +118,7 @@ impl Platform for TestPlatform { } fn text_system(&self) -> Arc { - Arc::new(crate::platform::mac::MacTextSystem::new()) + Arc::new(TestTextSystem {}) } fn run(&self, _on_finish_launching: Box) { diff --git a/crates/gpui/src/platform/test/text_system.rs b/crates/gpui/src/platform/test/text_system.rs new file mode 100644 index 0000000000000000000000000000000000000000..c72ee6f00c189a07d20b3df0236a823623616e29 --- /dev/null +++ b/crates/gpui/src/platform/test/text_system.rs @@ -0,0 +1,58 @@ +use crate::{ + Bounds, DevicePixels, Font, FontId, FontMetrics, FontRun, GlyphId, LineLayout, Pixels, + PlatformTextSystem, RenderGlyphParams, Size, +}; +use anyhow::Result; +use std::sync::Arc; + +pub(crate) struct TestTextSystem {} + +#[allow(unused)] +impl PlatformTextSystem for TestTextSystem { + fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { + unimplemented!() + } + fn all_font_names(&self) -> Vec { + unimplemented!() + } + fn all_font_families(&self) -> Vec { + unimplemented!() + } + fn font_id(&self, descriptor: &Font) -> Result { + unimplemented!() + } + fn font_metrics(&self, font_id: FontId) -> FontMetrics { + unimplemented!() + } + fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + unimplemented!() + } + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + unimplemented!() + } + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { + unimplemented!() + } + fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { + unimplemented!() + } + fn rasterize_glyph( + &self, + params: &RenderGlyphParams, + raster_bounds: Bounds, + ) -> Result<(Size, Vec)> { + unimplemented!() + } + fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { + unimplemented!() + } + fn wrap_line( + &self, + text: &str, + font_id: FontId, + font_size: Pixels, + width: Pixels, + ) -> Vec { + unimplemented!() + } +} diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index 4e21988a2ef37f2a11d403034d2b1b52d6043625..6e2e5bc7256d73762bd2d473b9b36179cdbc3a50 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -35,8 +35,8 @@ use crate::{ InputHandler, IsZero, KeyContext, KeyEvent, KeymatchMode, LayoutId, MonochromeSprite, MouseEvent, PaintQuad, Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, - StackingContext, StackingOrder, Style, TextStyleRefinement, Underline, UnderlineStyle, - Window, WindowContext, SUBPIXEL_VARIANTS, + StackingContext, StackingOrder, Style, TextStyleRefinement, Underline, UnderlineStyle, Window, + WindowContext, SUBPIXEL_VARIANTS, }; type AnyMouseListener = Box;