Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:00:58.945Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

window.rs

2085 lines · 73.1 KB · rust
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) = &params.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) = &params.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)) = &current_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            &params,
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(&region));
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
Served at tenant.openagents/omega Member data and write actions are omitted.