Skip to repository content288 lines · 7.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:59:32.467Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
window.rs
1//! Windows for the headless platform client.
2//!
3//! A headless window has no compositor surface and no GPU: layout, text
4//! shaping, and entity plumbing run normally, `draw` discards the scene, and
5//! the sprite atlas hands out tiles without uploading pixels (mirroring
6//! GPUI's `TestWindow`/`TestAtlas`). This lets command-line tools drive real
7//! `Window`-based code paths without a display server.
8
9use std::cell::RefCell;
10use std::rc::Rc;
11use std::sync::Arc;
12
13use collections::HashMap;
14use parking_lot::Mutex;
15use uuid::Uuid;
16
17use gpui::{
18 AtlasKey, AtlasTextureId, AtlasTile, Bounds, Capslock, DevicePixels, DispatchEventResult,
19 DisplayId, GpuSpecs, Modifiers, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
20 PlatformInputHandler, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions,
21 Scene, Size, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
22 WindowControlArea, WindowParams, px,
23};
24
25#[derive(Debug)]
26pub(crate) struct HeadlessDisplay {
27 bounds: Bounds<Pixels>,
28}
29
30impl HeadlessDisplay {
31 pub(crate) fn new() -> Self {
32 Self {
33 bounds: Bounds::from_corners(Point::default(), Point::new(px(1920.), px(1080.))),
34 }
35 }
36}
37
38impl PlatformDisplay for HeadlessDisplay {
39 fn id(&self) -> DisplayId {
40 DisplayId::new(0)
41 }
42
43 fn uuid(&self) -> anyhow::Result<Uuid> {
44 // Stable identity: there is exactly one headless display.
45 Ok(Uuid::nil())
46 }
47
48 fn bounds(&self) -> Bounds<Pixels> {
49 self.bounds
50 }
51}
52
53struct HeadlessWindowState {
54 bounds: Bounds<Pixels>,
55 display: Rc<dyn PlatformDisplay>,
56 input_handler: Option<PlatformInputHandler>,
57 title: Option<String>,
58 is_fullscreen: bool,
59}
60
61pub(crate) struct HeadlessWindow(Rc<RefCell<HeadlessWindowState>>);
62
63impl raw_window_handle::HasWindowHandle for HeadlessWindow {
64 fn window_handle(
65 &self,
66 ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
67 // Headless windows are not backed by a native window.
68 Err(raw_window_handle::HandleError::NotSupported)
69 }
70}
71
72impl raw_window_handle::HasDisplayHandle for HeadlessWindow {
73 fn display_handle(
74 &self,
75 ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
76 Err(raw_window_handle::HandleError::NotSupported)
77 }
78}
79
80impl HeadlessWindow {
81 pub(crate) fn new(params: WindowParams, display: Rc<dyn PlatformDisplay>) -> Self {
82 Self(Rc::new(RefCell::new(HeadlessWindowState {
83 bounds: params.bounds,
84 display,
85 input_handler: None,
86 title: None,
87 is_fullscreen: false,
88 })))
89 }
90}
91
92impl PlatformWindow for HeadlessWindow {
93 fn bounds(&self) -> Bounds<Pixels> {
94 self.0.borrow().bounds
95 }
96
97 fn is_maximized(&self) -> bool {
98 false
99 }
100
101 fn window_bounds(&self) -> WindowBounds {
102 WindowBounds::Windowed(self.bounds())
103 }
104
105 fn content_size(&self) -> Size<Pixels> {
106 self.bounds().size
107 }
108
109 fn resize(&mut self, size: Size<Pixels>) {
110 self.0.borrow_mut().bounds.size = size;
111 }
112
113 fn scale_factor(&self) -> f32 {
114 1.0
115 }
116
117 fn appearance(&self) -> WindowAppearance {
118 WindowAppearance::Dark
119 }
120
121 fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
122 Some(self.0.borrow().display.clone())
123 }
124
125 fn mouse_position(&self) -> Point<Pixels> {
126 Point::default()
127 }
128
129 fn modifiers(&self) -> Modifiers {
130 Modifiers::default()
131 }
132
133 fn capslock(&self) -> Capslock {
134 Capslock::default()
135 }
136
137 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
138 self.0.borrow_mut().input_handler = Some(input_handler);
139 }
140
141 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
142 self.0.borrow_mut().input_handler.take()
143 }
144
145 fn prompt(
146 &self,
147 _level: PromptLevel,
148 _msg: &str,
149 _detail: Option<&str>,
150 _answers: &[PromptButton],
151 ) -> Option<futures::channel::oneshot::Receiver<usize>> {
152 // Fall back to GPUI's rendered prompts.
153 None
154 }
155
156 fn activate(&self) {}
157
158 fn is_active(&self) -> bool {
159 false
160 }
161
162 fn is_hovered(&self) -> bool {
163 false
164 }
165
166 fn background_appearance(&self) -> WindowBackgroundAppearance {
167 WindowBackgroundAppearance::Opaque
168 }
169
170 fn set_title(&mut self, title: &str) {
171 self.0.borrow_mut().title = Some(title.to_owned());
172 }
173
174 fn get_title(&self) -> String {
175 self.0.borrow().title.clone().unwrap_or_default()
176 }
177
178 fn set_background_appearance(&self, _background: WindowBackgroundAppearance) {}
179
180 fn minimize(&self) {}
181
182 fn zoom(&self) {}
183
184 fn toggle_fullscreen(&self) {
185 let mut state = self.0.borrow_mut();
186 state.is_fullscreen = !state.is_fullscreen;
187 }
188
189 fn is_fullscreen(&self) -> bool {
190 self.0.borrow().is_fullscreen
191 }
192
193 // No compositor drives a frame loop, so frame and status callbacks are
194 // dropped: anything that awaits a frame will never resolve headlessly.
195 fn on_request_frame(&self, _callback: Box<dyn FnMut(RequestFrameOptions)>) {}
196
197 fn on_input(&self, _callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>) {}
198
199 fn on_active_status_change(&self, _callback: Box<dyn FnMut(bool)>) {}
200
201 fn on_hover_status_change(&self, _callback: Box<dyn FnMut(bool)>) {}
202
203 fn on_resize(&self, _callback: Box<dyn FnMut(Size<Pixels>, f32)>) {}
204
205 fn on_moved(&self, _callback: Box<dyn FnMut()>) {}
206
207 fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {}
208
209 fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
210
211 fn on_hit_test_window_control(&self, _callback: Box<dyn FnMut() -> Option<WindowControlArea>>) {
212 }
213
214 fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
215
216 fn draw(&self, _scene: &Scene) {}
217
218 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
219 Arc::new(HeadlessAtlas::default())
220 }
221
222 fn is_subpixel_rendering_supported(&self) -> bool {
223 false
224 }
225
226 fn update_ime_position(&self, _bounds: Bounds<Pixels>) {}
227
228 fn gpu_specs(&self) -> Option<GpuSpecs> {
229 None
230 }
231}
232
233/// Allocates atlas tiles without uploading pixels, so glyph and sprite
234/// painting completes headlessly.
235#[derive(Default)]
236struct HeadlessAtlas(Mutex<HeadlessAtlasState>);
237
238#[derive(Default)]
239struct HeadlessAtlasState {
240 next_id: u32,
241 tiles: HashMap<AtlasKey, AtlasTile>,
242}
243
244impl PlatformAtlas for HeadlessAtlas {
245 fn get_or_insert_with<'a>(
246 &self,
247 key: &AtlasKey,
248 build: &mut dyn FnMut() -> anyhow::Result<
249 Option<(Size<DevicePixels>, std::borrow::Cow<'a, [u8]>)>,
250 >,
251 ) -> anyhow::Result<Option<AtlasTile>> {
252 {
253 let state = self.0.lock();
254 if let Some(&tile) = state.tiles.get(key) {
255 return Ok(Some(tile));
256 }
257 }
258
259 let Some((size, _)) = build()? else {
260 return Ok(None);
261 };
262
263 let mut state = self.0.lock();
264 state.next_id += 1;
265 let texture_id = state.next_id;
266 state.next_id += 1;
267 let tile_id = state.next_id;
268 let tile = AtlasTile {
269 texture_id: AtlasTextureId {
270 index: texture_id,
271 kind: key.texture_kind(),
272 },
273 tile_id: TileId(tile_id),
274 padding: 0,
275 bounds: Bounds {
276 origin: Point::default(),
277 size,
278 },
279 };
280 state.tiles.insert(key.clone(), tile);
281 Ok(Some(tile))
282 }
283
284 fn remove(&self, key: &AtlasKey) {
285 self.0.lock().tiles.remove(key);
286 }
287}
288