use crate::{
ActivityGuard, AppLifecyclePhase, BackgroundExecutor, ClipboardItem, ClipboardReadError,
CursorStyle, ForegroundExecutor, MenuCommandId, PathPromptOptions, PlatformDisplay,
PlatformGestures, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformMenu,
PlatformMenuItem, PlatformWindow, ScreenCaptureSource, SystemNotification,
SystemNotificationResponse, Task, ThermalState, WindowAppearance, WindowButtonLayout, WindowId,
WindowParams,
};
use anyhow::Result;
use futures::channel::oneshot;
use gpui_engine::PlatformTextSystem;
use smallvec::SmallVec;
use std::{
ffi::OsString,
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
};
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[inline]
pub fn guess_compositor() -> &'static str {
if std::env::var_os("ZED_HEADLESS").is_some() {
return "Headless";
}
#[cfg(feature = "wayland")]
let wayland_display = std::env::var_os("WAYLAND_DISPLAY");
#[cfg(not(feature = "wayland"))]
let wayland_display: Option<std::ffi::OsString> = None;
#[cfg(feature = "x11")]
let x11_display = std::env::var_os("DISPLAY");
#[cfg(not(feature = "x11"))]
let x11_display: Option<std::ffi::OsString> = None;
let use_wayland = wayland_display.is_some_and(|display| !display.is_empty());
let use_x11 = x11_display.is_some_and(|display| !display.is_empty());
if use_wayland {
"Wayland"
} else if use_x11 {
"X11"
} else {
"Headless"
}
}
#[expect(missing_docs)]
pub trait Platform: 'static {
fn background_executor(&self) -> BackgroundExecutor;
fn foreground_executor(&self) -> ForegroundExecutor;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
fn quit(&self);
fn restart(&self, binary_path: Option<PathBuf>, arguments: Vec<OsString>);
fn activate(&self, ignoring_other_apps: bool);
fn hide(&self);
fn hide_other_apps(&self);
fn unhide_other_apps(&self);
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
fn active_window(&self) -> Option<WindowId>;
fn window_stack(&self) -> Option<Vec<WindowId>> {
None
}
fn is_screen_capture_supported(&self) -> bool {
false
}
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<anyhow::Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
let (sources_tx, sources_rx) = oneshot::channel();
sources_tx
.send(Err(anyhow::anyhow!(
"gpui was compiled without the screen-capture feature"
)))
.ok();
sources_rx
}
fn open_window(
&self,
handle: WindowId,
options: WindowParams,
) -> anyhow::Result<Box<dyn PlatformWindow>>;
fn window_appearance(&self) -> WindowAppearance;
fn set_window_appearance(&self, _appearance: Option<WindowAppearance>) {}
fn button_layout(&self) -> Option<WindowButtonLayout> {
None
}
fn open_url(&self, url: &str);
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
fn register_url_scheme(&self, url: &str) -> Task<Result<()>>;
fn prompt_for_paths(
&self,
options: PathPromptOptions,
) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>>;
fn prompt_for_new_path(
&self,
directory: &Path,
suggested_name: Option<&str>,
) -> oneshot::Receiver<Result<Option<PathBuf>>>;
fn can_select_mixed_files_and_dirs(&self) -> bool;
fn reveal_path(&self, path: &Path);
fn open_with_system(&self, path: &Path);
fn on_quit(&self, callback: Box<dyn FnMut() -> bool>);
fn on_reopen(&self, callback: Box<dyn FnMut()>);
fn on_system_sleep(&self, callback: Box<dyn FnMut()>);
fn on_system_wake(&self, callback: Box<dyn FnMut()>);
fn on_app_lifecycle(&self, _callback: Box<dyn FnMut(AppLifecyclePhase)>) {}
fn on_memory_warning(&self, _callback: Box<dyn FnMut()>) {}
fn gestures(&self) -> Option<Rc<dyn PlatformGestures>> {
None
}
fn set_menus(&self, menus: Vec<PlatformMenu>);
fn set_dock_menu(&self, menu: Vec<PlatformMenuItem>);
fn perform_dock_menu_action(&self, _action: usize) {}
fn add_recent_document(&self, _path: &Path) {}
fn update_jump_list(
&self,
_menus: Vec<PlatformMenuItem>,
_entries: Vec<SmallVec<[PathBuf; 2]>>,
) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
Task::ready(Vec::new())
}
fn on_app_menu_action(&self, callback: Box<dyn FnMut(MenuCommandId)>);
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(MenuCommandId) -> bool>);
fn thermal_state(&self) -> ThermalState;
fn on_thermal_state_change(&self, callback: Box<dyn FnMut()>);
fn prevent_idle_sleep(&self, reason: &str) -> Task<Result<ActivityGuard>>;
fn set_app_identity(&self, identifier: &str, name: &str) {
_ = (identifier, name);
}
fn show_system_notification(&self, notification: SystemNotification) {
_ = notification;
}
fn dismiss_system_notification(&self, tag: &str) {
_ = tag;
}
fn on_system_notification_response(
&self,
callback: Box<dyn FnMut(SystemNotificationResponse)>,
) {
_ = callback;
}
fn compositor_name(&self) -> &'static str {
""
}
fn app_path(&self) -> Result<PathBuf>;
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
fn set_cursor_style(&self, style: CursorStyle);
fn hide_cursor_until_mouse_moves(&self);
fn is_cursor_visible(&self) -> bool;
fn should_auto_hide_scrollbars(&self) -> bool;
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
fn write_to_clipboard(&self, item: ClipboardItem);
fn read_from_clipboard_async(&self) -> Task<Result<Option<ClipboardItem>, ClipboardReadError>> {
Task::ready(Ok(self.read_from_clipboard()))
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn read_from_primary(&self) -> Option<ClipboardItem>;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn write_to_primary(&self, item: ClipboardItem);
#[cfg(target_os = "macos")]
fn read_from_find_pasteboard(&self) -> Option<ClipboardItem>;
#[cfg(target_os = "macos")]
fn write_to_find_pasteboard(&self, item: ClipboardItem);
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>>;
fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>>;
fn delete_credentials(&self, url: &str) -> Task<Result<()>>;
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper>;
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>);
}