Skip to main content

gpui_platform/
platform_window.rs

1//! Platform window contract shared by `gpui` and its platform backends.
2
3#[cfg(all(target_os = "linux", feature = "wayland"))]
4use crate::layer_shell;
5use crate::{
6    Decorations, DispatchEventResult, ExternalDragPayload, GpuSpecs, PlatformDisplay,
7    PlatformInput, PlatformInputHandler, PromptButton, PromptLevel, RequestFrameOptions,
8    ResizeEdge, SystemWindowTab, TextInputConfiguration, TextInputStateChange, WindowAppearance,
9    WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
10    WindowInsets,
11};
12use futures::channel::oneshot;
13use gpui_engine::SceneRenderer;
14use gpui_types::{Bounds, Capslock, Modifiers, Pixels, Point, Size};
15use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
16#[cfg(any(test, feature = "test-support", feature = "bench-support"))]
17use std::any::Any;
18use std::rc::Rc;
19
20/// Whether the platform is presenting a window's frames.
21///
22/// This is about presentation, not the window's shown/hidden state: a shown
23/// window that is fully covered by other windows, minimized, on another
24/// Space or virtual desktop, or on a display that is asleep is `Hidden`. A
25/// window only partly covered by other windows is `Visible`.
26///
27/// Each platform reports from a single source, and what that source can see
28/// differs:
29///
30/// * macOS: `NSWindow.occlusionState`. Covers all of the cases above.
31/// * Windows: `WS_VISIBLE` and the minimized state. Windows keeps compositing
32///   covered windows for thumbnails and Alt-Tab and offers no occlusion
33///   notification, so a fully covered window stays `Visible`. Display sleep is
34///   not reported either.
35/// * Wayland: the `xdg_toplevel` `suspended` state (xdg-shell v6). Compositors
36///   that don't support it never report `Hidden`.
37/// * X11: mapped state plus `VisibilityNotify`. Compositing window managers
38///   generally never report a window as fully obscured, so covering is only
39///   detected without compositing.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum WindowVisibility {
42    /// At least part of the window is being presented; frames drawn for it
43    /// will be shown.
44    Visible,
45    /// No part of the window is being presented. The platform will not
46    /// request frames for it until it becomes visible again.
47    Hidden,
48}
49
50impl WindowVisibility {
51    /// Whether frames drawn for the window will be shown.
52    pub fn is_visible(self) -> bool {
53        self == Self::Visible
54    }
55}
56
57/// Callbacks for the accessibility adapter.
58pub struct A11yCallbacks {
59    /// Called when the adapter is activated (a screen reader connects).
60    pub activation: Box<dyn Fn() -> Option<accesskit::TreeUpdate> + Send + 'static>,
61    /// Called when an action is requested by the screen reader.
62    pub action: Box<dyn Fn(accesskit::ActionRequest) + Send + 'static>,
63    /// Called when the adapter is deactivated (screen reader disconnects).
64    pub deactivation: Box<dyn Fn() + Send + 'static>,
65}
66
67#[expect(missing_docs)]
68pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
69    fn bounds(&self) -> Bounds<Pixels>;
70    fn is_maximized(&self) -> bool;
71    fn window_bounds(&self) -> WindowBounds;
72    fn content_size(&self) -> Size<Pixels>;
73    /// Returns the visible viewport in logical pixels relative to the content origin.
74    ///
75    /// This may be smaller or offset when a keyboard or zoom obscures content;
76    /// it must not change the full layout size returned by `content_size`.
77    /// Implementations should return a frame snapshot, not query platform layout here.
78    fn visual_viewport_bounds(&self) -> Bounds<Pixels> {
79        Bounds::new(Point::default(), self.content_size())
80    }
81    /// Registers a callback when visible geometry may have changed.
82    ///
83    /// This requests a frame; backends can sample the new viewport and safe-area
84    /// geometry in `prepare_frame` rather than updating it inside the callback.
85    fn on_visual_viewport_changed(&self, _callback: Box<dyn FnMut()>) {}
86    /// Samples platform geometry before a draw, returning whether view caches must be invalidated.
87    ///
88    /// Geometry getters must remain consistent throughout the ensuing draw.
89    /// Do not invoke callbacks here: GPUI is already updating this window.
90    fn prepare_frame(&self) -> bool {
91        false
92    }
93    fn resize(&mut self, size: Size<Pixels>);
94    fn scale_factor(&self) -> f32;
95    fn appearance(&self) -> WindowAppearance;
96    fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
97    fn mouse_position(&self) -> Point<Pixels>;
98    fn modifiers(&self) -> Modifiers;
99    fn capslock(&self) -> Capslock;
100    fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
101    fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
102    /// Apply the focused text region's [`TextInputConfiguration`] to the
103    /// platform's text input session (e.g. attributes of the hidden editable
104    /// element on web). Called only when the configuration changes, because
105    /// reconfiguring a live input session can restart the IME connection.
106    fn set_text_input_configuration(&mut self, _configuration: TextInputConfiguration) {}
107    fn prompt(
108        &self,
109        level: PromptLevel,
110        msg: &str,
111        detail: Option<&str>,
112        answers: &[PromptButton],
113    ) -> Option<oneshot::Receiver<usize>>;
114    fn activate(&self);
115    /// Requests that the operating system draw attention to this window.
116    fn request_attention(&self) {}
117    fn is_active(&self) -> bool;
118    /// The current [`WindowVisibility`]. Read once when the window is created;
119    /// afterwards changes arrive through [`Self::on_visibility_change`].
120    fn visibility(&self) -> WindowVisibility;
121    fn is_hovered(&self) -> bool;
122    fn background_appearance(&self) -> WindowBackgroundAppearance;
123    fn set_title(&mut self, title: &str);
124    fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance);
125    fn minimize(&self);
126    fn zoom(&self);
127    fn toggle_fullscreen(&self);
128    fn is_fullscreen(&self) -> bool;
129    fn frame_waker(&self) -> Option<Rc<dyn Fn()>> {
130        None
131    }
132    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>);
133    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>);
134    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
135    /// Registers the callback invoked when [`Self::visibility`] changes. Only
136    /// transitions are reported; the callback runs on the main thread outside
137    /// of any window update.
138    fn on_visibility_change(&self, callback: Box<dyn FnMut(WindowVisibility)>);
139    fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>);
140    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
141    fn on_moved(&self, callback: Box<dyn FnMut()>);
142    /// Registers the callback invoked when the window moves to another
143    /// display. Calls may be spurious. The callback runs on the main thread
144    /// outside of any window update.
145    fn on_display_changed(&self, callback: Box<dyn FnMut()>);
146    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
147    fn on_hit_test_window_control(&self, callback: Box<dyn FnMut() -> Option<WindowControlArea>>);
148    fn on_close(&self, callback: Box<dyn FnOnce()>);
149    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
150    fn on_button_layout_changed(&self, _callback: Box<dyn FnMut()>) {}
151    /// Borrows this window's scene renderer without presenting a frame.
152    ///
153    /// Use this for renderer queries such as reading the sprite atlas or
154    /// capturing an image; backends must not advance their frame loop here.
155    fn with_renderer(&mut self, f: &mut dyn FnMut(&mut dyn SceneRenderer));
156    /// Submits a frame through this window's scene renderer.
157    ///
158    /// `f` encodes and submits the frame and returns whether it was presented.
159    /// Backends that pace themselves on compositor callbacks use that result
160    /// to update their frame loop.
161    fn present(&mut self, f: &mut dyn FnMut(&mut dyn SceneRenderer) -> bool);
162    fn schedule_frame(&self) {}
163    fn is_subpixel_rendering_supported(&self) -> bool;
164
165    // macOS specific methods
166    fn get_title(&self) -> String {
167        String::new()
168    }
169    fn tabbed_windows(&self) -> Option<Vec<SystemWindowTab>> {
170        None
171    }
172    fn tab_bar_visible(&self) -> bool {
173        false
174    }
175    fn set_edited(&mut self, _edited: bool) {}
176    fn set_document_path(&self, _path: Option<&std::path::Path>) {}
177    fn toggle_simple_fullscreen(&self) {}
178    fn is_simple_fullscreen(&self) -> bool {
179        false
180    }
181    #[cfg(target_os = "macos")]
182    fn set_traffic_light_position(&self, _position: Point<Pixels>) {}
183    fn show_character_palette(&self) {}
184    fn titlebar_double_click(&self, _is_resizable: bool, _is_minimizable: bool) {}
185    fn on_move_tab_to_new_window(&self, _callback: Box<dyn FnMut()>) {}
186    fn on_merge_all_windows(&self, _callback: Box<dyn FnMut()>) {}
187    fn on_select_previous_tab(&self, _callback: Box<dyn FnMut()>) {}
188    fn on_select_next_tab(&self, _callback: Box<dyn FnMut()>) {}
189    fn on_toggle_tab_bar(&self, _callback: Box<dyn FnMut()>) {}
190    fn merge_all_windows(&self) {}
191    fn move_tab_to_new_window(&self) {}
192    fn toggle_window_tab_overview(&self) {}
193    fn set_tabbing_identifier(&self, _identifier: Option<String>) {}
194
195    fn native_window_state(&self) -> Option<Vec<u8>> {
196        None
197    }
198    fn restore_native_window_state(&self, _state: &[u8]) {}
199
200    #[cfg(target_os = "windows")]
201    fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND;
202
203    // Linux specific methods
204    fn inner_window_bounds(&self) -> WindowBounds {
205        self.window_bounds()
206    }
207    fn request_decorations(&self, _decorations: WindowDecorations) {}
208    fn show_window_menu(&self, _position: Point<Pixels>) {}
209    fn start_window_move(&self) {}
210    fn can_start_external_drag(&self) -> bool {
211        false
212    }
213    fn start_external_drag(&self, _payload: &ExternalDragPayload) -> bool {
214        false
215    }
216    fn start_window_resize(&self, _edge: ResizeEdge) {}
217    fn set_exclusive_zone(&self, _zone: Pixels) {}
218    #[cfg(all(target_os = "linux", feature = "wayland"))]
219    fn set_exclusive_edge(&self, _edge: layer_shell::Anchor) {}
220    fn set_input_region(&self, _region: Option<&[Bounds<Pixels>]>) {}
221    fn window_decorations(&self) -> Decorations {
222        Decorations::Server
223    }
224    fn set_app_id(&mut self, _app_id: &str) {}
225    fn map_window(&mut self) -> anyhow::Result<()> {
226        Ok(())
227    }
228    fn window_controls(&self) -> WindowControls {
229        WindowControls::default()
230    }
231    fn set_client_inset(&self, _inset: Pixels) {}
232    fn gpu_specs(&self) -> Option<GpuSpecs>;
233
234    fn update_ime_position(&self, _bounds: Bounds<Pixels>);
235
236    // Mobile platform methods.
237
238    /// The regions of this window currently obscured or reserved by the
239    /// system. Zero on platforms without such regions.
240    fn insets(&self) -> WindowInsets {
241        WindowInsets::default()
242    }
243
244    /// Registers a callback invoked whenever [`Self::insets`] change.
245    ///
246    /// Contract: fires continuously during animated transitions (Android
247    /// `WindowInsetsAnimation` progress; on iOS the platform interpolates
248    /// the keyboard animation curve on frame ticks) and is exact at rest.
249    fn on_insets_changed(&self, _callback: Box<dyn FnMut(WindowInsets)>) {}
250
251    /// Sets the handler for the system back action (Android back
252    /// button/gesture; no source on iOS or desktop).
253    fn set_back_handler(&self, _callback: Box<dyn FnMut()>) {}
254
255    /// Declares whether the application would currently handle the system
256    /// back action (e.g. navigation depth > 0).
257    fn set_back_enabled(&self, _enabled: bool) {}
258
259    /// Requests that the soft keyboard be shown.
260    fn show_soft_keyboard(&self) {}
261
262    /// Requests that the soft keyboard be hidden.
263    fn hide_soft_keyboard(&self) {}
264
265    /// Inform the operating system that the text input state has changed
266    fn text_input_state_changed(&self, _change: TextInputStateChange) {}
267
268    fn play_system_bell(&self) {}
269
270    /// Initialize the accessibility adapter with callbacks.
271    fn a11y_init(&self, _callbacks: A11yCallbacks) {}
272
273    /// Provide a TreeUpdate to the accessibility adapter.
274    fn a11y_tree_update(&self, _tree_update: accesskit::TreeUpdate) {}
275
276    /// Inform the adapter of updated window bounds.
277    fn a11y_update_window_bounds(&self) {}
278
279    #[cfg(any(test, feature = "test-support", feature = "bench-support"))]
280    fn as_test(&mut self) -> Option<&mut dyn Any> {
281        None
282    }
283}