platform.rs

   1mod app_menu;
   2mod keyboard;
   3mod keystroke;
   4
   5#[cfg(any(target_os = "linux", target_os = "freebsd"))]
   6mod linux;
   7
   8#[cfg(target_os = "macos")]
   9mod mac;
  10
  11#[cfg(any(
  12    all(
  13        any(target_os = "linux", target_os = "freebsd"),
  14        any(feature = "x11", feature = "wayland")
  15    ),
  16    target_os = "windows",
  17    feature = "macos-blade"
  18))]
  19mod blade;
  20
  21#[cfg(any(test, feature = "test-support"))]
  22mod test;
  23
  24#[cfg(target_os = "windows")]
  25mod windows;
  26
  27#[cfg(all(
  28    any(target_os = "linux", target_os = "freebsd"),
  29    any(feature = "wayland", feature = "x11"),
  30))]
  31pub(crate) mod scap_screen_capture;
  32
  33use crate::{
  34    Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
  35    DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
  36    ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput,
  37    Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
  38    ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window,
  39    WindowControlArea, hash, point, px, size,
  40};
  41use anyhow::Result;
  42use async_task::Runnable;
  43use futures::channel::oneshot;
  44use image::codecs::gif::GifDecoder;
  45use image::{AnimationDecoder as _, Frame};
  46use parking::Unparker;
  47use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
  48use schemars::JsonSchema;
  49use seahash::SeaHasher;
  50use serde::{Deserialize, Serialize};
  51use smallvec::SmallVec;
  52use std::borrow::Cow;
  53use std::hash::{Hash, Hasher};
  54use std::io::Cursor;
  55use std::ops;
  56use std::time::{Duration, Instant};
  57use std::{
  58    fmt::{self, Debug},
  59    ops::Range,
  60    path::{Path, PathBuf},
  61    rc::Rc,
  62    sync::Arc,
  63};
  64use strum::EnumIter;
  65use uuid::Uuid;
  66
  67pub use app_menu::*;
  68pub use keyboard::*;
  69pub use keystroke::*;
  70
  71#[cfg(any(target_os = "linux", target_os = "freebsd"))]
  72pub(crate) use linux::*;
  73#[cfg(target_os = "macos")]
  74pub(crate) use mac::*;
  75pub use semantic_version::SemanticVersion;
  76#[cfg(any(test, feature = "test-support"))]
  77pub(crate) use test::*;
  78#[cfg(target_os = "windows")]
  79pub(crate) use windows::*;
  80
  81#[cfg(any(test, feature = "test-support"))]
  82pub use test::{TestDispatcher, TestScreenCaptureSource};
  83
  84/// Returns a background executor for the current platform.
  85pub fn background_executor() -> BackgroundExecutor {
  86    current_platform(true).background_executor()
  87}
  88
  89#[cfg(target_os = "macos")]
  90pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
  91    Rc::new(MacPlatform::new(headless))
  92}
  93
  94#[cfg(any(target_os = "linux", target_os = "freebsd"))]
  95pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
  96    if headless {
  97        return Rc::new(HeadlessClient::new());
  98    }
  99
 100    match guess_compositor() {
 101        #[cfg(feature = "wayland")]
 102        "Wayland" => Rc::new(WaylandClient::new()),
 103
 104        #[cfg(feature = "x11")]
 105        "X11" => Rc::new(X11Client::new()),
 106
 107        "Headless" => Rc::new(HeadlessClient::new()),
 108        _ => unreachable!(),
 109    }
 110}
 111
 112/// Return which compositor we're guessing we'll use.
 113/// Does not attempt to connect to the given compositor
 114#[cfg(any(target_os = "linux", target_os = "freebsd"))]
 115#[inline]
 116pub fn guess_compositor() -> &'static str {
 117    if std::env::var_os("ZED_HEADLESS").is_some() {
 118        return "Headless";
 119    }
 120
 121    #[cfg(feature = "wayland")]
 122    let wayland_display = std::env::var_os("WAYLAND_DISPLAY");
 123    #[cfg(not(feature = "wayland"))]
 124    let wayland_display: Option<std::ffi::OsString> = None;
 125
 126    #[cfg(feature = "x11")]
 127    let x11_display = std::env::var_os("DISPLAY");
 128    #[cfg(not(feature = "x11"))]
 129    let x11_display: Option<std::ffi::OsString> = None;
 130
 131    let use_wayland = wayland_display.is_some_and(|display| !display.is_empty());
 132    let use_x11 = x11_display.is_some_and(|display| !display.is_empty());
 133
 134    if use_wayland {
 135        "Wayland"
 136    } else if use_x11 {
 137        "X11"
 138    } else {
 139        "Headless"
 140    }
 141}
 142
 143#[cfg(target_os = "windows")]
 144pub(crate) fn current_platform(_headless: bool) -> Rc<dyn Platform> {
 145    Rc::new(
 146        WindowsPlatform::new()
 147            .inspect_err(|err| show_error("Error: Zed failed to launch", err.to_string()))
 148            .unwrap(),
 149    )
 150}
 151
 152pub(crate) trait Platform: 'static {
 153    fn background_executor(&self) -> BackgroundExecutor;
 154    fn foreground_executor(&self) -> ForegroundExecutor;
 155    fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 156
 157    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
 158    fn quit(&self);
 159    fn restart(&self, binary_path: Option<PathBuf>);
 160    fn activate(&self, ignoring_other_apps: bool);
 161    fn hide(&self);
 162    fn hide_other_apps(&self);
 163    fn unhide_other_apps(&self);
 164
 165    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
 166    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
 167    fn active_window(&self) -> Option<AnyWindowHandle>;
 168    fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
 169        None
 170    }
 171
 172    fn is_screen_capture_supported(&self) -> bool;
 173    fn screen_capture_sources(
 174        &self,
 175    ) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>>;
 176
 177    fn open_window(
 178        &self,
 179        handle: AnyWindowHandle,
 180        options: WindowParams,
 181    ) -> anyhow::Result<Box<dyn PlatformWindow>>;
 182
 183    /// Returns the appearance of the application's windows.
 184    fn window_appearance(&self) -> WindowAppearance;
 185
 186    fn open_url(&self, url: &str);
 187    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
 188    fn register_url_scheme(&self, url: &str) -> Task<Result<()>>;
 189
 190    fn prompt_for_paths(
 191        &self,
 192        options: PathPromptOptions,
 193    ) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>>;
 194    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Result<Option<PathBuf>>>;
 195    fn can_select_mixed_files_and_dirs(&self) -> bool;
 196    fn reveal_path(&self, path: &Path);
 197    fn open_with_system(&self, path: &Path);
 198
 199    fn on_quit(&self, callback: Box<dyn FnMut()>);
 200    fn on_reopen(&self, callback: Box<dyn FnMut()>);
 201    fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>);
 202
 203    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
 204    fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
 205        None
 206    }
 207
 208    fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap);
 209    fn perform_dock_menu_action(&self, _action: usize) {}
 210    fn add_recent_document(&self, _path: &Path) {}
 211    fn update_jump_list(
 212        &self,
 213        _menus: Vec<MenuItem>,
 214        _entries: Vec<SmallVec<[PathBuf; 2]>>,
 215    ) -> Vec<SmallVec<[PathBuf; 2]>> {
 216        Vec::new()
 217    }
 218    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
 219    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
 220    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
 221    fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
 222
 223    fn compositor_name(&self) -> &'static str {
 224        ""
 225    }
 226    fn app_path(&self) -> Result<PathBuf>;
 227    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
 228
 229    fn set_cursor_style(&self, style: CursorStyle);
 230    fn should_auto_hide_scrollbars(&self) -> bool;
 231
 232    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 233    fn write_to_primary(&self, item: ClipboardItem);
 234    fn write_to_clipboard(&self, item: ClipboardItem);
 235    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 236    fn read_from_primary(&self) -> Option<ClipboardItem>;
 237    fn read_from_clipboard(&self) -> Option<ClipboardItem>;
 238
 239    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>>;
 240    fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>>;
 241    fn delete_credentials(&self, url: &str) -> Task<Result<()>>;
 242}
 243
 244/// A handle to a platform's display, e.g. a monitor or laptop screen.
 245pub trait PlatformDisplay: Send + Sync + Debug {
 246    /// Get the ID for this display
 247    fn id(&self) -> DisplayId;
 248
 249    /// Returns a stable identifier for this display that can be persisted and used
 250    /// across system restarts.
 251    fn uuid(&self) -> Result<Uuid>;
 252
 253    /// Get the bounds for this display
 254    fn bounds(&self) -> Bounds<Pixels>;
 255
 256    /// Get the default bounds for this display to place a window
 257    fn default_bounds(&self) -> Bounds<Pixels> {
 258        let center = self.bounds().center();
 259        let offset = DEFAULT_WINDOW_SIZE / 2.0;
 260        let origin = point(center.x - offset.width, center.y - offset.height);
 261        Bounds::new(origin, DEFAULT_WINDOW_SIZE)
 262    }
 263}
 264
 265/// A source of on-screen video content that can be captured.
 266pub trait ScreenCaptureSource {
 267    /// Returns the video resolution of this source.
 268    fn resolution(&self) -> Result<Size<DevicePixels>>;
 269
 270    /// Start capture video from this source, invoking the given callback
 271    /// with each frame.
 272    fn stream(
 273        &self,
 274        foreground_executor: &ForegroundExecutor,
 275        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
 276    ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>>;
 277}
 278
 279/// A video stream captured from a screen.
 280pub trait ScreenCaptureStream {}
 281
 282/// A frame of video captured from a screen.
 283pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
 284
 285/// An opaque identifier for a hardware display
 286#[derive(PartialEq, Eq, Hash, Copy, Clone)]
 287pub struct DisplayId(pub(crate) u32);
 288
 289impl From<DisplayId> for u32 {
 290    fn from(id: DisplayId) -> Self {
 291        id.0
 292    }
 293}
 294
 295impl Debug for DisplayId {
 296    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 297        write!(f, "DisplayId({})", self.0)
 298    }
 299}
 300
 301unsafe impl Send for DisplayId {}
 302
 303/// Which part of the window to resize
 304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 305pub enum ResizeEdge {
 306    /// The top edge
 307    Top,
 308    /// The top right corner
 309    TopRight,
 310    /// The right edge
 311    Right,
 312    /// The bottom right corner
 313    BottomRight,
 314    /// The bottom edge
 315    Bottom,
 316    /// The bottom left corner
 317    BottomLeft,
 318    /// The left edge
 319    Left,
 320    /// The top left corner
 321    TopLeft,
 322}
 323
 324/// A type to describe the appearance of a window
 325#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
 326pub enum WindowDecorations {
 327    #[default]
 328    /// Server side decorations
 329    Server,
 330    /// Client side decorations
 331    Client,
 332}
 333
 334/// A type to describe how this window is currently configured
 335#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
 336pub enum Decorations {
 337    /// The window is configured to use server side decorations
 338    #[default]
 339    Server,
 340    /// The window is configured to use client side decorations
 341    Client {
 342        /// The edge tiling state
 343        tiling: Tiling,
 344    },
 345}
 346
 347/// What window controls this platform supports
 348#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 349pub struct WindowControls {
 350    /// Whether this platform supports fullscreen
 351    pub fullscreen: bool,
 352    /// Whether this platform supports maximize
 353    pub maximize: bool,
 354    /// Whether this platform supports minimize
 355    pub minimize: bool,
 356    /// Whether this platform supports a window menu
 357    pub window_menu: bool,
 358}
 359
 360impl Default for WindowControls {
 361    fn default() -> Self {
 362        // Assume that we can do anything, unless told otherwise
 363        Self {
 364            fullscreen: true,
 365            maximize: true,
 366            minimize: true,
 367            window_menu: true,
 368        }
 369    }
 370}
 371
 372/// A type to describe which sides of the window are currently tiled in some way
 373#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
 374pub struct Tiling {
 375    /// Whether the top edge is tiled
 376    pub top: bool,
 377    /// Whether the left edge is tiled
 378    pub left: bool,
 379    /// Whether the right edge is tiled
 380    pub right: bool,
 381    /// Whether the bottom edge is tiled
 382    pub bottom: bool,
 383}
 384
 385impl Tiling {
 386    /// Initializes a [`Tiling`] type with all sides tiled
 387    pub fn tiled() -> Self {
 388        Self {
 389            top: true,
 390            left: true,
 391            right: true,
 392            bottom: true,
 393        }
 394    }
 395
 396    /// Whether any edge is tiled
 397    pub fn is_tiled(&self) -> bool {
 398        self.top || self.left || self.right || self.bottom
 399    }
 400}
 401
 402#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
 403pub(crate) struct RequestFrameOptions {
 404    pub(crate) require_presentation: bool,
 405}
 406
 407pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
 408    fn bounds(&self) -> Bounds<Pixels>;
 409    fn is_maximized(&self) -> bool;
 410    fn window_bounds(&self) -> WindowBounds;
 411    fn content_size(&self) -> Size<Pixels>;
 412    fn resize(&mut self, size: Size<Pixels>);
 413    fn scale_factor(&self) -> f32;
 414    fn appearance(&self) -> WindowAppearance;
 415    fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
 416    fn mouse_position(&self) -> Point<Pixels>;
 417    fn modifiers(&self) -> Modifiers;
 418    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
 419    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
 420    fn prompt(
 421        &self,
 422        level: PromptLevel,
 423        msg: &str,
 424        detail: Option<&str>,
 425        answers: &[PromptButton],
 426    ) -> Option<oneshot::Receiver<usize>>;
 427    fn activate(&self);
 428    fn is_active(&self) -> bool;
 429    fn is_hovered(&self) -> bool;
 430    fn set_title(&mut self, title: &str);
 431    fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance);
 432    fn minimize(&self);
 433    fn zoom(&self);
 434    fn toggle_fullscreen(&self);
 435    fn is_fullscreen(&self) -> bool;
 436    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>);
 437    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>);
 438    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
 439    fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>);
 440    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
 441    fn on_moved(&self, callback: Box<dyn FnMut()>);
 442    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
 443    fn on_hit_test_window_control(&self, callback: Box<dyn FnMut() -> Option<WindowControlArea>>);
 444    fn on_close(&self, callback: Box<dyn FnOnce()>);
 445    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
 446    fn draw(&self, scene: &Scene);
 447    fn completed_frame(&self) {}
 448    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
 449
 450    // macOS specific methods
 451    fn set_edited(&mut self, _edited: bool) {}
 452    fn show_character_palette(&self) {}
 453    fn titlebar_double_click(&self) {}
 454
 455    #[cfg(target_os = "windows")]
 456    fn get_raw_handle(&self) -> windows::HWND;
 457
 458    // Linux specific methods
 459    fn inner_window_bounds(&self) -> WindowBounds {
 460        self.window_bounds()
 461    }
 462    fn request_decorations(&self, _decorations: WindowDecorations) {}
 463    fn show_window_menu(&self, _position: Point<Pixels>) {}
 464    fn start_window_move(&self) {}
 465    fn start_window_resize(&self, _edge: ResizeEdge) {}
 466    fn window_decorations(&self) -> Decorations {
 467        Decorations::Server
 468    }
 469    fn set_app_id(&mut self, _app_id: &str) {}
 470    fn map_window(&mut self) -> anyhow::Result<()> {
 471        Ok(())
 472    }
 473    fn window_controls(&self) -> WindowControls {
 474        WindowControls::default()
 475    }
 476    fn set_client_inset(&self, _inset: Pixels) {}
 477    fn gpu_specs(&self) -> Option<GpuSpecs>;
 478
 479    fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>);
 480
 481    #[cfg(any(test, feature = "test-support"))]
 482    fn as_test(&mut self) -> Option<&mut TestWindow> {
 483        None
 484    }
 485}
 486
 487/// This type is public so that our test macro can generate and use it, but it should not
 488/// be considered part of our public API.
 489#[doc(hidden)]
 490pub trait PlatformDispatcher: Send + Sync {
 491    fn is_main_thread(&self) -> bool;
 492    fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
 493    fn dispatch_on_main_thread(&self, runnable: Runnable);
 494    fn dispatch_after(&self, duration: Duration, runnable: Runnable);
 495    fn park(&self, timeout: Option<Duration>) -> bool;
 496    fn unparker(&self) -> Unparker;
 497    fn now(&self) -> Instant {
 498        Instant::now()
 499    }
 500
 501    #[cfg(any(test, feature = "test-support"))]
 502    fn as_test(&self) -> Option<&TestDispatcher> {
 503        None
 504    }
 505}
 506
 507pub(crate) trait PlatformTextSystem: Send + Sync {
 508    fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()>;
 509    fn all_font_names(&self) -> Vec<String>;
 510    fn font_id(&self, descriptor: &Font) -> Result<FontId>;
 511    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
 512    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
 513    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
 514    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
 515    fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
 516    fn rasterize_glyph(
 517        &self,
 518        params: &RenderGlyphParams,
 519        raster_bounds: Bounds<DevicePixels>,
 520    ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
 521    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
 522}
 523
 524pub(crate) struct NoopTextSystem;
 525
 526impl NoopTextSystem {
 527    #[allow(dead_code)]
 528    pub fn new() -> Self {
 529        Self
 530    }
 531}
 532
 533impl PlatformTextSystem for NoopTextSystem {
 534    fn add_fonts(&self, _fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
 535        Ok(())
 536    }
 537
 538    fn all_font_names(&self) -> Vec<String> {
 539        Vec::new()
 540    }
 541
 542    fn font_id(&self, _descriptor: &Font) -> Result<FontId> {
 543        return Ok(FontId(1));
 544    }
 545
 546    fn font_metrics(&self, _font_id: FontId) -> FontMetrics {
 547        FontMetrics {
 548            units_per_em: 1000,
 549            ascent: 1025.0,
 550            descent: -275.0,
 551            line_gap: 0.0,
 552            underline_position: -95.0,
 553            underline_thickness: 60.0,
 554            cap_height: 698.0,
 555            x_height: 516.0,
 556            bounding_box: Bounds {
 557                origin: Point {
 558                    x: -260.0,
 559                    y: -245.0,
 560                },
 561                size: Size {
 562                    width: 1501.0,
 563                    height: 1364.0,
 564                },
 565            },
 566        }
 567    }
 568
 569    fn typographic_bounds(&self, _font_id: FontId, _glyph_id: GlyphId) -> Result<Bounds<f32>> {
 570        Ok(Bounds {
 571            origin: Point { x: 54.0, y: 0.0 },
 572            size: size(392.0, 528.0),
 573        })
 574    }
 575
 576    fn advance(&self, _font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
 577        Ok(size(600.0 * glyph_id.0 as f32, 0.0))
 578    }
 579
 580    fn glyph_for_char(&self, _font_id: FontId, ch: char) -> Option<GlyphId> {
 581        Some(GlyphId(ch.len_utf16() as u32))
 582    }
 583
 584    fn glyph_raster_bounds(&self, _params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
 585        Ok(Default::default())
 586    }
 587
 588    fn rasterize_glyph(
 589        &self,
 590        _params: &RenderGlyphParams,
 591        raster_bounds: Bounds<DevicePixels>,
 592    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
 593        Ok((raster_bounds.size, Vec::new()))
 594    }
 595
 596    fn layout_line(&self, text: &str, font_size: Pixels, _runs: &[FontRun]) -> LineLayout {
 597        let mut position = px(0.);
 598        let metrics = self.font_metrics(FontId(0));
 599        let em_width = font_size
 600            * self
 601                .advance(FontId(0), self.glyph_for_char(FontId(0), 'm').unwrap())
 602                .unwrap()
 603                .width
 604            / metrics.units_per_em as f32;
 605        let mut glyphs = Vec::new();
 606        for (ix, c) in text.char_indices() {
 607            if let Some(glyph) = self.glyph_for_char(FontId(0), c) {
 608                glyphs.push(ShapedGlyph {
 609                    id: glyph,
 610                    position: point(position, px(0.)),
 611                    index: ix,
 612                    is_emoji: glyph.0 == 2,
 613                });
 614                if glyph.0 == 2 {
 615                    position += em_width * 2.0;
 616                } else {
 617                    position += em_width;
 618                }
 619            } else {
 620                position += em_width
 621            }
 622        }
 623        let mut runs = Vec::default();
 624        if glyphs.len() > 0 {
 625            runs.push(ShapedRun {
 626                font_id: FontId(0),
 627                glyphs,
 628            });
 629        } else {
 630            position = px(0.);
 631        }
 632
 633        LineLayout {
 634            font_size,
 635            width: position,
 636            ascent: font_size * (metrics.ascent / metrics.units_per_em as f32),
 637            descent: font_size * (metrics.descent / metrics.units_per_em as f32),
 638            runs,
 639            len: text.len(),
 640        }
 641    }
 642}
 643
 644#[derive(PartialEq, Eq, Hash, Clone)]
 645pub(crate) enum AtlasKey {
 646    Glyph(RenderGlyphParams),
 647    Svg(RenderSvgParams),
 648    Image(RenderImageParams),
 649}
 650
 651impl AtlasKey {
 652    #[cfg_attr(
 653        all(
 654            any(target_os = "linux", target_os = "freebsd"),
 655            not(any(feature = "x11", feature = "wayland"))
 656        ),
 657        allow(dead_code)
 658    )]
 659    pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
 660        match self {
 661            AtlasKey::Glyph(params) => {
 662                if params.is_emoji {
 663                    AtlasTextureKind::Polychrome
 664                } else {
 665                    AtlasTextureKind::Monochrome
 666                }
 667            }
 668            AtlasKey::Svg(_) => AtlasTextureKind::Monochrome,
 669            AtlasKey::Image(_) => AtlasTextureKind::Polychrome,
 670        }
 671    }
 672}
 673
 674impl From<RenderGlyphParams> for AtlasKey {
 675    fn from(params: RenderGlyphParams) -> Self {
 676        Self::Glyph(params)
 677    }
 678}
 679
 680impl From<RenderSvgParams> for AtlasKey {
 681    fn from(params: RenderSvgParams) -> Self {
 682        Self::Svg(params)
 683    }
 684}
 685
 686impl From<RenderImageParams> for AtlasKey {
 687    fn from(params: RenderImageParams) -> Self {
 688        Self::Image(params)
 689    }
 690}
 691
 692pub(crate) trait PlatformAtlas: Send + Sync {
 693    fn get_or_insert_with<'a>(
 694        &self,
 695        key: &AtlasKey,
 696        build: &mut dyn FnMut() -> Result<Option<(Size<DevicePixels>, Cow<'a, [u8]>)>>,
 697    ) -> Result<Option<AtlasTile>>;
 698    fn remove(&self, key: &AtlasKey);
 699}
 700
 701struct AtlasTextureList<T> {
 702    textures: Vec<Option<T>>,
 703    free_list: Vec<usize>,
 704}
 705
 706impl<T> Default for AtlasTextureList<T> {
 707    fn default() -> Self {
 708        Self {
 709            textures: Vec::default(),
 710            free_list: Vec::default(),
 711        }
 712    }
 713}
 714
 715impl<T> ops::Index<usize> for AtlasTextureList<T> {
 716    type Output = Option<T>;
 717
 718    fn index(&self, index: usize) -> &Self::Output {
 719        &self.textures[index]
 720    }
 721}
 722
 723impl<T> AtlasTextureList<T> {
 724    #[allow(unused)]
 725    fn drain(&mut self) -> std::vec::Drain<'_, Option<T>> {
 726        self.free_list.clear();
 727        self.textures.drain(..)
 728    }
 729
 730    #[allow(dead_code)]
 731    fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
 732        self.textures.iter_mut().flatten()
 733    }
 734}
 735
 736#[derive(Clone, Debug, PartialEq, Eq)]
 737#[repr(C)]
 738pub(crate) struct AtlasTile {
 739    pub(crate) texture_id: AtlasTextureId,
 740    pub(crate) tile_id: TileId,
 741    pub(crate) padding: u32,
 742    pub(crate) bounds: Bounds<DevicePixels>,
 743}
 744
 745#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 746#[repr(C)]
 747pub(crate) struct AtlasTextureId {
 748    // We use u32 instead of usize for Metal Shader Language compatibility
 749    pub(crate) index: u32,
 750    pub(crate) kind: AtlasTextureKind,
 751}
 752
 753#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 754#[repr(C)]
 755#[cfg_attr(
 756    all(
 757        any(target_os = "linux", target_os = "freebsd"),
 758        not(any(feature = "x11", feature = "wayland"))
 759    ),
 760    allow(dead_code)
 761)]
 762pub(crate) enum AtlasTextureKind {
 763    Monochrome = 0,
 764    Polychrome = 1,
 765    Path = 2,
 766}
 767
 768#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 769#[repr(C)]
 770pub(crate) struct TileId(pub(crate) u32);
 771
 772impl From<etagere::AllocId> for TileId {
 773    fn from(id: etagere::AllocId) -> Self {
 774        Self(id.serialize())
 775    }
 776}
 777
 778impl From<TileId> for etagere::AllocId {
 779    fn from(id: TileId) -> Self {
 780        Self::deserialize(id.0)
 781    }
 782}
 783
 784pub(crate) struct PlatformInputHandler {
 785    cx: AsyncWindowContext,
 786    handler: Box<dyn InputHandler>,
 787}
 788
 789#[cfg_attr(
 790    all(
 791        any(target_os = "linux", target_os = "freebsd"),
 792        not(any(feature = "x11", feature = "wayland"))
 793    ),
 794    allow(dead_code)
 795)]
 796impl PlatformInputHandler {
 797    pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
 798        Self { cx, handler }
 799    }
 800
 801    fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection> {
 802        self.cx
 803            .update(|window, cx| {
 804                self.handler
 805                    .selected_text_range(ignore_disabled_input, window, cx)
 806            })
 807            .ok()
 808            .flatten()
 809    }
 810
 811    #[cfg_attr(target_os = "windows", allow(dead_code))]
 812    fn marked_text_range(&mut self) -> Option<Range<usize>> {
 813        self.cx
 814            .update(|window, cx| self.handler.marked_text_range(window, cx))
 815            .ok()
 816            .flatten()
 817    }
 818
 819    #[cfg_attr(
 820        any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
 821        allow(dead_code)
 822    )]
 823    fn text_for_range(
 824        &mut self,
 825        range_utf16: Range<usize>,
 826        adjusted: &mut Option<Range<usize>>,
 827    ) -> Option<String> {
 828        self.cx
 829            .update(|window, cx| {
 830                self.handler
 831                    .text_for_range(range_utf16, adjusted, window, cx)
 832            })
 833            .ok()
 834            .flatten()
 835    }
 836
 837    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
 838        self.cx
 839            .update(|window, cx| {
 840                self.handler
 841                    .replace_text_in_range(replacement_range, text, window, cx);
 842            })
 843            .ok();
 844    }
 845
 846    pub fn replace_and_mark_text_in_range(
 847        &mut self,
 848        range_utf16: Option<Range<usize>>,
 849        new_text: &str,
 850        new_selected_range: Option<Range<usize>>,
 851    ) {
 852        self.cx
 853            .update(|window, cx| {
 854                self.handler.replace_and_mark_text_in_range(
 855                    range_utf16,
 856                    new_text,
 857                    new_selected_range,
 858                    window,
 859                    cx,
 860                )
 861            })
 862            .ok();
 863    }
 864
 865    #[cfg_attr(target_os = "windows", allow(dead_code))]
 866    fn unmark_text(&mut self) {
 867        self.cx
 868            .update(|window, cx| self.handler.unmark_text(window, cx))
 869            .ok();
 870    }
 871
 872    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
 873        self.cx
 874            .update(|window, cx| self.handler.bounds_for_range(range_utf16, window, cx))
 875            .ok()
 876            .flatten()
 877    }
 878
 879    #[allow(dead_code)]
 880    fn apple_press_and_hold_enabled(&mut self) -> bool {
 881        self.handler.apple_press_and_hold_enabled()
 882    }
 883
 884    pub(crate) fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) {
 885        self.handler.replace_text_in_range(None, input, window, cx);
 886    }
 887
 888    pub fn selected_bounds(&mut self, window: &mut Window, cx: &mut App) -> Option<Bounds<Pixels>> {
 889        let selection = self.handler.selected_text_range(true, window, cx)?;
 890        self.handler.bounds_for_range(
 891            if selection.reversed {
 892                selection.range.start..selection.range.start
 893            } else {
 894                selection.range.end..selection.range.end
 895            },
 896            window,
 897            cx,
 898        )
 899    }
 900
 901    #[allow(unused)]
 902    pub fn character_index_for_point(&mut self, point: Point<Pixels>) -> Option<usize> {
 903        self.cx
 904            .update(|window, cx| self.handler.character_index_for_point(point, window, cx))
 905            .ok()
 906            .flatten()
 907    }
 908}
 909
 910/// A struct representing a selection in a text buffer, in UTF16 characters.
 911/// This is different from a range because the head may be before the tail.
 912#[derive(Debug)]
 913pub struct UTF16Selection {
 914    /// The range of text in the document this selection corresponds to
 915    /// in UTF16 characters.
 916    pub range: Range<usize>,
 917    /// Whether the head of this selection is at the start (true), or end (false)
 918    /// of the range
 919    pub reversed: bool,
 920}
 921
 922/// Zed's interface for handling text input from the platform's IME system
 923/// This is currently a 1:1 exposure of the NSTextInputClient API:
 924///
 925/// <https://developer.apple.com/documentation/appkit/nstextinputclient>
 926pub trait InputHandler: 'static {
 927    /// Get the range of the user's currently selected text, if any
 928    /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange)
 929    ///
 930    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
 931    fn selected_text_range(
 932        &mut self,
 933        ignore_disabled_input: bool,
 934        window: &mut Window,
 935        cx: &mut App,
 936    ) -> Option<UTF16Selection>;
 937
 938    /// Get the range of the currently marked text, if any
 939    /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange)
 940    ///
 941    /// Return value is in terms of UTF-16 characters, from 0 to the length of the document
 942    fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option<Range<usize>>;
 943
 944    /// Get the text for the given document range in UTF-16 characters
 945    /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring)
 946    ///
 947    /// range_utf16 is in terms of UTF-16 characters
 948    fn text_for_range(
 949        &mut self,
 950        range_utf16: Range<usize>,
 951        adjusted_range: &mut Option<Range<usize>>,
 952        window: &mut Window,
 953        cx: &mut App,
 954    ) -> Option<String>;
 955
 956    /// Replace the text in the given document range with the given text
 957    /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext)
 958    ///
 959    /// replacement_range is in terms of UTF-16 characters
 960    fn replace_text_in_range(
 961        &mut self,
 962        replacement_range: Option<Range<usize>>,
 963        text: &str,
 964        window: &mut Window,
 965        cx: &mut App,
 966    );
 967
 968    /// Replace the text in the given document range with the given text,
 969    /// and mark the given text as part of an IME 'composing' state
 970    /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext)
 971    ///
 972    /// range_utf16 is in terms of UTF-16 characters
 973    /// new_selected_range is in terms of UTF-16 characters
 974    fn replace_and_mark_text_in_range(
 975        &mut self,
 976        range_utf16: Option<Range<usize>>,
 977        new_text: &str,
 978        new_selected_range: Option<Range<usize>>,
 979        window: &mut Window,
 980        cx: &mut App,
 981    );
 982
 983    /// Remove the IME 'composing' state from the document
 984    /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext)
 985    fn unmark_text(&mut self, window: &mut Window, cx: &mut App);
 986
 987    /// Get the bounds of the given document range in screen coordinates
 988    /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect)
 989    ///
 990    /// This is used for positioning the IME candidate window
 991    fn bounds_for_range(
 992        &mut self,
 993        range_utf16: Range<usize>,
 994        window: &mut Window,
 995        cx: &mut App,
 996    ) -> Option<Bounds<Pixels>>;
 997
 998    /// Get the character offset for the given point in terms of UTF16 characters
 999    ///
1000    /// Corresponds to [characterIndexForPoint:](https://developer.apple.com/documentation/appkit/nstextinputclient/characterindex(for:))
1001    fn character_index_for_point(
1002        &mut self,
1003        point: Point<Pixels>,
1004        window: &mut Window,
1005        cx: &mut App,
1006    ) -> Option<usize>;
1007
1008    /// Allows a given input context to opt into getting raw key repeats instead of
1009    /// sending these to the platform.
1010    /// TODO: Ideally we should be able to set ApplePressAndHoldEnabled in NSUserDefaults
1011    /// (which is how iTerm does it) but it doesn't seem to work for me.
1012    #[allow(dead_code)]
1013    fn apple_press_and_hold_enabled(&mut self) -> bool {
1014        true
1015    }
1016}
1017
1018/// The variables that can be configured when creating a new window
1019#[derive(Debug)]
1020pub struct WindowOptions {
1021    /// Specifies the state and bounds of the window in screen coordinates.
1022    /// - `None`: Inherit the bounds.
1023    /// - `Some(WindowBounds)`: Open a window with corresponding state and its restore size.
1024    pub window_bounds: Option<WindowBounds>,
1025
1026    /// The titlebar configuration of the window
1027    pub titlebar: Option<TitlebarOptions>,
1028
1029    /// Whether the window should be focused when created
1030    pub focus: bool,
1031
1032    /// Whether the window should be shown when created
1033    pub show: bool,
1034
1035    /// The kind of window to create
1036    pub kind: WindowKind,
1037
1038    /// Whether the window should be movable by the user
1039    pub is_movable: bool,
1040
1041    /// The display to create the window on, if this is None,
1042    /// the window will be created on the main display
1043    pub display_id: Option<DisplayId>,
1044
1045    /// The appearance of the window background.
1046    pub window_background: WindowBackgroundAppearance,
1047
1048    /// Application identifier of the window. Can by used by desktop environments to group applications together.
1049    pub app_id: Option<String>,
1050
1051    /// Window minimum size
1052    pub window_min_size: Option<Size<Pixels>>,
1053
1054    /// Whether to use client or server side decorations. Wayland only
1055    /// Note that this may be ignored.
1056    pub window_decorations: Option<WindowDecorations>,
1057}
1058
1059/// The variables that can be configured when creating a new window
1060#[derive(Debug)]
1061#[cfg_attr(
1062    all(
1063        any(target_os = "linux", target_os = "freebsd"),
1064        not(any(feature = "x11", feature = "wayland"))
1065    ),
1066    allow(dead_code)
1067)]
1068pub(crate) struct WindowParams {
1069    pub bounds: Bounds<Pixels>,
1070
1071    /// The titlebar configuration of the window
1072    #[cfg_attr(feature = "wayland", allow(dead_code))]
1073    pub titlebar: Option<TitlebarOptions>,
1074
1075    /// The kind of window to create
1076    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1077    pub kind: WindowKind,
1078
1079    /// Whether the window should be movable by the user
1080    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1081    pub is_movable: bool,
1082
1083    #[cfg_attr(
1084        any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
1085        allow(dead_code)
1086    )]
1087    pub focus: bool,
1088
1089    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1090    pub show: bool,
1091
1092    #[cfg_attr(feature = "wayland", allow(dead_code))]
1093    pub display_id: Option<DisplayId>,
1094
1095    pub window_min_size: Option<Size<Pixels>>,
1096}
1097
1098/// Represents the status of how a window should be opened.
1099#[derive(Debug, Copy, Clone, PartialEq)]
1100pub enum WindowBounds {
1101    /// Indicates that the window should open in a windowed state with the given bounds.
1102    Windowed(Bounds<Pixels>),
1103    /// Indicates that the window should open in a maximized state.
1104    /// The bounds provided here represent the restore size of the window.
1105    Maximized(Bounds<Pixels>),
1106    /// Indicates that the window should open in fullscreen mode.
1107    /// The bounds provided here represent the restore size of the window.
1108    Fullscreen(Bounds<Pixels>),
1109}
1110
1111impl Default for WindowBounds {
1112    fn default() -> Self {
1113        WindowBounds::Windowed(Bounds::default())
1114    }
1115}
1116
1117impl WindowBounds {
1118    /// Retrieve the inner bounds
1119    pub fn get_bounds(&self) -> Bounds<Pixels> {
1120        match self {
1121            WindowBounds::Windowed(bounds) => *bounds,
1122            WindowBounds::Maximized(bounds) => *bounds,
1123            WindowBounds::Fullscreen(bounds) => *bounds,
1124        }
1125    }
1126}
1127
1128impl Default for WindowOptions {
1129    fn default() -> Self {
1130        Self {
1131            window_bounds: None,
1132            titlebar: Some(TitlebarOptions {
1133                title: Default::default(),
1134                appears_transparent: Default::default(),
1135                traffic_light_position: Default::default(),
1136            }),
1137            focus: true,
1138            show: true,
1139            kind: WindowKind::Normal,
1140            is_movable: true,
1141            display_id: None,
1142            window_background: WindowBackgroundAppearance::default(),
1143            app_id: None,
1144            window_min_size: None,
1145            window_decorations: None,
1146        }
1147    }
1148}
1149
1150/// The options that can be configured for a window's titlebar
1151#[derive(Debug, Default)]
1152pub struct TitlebarOptions {
1153    /// The initial title of the window
1154    pub title: Option<SharedString>,
1155
1156    /// Should the default system titlebar be hidden to allow for a custom-drawn titlebar? (macOS and Windows only)
1157    /// Refer to [`WindowOptions::window_decorations`] on Linux
1158    pub appears_transparent: bool,
1159
1160    /// The position of the macOS traffic light buttons
1161    pub traffic_light_position: Option<Point<Pixels>>,
1162}
1163
1164/// The kind of window to create
1165#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1166pub enum WindowKind {
1167    /// A normal application window
1168    Normal,
1169
1170    /// A window that appears above all other windows, usually used for alerts or popups
1171    /// use sparingly!
1172    PopUp,
1173}
1174
1175/// The appearance of the window, as defined by the operating system.
1176///
1177/// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance)
1178/// values.
1179#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1180pub enum WindowAppearance {
1181    /// A light appearance.
1182    ///
1183    /// On macOS, this corresponds to the `aqua` appearance.
1184    Light,
1185
1186    /// A light appearance with vibrant colors.
1187    ///
1188    /// On macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance.
1189    VibrantLight,
1190
1191    /// A dark appearance.
1192    ///
1193    /// On macOS, this corresponds to the `darkAqua` appearance.
1194    Dark,
1195
1196    /// A dark appearance with vibrant colors.
1197    ///
1198    /// On macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance.
1199    VibrantDark,
1200}
1201
1202impl Default for WindowAppearance {
1203    fn default() -> Self {
1204        Self::Light
1205    }
1206}
1207
1208/// The appearance of the background of the window itself, when there is
1209/// no content or the content is transparent.
1210#[derive(Copy, Clone, Debug, Default, PartialEq)]
1211pub enum WindowBackgroundAppearance {
1212    /// Opaque.
1213    ///
1214    /// This lets the window manager know that content behind this
1215    /// window does not need to be drawn.
1216    ///
1217    /// Actual color depends on the system and themes should define a fully
1218    /// opaque background color instead.
1219    #[default]
1220    Opaque,
1221    /// Plain alpha transparency.
1222    Transparent,
1223    /// Transparency, but the contents behind the window are blurred.
1224    ///
1225    /// Not always supported.
1226    Blurred,
1227}
1228
1229/// The options that can be configured for a file dialog prompt
1230#[derive(Copy, Clone, Debug)]
1231pub struct PathPromptOptions {
1232    /// Should the prompt allow files to be selected?
1233    pub files: bool,
1234    /// Should the prompt allow directories to be selected?
1235    pub directories: bool,
1236    /// Should the prompt allow multiple files to be selected?
1237    pub multiple: bool,
1238}
1239
1240/// What kind of prompt styling to show
1241#[derive(Copy, Clone, Debug, PartialEq)]
1242pub enum PromptLevel {
1243    /// A prompt that is shown when the user should be notified of something
1244    Info,
1245
1246    /// A prompt that is shown when the user needs to be warned of a potential problem
1247    Warning,
1248
1249    /// A prompt that is shown when a critical problem has occurred
1250    Critical,
1251}
1252
1253/// Prompt Button
1254#[derive(Clone, Debug, PartialEq)]
1255pub enum PromptButton {
1256    /// Ok button
1257    Ok(SharedString),
1258    /// Cancel button
1259    Cancel(SharedString),
1260    /// Other button
1261    Other(SharedString),
1262}
1263
1264impl PromptButton {
1265    /// Create a button with label
1266    pub fn new(label: impl Into<SharedString>) -> Self {
1267        PromptButton::Other(label.into())
1268    }
1269
1270    /// Create an Ok button
1271    pub fn ok(label: impl Into<SharedString>) -> Self {
1272        PromptButton::Ok(label.into())
1273    }
1274
1275    /// Create a Cancel button
1276    pub fn cancel(label: impl Into<SharedString>) -> Self {
1277        PromptButton::Cancel(label.into())
1278    }
1279
1280    #[allow(dead_code)]
1281    pub(crate) fn is_cancel(&self) -> bool {
1282        matches!(self, PromptButton::Cancel(_))
1283    }
1284
1285    /// Returns the label of the button
1286    pub fn label(&self) -> &SharedString {
1287        match self {
1288            PromptButton::Ok(label) => label,
1289            PromptButton::Cancel(label) => label,
1290            PromptButton::Other(label) => label,
1291        }
1292    }
1293}
1294
1295impl From<&str> for PromptButton {
1296    fn from(value: &str) -> Self {
1297        match value.to_lowercase().as_str() {
1298            "ok" => PromptButton::Ok("Ok".into()),
1299            "cancel" => PromptButton::Cancel("Cancel".into()),
1300            _ => PromptButton::Other(SharedString::from(value.to_owned())),
1301        }
1302    }
1303}
1304
1305/// The style of the cursor (pointer)
1306#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
1307pub enum CursorStyle {
1308    /// The default cursor
1309    Arrow,
1310
1311    /// A text input cursor
1312    /// corresponds to the CSS cursor value `text`
1313    IBeam,
1314
1315    /// A crosshair cursor
1316    /// corresponds to the CSS cursor value `crosshair`
1317    Crosshair,
1318
1319    /// A closed hand cursor
1320    /// corresponds to the CSS cursor value `grabbing`
1321    ClosedHand,
1322
1323    /// An open hand cursor
1324    /// corresponds to the CSS cursor value `grab`
1325    OpenHand,
1326
1327    /// A pointing hand cursor
1328    /// corresponds to the CSS cursor value `pointer`
1329    PointingHand,
1330
1331    /// A resize left cursor
1332    /// corresponds to the CSS cursor value `w-resize`
1333    ResizeLeft,
1334
1335    /// A resize right cursor
1336    /// corresponds to the CSS cursor value `e-resize`
1337    ResizeRight,
1338
1339    /// A resize cursor to the left and right
1340    /// corresponds to the CSS cursor value `ew-resize`
1341    ResizeLeftRight,
1342
1343    /// A resize up cursor
1344    /// corresponds to the CSS cursor value `n-resize`
1345    ResizeUp,
1346
1347    /// A resize down cursor
1348    /// corresponds to the CSS cursor value `s-resize`
1349    ResizeDown,
1350
1351    /// A resize cursor directing up and down
1352    /// corresponds to the CSS cursor value `ns-resize`
1353    ResizeUpDown,
1354
1355    /// A resize cursor directing up-left and down-right
1356    /// corresponds to the CSS cursor value `nesw-resize`
1357    ResizeUpLeftDownRight,
1358
1359    /// A resize cursor directing up-right and down-left
1360    /// corresponds to the CSS cursor value `nwse-resize`
1361    ResizeUpRightDownLeft,
1362
1363    /// A cursor indicating that the item/column can be resized horizontally.
1364    /// corresponds to the CSS cursor value `col-resize`
1365    ResizeColumn,
1366
1367    /// A cursor indicating that the item/row can be resized vertically.
1368    /// corresponds to the CSS cursor value `row-resize`
1369    ResizeRow,
1370
1371    /// A text input cursor for vertical layout
1372    /// corresponds to the CSS cursor value `vertical-text`
1373    IBeamCursorForVerticalLayout,
1374
1375    /// A cursor indicating that the operation is not allowed
1376    /// corresponds to the CSS cursor value `not-allowed`
1377    OperationNotAllowed,
1378
1379    /// A cursor indicating that the operation will result in a link
1380    /// corresponds to the CSS cursor value `alias`
1381    DragLink,
1382
1383    /// A cursor indicating that the operation will result in a copy
1384    /// corresponds to the CSS cursor value `copy`
1385    DragCopy,
1386
1387    /// A cursor indicating that the operation will result in a context menu
1388    /// corresponds to the CSS cursor value `context-menu`
1389    ContextualMenu,
1390
1391    /// Hide the cursor
1392    None,
1393}
1394
1395impl Default for CursorStyle {
1396    fn default() -> Self {
1397        Self::Arrow
1398    }
1399}
1400
1401/// A clipboard item that should be copied to the clipboard
1402#[derive(Clone, Debug, Eq, PartialEq)]
1403pub struct ClipboardItem {
1404    entries: Vec<ClipboardEntry>,
1405}
1406
1407/// Either a ClipboardString or a ClipboardImage
1408#[derive(Clone, Debug, Eq, PartialEq)]
1409pub enum ClipboardEntry {
1410    /// A string entry
1411    String(ClipboardString),
1412    /// An image entry
1413    Image(Image),
1414}
1415
1416impl ClipboardItem {
1417    /// Create a new ClipboardItem::String with no associated metadata
1418    pub fn new_string(text: String) -> Self {
1419        Self {
1420            entries: vec![ClipboardEntry::String(ClipboardString::new(text))],
1421        }
1422    }
1423
1424    /// Create a new ClipboardItem::String with the given text and associated metadata
1425    pub fn new_string_with_metadata(text: String, metadata: String) -> Self {
1426        Self {
1427            entries: vec![ClipboardEntry::String(ClipboardString {
1428                text,
1429                metadata: Some(metadata),
1430            })],
1431        }
1432    }
1433
1434    /// Create a new ClipboardItem::String with the given text and associated metadata
1435    pub fn new_string_with_json_metadata<T: Serialize>(text: String, metadata: T) -> Self {
1436        Self {
1437            entries: vec![ClipboardEntry::String(
1438                ClipboardString::new(text).with_json_metadata(metadata),
1439            )],
1440        }
1441    }
1442
1443    /// Create a new ClipboardItem::Image with the given image with no associated metadata
1444    pub fn new_image(image: &Image) -> Self {
1445        Self {
1446            entries: vec![ClipboardEntry::Image(image.clone())],
1447        }
1448    }
1449
1450    /// Concatenates together all the ClipboardString entries in the item.
1451    /// Returns None if there were no ClipboardString entries.
1452    pub fn text(&self) -> Option<String> {
1453        let mut answer = String::new();
1454        let mut any_entries = false;
1455
1456        for entry in self.entries.iter() {
1457            if let ClipboardEntry::String(ClipboardString { text, metadata: _ }) = entry {
1458                answer.push_str(&text);
1459                any_entries = true;
1460            }
1461        }
1462
1463        if any_entries { Some(answer) } else { None }
1464    }
1465
1466    /// If this item is one ClipboardEntry::String, returns its metadata.
1467    #[cfg_attr(not(target_os = "windows"), allow(dead_code))]
1468    pub fn metadata(&self) -> Option<&String> {
1469        match self.entries().first() {
1470            Some(ClipboardEntry::String(clipboard_string)) if self.entries.len() == 1 => {
1471                clipboard_string.metadata.as_ref()
1472            }
1473            _ => None,
1474        }
1475    }
1476
1477    /// Get the item's entries
1478    pub fn entries(&self) -> &[ClipboardEntry] {
1479        &self.entries
1480    }
1481
1482    /// Get owned versions of the item's entries
1483    pub fn into_entries(self) -> impl Iterator<Item = ClipboardEntry> {
1484        self.entries.into_iter()
1485    }
1486}
1487
1488impl From<ClipboardString> for ClipboardEntry {
1489    fn from(value: ClipboardString) -> Self {
1490        Self::String(value)
1491    }
1492}
1493
1494impl From<String> for ClipboardEntry {
1495    fn from(value: String) -> Self {
1496        Self::from(ClipboardString::from(value))
1497    }
1498}
1499
1500impl From<Image> for ClipboardEntry {
1501    fn from(value: Image) -> Self {
1502        Self::Image(value)
1503    }
1504}
1505
1506impl From<ClipboardEntry> for ClipboardItem {
1507    fn from(value: ClipboardEntry) -> Self {
1508        Self {
1509            entries: vec![value],
1510        }
1511    }
1512}
1513
1514impl From<String> for ClipboardItem {
1515    fn from(value: String) -> Self {
1516        Self::from(ClipboardEntry::from(value))
1517    }
1518}
1519
1520impl From<Image> for ClipboardItem {
1521    fn from(value: Image) -> Self {
1522        Self::from(ClipboardEntry::from(value))
1523    }
1524}
1525
1526/// One of the editor's supported image formats (e.g. PNG, JPEG) - used when dealing with images in the clipboard
1527#[derive(Clone, Copy, Debug, Eq, PartialEq, EnumIter, Hash)]
1528pub enum ImageFormat {
1529    // Sorted from most to least likely to be pasted into an editor,
1530    // which matters when we iterate through them trying to see if
1531    // clipboard content matches them.
1532    /// .png
1533    Png,
1534    /// .jpeg or .jpg
1535    Jpeg,
1536    /// .webp
1537    Webp,
1538    /// .gif
1539    Gif,
1540    /// .svg
1541    Svg,
1542    /// .bmp
1543    Bmp,
1544    /// .tif or .tiff
1545    Tiff,
1546}
1547
1548impl ImageFormat {
1549    /// Returns the mime type for the ImageFormat
1550    pub const fn mime_type(self) -> &'static str {
1551        match self {
1552            ImageFormat::Png => "image/png",
1553            ImageFormat::Jpeg => "image/jpeg",
1554            ImageFormat::Webp => "image/webp",
1555            ImageFormat::Gif => "image/gif",
1556            ImageFormat::Svg => "image/svg+xml",
1557            ImageFormat::Bmp => "image/bmp",
1558            ImageFormat::Tiff => "image/tiff",
1559        }
1560    }
1561
1562    /// Returns the ImageFormat for the given mime type
1563    pub fn from_mime_type(mime_type: &str) -> Option<Self> {
1564        match mime_type {
1565            "image/png" => Some(Self::Png),
1566            "image/jpeg" | "image/jpg" => Some(Self::Jpeg),
1567            "image/webp" => Some(Self::Webp),
1568            "image/gif" => Some(Self::Gif),
1569            "image/svg+xml" => Some(Self::Svg),
1570            "image/bmp" => Some(Self::Bmp),
1571            "image/tiff" | "image/tif" => Some(Self::Tiff),
1572            _ => None,
1573        }
1574    }
1575}
1576
1577/// An image, with a format and certain bytes
1578#[derive(Clone, Debug, PartialEq, Eq)]
1579pub struct Image {
1580    /// The image format the bytes represent (e.g. PNG)
1581    pub format: ImageFormat,
1582    /// The raw image bytes
1583    pub bytes: Vec<u8>,
1584    /// The unique ID for the image
1585    id: u64,
1586}
1587
1588impl Hash for Image {
1589    fn hash<H: Hasher>(&self, state: &mut H) {
1590        state.write_u64(self.id);
1591    }
1592}
1593
1594impl Image {
1595    /// An empty image containing no data
1596    pub fn empty() -> Self {
1597        Self::from_bytes(ImageFormat::Png, Vec::new())
1598    }
1599
1600    /// Create an image from a format and bytes
1601    pub fn from_bytes(format: ImageFormat, bytes: Vec<u8>) -> Self {
1602        Self {
1603            id: hash(&bytes),
1604            format,
1605            bytes,
1606        }
1607    }
1608
1609    /// Get this image's ID
1610    pub fn id(&self) -> u64 {
1611        self.id
1612    }
1613
1614    /// Use the GPUI `use_asset` API to make this image renderable
1615    pub fn use_render_image(
1616        self: Arc<Self>,
1617        window: &mut Window,
1618        cx: &mut App,
1619    ) -> Option<Arc<RenderImage>> {
1620        ImageSource::Image(self)
1621            .use_data(None, window, cx)
1622            .and_then(|result| result.ok())
1623    }
1624
1625    /// Use the GPUI `get_asset` API to make this image renderable
1626    pub fn get_render_image(
1627        self: Arc<Self>,
1628        window: &mut Window,
1629        cx: &mut App,
1630    ) -> Option<Arc<RenderImage>> {
1631        ImageSource::Image(self)
1632            .get_data(None, window, cx)
1633            .and_then(|result| result.ok())
1634    }
1635
1636    /// Use the GPUI `remove_asset` API to drop this image, if possible.
1637    pub fn remove_asset(self: Arc<Self>, cx: &mut App) {
1638        ImageSource::Image(self).remove_asset(cx);
1639    }
1640
1641    /// Convert the clipboard image to an `ImageData` object.
1642    pub fn to_image_data(&self, svg_renderer: SvgRenderer) -> Result<Arc<RenderImage>> {
1643        fn frames_for_image(
1644            bytes: &[u8],
1645            format: image::ImageFormat,
1646        ) -> Result<SmallVec<[Frame; 1]>> {
1647            let mut data = image::load_from_memory_with_format(bytes, format)?.into_rgba8();
1648
1649            // Convert from RGBA to BGRA.
1650            for pixel in data.chunks_exact_mut(4) {
1651                pixel.swap(0, 2);
1652            }
1653
1654            Ok(SmallVec::from_elem(Frame::new(data), 1))
1655        }
1656
1657        let frames = match self.format {
1658            ImageFormat::Gif => {
1659                let decoder = GifDecoder::new(Cursor::new(&self.bytes))?;
1660                let mut frames = SmallVec::new();
1661
1662                for frame in decoder.into_frames() {
1663                    let mut frame = frame?;
1664                    // Convert from RGBA to BGRA.
1665                    for pixel in frame.buffer_mut().chunks_exact_mut(4) {
1666                        pixel.swap(0, 2);
1667                    }
1668                    frames.push(frame);
1669                }
1670
1671                frames
1672            }
1673            ImageFormat::Png => frames_for_image(&self.bytes, image::ImageFormat::Png)?,
1674            ImageFormat::Jpeg => frames_for_image(&self.bytes, image::ImageFormat::Jpeg)?,
1675            ImageFormat::Webp => frames_for_image(&self.bytes, image::ImageFormat::WebP)?,
1676            ImageFormat::Bmp => frames_for_image(&self.bytes, image::ImageFormat::Bmp)?,
1677            ImageFormat::Tiff => frames_for_image(&self.bytes, image::ImageFormat::Tiff)?,
1678            ImageFormat::Svg => {
1679                let pixmap = svg_renderer.render_pixmap(&self.bytes, SvgSize::ScaleFactor(1.0))?;
1680
1681                let buffer =
1682                    image::ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take())
1683                        .unwrap();
1684
1685                SmallVec::from_elem(Frame::new(buffer), 1)
1686            }
1687        };
1688
1689        Ok(Arc::new(RenderImage::new(frames)))
1690    }
1691
1692    /// Get the format of the clipboard image
1693    pub fn format(&self) -> ImageFormat {
1694        self.format
1695    }
1696
1697    /// Get the raw bytes of the clipboard image
1698    pub fn bytes(&self) -> &[u8] {
1699        self.bytes.as_slice()
1700    }
1701}
1702
1703/// A clipboard item that should be copied to the clipboard
1704#[derive(Clone, Debug, Eq, PartialEq)]
1705pub struct ClipboardString {
1706    pub(crate) text: String,
1707    pub(crate) metadata: Option<String>,
1708}
1709
1710impl ClipboardString {
1711    /// Create a new clipboard string with the given text
1712    pub fn new(text: String) -> Self {
1713        Self {
1714            text,
1715            metadata: None,
1716        }
1717    }
1718
1719    /// Return a new clipboard item with the metadata replaced by the given metadata,
1720    /// after serializing it as JSON.
1721    pub fn with_json_metadata<T: Serialize>(mut self, metadata: T) -> Self {
1722        self.metadata = Some(serde_json::to_string(&metadata).unwrap());
1723        self
1724    }
1725
1726    /// Get the text of the clipboard string
1727    pub fn text(&self) -> &String {
1728        &self.text
1729    }
1730
1731    /// Get the owned text of the clipboard string
1732    pub fn into_text(self) -> String {
1733        self.text
1734    }
1735
1736    /// Get the metadata of the clipboard string, formatted as JSON
1737    pub fn metadata_json<T>(&self) -> Option<T>
1738    where
1739        T: for<'a> Deserialize<'a>,
1740    {
1741        self.metadata
1742            .as_ref()
1743            .and_then(|m| serde_json::from_str(m).ok())
1744    }
1745
1746    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
1747    pub(crate) fn text_hash(text: &str) -> u64 {
1748        let mut hasher = SeaHasher::new();
1749        text.hash(&mut hasher);
1750        hasher.finish()
1751    }
1752}
1753
1754impl From<String> for ClipboardString {
1755    fn from(value: String) -> Self {
1756        Self {
1757            text: value,
1758            metadata: None,
1759        }
1760    }
1761}