Skip to main content

open_gpui_platform/
gpui_platform.rs

1//! Convenience crate that re-exports GPUI's platform traits and the
2//! `current_platform` constructor so consumers don't need `#[cfg]` gating.
3
4pub use open_gpui::Platform;
5
6use std::rc::Rc;
7
8/// Returns a background executor for the current platform.
9pub fn background_executor() -> open_gpui::BackgroundExecutor {
10    current_platform(true).background_executor()
11}
12
13pub fn application() -> open_gpui::Application {
14    open_gpui::Application::with_platform(current_platform(false))
15}
16
17pub fn headless() -> open_gpui::Application {
18    open_gpui::Application::with_platform(current_platform(true))
19}
20
21/// Unlike `application`, this function returns a single-threaded web application.
22#[cfg(target_family = "wasm")]
23pub fn single_threaded_web() -> open_gpui::Application {
24    open_gpui::Application::with_platform(Rc::new(open_gpui_web::WebPlatform::new(false)))
25}
26
27/// Initializes panic hooks and logging for the web platform.
28/// Call this before running the application in a wasm_bindgen entrypoint.
29#[cfg(target_family = "wasm")]
30pub fn web_init() {
31    console_error_panic_hook::set_once();
32    open_gpui_web::init_logging();
33}
34
35/// Returns the default [`Platform`] for the current OS.
36pub fn current_platform(headless: bool) -> Rc<dyn Platform> {
37    #[cfg(target_os = "macos")]
38    {
39        Rc::new(open_gpui_macos::MacPlatform::new(headless))
40    }
41
42    #[cfg(target_os = "windows")]
43    {
44        Rc::new(
45            open_gpui_windows::WindowsPlatform::new(headless)
46                .expect("failed to initialize Windows platform"),
47        )
48    }
49
50    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
51    {
52        open_gpui_linux::current_platform(headless)
53    }
54
55    #[cfg(target_family = "wasm")]
56    {
57        let _ = headless;
58        Rc::new(open_gpui_web::WebPlatform::new(true))
59    }
60}
61
62/// Returns a new [`HeadlessRenderer`] for the current platform, if available.
63#[cfg(feature = "test-support")]
64pub fn current_headless_renderer() -> Option<Box<dyn open_gpui::PlatformHeadlessRenderer>> {
65    #[cfg(target_os = "macos")]
66    {
67        Some(Box::new(
68            open_gpui_macos::metal_renderer::MetalHeadlessRenderer::new(),
69        ))
70    }
71
72    #[cfg(not(target_os = "macos"))]
73    {
74        None
75    }
76}
77
78#[cfg(all(test, target_os = "macos"))]
79mod tests {
80    use super::*;
81    use open_gpui::{AppContext, Empty, VisualTestAppContext};
82    use std::cell::RefCell;
83    use std::time::Duration;
84
85    // Note: All VisualTestAppContext tests are ignored by default because they require
86    // the macOS main thread. Standard Rust tests run on worker threads, which causes
87    // SIGABRT when interacting with macOS AppKit/Cocoa APIs.
88    //
89    // To run these tests, use:
90    // cargo test -p gpui visual_test_context -- --ignored --test-threads=1
91
92    #[test]
93    #[ignore] // Requires macOS main thread
94    fn test_foreground_tasks_run_with_run_until_parked() {
95        let mut cx = VisualTestAppContext::new(current_platform(false));
96
97        let task_ran = Rc::new(RefCell::new(false));
98
99        // Spawn a foreground task via the App's spawn method
100        // This should use our TestDispatcher, not the MacDispatcher
101        {
102            let task_ran = task_ran.clone();
103            cx.update(|cx| {
104                cx.spawn(async move |_| {
105                    *task_ran.borrow_mut() = true;
106                })
107                .detach();
108            });
109        }
110
111        // The task should not have run yet
112        assert!(!*task_ran.borrow());
113
114        // Run until parked should execute the foreground task
115        cx.run_until_parked();
116
117        // Now the task should have run
118        assert!(*task_ran.borrow());
119    }
120
121    #[test]
122    #[ignore] // Requires macOS main thread
123    fn test_advance_clock_triggers_delayed_tasks() {
124        let mut cx = VisualTestAppContext::new(current_platform(false));
125
126        let task_ran = Rc::new(RefCell::new(false));
127
128        // Spawn a task that waits for a timer
129        {
130            let task_ran = task_ran.clone();
131            let executor = cx.background_executor.clone();
132            cx.update(|cx| {
133                cx.spawn(async move |_| {
134                    executor.timer(Duration::from_millis(500)).await;
135                    *task_ran.borrow_mut() = true;
136                })
137                .detach();
138            });
139        }
140
141        // Run until parked - the task should be waiting on the timer
142        cx.run_until_parked();
143        assert!(!*task_ran.borrow());
144
145        // Advance clock past the timer duration
146        cx.advance_clock(Duration::from_millis(600));
147
148        // Now the task should have completed
149        assert!(*task_ran.borrow());
150    }
151
152    #[test]
153    #[ignore] // Requires macOS main thread - window creation fails on test threads
154    fn test_window_spawn_uses_test_dispatcher() {
155        let mut cx = VisualTestAppContext::new(current_platform(false));
156
157        let task_ran = Rc::new(RefCell::new(false));
158
159        let window = cx
160            .open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
161            .expect("Failed to open window");
162
163        // Spawn a task via window.spawn - this is the critical test case
164        // for tooltip behavior, as tooltips use window.spawn for delayed show
165        {
166            let task_ran = task_ran.clone();
167            cx.update_window(window.into(), |_, window, cx| {
168                window
169                    .spawn(cx, async move |_| {
170                        *task_ran.borrow_mut() = true;
171                    })
172                    .detach();
173            })
174            .ok();
175        }
176
177        // The task should not have run yet
178        assert!(!*task_ran.borrow());
179
180        // Run until parked should execute the foreground task spawned via window
181        cx.run_until_parked();
182
183        // Now the task should have run
184        assert!(*task_ran.borrow());
185    }
186}