bite-gpui 1.25.202

Zed's GPU-accelerated UI framework
Documentation
//! Platform entrypoints: construct the default [`crate::Platform`] for the
//! current operating system and turn it into an [`crate::Application`].
//!
//! These live behind the `platform` feature, which pulls in the
//! backend for the target OS.

use crate::{Application, BackgroundExecutor, Platform};
use std::rc::Rc;

/// Returns a background executor for the current platform.
pub fn background_executor() -> BackgroundExecutor {
    current_platform(true).background_executor()
}

/// Returns an [`Application`] backed by the default platform for this OS.
pub fn application() -> Application {
    #[cfg(target_family = "wasm")]
    {
        application_with_web_backend(gpui_web::WebBackendPreference::Auto)
    }

    #[cfg(not(target_family = "wasm"))]
    Application::with_platform(current_platform(false))
}

/// Returns a headless [`Application`] backed by the default platform for this OS.
pub fn headless() -> Application {
    Application::with_platform(current_platform(true))
}

/// Returns a Linux app that may switch among `allowed_modes`.
///
/// It starts windowed in the process's own environment, or headless if that names no allowed
/// display server. Set another initial mode with [`gpui::Application::with_windowing`], and
/// switch later with [`gpui::App::request_windowing`].
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub fn linux(allowed_modes: gpui::WindowingModes) -> gpui::Application {
    gpui::Application::with_platform(gpui_linux::linux_platform(allowed_modes))
}

#[cfg(target_family = "wasm")]
pub use gpui_web::WebBackendPreference;

/// The producer's half of the imported-texture path on Windows: a producer reaches the Direct3D
/// renderer's device through a `gpu_canvas` callback
/// ([`GpuCanvasContext::device`](crate::GpuCanvasContext::device)), and a texture made on it becomes
/// a token through this extension.
#[cfg(target_os = "windows")]
pub use gpui_windows::{DirectXImportedTexture, DirectXRenderer, DirectXTextureExt};

/// The producer's half of the imported-texture path on macOS, the same shape as the Windows pair above: the device
/// comes from the canvas's `device::<R>()`, and a texture made on it becomes a token through this extension.
/// Unlike the Windows one it names no payload type of its own -- the engine's MetalTexture is the
/// handle the renderer resolves.
#[cfg(target_os = "macos")]
pub use gpui_macos::MetalTextureExt;

/// The producer's half of the imported-texture path where wgpu is the renderer, which is the platform's own default on
/// these two targets: the same shape, from the crate that re-exports the wgpu renderer. A window on
/// Windows whose factory installed `WgpuRenderer` takes the trait from `gpui_wgpu` instead, which
/// is the crate that renderer comes from.
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub use gpui_linux::ImportedTextureExt;

#[cfg(target_family = "wasm")]
pub fn application_with_web_backend(backend_preference: WebBackendPreference) -> Application {
    let platform = Rc::new(gpui_web::WebPlatform::new_with_backend(
        true,
        backend_preference,
    ));
    let http_client = std::sync::Arc::new(platform.fetch_http_client());
    Application::with_platform(platform).with_http_client(http_client)
}

/// Unlike `application`, this function returns a single-threaded web application.
#[cfg(target_family = "wasm")]
pub fn single_threaded_web() -> Application {
    let platform = Rc::new(gpui_web::WebPlatform::new(false));
    let http_client = std::sync::Arc::new(platform.fetch_http_client());
    Application::with_platform(platform).with_http_client(http_client)
}

/// Initializes logging for the web platform. `gpui_web::init_logging` also
/// installs the panic hook. Call this before running the application in a
/// wasm_bindgen entrypoint.
#[cfg(target_family = "wasm")]
pub fn web_init() {
    gpui_web::init_logging();
}

/// Returns the default [`Platform`] for the current OS.
pub fn current_platform(headless: bool) -> Rc<dyn Platform> {
    #[cfg(target_os = "macos")]
    {
        Rc::new(gpui_macos::MacPlatform::new(headless))
    }

    #[cfg(target_os = "windows")]
    {
        Rc::new(
            gpui_windows::WindowsPlatform::new(headless)
                .expect("failed to initialize Windows platform"),
        )
    }

    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
    {
        gpui_linux::current_platform(headless)
    }

    #[cfg(target_family = "wasm")]
    {
        let _ = headless;
        Rc::new(gpui_web::WebPlatform::new(true))
    }
}

/// Returns a new [`crate::SceneRenderer`] for the current platform, if available.
#[cfg(any(feature = "bench-support", feature = "test-support"))]
pub fn current_headless_renderer() -> anyhow::Result<Option<Box<dyn crate::SceneRenderer>>> {
    #[cfg(all(feature = "platform", any(target_os = "linux", target_os = "freebsd")))]
    {
        // Headless has no platform instance to ask, so the platform crate supplies its debug
        // preference directly: the same `ZED_DEVICE_ID` lowering a windowed run gets through
        // `set_gpu_preference`.
        current_headless_renderer_with(
            gpui_linux::gpu_preference_from_env().unwrap_or(crate::GpuPreference::Auto),
        )
    }

    #[cfg(not(all(feature = "platform", any(target_os = "linux", target_os = "freebsd"))))]
    {
        current_headless_renderer_with(crate::GpuPreference::Auto)
    }
}

/// As [`current_headless_renderer`], selecting the adapter by `gpu_preference` where the headless
/// renderer is `wgpu`.
///
/// Metal's headless renderer takes no preference, and a platform with no headless renderer returns
/// `Ok(None)`; the argument is ignored in both cases. The wgpu context is cached process-wide, so
/// only the first renderer's preference takes effect.
#[cfg(any(feature = "bench-support", feature = "test-support"))]
pub fn current_headless_renderer_with(
    gpu_preference: crate::GpuPreference,
) -> anyhow::Result<Option<Box<dyn crate::SceneRenderer>>> {
    let _ = &gpu_preference;

    #[cfg(target_os = "macos")]
    {
        Ok(Some(Box::new(
            gpui_macos::metal_renderer::MetalHeadlessRenderer::new(),
        )))
    }

    #[cfg(all(feature = "platform", any(target_os = "linux", target_os = "freebsd")))]
    {
        // The Linux platform owns no renderer of its own: wgpu is the renderer a window uses here,
        // so a headless context renders through the same code a window drives.
        gpui_linux::WgpuHeadlessRenderer::with_gpu_preference(gpu_preference)
            .map(|renderer| Some(Box::new(renderer) as Box<dyn crate::SceneRenderer>))
    }

    #[cfg(not(any(
        target_os = "macos",
        all(feature = "platform", any(target_os = "linux", target_os = "freebsd")),
    )))]
    {
        Ok(None)
    }
}

/// Returns the current platform's text system for benchmark contexts, built once per
/// thread and shared by every context on it.
///
/// `#[gpui::bench]` creates a context for every Criterion routine call. Building a whole
/// platform each time only to take its text system leaks the platform's threads on Linux
/// (a 20-worker dispatcher and a timer thread per call), which slows every later
/// benchmark in the process.
#[cfg(feature = "bench-support")]
pub fn bench_text_system() -> std::sync::Arc<dyn crate::PlatformTextSystem> {
    thread_local! {
        static TEXT_SYSTEM: std::sync::Arc<dyn crate::PlatformTextSystem> =
            current_platform(true).text_system();
    }
    TEXT_SYSTEM.with(|text_system| text_system.clone())
}

#[cfg(all(test, target_os = "macos"))]
mod tests {
    use super::*;
    use crate::{AppContext, Empty, VisualTestAppContext};
    use std::cell::RefCell;
    use std::time::Duration;

    // Note: All VisualTestAppContext tests are ignored by default because they require
    // the macOS main thread. Standard Rust tests run on worker threads, which causes
    // SIGABRT when interacting with macOS AppKit/Cocoa APIs.
    //
    // To run these tests, use:
    // cargo test -p gpui visual_test_context -- --ignored --test-threads=1

    #[test]
    #[ignore] // Requires macOS main thread
    fn test_foreground_tasks_run_with_run_until_parked() {
        let mut cx = VisualTestAppContext::new(current_platform(false));

        let task_ran = Rc::new(RefCell::new(false));

        // Spawn a foreground task via the App's spawn method
        // This should use our TestDispatcher, not the MacDispatcher
        {
            let task_ran = task_ran.clone();
            cx.update(|cx| {
                cx.spawn(async move |_| {
                    *task_ran.borrow_mut() = true;
                })
                .detach();
            });
        }

        // The task should not have run yet
        assert!(!*task_ran.borrow());

        // Run until parked should execute the foreground task
        cx.run_until_parked();

        // Now the task should have run
        assert!(*task_ran.borrow());
    }

    #[test]
    #[ignore] // Requires macOS main thread
    fn test_advance_clock_triggers_delayed_tasks() {
        let mut cx = VisualTestAppContext::new(current_platform(false));

        let task_ran = Rc::new(RefCell::new(false));

        // Spawn a task that waits for a timer
        {
            let task_ran = task_ran.clone();
            let executor = cx.background_executor.clone();
            cx.update(|cx| {
                cx.spawn(async move |_| {
                    executor.timer(Duration::from_millis(500)).await;
                    *task_ran.borrow_mut() = true;
                })
                .detach();
            });
        }

        // Run until parked - the task should be waiting on the timer
        cx.run_until_parked();
        assert!(!*task_ran.borrow());

        // Advance clock past the timer duration
        cx.advance_clock(Duration::from_millis(600));

        // Now the task should have completed
        assert!(*task_ran.borrow());
    }

    #[test]
    #[ignore] // Requires macOS main thread - window creation fails on test threads
    fn test_window_spawn_uses_test_dispatcher() {
        let mut cx = VisualTestAppContext::new(current_platform(false));

        let task_ran = Rc::new(RefCell::new(false));

        let window = cx
            .open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
            .expect("Failed to open window");

        // Spawn a task via window.spawn - this is the critical test case
        // for tooltip behavior, as tooltips use window.spawn for delayed show
        {
            let task_ran = task_ran.clone();
            cx.update_window(window.into(), |_, window, cx| {
                window
                    .spawn(cx, async move |_| {
                        *task_ran.borrow_mut() = true;
                    })
                    .detach();
            })
            .ok();
        }

        // The task should not have run yet
        assert!(!*task_ran.borrow());

        // Run until parked should execute the foreground task spawned via window
        cx.run_until_parked();

        // Now the task should have run
        assert!(*task_ran.borrow());
    }
}