Skip to repository content2085 lines · 73.1 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:03:35.202Z 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
1use std::{
2 cell::{Cell, Ref, RefCell, RefMut},
3 ffi::c_void,
4 ptr::NonNull,
5 rc::Rc,
6 sync::Arc,
7};
8
9use collections::{FxHashMap, HashMap};
10use futures::channel::oneshot::Receiver;
11
12use raw_window_handle as rwh;
13use wayland_backend::client::ObjectId;
14use wayland_client::WEnum;
15use wayland_client::{
16 Proxy,
17 protocol::{wl_output, wl_seat, wl_surface},
18};
19use wayland_protocols::wp::viewporter::client::wp_viewport;
20use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
21use wayland_protocols::xdg::shell::client::xdg_popup;
22use wayland_protocols::xdg::shell::client::xdg_positioner;
23use wayland_protocols::xdg::shell::client::xdg_surface;
24use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
25use wayland_protocols::{
26 wp::fractional_scale::v1::client::wp_fractional_scale_v1,
27 xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1,
28};
29use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
30use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1;
31
32use crate::linux::wayland::{display::WaylandDisplay, serial::SerialKind};
33use crate::linux::{Globals, Output, WaylandClientStatePtr, get_window};
34use gpui::{
35 AnyWindowHandle, Bounds, Capslock, Decorations, DevicePixels, GpuSpecs, Modifiers, Pixels,
36 PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
37 PromptButton, PromptLevel, RequestFrameOptions, ResizeEdge, Scene, Size, Tiling,
38 WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls,
39 WindowDecorations, WindowKind, WindowParams,
40 layer_shell::{Anchor, LayerShellNotSupportedError},
41 popup::PopupOptions,
42 px, size,
43};
44use gpui_wgpu::{CompositorGpuHint, WgpuRenderer, WgpuSurfaceConfig, wgpu};
45
46#[derive(Default)]
47pub(crate) struct Callbacks {
48 request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
49 input: Option<Box<dyn FnMut(gpui::PlatformInput) -> gpui::DispatchEventResult>>,
50 active_status_change: Option<Box<dyn FnMut(bool)>>,
51 hover_status_change: Option<Box<dyn FnMut(bool)>>,
52 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
53 moved: Option<Box<dyn FnMut()>>,
54 should_close: Option<Box<dyn FnMut() -> bool>>,
55 close: Option<Box<dyn FnOnce()>>,
56 appearance_changed: Option<Box<dyn FnMut()>>,
57 button_layout_changed: Option<Box<dyn FnMut()>>,
58}
59
60#[derive(Debug, Clone, Copy)]
61struct RawWindow {
62 window: *mut c_void,
63 display: *mut c_void,
64}
65
66// Safety: The raw pointers in RawWindow point to Wayland surface/display
67// which are valid for the window's lifetime. These are used only for
68// passing to wgpu which needs Send+Sync for surface creation.
69unsafe impl Send for RawWindow {}
70unsafe impl Sync for RawWindow {}
71
72impl rwh::HasWindowHandle for RawWindow {
73 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
74 let window = NonNull::new(self.window).unwrap();
75 let handle = rwh::WaylandWindowHandle::new(window);
76 Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
77 }
78}
79impl rwh::HasDisplayHandle for RawWindow {
80 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
81 let display = NonNull::new(self.display).unwrap();
82 let handle = rwh::WaylandDisplayHandle::new(display);
83 Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
84 }
85}
86
87#[derive(Debug)]
88struct InProgressConfigure {
89 size: Option<Size<Pixels>>,
90 fullscreen: bool,
91 maximized: bool,
92 resizing: bool,
93 tiling: Tiling,
94}
95
96pub struct WaylandWindowState {
97 surface_state: WaylandSurfaceState,
98 acknowledged_first_configure: bool,
99 parent: Option<WaylandWindowStatePtr>,
100 /// Child surfaces mapped to whether they block this window's input (dialogs
101 /// block, popups don't). Children are closed before this window closes.
102 children: FxHashMap<ObjectId, bool>,
103 pub surface: wl_surface::WlSurface,
104 app_id: Option<String>,
105 appearance: WindowAppearance,
106 blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
107 viewport: Option<wp_viewport::WpViewport>,
108 outputs: HashMap<ObjectId, Output>,
109 display: Option<(ObjectId, Output)>,
110 globals: Globals,
111 renderer: WgpuRenderer,
112 bounds: Bounds<Pixels>,
113 scale: f32,
114 input_handler: Option<PlatformInputHandler>,
115 decorations: WindowDecorations,
116 background_appearance: WindowBackgroundAppearance,
117 fullscreen: bool,
118 maximized: bool,
119 tiling: Tiling,
120 window_bounds: Bounds<Pixels>,
121 client: WaylandClientStatePtr,
122 handle: AnyWindowHandle,
123 active: bool,
124 hovered: bool,
125 pub(crate) force_render_after_recovery: bool,
126 renderer_presented: bool,
127 in_progress_configure: Option<InProgressConfigure>,
128 resize_throttle: bool,
129 in_progress_window_controls: Option<WindowControls>,
130 window_controls: WindowControls,
131 client_inset: Option<Pixels>,
132 accesskit_adapter: Option<accesskit_unix::Adapter>,
133}
134
135pub enum WaylandSurfaceState {
136 Xdg(WaylandXdgSurfaceState),
137 LayerShell(WaylandLayerSurfaceState),
138 Popup(WaylandPopupSurfaceState),
139}
140
141impl WaylandSurfaceState {
142 fn new(
143 surface: &wl_surface::WlSurface,
144 globals: &Globals,
145 params: &WindowParams,
146 parent: Option<WaylandWindowStatePtr>,
147 popup_grab: Option<(u32, wl_seat::WlSeat)>,
148 target_output: Option<wl_output::WlOutput>,
149 ) -> anyhow::Result<Self> {
150 // For layer_shell windows, create a layer surface instead of an xdg surface
151 if let WindowKind::LayerShell(options) = ¶ms.kind {
152 let Some(layer_shell) = globals.layer_shell.as_ref() else {
153 return Err(LayerShellNotSupportedError.into());
154 };
155
156 let layer_surface = layer_shell.get_layer_surface(
157 &surface,
158 target_output.as_ref(),
159 super::layer_shell::wayland_layer(options.layer),
160 options.namespace.clone(),
161 &globals.qh,
162 surface.id(),
163 );
164
165 let width = f32::from(params.bounds.size.width);
166 let height = f32::from(params.bounds.size.height);
167 layer_surface.set_size(width as u32, height as u32);
168
169 layer_surface.set_anchor(super::layer_shell::wayland_anchor(options.anchor));
170 layer_surface.set_keyboard_interactivity(
171 super::layer_shell::wayland_keyboard_interactivity(options.keyboard_interactivity),
172 );
173
174 if let Some(margin) = options.margin {
175 layer_surface.set_margin(
176 f32::from(margin.0) as i32,
177 f32::from(margin.1) as i32,
178 f32::from(margin.2) as i32,
179 f32::from(margin.3) as i32,
180 )
181 }
182
183 if let Some(exclusive_zone) = options.exclusive_zone {
184 layer_surface.set_exclusive_zone(f32::from(exclusive_zone) as i32);
185 }
186
187 if let Some(exclusive_edge) = options.exclusive_edge {
188 Self::apply_exclusive_edge(&layer_surface, options.anchor, exclusive_edge);
189 }
190
191 return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {
192 layer_surface,
193 anchor: options.anchor,
194 }));
195 }
196
197 if let WindowKind::AnchoredPopup(options) = ¶ms.kind {
198 let Some(parent) = parent.as_ref() else {
199 return Err(anyhow::anyhow!("popup parent window not found"));
200 };
201
202 let positioner = build_popup_positioner(
203 globals,
204 options,
205 params.bounds.size,
206 parent.window_geometry(),
207 );
208
209 let xdg_surface = globals
210 .wm_base
211 .get_xdg_surface(&surface, &globals.qh, surface.id());
212
213 // A layer-shell parent takes a null xdg parent and is attached via the layer
214 // surface. Every other surface kind has an xdg_surface to parent to directly.
215 let xdg_popup = if let Some(parent_layer_surface) = parent.layer_surface() {
216 let xdg_popup = xdg_surface.get_popup(None, &positioner, &globals.qh, surface.id());
217 parent_layer_surface.get_popup(&xdg_popup);
218 xdg_popup
219 } else {
220 xdg_surface.get_popup(
221 parent.xdg_surface().as_ref(),
222 &positioner,
223 &globals.qh,
224 surface.id(),
225 )
226 };
227 positioner.destroy();
228
229 if let Some((serial, seat)) = popup_grab {
230 xdg_popup.grab(&seat, serial);
231 }
232
233 // Non-blocking: the parent keeps its input so it can dismiss the popup on
234 // clicks in its own window.
235 parent.add_child(surface.id(), false);
236
237 return Ok(WaylandSurfaceState::Popup(WaylandPopupSurfaceState {
238 xdg_surface,
239 xdg_popup,
240 options: options.clone(),
241 next_reposition_token: Cell::new(0),
242 }));
243 }
244
245 // All other WindowKinds result in a regular xdg surface
246 let xdg_surface = globals
247 .wm_base
248 .get_xdg_surface(&surface, &globals.qh, surface.id());
249
250 let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
251 let xdg_parent = parent.as_ref().and_then(|w| w.toplevel());
252
253 if params.kind == WindowKind::Floating || params.kind == WindowKind::Dialog {
254 toplevel.set_parent(xdg_parent.as_ref());
255 }
256
257 let dialog = if params.kind == WindowKind::Dialog {
258 let dialog = globals.dialog.as_ref().map(|dialog| {
259 let xdg_dialog = dialog.get_xdg_dialog(&toplevel, &globals.qh, ());
260 xdg_dialog.set_modal();
261 xdg_dialog
262 });
263
264 if let Some(parent) = parent.as_ref() {
265 parent.add_child(surface.id(), true);
266 }
267
268 dialog
269 } else {
270 None
271 };
272
273 if let Some(size) = params.window_min_size {
274 toplevel.set_min_size(f32::from(size.width) as i32, f32::from(size.height) as i32);
275 }
276
277 // Attempt to set up window decorations based on the requested configuration
278 let decoration = globals
279 .decoration_manager
280 .as_ref()
281 .map(|decoration_manager| {
282 decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())
283 });
284
285 Ok(WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
286 xdg_surface,
287 toplevel,
288 decoration,
289 dialog,
290 }))
291 }
292}
293
294pub struct WaylandXdgSurfaceState {
295 xdg_surface: xdg_surface::XdgSurface,
296 toplevel: xdg_toplevel::XdgToplevel,
297 decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
298 dialog: Option<XdgDialogV1>,
299}
300
301pub struct WaylandLayerSurfaceState {
302 layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
303 anchor: Anchor,
304}
305
306pub struct WaylandPopupSurfaceState {
307 xdg_surface: xdg_surface::XdgSurface,
308 xdg_popup: xdg_popup::XdgPopup,
309 // Kept so the popup can be re-anchored via `xdg_popup.reposition` when resized.
310 options: PopupOptions,
311 next_reposition_token: Cell<u32>,
312}
313
314fn build_popup_positioner(
315 globals: &Globals,
316 options: &PopupOptions,
317 size: Size<Pixels>,
318 parent_geometry: Bounds<Pixels>,
319) -> xdg_positioner::XdgPositioner {
320 let positioner = globals.wm_base.create_positioner(&globals.qh, ());
321 // A zero or negative size is a protocol error.
322 positioner.set_size(
323 f32::from(size.width).max(1.0) as i32,
324 f32::from(size.height).max(1.0) as i32,
325 );
326
327 // The protocol wants the anchor rect relative to the parent's window geometry, while
328 // `options.anchor_rect` is in gpui window coordinates (surface-local). A rect extending
329 // outside the geometry or with a zero size is a protocol error, so translate, then clamp
330 // to at least one pixel inside the geometry, pulling the origin inward at the edges.
331 let anchor_rect = Bounds {
332 origin: options.anchor_rect.origin - parent_geometry.origin,
333 size: options.anchor_rect.size,
334 };
335 let one = Point::new(px(1.0), px(1.0));
336 let geometry_bottom_right: Point<Pixels> = parent_geometry.size.into();
337 let top_left = anchor_rect
338 .origin
339 .min(&(geometry_bottom_right - one))
340 .max(&Point::default());
341 let bottom_right = anchor_rect
342 .bottom_right()
343 .min(&geometry_bottom_right)
344 .max(&(top_left + one));
345 let anchor_rect = Bounds::from_corners(top_left, bottom_right);
346 positioner.set_anchor_rect(
347 f32::from(anchor_rect.origin.x) as i32,
348 f32::from(anchor_rect.origin.y) as i32,
349 f32::from(anchor_rect.size.width) as i32,
350 f32::from(anchor_rect.size.height) as i32,
351 );
352
353 positioner.set_anchor(super::popup::wayland_anchor(options.anchor));
354 positioner.set_gravity(super::popup::wayland_gravity(options.gravity));
355 positioner.set_constraint_adjustment(super::popup::wayland_constraint_adjustment(
356 options.constraint_adjustment,
357 ));
358 positioner.set_offset(
359 f32::from(options.offset.x) as i32,
360 f32::from(options.offset.y) as i32,
361 );
362 positioner
363}
364
365impl WaylandSurfaceState {
366 fn ack_configure(&self, serial: u32) {
367 match self {
368 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
369 xdg_surface.ack_configure(serial);
370 }
371 WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
372 layer_surface.ack_configure(serial);
373 }
374 WaylandSurfaceState::Popup(WaylandPopupSurfaceState { xdg_surface, .. }) => {
375 xdg_surface.ack_configure(serial);
376 }
377 }
378 }
379
380 fn decoration(&self) -> Option<&zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1> {
381 if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { decoration, .. }) = self {
382 decoration.as_ref()
383 } else {
384 None
385 }
386 }
387
388 fn toplevel(&self) -> Option<&xdg_toplevel::XdgToplevel> {
389 if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { toplevel, .. }) = self {
390 Some(toplevel)
391 } else {
392 None
393 }
394 }
395
396 fn xdg_surface(&self) -> Option<&xdg_surface::XdgSurface> {
397 match self {
398 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
399 Some(xdg_surface)
400 }
401 WaylandSurfaceState::Popup(WaylandPopupSurfaceState { xdg_surface, .. }) => {
402 Some(xdg_surface)
403 }
404 WaylandSurfaceState::LayerShell(_) => None,
405 }
406 }
407
408 fn layer_surface(&self) -> Option<&zwlr_layer_surface_v1::ZwlrLayerSurfaceV1> {
409 if let WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) =
410 self
411 {
412 Some(layer_surface)
413 } else {
414 None
415 }
416 }
417
418 fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {
419 match self {
420 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
421 xdg_surface.set_window_geometry(x, y, width, height);
422 }
423 WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
424 // cannot set window position of a layer surface
425 layer_surface.set_size(width as u32, height as u32);
426 }
427 WaylandSurfaceState::Popup(WaylandPopupSurfaceState { xdg_surface, .. }) => {
428 xdg_surface.set_window_geometry(x, y, width, height);
429 }
430 }
431 }
432
433 // Re-anchors a mapped popup at a new size via `xdg_popup.reposition`. Repositioning an
434 // unmapped popup (before the first configure) is a protocol error.
435 fn reposition_popup(
436 &self,
437 globals: &Globals,
438 size: Size<Pixels>,
439 parent_geometry: Bounds<Pixels>,
440 ) {
441 if let WaylandSurfaceState::Popup(WaylandPopupSurfaceState {
442 xdg_popup,
443 options,
444 next_reposition_token,
445 ..
446 }) = self
447 && xdg_popup.version() >= xdg_popup::REQ_REPOSITION_SINCE
448 {
449 let token = next_reposition_token.get();
450 next_reposition_token.set(token.wrapping_add(1));
451
452 let positioner = build_popup_positioner(globals, options, size, parent_geometry);
453 xdg_popup.reposition(&positioner, token);
454 positioner.destroy();
455 }
456 }
457
458 fn set_exclusive_zone(&self, zone: i32) -> bool {
459 if let WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) =
460 self
461 {
462 layer_surface.set_exclusive_zone(zone);
463 true
464 } else {
465 false
466 }
467 }
468
469 /// An exclusive edge must be a single edge that the surface is anchored to,
470 /// otherwise the compositor raises a fatal `invalid_exclusive_edge` protocol
471 /// error. An invalid edge is logged and ignored. Returns whether it applied.
472 fn apply_exclusive_edge(
473 layer_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
474 anchor: Anchor,
475 edge: Anchor,
476 ) -> bool {
477 if edge.bits().count_ones() == 1 && anchor.contains(edge) {
478 layer_surface.set_exclusive_edge(super::layer_shell::wayland_anchor(edge));
479 true
480 } else {
481 log::warn!(
482 "ignoring exclusive edge {edge:?}: must be a single edge of the surface anchor {anchor:?}"
483 );
484 false
485 }
486 }
487
488 fn set_exclusive_edge(&self, edge: Anchor) -> bool {
489 if let WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {
490 layer_surface,
491 anchor,
492 ..
493 }) = self
494 {
495 Self::apply_exclusive_edge(layer_surface, *anchor, edge)
496 } else {
497 false
498 }
499 }
500
501 fn destroy(&mut self) {
502 match self {
503 WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
504 xdg_surface,
505 toplevel,
506 decoration: _decoration,
507 dialog,
508 }) => {
509 // drop the dialog before toplevel so compositor can explicitly unapply it's effects
510 if let Some(dialog) = dialog {
511 dialog.destroy();
512 }
513
514 // The role object (toplevel) must always be destroyed before the xdg_surface.
515 // See https://wayland.app/protocols/xdg-shell#xdg_surface:request:destroy
516 toplevel.destroy();
517 xdg_surface.destroy();
518 }
519 WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
520 layer_surface.destroy();
521 }
522 WaylandSurfaceState::Popup(WaylandPopupSurfaceState {
523 xdg_surface,
524 xdg_popup,
525 ..
526 }) => {
527 // Role object before its xdg_surface, as with the toplevel above.
528 xdg_popup.destroy();
529 xdg_surface.destroy();
530 }
531 }
532 }
533}
534
535#[derive(Clone)]
536pub struct WaylandWindowStatePtr {
537 state: Rc<RefCell<WaylandWindowState>>,
538 callbacks: Rc<RefCell<Callbacks>>,
539}
540
541impl WaylandWindowState {
542 pub(crate) fn new(
543 handle: AnyWindowHandle,
544 surface: wl_surface::WlSurface,
545 surface_state: WaylandSurfaceState,
546 appearance: WindowAppearance,
547 viewport: Option<wp_viewport::WpViewport>,
548 client: WaylandClientStatePtr,
549 globals: Globals,
550 gpu_context: gpui_wgpu::GpuContext,
551 compositor_gpu: Option<CompositorGpuHint>,
552 options: WindowParams,
553 parent: Option<WaylandWindowStatePtr>,
554 ) -> anyhow::Result<Self> {
555 let renderer = {
556 let raw_window = RawWindow {
557 window: surface.id().as_ptr().cast::<c_void>(),
558 display: surface
559 .backend()
560 .upgrade()
561 .unwrap()
562 .display_ptr()
563 .cast::<c_void>(),
564 };
565 let config = WgpuSurfaceConfig {
566 size: Size {
567 width: DevicePixels(f32::from(options.bounds.size.width) as i32),
568 height: DevicePixels(f32::from(options.bounds.size.height) as i32),
569 },
570 transparent: true,
571 // Prefer Mailbox to avoid blocking. Falls back to FIFO if Mailbox is unsupported.
572 preferred_present_mode: Some(wgpu::PresentMode::Mailbox),
573 };
574 WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)?
575 };
576
577 if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state {
578 if let Some(title) = options.titlebar.and_then(|titlebar| titlebar.title) {
579 xdg_state.toplevel.set_title(title.to_string());
580 }
581
582 if let Some(app_id) = options.app_id.as_ref() {
583 xdg_state.toplevel.set_app_id(app_id.clone());
584 }
585
586 // Set max window size based on the GPU's maximum texture dimension.
587 // This prevents the window from being resized larger than what the GPU can render.
588 let max_texture_size = renderer.max_texture_size() as i32;
589 xdg_state
590 .toplevel
591 .set_max_size(max_texture_size, max_texture_size);
592 }
593
594 Ok(Self {
595 surface_state,
596 acknowledged_first_configure: false,
597 parent,
598 children: FxHashMap::default(),
599 surface,
600 app_id: options.app_id,
601 blur: None,
602 viewport,
603 globals,
604 outputs: HashMap::default(),
605 display: None,
606 renderer,
607 bounds: options.bounds,
608 scale: 1.0,
609 input_handler: None,
610 decorations: WindowDecorations::Client,
611 background_appearance: WindowBackgroundAppearance::Opaque,
612 fullscreen: false,
613 maximized: false,
614 tiling: Tiling::default(),
615 window_bounds: options.bounds,
616 in_progress_configure: None,
617 resize_throttle: false,
618 client,
619 appearance,
620 handle,
621 active: false,
622 hovered: false,
623 force_render_after_recovery: false,
624 renderer_presented: false,
625 in_progress_window_controls: None,
626 window_controls: WindowControls::default(),
627 client_inset: None,
628 accesskit_adapter: None,
629 })
630 }
631
632 pub fn is_transparent(&self) -> bool {
633 self.decorations == WindowDecorations::Client
634 || self.background_appearance != WindowBackgroundAppearance::Opaque
635 }
636
637 fn update_subpixel_layout(&mut self) {
638 use wayland_client::protocol::wl_output::Subpixel;
639 let is_bgr = self
640 .display
641 .as_ref()
642 .and_then(|(_, output)| output.subpixel)
643 .is_some_and(|s| s == Subpixel::HorizontalBgr);
644 self.renderer.set_subpixel_layout(is_bgr);
645 }
646
647 pub fn primary_output_scale(&mut self) -> i32 {
648 let mut scale = 1;
649 let mut current_output = self.display.take();
650 for (id, output) in self.outputs.iter() {
651 if let Some((_, output_data)) = ¤t_output {
652 if output.scale > output_data.scale {
653 current_output = Some((id.clone(), output.clone()));
654 }
655 } else {
656 current_output = Some((id.clone(), output.clone()));
657 }
658 scale = scale.max(output.scale);
659 }
660 self.display = current_output;
661 scale
662 }
663
664 pub fn inset(&self) -> Pixels {
665 match self.decorations {
666 WindowDecorations::Server => px(0.0),
667 WindowDecorations::Client => self.client_inset.unwrap_or(px(0.0)),
668 }
669 }
670}
671
672pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
673pub enum ImeInput {
674 InsertText(String),
675 SetMarkedText(String),
676 UnmarkText,
677 DeleteText,
678}
679
680impl Drop for WaylandWindow {
681 fn drop(&mut self) {
682 let mut state = self.0.state.borrow_mut();
683 let surface_id = state.surface.id();
684 if let Some(parent) = state.parent.as_ref() {
685 parent.state.borrow_mut().children.remove(&surface_id);
686 }
687
688 let client = state.client.clone();
689
690 state.renderer.destroy();
691
692 // Destroy blur first, this has no dependencies.
693 if let Some(blur) = &state.blur {
694 blur.release();
695 }
696
697 // Decorations must be destroyed before the xdg state.
698 // See https://wayland.app/protocols/xdg-decoration-unstable-v1#zxdg_toplevel_decoration_v1
699 if let Some(decoration) = &state.surface_state.decoration() {
700 decoration.destroy();
701 }
702
703 // Surface state might contain xdg_toplevel/xdg_surface which can be destroyed now that
704 // decorations are gone. layer_surface has no dependencies.
705 state.surface_state.destroy();
706
707 // Viewport must be destroyed before the wl_surface.
708 // See https://wayland.app/protocols/viewporter#wp_viewport
709 if let Some(viewport) = &state.viewport {
710 viewport.destroy();
711 }
712
713 // The wl_surface itself should always be destroyed last.
714 state.surface.destroy();
715
716 let state_ptr = self.0.clone();
717 state
718 .globals
719 .executor
720 .spawn(async move {
721 state_ptr.close();
722 client.drop_window(&surface_id)
723 })
724 .detach();
725 drop(state);
726 }
727}
728
729impl WaylandWindow {
730 fn borrow(&self) -> Ref<'_, WaylandWindowState> {
731 self.0.state.borrow()
732 }
733
734 fn borrow_mut(&self) -> RefMut<'_, WaylandWindowState> {
735 self.0.state.borrow_mut()
736 }
737
738 pub fn new(
739 handle: AnyWindowHandle,
740 globals: Globals,
741 gpu_context: gpui_wgpu::GpuContext,
742 compositor_gpu: Option<CompositorGpuHint>,
743 client: WaylandClientStatePtr,
744 params: WindowParams,
745 appearance: WindowAppearance,
746 parent: Option<WaylandWindowStatePtr>,
747 popup_grab: Option<(u32, wl_seat::WlSeat)>,
748 target_output: Option<wl_output::WlOutput>,
749 ) -> anyhow::Result<(Self, ObjectId)> {
750 let surface = globals.compositor.create_surface(&globals.qh, ());
751 let surface_state = WaylandSurfaceState::new(
752 &surface,
753 &globals,
754 ¶ms,
755 parent.clone(),
756 popup_grab,
757 target_output,
758 )?;
759
760 if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
761 fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
762 }
763
764 let viewport = globals
765 .viewporter
766 .as_ref()
767 .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));
768
769 let this = Self(WaylandWindowStatePtr {
770 state: Rc::new(RefCell::new(WaylandWindowState::new(
771 handle,
772 surface.clone(),
773 surface_state,
774 appearance,
775 viewport,
776 client,
777 globals,
778 gpu_context,
779 compositor_gpu,
780 params,
781 parent,
782 )?)),
783 callbacks: Rc::new(RefCell::new(Callbacks::default())),
784 });
785
786 // Kick things off
787 surface.commit();
788
789 Ok((this, surface.id()))
790 }
791}
792
793impl WaylandWindowStatePtr {
794 pub fn handle(&self) -> AnyWindowHandle {
795 self.state.borrow().handle
796 }
797
798 pub fn surface(&self) -> wl_surface::WlSurface {
799 self.state.borrow().surface.clone()
800 }
801
802 pub fn toplevel(&self) -> Option<xdg_toplevel::XdgToplevel> {
803 self.state.borrow().surface_state.toplevel().cloned()
804 }
805
806 /// The `xdg_surface` backing this window, if it has one. Used to anchor child popups.
807 pub fn xdg_surface(&self) -> Option<xdg_surface::XdgSurface> {
808 self.state.borrow().surface_state.xdg_surface().cloned()
809 }
810
811 /// The layer-shell surface backing this window, if it is one. Used to anchor child popups.
812 pub fn layer_surface(&self) -> Option<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1> {
813 self.state.borrow().surface_state.layer_surface().cloned()
814 }
815
816 /// This window's xdg window geometry in surface-local coordinates. Child popup anchor
817 /// rectangles are relative to it, while gpui coordinates are surface-local.
818 pub fn window_geometry(&self) -> Bounds<Pixels> {
819 let state = self.state.borrow();
820 inset_by_tiling(
821 state.bounds.map_origin(|_| px(0.0)),
822 state.inset(),
823 state.tiling,
824 )
825 }
826
827 pub fn ptr_eq(&self, other: &Self) -> bool {
828 Rc::ptr_eq(&self.state, &other.state)
829 }
830
831 pub fn add_child(&self, child: ObjectId, blocking: bool) {
832 let mut state = self.state.borrow_mut();
833 state.children.insert(child, blocking);
834 }
835
836 pub fn is_blocked(&self) -> bool {
837 let state = self.state.borrow();
838 state.children.values().any(|&blocking| blocking)
839 }
840
841 pub fn frame(&self) {
842 let mut state = self.state.borrow_mut();
843 state.surface.frame(&state.globals.qh, state.surface.id());
844 state.resize_throttle = false;
845 let force_render = state.force_render_after_recovery;
846 state.force_render_after_recovery = false;
847 drop(state);
848
849 let mut cb = self.callbacks.borrow_mut();
850 if let Some(fun) = cb.request_frame.as_mut() {
851 fun(RequestFrameOptions {
852 force_render,
853 ..Default::default()
854 });
855 self.update_ime_enabled();
856 }
857 }
858
859 fn update_ime_enabled(&self) {
860 let mut state = self.state.borrow_mut();
861 if !state.active {
862 return;
863 }
864 let client = state.client.clone();
865 let ime_enabled = state
866 .input_handler
867 .as_mut()
868 .map(|input_handler| input_handler.query_accepts_text_input())
869 .unwrap_or(true);
870 drop(state);
871 if Some(ime_enabled) == client.ime_enabled() {
872 return;
873 }
874
875 if ime_enabled {
876 client.enable_ime();
877 } else {
878 client.disable_ime();
879 }
880 }
881
882 pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
883 if let xdg_surface::Event::Configure { serial } = event {
884 {
885 let mut state = self.state.borrow_mut();
886 if let Some(window_controls) = state.in_progress_window_controls.take() {
887 state.window_controls = window_controls;
888
889 drop(state);
890 let mut callbacks = self.callbacks.borrow_mut();
891 if let Some(appearance_changed) = callbacks.appearance_changed.as_mut() {
892 appearance_changed();
893 }
894 }
895 }
896 {
897 let mut state = self.state.borrow_mut();
898
899 if let Some(mut configure) = state.in_progress_configure.take() {
900 let got_unmaximized = state.maximized && !configure.maximized;
901 state.fullscreen = configure.fullscreen;
902 state.maximized = configure.maximized;
903 state.tiling = configure.tiling;
904 // Limit interactive resizes to once per vblank
905 if configure.resizing && state.resize_throttle {
906 state.surface_state.ack_configure(serial);
907 return;
908 } else if configure.resizing {
909 state.resize_throttle = true;
910 }
911 if !configure.fullscreen && !configure.maximized {
912 configure.size = if got_unmaximized {
913 Some(state.window_bounds.size)
914 } else {
915 compute_outer_size(state.inset(), configure.size, state.tiling)
916 };
917 if let Some(size) = configure.size {
918 state.window_bounds = Bounds {
919 origin: Point::default(),
920 size,
921 };
922 }
923 }
924 drop(state);
925 if let Some(size) = configure.size {
926 self.resize(size);
927 }
928 }
929 }
930 let mut state = self.state.borrow_mut();
931 state.surface_state.ack_configure(serial);
932
933 let window_geometry = inset_by_tiling(
934 state.bounds.map_origin(|_| px(0.0)),
935 state.inset(),
936 state.tiling,
937 )
938 .map(|v| f32::from(v) as i32)
939 .map_size(|v| if v <= 0 { 1 } else { v });
940
941 state.surface_state.set_geometry(
942 window_geometry.origin.x,
943 window_geometry.origin.y,
944 window_geometry.size.width,
945 window_geometry.size.height,
946 );
947
948 let request_frame_callback = !state.acknowledged_first_configure;
949 if request_frame_callback {
950 state.acknowledged_first_configure = true;
951 drop(state);
952 self.frame();
953 }
954 }
955 }
956
957 pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
958 if let zxdg_toplevel_decoration_v1::Event::Configure { mode } = event {
959 match mode {
960 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
961 self.state.borrow_mut().decorations = WindowDecorations::Server;
962 let callback = self.callbacks.borrow_mut().appearance_changed.take();
963 if let Some(mut fun) = callback {
964 fun();
965 self.callbacks.borrow_mut().appearance_changed = Some(fun);
966 }
967 }
968 WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
969 self.state.borrow_mut().decorations = WindowDecorations::Client;
970 // Update background to be transparent
971 let callback = self.callbacks.borrow_mut().appearance_changed.take();
972 if let Some(mut fun) = callback {
973 fun();
974 self.callbacks.borrow_mut().appearance_changed = Some(fun);
975 }
976 }
977 WEnum::Value(_) => {
978 log::warn!("Unknown decoration mode");
979 }
980 WEnum::Unknown(v) => {
981 log::warn!("Unknown decoration mode: {}", v);
982 }
983 }
984 }
985 }
986
987 pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
988 if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
989 self.rescale(scale as f32 / 120.0);
990 }
991 }
992
993 pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
994 match event {
995 xdg_toplevel::Event::Configure {
996 width,
997 height,
998 states,
999 } => {
1000 let size = if width == 0 || height == 0 {
1001 None
1002 } else {
1003 Some(size(px(width as f32), px(height as f32)))
1004 };
1005
1006 let states = extract_states::<xdg_toplevel::State>(&states);
1007
1008 let mut tiling = Tiling::default();
1009 let mut fullscreen = false;
1010 let mut maximized = false;
1011 let mut resizing = false;
1012
1013 for state in states {
1014 match state {
1015 xdg_toplevel::State::Maximized => {
1016 maximized = true;
1017 }
1018 xdg_toplevel::State::Fullscreen => {
1019 fullscreen = true;
1020 }
1021 xdg_toplevel::State::Resizing => resizing = true,
1022 xdg_toplevel::State::TiledTop => {
1023 tiling.top = true;
1024 }
1025 xdg_toplevel::State::TiledLeft => {
1026 tiling.left = true;
1027 }
1028 xdg_toplevel::State::TiledRight => {
1029 tiling.right = true;
1030 }
1031 xdg_toplevel::State::TiledBottom => {
1032 tiling.bottom = true;
1033 }
1034 _ => {
1035 // noop
1036 }
1037 }
1038 }
1039
1040 if fullscreen || maximized {
1041 tiling = Tiling::tiled();
1042 }
1043
1044 let mut state = self.state.borrow_mut();
1045 state.in_progress_configure = Some(InProgressConfigure {
1046 size,
1047 fullscreen,
1048 maximized,
1049 resizing,
1050 tiling,
1051 });
1052
1053 false
1054 }
1055 xdg_toplevel::Event::Close => {
1056 let mut cb = self.callbacks.borrow_mut();
1057 if let Some(mut should_close) = cb.should_close.take() {
1058 let result = (should_close)();
1059 cb.should_close = Some(should_close);
1060 if result {
1061 drop(cb);
1062 self.close();
1063 }
1064 result
1065 } else {
1066 true
1067 }
1068 }
1069 xdg_toplevel::Event::WmCapabilities { capabilities } => {
1070 let mut window_controls = WindowControls {
1071 maximize: false,
1072 minimize: false,
1073 fullscreen: false,
1074 window_menu: false,
1075 };
1076
1077 let states = extract_states::<xdg_toplevel::WmCapabilities>(&capabilities);
1078
1079 for state in states {
1080 match state {
1081 xdg_toplevel::WmCapabilities::Maximize => {
1082 window_controls.maximize = true;
1083 }
1084 xdg_toplevel::WmCapabilities::Minimize => {
1085 window_controls.minimize = true;
1086 }
1087 xdg_toplevel::WmCapabilities::Fullscreen => {
1088 window_controls.fullscreen = true;
1089 }
1090 xdg_toplevel::WmCapabilities::WindowMenu => {
1091 window_controls.window_menu = true;
1092 }
1093 _ => {}
1094 }
1095 }
1096
1097 let mut state = self.state.borrow_mut();
1098 state.in_progress_window_controls = Some(window_controls);
1099 false
1100 }
1101 _ => false,
1102 }
1103 }
1104
1105 pub fn handle_layersurface_event(&self, event: zwlr_layer_surface_v1::Event) -> bool {
1106 match event {
1107 zwlr_layer_surface_v1::Event::Configure {
1108 width,
1109 height,
1110 serial,
1111 } => {
1112 let size = if width == 0 || height == 0 {
1113 None
1114 } else {
1115 Some(size(px(width as f32), px(height as f32)))
1116 };
1117
1118 let mut state = self.state.borrow_mut();
1119 state.in_progress_configure = Some(InProgressConfigure {
1120 size,
1121 fullscreen: false,
1122 maximized: false,
1123 resizing: false,
1124 tiling: Tiling::default(),
1125 });
1126 drop(state);
1127
1128 // just do the same thing we'd do as an xdg_surface
1129 self.handle_xdg_surface_event(xdg_surface::Event::Configure { serial });
1130
1131 false
1132 }
1133 zwlr_layer_surface_v1::Event::Closed => {
1134 // unlike xdg, we don't have a choice here: the surface is closing.
1135 true
1136 }
1137 _ => false,
1138 }
1139 }
1140
1141 // Returns `true` if the popup should be closed.
1142 pub fn handle_popup_event(&self, event: xdg_popup::Event) -> bool {
1143 match event {
1144 // Only the size is needed, the position is the compositor's. The following
1145 // xdg_surface.configure applies the change.
1146 xdg_popup::Event::Configure { width, height, .. } => {
1147 let size = if width <= 0 || height <= 0 {
1148 None
1149 } else {
1150 Some(size(px(width as f32), px(height as f32)))
1151 };
1152
1153 self.state.borrow_mut().in_progress_configure = Some(InProgressConfigure {
1154 size,
1155 fullscreen: false,
1156 maximized: false,
1157 resizing: false,
1158 tiling: Tiling::default(),
1159 });
1160
1161 false
1162 }
1163 xdg_popup::Event::PopupDone => true,
1164 // Precedes the reposition's Configure, which does the work. The token is not needed.
1165 xdg_popup::Event::Repositioned { .. } => false,
1166 _ => false,
1167 }
1168 }
1169
1170 #[allow(clippy::mutable_key_type)]
1171 pub fn handle_surface_event(
1172 &self,
1173 event: wl_surface::Event,
1174 outputs: HashMap<ObjectId, Output>,
1175 ) {
1176 let mut state = self.state.borrow_mut();
1177
1178 match event {
1179 wl_surface::Event::Enter { output } => {
1180 let id = output.id();
1181
1182 let Some(output) = outputs.get(&id) else {
1183 return;
1184 };
1185
1186 state.outputs.insert(id, output.clone());
1187
1188 let scale = state.primary_output_scale();
1189 state.update_subpixel_layout();
1190
1191 // We use `PreferredBufferScale` instead to set the scale if it's available
1192 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
1193 state.surface.set_buffer_scale(scale);
1194 drop(state);
1195 self.rescale(scale as f32);
1196 }
1197 }
1198 wl_surface::Event::Leave { output } => {
1199 state.outputs.remove(&output.id());
1200
1201 let scale = state.primary_output_scale();
1202 state.update_subpixel_layout();
1203
1204 // We use `PreferredBufferScale` instead to set the scale if it's available
1205 if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
1206 state.surface.set_buffer_scale(scale);
1207 drop(state);
1208 self.rescale(scale as f32);
1209 }
1210 }
1211 wl_surface::Event::PreferredBufferScale { factor } => {
1212 // We use `WpFractionalScale` instead to set the scale if it's available
1213 if state.globals.fractional_scale_manager.is_none() {
1214 state.surface.set_buffer_scale(factor);
1215 drop(state);
1216 self.rescale(factor as f32);
1217 }
1218 }
1219 _ => {}
1220 }
1221 }
1222
1223 pub fn handle_ime(&self, ime: ImeInput) {
1224 if self.is_blocked() {
1225 return;
1226 }
1227 let mut state = self.state.borrow_mut();
1228 if let Some(mut input_handler) = state.input_handler.take() {
1229 drop(state);
1230 match ime {
1231 ImeInput::InsertText(text) => {
1232 input_handler.replace_text_in_range(None, &text);
1233 }
1234 ImeInput::SetMarkedText(text) => {
1235 input_handler.replace_and_mark_text_in_range(None, &text, None);
1236 }
1237 ImeInput::UnmarkText => {
1238 input_handler.unmark_text();
1239 }
1240 ImeInput::DeleteText => {
1241 if let Some(marked) = input_handler.marked_text_range() {
1242 input_handler.replace_text_in_range(Some(marked), "");
1243 }
1244 }
1245 }
1246 self.state.borrow_mut().input_handler = Some(input_handler);
1247 }
1248 }
1249
1250 pub fn get_ime_area(&self) -> Option<Bounds<Pixels>> {
1251 let mut state = self.state.borrow_mut();
1252 let mut bounds: Option<Bounds<Pixels>> = None;
1253 if let Some(mut input_handler) = state.input_handler.take() {
1254 drop(state);
1255 bounds = input_handler.ime_candidate_bounds();
1256 self.state.borrow_mut().input_handler = Some(input_handler);
1257 }
1258 bounds
1259 }
1260
1261 pub fn set_size_and_scale(&self, size: Option<Size<Pixels>>, scale: Option<f32>) {
1262 let (size, scale) = {
1263 let mut state = self.state.borrow_mut();
1264 if size.is_none_or(|size| size == state.bounds.size)
1265 && scale.is_none_or(|scale| scale == state.scale)
1266 {
1267 return;
1268 }
1269 if let Some(size) = size {
1270 state.bounds.size = size;
1271 }
1272 if let Some(scale) = scale {
1273 state.scale = scale;
1274 }
1275 let device_bounds = state.bounds.to_device_pixels(state.scale);
1276 state.renderer.update_drawable_size(device_bounds.size);
1277 (state.bounds.size, state.scale)
1278 };
1279
1280 let callback = self.callbacks.borrow_mut().resize.take();
1281 if let Some(mut fun) = callback {
1282 fun(size, scale);
1283 self.callbacks.borrow_mut().resize = Some(fun);
1284 }
1285
1286 {
1287 let state = self.state.borrow();
1288 if let Some(viewport) = &state.viewport {
1289 viewport
1290 .set_destination(f32::from(size.width) as i32, f32::from(size.height) as i32);
1291 }
1292 }
1293 }
1294
1295 pub fn resize(&self, size: Size<Pixels>) {
1296 self.set_size_and_scale(Some(size), None);
1297 }
1298
1299 pub fn rescale(&self, scale: f32) {
1300 self.set_size_and_scale(None, Some(scale));
1301 }
1302
1303 pub fn close(&self) {
1304 let state = self.state.borrow();
1305 let client = state.client.get_client();
1306 let children = state.children.keys().cloned().collect::<Vec<_>>();
1307 drop(state);
1308
1309 for child in children {
1310 let mut client_state = client.borrow_mut();
1311 let window = get_window(&mut client_state, &child);
1312 drop(client_state);
1313
1314 if let Some(child) = window {
1315 child.close();
1316 }
1317 }
1318 let mut callbacks = self.callbacks.borrow_mut();
1319 if let Some(fun) = callbacks.close.take() {
1320 fun()
1321 }
1322 }
1323
1324 pub fn handle_input(&self, input: PlatformInput) {
1325 if self.is_blocked() {
1326 return;
1327 }
1328 let callback = self.callbacks.borrow_mut().input.take();
1329 if let Some(mut fun) = callback {
1330 let result = fun(input.clone());
1331 self.callbacks.borrow_mut().input = Some(fun);
1332 if !result.propagate {
1333 return;
1334 }
1335 }
1336 if let PlatformInput::KeyDown(event) = input
1337 && event.keystroke.modifiers.is_subset_of(&Modifiers::shift())
1338 && let Some(key_char) = &event.keystroke.key_char
1339 {
1340 let mut state = self.state.borrow_mut();
1341 if let Some(mut input_handler) = state.input_handler.take() {
1342 drop(state);
1343 input_handler.replace_text_in_range(None, key_char);
1344 self.state.borrow_mut().input_handler = Some(input_handler);
1345 }
1346 }
1347 }
1348
1349 pub fn set_focused(&self, focus: bool) {
1350 self.state.borrow_mut().active = focus;
1351 let callback = self.callbacks.borrow_mut().active_status_change.take();
1352 if let Some(mut fun) = callback {
1353 fun(focus);
1354 self.callbacks.borrow_mut().active_status_change = Some(fun);
1355 }
1356 if let Some(adapter) = self.state.borrow_mut().accesskit_adapter.as_mut() {
1357 adapter.update_window_focus_state(focus);
1358 }
1359 }
1360
1361 pub fn set_hovered(&self, focus: bool) {
1362 let callback = self.callbacks.borrow_mut().hover_status_change.take();
1363 if let Some(mut fun) = callback {
1364 fun(focus);
1365 self.callbacks.borrow_mut().hover_status_change = Some(fun);
1366 }
1367 }
1368
1369 pub fn set_appearance(&mut self, appearance: WindowAppearance) {
1370 self.state.borrow_mut().appearance = appearance;
1371
1372 let callback = self.callbacks.borrow_mut().appearance_changed.take();
1373 if let Some(mut fun) = callback {
1374 fun();
1375 self.callbacks.borrow_mut().appearance_changed = Some(fun);
1376 }
1377 }
1378
1379 pub fn set_button_layout(&self) {
1380 let callback = self.callbacks.borrow_mut().button_layout_changed.take();
1381 if let Some(mut fun) = callback {
1382 fun();
1383 self.callbacks.borrow_mut().button_layout_changed = Some(fun);
1384 }
1385 }
1386
1387 pub fn primary_output_scale(&self) -> i32 {
1388 self.state.borrow_mut().primary_output_scale()
1389 }
1390}
1391
1392fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
1393where
1394 <S as TryFrom<u32>>::Error: 'a,
1395{
1396 states
1397 .chunks_exact(4)
1398 .flat_map(TryInto::<[u8; 4]>::try_into)
1399 .map(u32::from_ne_bytes)
1400 .flat_map(S::try_from)
1401}
1402
1403impl rwh::HasWindowHandle for WaylandWindow {
1404 fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
1405 let surface = self.0.surface().id().as_ptr() as *mut libc::c_void;
1406 let c_ptr = NonNull::new(surface).ok_or(rwh::HandleError::Unavailable)?;
1407 let handle = rwh::WaylandWindowHandle::new(c_ptr);
1408 let raw_handle = rwh::RawWindowHandle::Wayland(handle);
1409 Ok(unsafe { rwh::WindowHandle::borrow_raw(raw_handle) })
1410 }
1411}
1412
1413impl rwh::HasDisplayHandle for WaylandWindow {
1414 fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
1415 let display = self
1416 .0
1417 .surface()
1418 .backend()
1419 .upgrade()
1420 .ok_or(rwh::HandleError::Unavailable)?
1421 .display_ptr() as *mut libc::c_void;
1422
1423 let c_ptr = NonNull::new(display).ok_or(rwh::HandleError::Unavailable)?;
1424 let handle = rwh::WaylandDisplayHandle::new(c_ptr);
1425 let raw_handle = rwh::RawDisplayHandle::Wayland(handle);
1426 Ok(unsafe { rwh::DisplayHandle::borrow_raw(raw_handle) })
1427 }
1428}
1429
1430impl PlatformWindow for WaylandWindow {
1431 fn bounds(&self) -> Bounds<Pixels> {
1432 self.borrow().bounds
1433 }
1434
1435 fn is_maximized(&self) -> bool {
1436 self.borrow().maximized
1437 }
1438
1439 fn window_bounds(&self) -> WindowBounds {
1440 let state = self.borrow();
1441 if state.fullscreen {
1442 WindowBounds::Fullscreen(state.window_bounds)
1443 } else if state.maximized {
1444 WindowBounds::Maximized(state.window_bounds)
1445 } else {
1446 drop(state);
1447 WindowBounds::Windowed(self.bounds())
1448 }
1449 }
1450
1451 fn inner_window_bounds(&self) -> WindowBounds {
1452 let state = self.borrow();
1453 if state.fullscreen {
1454 WindowBounds::Fullscreen(state.window_bounds)
1455 } else if state.maximized {
1456 WindowBounds::Maximized(state.window_bounds)
1457 } else {
1458 let inset = state.inset();
1459 drop(state);
1460 WindowBounds::Windowed(self.bounds().inset(inset))
1461 }
1462 }
1463
1464 fn content_size(&self) -> Size<Pixels> {
1465 self.borrow().bounds.size
1466 }
1467
1468 fn resize(&mut self, size: Size<Pixels>) {
1469 let state = self.borrow();
1470 let state_ptr = self.0.clone();
1471
1472 // A popup's placement is the compositor's, so a resize re-runs the positioner and the
1473 // configure reply drives the buffer resize. Before the first configure the popup is
1474 // unmapped and cannot reposition, but the initial positioner already carries the size.
1475 if matches!(state.surface_state, WaylandSurfaceState::Popup(_)) {
1476 if state.acknowledged_first_configure {
1477 let parent_geometry = state
1478 .parent
1479 .as_ref()
1480 .map(|parent| parent.window_geometry())
1481 .unwrap_or_default();
1482 state
1483 .surface_state
1484 .reposition_popup(&state.globals, size, parent_geometry);
1485 }
1486 return;
1487 }
1488
1489 // Keep window geometry consistent with configure handling. On Wayland, window geometry is
1490 // surface-local: resizing should not attempt to translate the window; the compositor
1491 // controls placement. We also account for client-side decoration insets and tiling.
1492 let window_geometry = inset_by_tiling(
1493 Bounds {
1494 origin: Point::default(),
1495 size,
1496 },
1497 state.inset(),
1498 state.tiling,
1499 )
1500 .map(|v| f32::from(v) as i32)
1501 .map_size(|v| if v <= 0 { 1 } else { v });
1502
1503 state.surface_state.set_geometry(
1504 window_geometry.origin.x,
1505 window_geometry.origin.y,
1506 window_geometry.size.width,
1507 window_geometry.size.height,
1508 );
1509
1510 state
1511 .globals
1512 .executor
1513 .spawn(async move { state_ptr.resize(size) })
1514 .detach();
1515 }
1516
1517 fn scale_factor(&self) -> f32 {
1518 self.borrow().scale
1519 }
1520
1521 fn appearance(&self) -> WindowAppearance {
1522 self.borrow().appearance
1523 }
1524
1525 fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1526 let state = self.borrow();
1527 state.display.as_ref().map(|(id, display)| {
1528 Rc::new(WaylandDisplay {
1529 id: id.clone(),
1530 name: display.name.clone(),
1531 bounds: display.bounds.to_pixels(state.scale),
1532 }) as Rc<dyn PlatformDisplay>
1533 })
1534 }
1535
1536 fn mouse_position(&self) -> Point<Pixels> {
1537 self.borrow()
1538 .client
1539 .get_client()
1540 .borrow()
1541 .mouse_location
1542 .unwrap_or_default()
1543 }
1544
1545 fn modifiers(&self) -> Modifiers {
1546 self.borrow().client.get_client().borrow().modifiers
1547 }
1548
1549 fn capslock(&self) -> Capslock {
1550 self.borrow().client.get_client().borrow().capslock
1551 }
1552
1553 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
1554 self.borrow_mut().input_handler = Some(input_handler);
1555 }
1556
1557 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
1558 self.borrow_mut().input_handler.take()
1559 }
1560
1561 fn prompt(
1562 &self,
1563 _level: PromptLevel,
1564 _msg: &str,
1565 _detail: Option<&str>,
1566 _answers: &[PromptButton],
1567 ) -> Option<Receiver<usize>> {
1568 None
1569 }
1570
1571 fn activate(&self) {
1572 // Try to request an activation token. Even though the activation is likely going to be rejected,
1573 // KWin and Mutter can use the app_id to visually indicate we're requesting attention.
1574 let state = self.borrow();
1575 if let (Some(activation), Some(app_id)) = (&state.globals.activation, state.app_id.clone())
1576 {
1577 state.client.set_pending_activation(state.surface.id());
1578 let token = activation.get_activation_token(&state.globals.qh, ());
1579 // The serial isn't exactly important here, since the activation is probably going to be rejected anyway.
1580 let serial = state.client.get_serial(SerialKind::MousePress);
1581 token.set_app_id(app_id);
1582 token.set_serial(serial.as_raw(), &state.globals.seat);
1583 token.set_surface(&state.surface);
1584 token.commit();
1585 }
1586 }
1587
1588 fn request_attention(&self) {}
1589
1590 fn is_active(&self) -> bool {
1591 self.borrow().active
1592 }
1593
1594 fn is_hovered(&self) -> bool {
1595 self.borrow().hovered
1596 }
1597
1598 fn set_title(&mut self, title: &str) {
1599 if let Some(toplevel) = self.borrow().surface_state.toplevel() {
1600 toplevel.set_title(title.to_string());
1601 }
1602 }
1603
1604 fn set_app_id(&mut self, app_id: &str) {
1605 let mut state = self.borrow_mut();
1606 if let Some(toplevel) = state.surface_state.toplevel() {
1607 toplevel.set_app_id(app_id.to_owned());
1608 }
1609 state.app_id = Some(app_id.to_owned());
1610 }
1611
1612 fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
1613 let mut state = self.borrow_mut();
1614 state.background_appearance = background_appearance;
1615 update_window(state);
1616 }
1617
1618 fn background_appearance(&self) -> WindowBackgroundAppearance {
1619 self.borrow().background_appearance
1620 }
1621
1622 fn is_subpixel_rendering_supported(&self) -> bool {
1623 let client = self.borrow().client.get_client();
1624 let state = client.borrow();
1625 state
1626 .gpu_context
1627 .borrow()
1628 .as_ref()
1629 .is_some_and(|ctx| ctx.supports_dual_source_blending())
1630 }
1631
1632 fn minimize(&self) {
1633 if let Some(toplevel) = self.borrow().surface_state.toplevel() {
1634 toplevel.set_minimized();
1635 }
1636 }
1637
1638 fn zoom(&self) {
1639 let state = self.borrow();
1640 if let Some(toplevel) = state.surface_state.toplevel() {
1641 if !state.maximized {
1642 toplevel.set_maximized();
1643 } else {
1644 toplevel.unset_maximized();
1645 }
1646 }
1647 }
1648
1649 fn toggle_fullscreen(&self) {
1650 let state = self.borrow();
1651 if let Some(toplevel) = state.surface_state.toplevel() {
1652 if !state.fullscreen {
1653 toplevel.set_fullscreen(None);
1654 } else {
1655 toplevel.unset_fullscreen();
1656 }
1657 }
1658 }
1659
1660 fn is_fullscreen(&self) -> bool {
1661 self.borrow().fullscreen
1662 }
1663
1664 fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
1665 self.0.callbacks.borrow_mut().request_frame = Some(callback);
1666 }
1667
1668 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>) {
1669 self.0.callbacks.borrow_mut().input = Some(callback);
1670 }
1671
1672 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1673 self.0.callbacks.borrow_mut().active_status_change = Some(callback);
1674 }
1675
1676 fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1677 self.0.callbacks.borrow_mut().hover_status_change = Some(callback);
1678 }
1679
1680 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
1681 self.0.callbacks.borrow_mut().resize = Some(callback);
1682 }
1683
1684 fn on_moved(&self, callback: Box<dyn FnMut()>) {
1685 self.0.callbacks.borrow_mut().moved = Some(callback);
1686 }
1687
1688 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
1689 self.0.callbacks.borrow_mut().should_close = Some(callback);
1690 }
1691
1692 fn on_close(&self, callback: Box<dyn FnOnce()>) {
1693 self.0.callbacks.borrow_mut().close = Some(callback);
1694 }
1695
1696 fn on_hit_test_window_control(&self, _callback: Box<dyn FnMut() -> Option<WindowControlArea>>) {
1697 }
1698
1699 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1700 self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
1701 }
1702
1703 fn on_button_layout_changed(&self, callback: Box<dyn FnMut()>) {
1704 self.0.callbacks.borrow_mut().button_layout_changed = Some(callback);
1705 }
1706
1707 fn draw(&self, scene: &Scene) {
1708 let mut state = self.borrow_mut();
1709
1710 if state.renderer.device_lost() {
1711 let raw_window = RawWindow {
1712 window: state.surface.id().as_ptr().cast::<std::ffi::c_void>(),
1713 display: state
1714 .surface
1715 .backend()
1716 .upgrade()
1717 .unwrap()
1718 .display_ptr()
1719 .cast::<std::ffi::c_void>(),
1720 };
1721 match state.renderer.recover(&raw_window) {
1722 Ok(()) => {}
1723 Err(err) => {
1724 log::warn!("GPU recovery failed, will retry on next frame: {err}");
1725 }
1726 }
1727
1728 state.force_render_after_recovery = true;
1729 return;
1730 }
1731
1732 state.renderer_presented = state.renderer.draw(scene);
1733
1734 if state.renderer.needs_redraw() {
1735 state.force_render_after_recovery = true;
1736 }
1737 }
1738
1739 fn completed_frame(&self) {
1740 let mut state = self.borrow_mut();
1741
1742 // Work around a bug in old versions of wlroots where committing without a buffer attached
1743 // can cause invalid synchronization that leads to graphical corruption.
1744 if !state.renderer_presented {
1745 state.surface.commit();
1746 }
1747
1748 state.renderer_presented = false;
1749 }
1750
1751 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1752 let state = self.borrow();
1753 state.renderer.sprite_atlas().clone()
1754 }
1755
1756 fn show_window_menu(&self, position: Point<Pixels>) {
1757 let state = self.borrow();
1758 let serial = state.client.get_serial(SerialKind::MousePress);
1759 if let Some(toplevel) = state.surface_state.toplevel() {
1760 toplevel.show_window_menu(
1761 &state.globals.seat,
1762 serial.as_raw(),
1763 f32::from(position.x) as i32,
1764 f32::from(position.y) as i32,
1765 );
1766 }
1767 }
1768
1769 fn start_window_move(&self) {
1770 let state = self.borrow();
1771 let serial = state.client.get_serial(SerialKind::MousePress);
1772 if let Some(toplevel) = state.surface_state.toplevel() {
1773 toplevel._move(&state.globals.seat, serial.as_raw());
1774 }
1775 }
1776
1777 fn start_window_resize(&self, edge: gpui::ResizeEdge) {
1778 let state = self.borrow();
1779 if let Some(toplevel) = state.surface_state.toplevel() {
1780 toplevel.resize(
1781 &state.globals.seat,
1782 state.client.get_serial(SerialKind::MousePress).as_raw(),
1783 edge.to_xdg(),
1784 )
1785 }
1786 }
1787
1788 fn set_exclusive_zone(&self, zone: Pixels) {
1789 let state = self.borrow();
1790 if state
1791 .surface_state
1792 .set_exclusive_zone(f32::from(zone) as i32)
1793 {
1794 // Commit to apply it immediately, otherwise it only takes effect
1795 // on the next frame.
1796 state.surface.commit();
1797 }
1798 }
1799
1800 fn set_exclusive_edge(&self, edge: Anchor) {
1801 let state = self.borrow();
1802 if state.surface_state.set_exclusive_edge(edge) {
1803 // Commit to apply it immediately, otherwise it only takes effect
1804 // on the next frame.
1805 state.surface.commit();
1806 }
1807 }
1808
1809 fn set_input_region(&self, region: Option<&[Bounds<Pixels>]>) {
1810 let state = self.borrow();
1811 match region {
1812 // No region means the whole surface receives input.
1813 None => state.surface.set_input_region(None),
1814 // A region restricts input to its rectangles. An empty region
1815 // receives no input at all.
1816 Some(rects) => {
1817 let wl_region = state
1818 .globals
1819 .compositor
1820 .create_region(&state.globals.qh, ());
1821 for rect in rects {
1822 let rect = rect.map(|pixels| f32::from(pixels) as i32);
1823 wl_region.add(
1824 rect.origin.x,
1825 rect.origin.y,
1826 rect.size.width,
1827 rect.size.height,
1828 );
1829 }
1830 state.surface.set_input_region(Some(&wl_region));
1831 wl_region.destroy();
1832 }
1833 }
1834
1835 // Commit so the new input region applies immediately. Otherwise it
1836 // waits for the next frame, which could be the very click we want to
1837 // allow passing through.
1838 state.surface.commit();
1839 }
1840
1841 fn window_decorations(&self) -> Decorations {
1842 let state = self.borrow();
1843 match state.decorations {
1844 WindowDecorations::Server => Decorations::Server,
1845 WindowDecorations::Client => Decorations::Client {
1846 tiling: state.tiling,
1847 },
1848 }
1849 }
1850
1851 fn request_decorations(&self, decorations: WindowDecorations) {
1852 let mut state = self.borrow_mut();
1853 match state.surface_state.decoration().as_ref() {
1854 Some(decoration) => {
1855 decoration.set_mode(decorations.to_xdg());
1856 state.decorations = decorations;
1857 update_window(state);
1858 }
1859 None => {
1860 if matches!(decorations, WindowDecorations::Server) {
1861 log::info!(
1862 "Server-side decorations requested, but the Wayland server does not support them. Falling back to client-side decorations."
1863 );
1864 }
1865 state.decorations = WindowDecorations::Client;
1866 update_window(state);
1867 }
1868 }
1869 }
1870
1871 fn window_controls(&self) -> WindowControls {
1872 self.borrow().window_controls
1873 }
1874
1875 fn set_client_inset(&self, inset: Pixels) {
1876 let mut state = self.borrow_mut();
1877 if Some(inset) != state.client_inset {
1878 state.client_inset = Some(inset);
1879 update_window(state);
1880 }
1881 }
1882
1883 fn update_ime_position(&self, bounds: Bounds<Pixels>) {
1884 let state = self.borrow();
1885 state.client.update_ime_position(bounds);
1886 }
1887
1888 fn gpu_specs(&self) -> Option<GpuSpecs> {
1889 self.borrow().renderer.gpu_specs().into()
1890 }
1891
1892 fn play_system_bell(&self) {
1893 let state = self.borrow();
1894 let surface = if state.surface_state.toplevel().is_some() {
1895 Some(&state.surface)
1896 } else {
1897 None
1898 };
1899 if let Some(bell) = state.globals.system_bell.as_ref() {
1900 bell.ring(surface);
1901 }
1902 }
1903
1904 fn a11y_init(&self, callbacks: gpui::A11yCallbacks) {
1905 let activation_handler = TrivialActivationHandler {
1906 callback: callbacks.activation,
1907 };
1908 let action_handler = TrivialActionHandler(callbacks.action);
1909 let deactivation_handler = TrivialDeactivationHandler {
1910 callback: callbacks.deactivation,
1911 };
1912
1913 let adapter =
1914 accesskit_unix::Adapter::new(activation_handler, action_handler, deactivation_handler);
1915
1916 self.borrow_mut().accesskit_adapter = Some(adapter);
1917 }
1918
1919 fn a11y_tree_update(&self, tree_update: accesskit::TreeUpdate) {
1920 let mut state = self.borrow_mut();
1921 if let Some(adapter) = state.accesskit_adapter.as_mut() {
1922 adapter.update_if_active(|| tree_update);
1923 }
1924 }
1925
1926 fn a11y_update_window_bounds(&self) {
1927 // Wayland doesn't expose window position, so this is a no-op
1928 }
1929}
1930
1931struct TrivialActivationHandler {
1932 callback: Box<dyn Fn() -> Option<accesskit::TreeUpdate> + Send + 'static>,
1933}
1934
1935impl accesskit::ActivationHandler for TrivialActivationHandler {
1936 fn request_initial_tree(&mut self) -> Option<accesskit::TreeUpdate> {
1937 (self.callback)()
1938 }
1939}
1940
1941struct TrivialActionHandler(Box<dyn Fn(accesskit::ActionRequest) + Send + 'static>);
1942
1943impl accesskit::ActionHandler for TrivialActionHandler {
1944 fn do_action(&mut self, request: accesskit::ActionRequest) {
1945 (self.0)(request);
1946 }
1947}
1948
1949struct TrivialDeactivationHandler {
1950 callback: Box<dyn Fn() + Send + 'static>,
1951}
1952
1953impl accesskit::DeactivationHandler for TrivialDeactivationHandler {
1954 fn deactivate_accessibility(&mut self) {
1955 (self.callback)();
1956 }
1957}
1958
1959fn update_window(mut state: RefMut<WaylandWindowState>) {
1960 let opaque = !state.is_transparent();
1961
1962 state.renderer.update_transparency(!opaque);
1963 let opaque_area = state.window_bounds.map(|v| f32::from(v) as i32);
1964 opaque_area.inset(f32::from(state.inset()) as i32);
1965
1966 let region = state
1967 .globals
1968 .compositor
1969 .create_region(&state.globals.qh, ());
1970 region.add(
1971 opaque_area.origin.x,
1972 opaque_area.origin.y,
1973 opaque_area.size.width,
1974 opaque_area.size.height,
1975 );
1976
1977 // Note that rounded corners make this rectangle API hard to work with.
1978 // As this is common when using CSD, let's just disable this API.
1979 if state.background_appearance == WindowBackgroundAppearance::Opaque
1980 && state.decorations == WindowDecorations::Server
1981 {
1982 // Promise the compositor that this region of the window surface
1983 // contains no transparent pixels. This allows the compositor to skip
1984 // updating whatever is behind the surface for better performance.
1985 state.surface.set_opaque_region(Some(®ion));
1986 } else {
1987 state.surface.set_opaque_region(None);
1988 }
1989
1990 if let Some(ref blur_manager) = state.globals.blur_manager {
1991 if state.background_appearance == WindowBackgroundAppearance::Blurred {
1992 if state.blur.is_none() {
1993 let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
1994 state.blur = Some(blur);
1995 }
1996 state.blur.as_ref().unwrap().commit();
1997 } else {
1998 // It probably doesn't hurt to clear the blur for opaque windows
1999 blur_manager.unset(&state.surface);
2000 if let Some(b) = state.blur.take() {
2001 b.release()
2002 }
2003 }
2004 }
2005
2006 region.destroy();
2007}
2008
2009pub(crate) trait WindowDecorationsExt {
2010 fn to_xdg(self) -> zxdg_toplevel_decoration_v1::Mode;
2011}
2012
2013impl WindowDecorationsExt for WindowDecorations {
2014 fn to_xdg(self) -> zxdg_toplevel_decoration_v1::Mode {
2015 match self {
2016 WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
2017 WindowDecorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
2018 }
2019 }
2020}
2021
2022pub(crate) trait ResizeEdgeWaylandExt {
2023 fn to_xdg(self) -> xdg_toplevel::ResizeEdge;
2024}
2025
2026impl ResizeEdgeWaylandExt for ResizeEdge {
2027 fn to_xdg(self) -> xdg_toplevel::ResizeEdge {
2028 match self {
2029 ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
2030 ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
2031 ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
2032 ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
2033 ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
2034 ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
2035 ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
2036 ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
2037 }
2038 }
2039}
2040
2041/// The configuration event is in terms of the window geometry, which we are constantly
2042/// updating to account for the client decorations. But that's not the area we want to render
2043/// to, due to our intrusize CSD. So, here we calculate the 'actual' size, by adding back in the insets
2044fn compute_outer_size(
2045 inset: Pixels,
2046 new_size: Option<Size<Pixels>>,
2047 tiling: Tiling,
2048) -> Option<Size<Pixels>> {
2049 new_size.map(|mut new_size| {
2050 if !tiling.top {
2051 new_size.height += inset;
2052 }
2053 if !tiling.bottom {
2054 new_size.height += inset;
2055 }
2056 if !tiling.left {
2057 new_size.width += inset;
2058 }
2059 if !tiling.right {
2060 new_size.width += inset;
2061 }
2062
2063 new_size
2064 })
2065}
2066
2067fn inset_by_tiling(mut bounds: Bounds<Pixels>, inset: Pixels, tiling: Tiling) -> Bounds<Pixels> {
2068 if !tiling.top {
2069 bounds.origin.y += inset;
2070 bounds.size.height -= inset;
2071 }
2072 if !tiling.bottom {
2073 bounds.size.height -= inset;
2074 }
2075 if !tiling.left {
2076 bounds.origin.x += inset;
2077 bounds.size.width -= inset;
2078 }
2079 if !tiling.right {
2080 bounds.size.width -= inset;
2081 }
2082
2083 bounds
2084}
2085