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