window.rs

   1use std::{
   2    cell::{Ref, RefCell, RefMut},
   3    ffi::c_void,
   4    ptr::NonNull,
   5    rc::Rc,
   6    sync::Arc,
   7};
   8
   9use blade_graphics as gpu;
  10use collections::{FxHashSet, HashMap};
  11use futures::channel::oneshot::Receiver;
  12
  13use raw_window_handle as rwh;
  14use wayland_backend::client::ObjectId;
  15use wayland_client::WEnum;
  16use wayland_client::{Proxy, protocol::wl_surface};
  17use wayland_protocols::wp::viewporter::client::wp_viewport;
  18use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
  19use wayland_protocols::xdg::shell::client::xdg_surface;
  20use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
  21use wayland_protocols::{
  22    wp::fractional_scale::v1::client::wp_fractional_scale_v1,
  23    xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1,
  24};
  25use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
  26use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1;
  27
  28use crate::{
  29    AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels,
  30    PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
  31    ResizeEdge, Size, Tiling, WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance,
  32    WindowBounds, WindowControlArea, WindowControls, WindowDecorations, WindowParams, get_window,
  33    layer_shell::LayerShellNotSupportedError, px, size,
  34};
  35use crate::{
  36    Capslock,
  37    platform::{
  38        PlatformAtlas, PlatformInputHandler, PlatformWindow,
  39        blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
  40        linux::wayland::{display::WaylandDisplay, serial::SerialKind},
  41    },
  42};
  43use crate::{WindowKind, scene::Scene};
  44
  45#[derive(Default)]
  46pub(crate) struct Callbacks {
  47    request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
  48    input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
  49    active_status_change: Option<Box<dyn FnMut(bool)>>,
  50    hover_status_change: Option<Box<dyn FnMut(bool)>>,
  51    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
  52    moved: Option<Box<dyn FnMut()>>,
  53    should_close: Option<Box<dyn FnMut() -> bool>>,
  54    close: Option<Box<dyn FnOnce()>>,
  55    appearance_changed: Option<Box<dyn FnMut()>>,
  56}
  57
  58struct RawWindow {
  59    window: *mut c_void,
  60    display: *mut c_void,
  61}
  62
  63impl rwh::HasWindowHandle for RawWindow {
  64    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
  65        let window = NonNull::new(self.window).unwrap();
  66        let handle = rwh::WaylandWindowHandle::new(window);
  67        Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
  68    }
  69}
  70impl rwh::HasDisplayHandle for RawWindow {
  71    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
  72        let display = NonNull::new(self.display).unwrap();
  73        let handle = rwh::WaylandDisplayHandle::new(display);
  74        Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
  75    }
  76}
  77
  78#[derive(Debug)]
  79struct InProgressConfigure {
  80    size: Option<Size<Pixels>>,
  81    fullscreen: bool,
  82    maximized: bool,
  83    resizing: bool,
  84    tiling: Tiling,
  85}
  86
  87pub struct WaylandWindowState {
  88    surface_state: WaylandSurfaceState,
  89    acknowledged_first_configure: bool,
  90    parent: Option<WaylandWindowStatePtr>,
  91    children: FxHashSet<ObjectId>,
  92    pub surface: wl_surface::WlSurface,
  93    app_id: Option<String>,
  94    appearance: WindowAppearance,
  95    blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
  96    viewport: Option<wp_viewport::WpViewport>,
  97    outputs: HashMap<ObjectId, Output>,
  98    display: Option<(ObjectId, Output)>,
  99    globals: Globals,
 100    renderer: BladeRenderer,
 101    bounds: Bounds<Pixels>,
 102    scale: f32,
 103    input_handler: Option<PlatformInputHandler>,
 104    decorations: WindowDecorations,
 105    background_appearance: WindowBackgroundAppearance,
 106    fullscreen: bool,
 107    maximized: bool,
 108    tiling: Tiling,
 109    window_bounds: Bounds<Pixels>,
 110    client: WaylandClientStatePtr,
 111    handle: AnyWindowHandle,
 112    active: bool,
 113    hovered: bool,
 114    in_progress_configure: Option<InProgressConfigure>,
 115    resize_throttle: bool,
 116    in_progress_window_controls: Option<WindowControls>,
 117    window_controls: WindowControls,
 118    client_inset: Option<Pixels>,
 119}
 120
 121pub enum WaylandSurfaceState {
 122    Xdg(WaylandXdgSurfaceState),
 123    LayerShell(WaylandLayerSurfaceState),
 124}
 125
 126impl WaylandSurfaceState {
 127    fn new(
 128        surface: &wl_surface::WlSurface,
 129        globals: &Globals,
 130        params: &WindowParams,
 131        parent: Option<WaylandWindowStatePtr>,
 132    ) -> anyhow::Result<Self> {
 133        // For layer_shell windows, create a layer surface instead of an xdg surface
 134        if let WindowKind::LayerShell(options) = &params.kind {
 135            let Some(layer_shell) = globals.layer_shell.as_ref() else {
 136                return Err(LayerShellNotSupportedError.into());
 137            };
 138
 139            let layer_surface = layer_shell.get_layer_surface(
 140                &surface,
 141                None,
 142                options.layer.into(),
 143                options.namespace.clone(),
 144                &globals.qh,
 145                surface.id(),
 146            );
 147
 148            let width = params.bounds.size.width.0;
 149            let height = params.bounds.size.height.0;
 150            layer_surface.set_size(width as u32, height as u32);
 151
 152            layer_surface.set_anchor(options.anchor.into());
 153            layer_surface.set_keyboard_interactivity(options.keyboard_interactivity.into());
 154
 155            if let Some(margin) = options.margin {
 156                layer_surface.set_margin(
 157                    margin.0.0 as i32,
 158                    margin.1.0 as i32,
 159                    margin.2.0 as i32,
 160                    margin.3.0 as i32,
 161                )
 162            }
 163
 164            if let Some(exclusive_zone) = options.exclusive_zone {
 165                layer_surface.set_exclusive_zone(exclusive_zone.0 as i32);
 166            }
 167
 168            if let Some(exclusive_edge) = options.exclusive_edge {
 169                layer_surface.set_exclusive_edge(exclusive_edge.into());
 170            }
 171
 172            return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {
 173                layer_surface,
 174            }));
 175        }
 176
 177        // All other WindowKinds result in a regular xdg surface
 178        let xdg_surface = globals
 179            .wm_base
 180            .get_xdg_surface(&surface, &globals.qh, surface.id());
 181
 182        let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
 183        let xdg_parent = parent.as_ref().and_then(|w| w.toplevel());
 184
 185        if params.kind == WindowKind::Floating || params.kind == WindowKind::Dialog {
 186            toplevel.set_parent(xdg_parent.as_ref());
 187        }
 188
 189        let dialog = if params.kind == WindowKind::Dialog {
 190            let dialog = globals.dialog.as_ref().map(|dialog| {
 191                let xdg_dialog = dialog.get_xdg_dialog(&toplevel, &globals.qh, ());
 192                xdg_dialog.set_modal();
 193                xdg_dialog
 194            });
 195
 196            if let Some(parent) = parent.as_ref() {
 197                parent.add_child(surface.id());
 198            }
 199
 200            dialog
 201        } else {
 202            None
 203        };
 204
 205        if let Some(size) = params.window_min_size {
 206            toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
 207        }
 208
 209        // Attempt to set up window decorations based on the requested configuration
 210        let decoration = globals
 211            .decoration_manager
 212            .as_ref()
 213            .map(|decoration_manager| {
 214                decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())
 215            });
 216
 217        Ok(WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
 218            xdg_surface,
 219            toplevel,
 220            decoration,
 221            dialog,
 222        }))
 223    }
 224}
 225
 226pub struct WaylandXdgSurfaceState {
 227    xdg_surface: xdg_surface::XdgSurface,
 228    toplevel: xdg_toplevel::XdgToplevel,
 229    decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
 230    dialog: Option<XdgDialogV1>,
 231}
 232
 233pub struct WaylandLayerSurfaceState {
 234    layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
 235}
 236
 237impl WaylandSurfaceState {
 238    fn ack_configure(&self, serial: u32) {
 239        match self {
 240            WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
 241                xdg_surface.ack_configure(serial);
 242            }
 243            WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
 244                layer_surface.ack_configure(serial);
 245            }
 246        }
 247    }
 248
 249    fn decoration(&self) -> Option<&zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1> {
 250        if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { decoration, .. }) = self {
 251            decoration.as_ref()
 252        } else {
 253            None
 254        }
 255    }
 256
 257    fn toplevel(&self) -> Option<&xdg_toplevel::XdgToplevel> {
 258        if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { toplevel, .. }) = self {
 259            Some(toplevel)
 260        } else {
 261            None
 262        }
 263    }
 264
 265    fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {
 266        match self {
 267            WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {
 268                xdg_surface.set_window_geometry(x, y, width, height);
 269            }
 270            WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {
 271                // cannot set window position of a layer surface
 272                layer_surface.set_size(width as u32, height as u32);
 273            }
 274        }
 275    }
 276
 277    fn destroy(&mut self) {
 278        match self {
 279            WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
 280                xdg_surface,
 281                toplevel,
 282                decoration: _decoration,
 283                dialog,
 284            }) => {
 285                // drop the dialog before toplevel so compositor can explicitly unapply it's effects
 286                if let Some(dialog) = dialog {
 287                    dialog.destroy();
 288                }
 289
 290                // The role object (toplevel) must always be destroyed before the xdg_surface.
 291                // See https://wayland.app/protocols/xdg-shell#xdg_surface:request:destroy
 292                toplevel.destroy();
 293                xdg_surface.destroy();
 294            }
 295            WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface }) => {
 296                layer_surface.destroy();
 297            }
 298        }
 299    }
 300}
 301
 302#[derive(Clone)]
 303pub struct WaylandWindowStatePtr {
 304    state: Rc<RefCell<WaylandWindowState>>,
 305    callbacks: Rc<RefCell<Callbacks>>,
 306}
 307
 308impl WaylandWindowState {
 309    pub(crate) fn new(
 310        handle: AnyWindowHandle,
 311        surface: wl_surface::WlSurface,
 312        surface_state: WaylandSurfaceState,
 313        appearance: WindowAppearance,
 314        viewport: Option<wp_viewport::WpViewport>,
 315        client: WaylandClientStatePtr,
 316        globals: Globals,
 317        gpu_context: &BladeContext,
 318        options: WindowParams,
 319        parent: Option<WaylandWindowStatePtr>,
 320    ) -> anyhow::Result<Self> {
 321        let renderer = {
 322            let raw_window = RawWindow {
 323                window: surface.id().as_ptr().cast::<c_void>(),
 324                display: surface
 325                    .backend()
 326                    .upgrade()
 327                    .unwrap()
 328                    .display_ptr()
 329                    .cast::<c_void>(),
 330            };
 331            let config = BladeSurfaceConfig {
 332                size: gpu::Extent {
 333                    width: options.bounds.size.width.0 as u32,
 334                    height: options.bounds.size.height.0 as u32,
 335                    depth: 1,
 336                },
 337                transparent: true,
 338            };
 339            BladeRenderer::new(gpu_context, &raw_window, config)?
 340        };
 341
 342        if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state {
 343            if let Some(title) = options.titlebar.and_then(|titlebar| titlebar.title) {
 344                xdg_state.toplevel.set_title(title.to_string());
 345            }
 346        }
 347
 348        Ok(Self {
 349            surface_state,
 350            acknowledged_first_configure: false,
 351            parent,
 352            children: FxHashSet::default(),
 353            surface,
 354            app_id: None,
 355            blur: None,
 356            viewport,
 357            globals,
 358            outputs: HashMap::default(),
 359            display: None,
 360            renderer,
 361            bounds: options.bounds,
 362            scale: 1.0,
 363            input_handler: None,
 364            decorations: WindowDecorations::Client,
 365            background_appearance: WindowBackgroundAppearance::Opaque,
 366            fullscreen: false,
 367            maximized: false,
 368            tiling: Tiling::default(),
 369            window_bounds: options.bounds,
 370            in_progress_configure: None,
 371            resize_throttle: false,
 372            client,
 373            appearance,
 374            handle,
 375            active: false,
 376            hovered: false,
 377            in_progress_window_controls: None,
 378            window_controls: WindowControls::default(),
 379            client_inset: None,
 380        })
 381    }
 382
 383    pub fn is_transparent(&self) -> bool {
 384        self.decorations == WindowDecorations::Client
 385            || self.background_appearance != WindowBackgroundAppearance::Opaque
 386    }
 387
 388    pub fn primary_output_scale(&mut self) -> i32 {
 389        let mut scale = 1;
 390        let mut current_output = self.display.take();
 391        for (id, output) in self.outputs.iter() {
 392            if let Some((_, output_data)) = &current_output {
 393                if output.scale > output_data.scale {
 394                    current_output = Some((id.clone(), output.clone()));
 395                }
 396            } else {
 397                current_output = Some((id.clone(), output.clone()));
 398            }
 399            scale = scale.max(output.scale);
 400        }
 401        self.display = current_output;
 402        scale
 403    }
 404
 405    pub fn inset(&self) -> Pixels {
 406        match self.decorations {
 407            WindowDecorations::Server => px(0.0),
 408            WindowDecorations::Client => self.client_inset.unwrap_or(px(0.0)),
 409        }
 410    }
 411}
 412
 413pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);
 414pub enum ImeInput {
 415    InsertText(String),
 416    SetMarkedText(String),
 417    UnmarkText,
 418    DeleteText,
 419}
 420
 421impl Drop for WaylandWindow {
 422    fn drop(&mut self) {
 423        let mut state = self.0.state.borrow_mut();
 424        let surface_id = state.surface.id();
 425        if let Some(parent) = state.parent.as_ref() {
 426            parent.state.borrow_mut().children.remove(&surface_id);
 427        }
 428
 429        let client = state.client.clone();
 430
 431        state.renderer.destroy();
 432
 433        // Destroy blur first, this has no dependencies.
 434        if let Some(blur) = &state.blur {
 435            blur.release();
 436        }
 437
 438        // Decorations must be destroyed before the xdg state.
 439        // See https://wayland.app/protocols/xdg-decoration-unstable-v1#zxdg_toplevel_decoration_v1
 440        if let Some(decoration) = &state.surface_state.decoration() {
 441            decoration.destroy();
 442        }
 443
 444        // Surface state might contain xdg_toplevel/xdg_surface which can be destroyed now that
 445        // decorations are gone. layer_surface has no dependencies.
 446        state.surface_state.destroy();
 447
 448        // Viewport must be destroyed before the wl_surface.
 449        // See https://wayland.app/protocols/viewporter#wp_viewport
 450        if let Some(viewport) = &state.viewport {
 451            viewport.destroy();
 452        }
 453
 454        // The wl_surface itself should always be destroyed last.
 455        state.surface.destroy();
 456
 457        let state_ptr = self.0.clone();
 458        state
 459            .globals
 460            .executor
 461            .spawn(async move {
 462                state_ptr.close();
 463                client.drop_window(&surface_id)
 464            })
 465            .detach();
 466        drop(state);
 467    }
 468}
 469
 470impl WaylandWindow {
 471    fn borrow(&self) -> Ref<'_, WaylandWindowState> {
 472        self.0.state.borrow()
 473    }
 474
 475    fn borrow_mut(&self) -> RefMut<'_, WaylandWindowState> {
 476        self.0.state.borrow_mut()
 477    }
 478
 479    pub fn new(
 480        handle: AnyWindowHandle,
 481        globals: Globals,
 482        gpu_context: &BladeContext,
 483        client: WaylandClientStatePtr,
 484        params: WindowParams,
 485        appearance: WindowAppearance,
 486        parent: Option<WaylandWindowStatePtr>,
 487    ) -> anyhow::Result<(Self, ObjectId)> {
 488        let surface = globals.compositor.create_surface(&globals.qh, ());
 489        let surface_state = WaylandSurfaceState::new(&surface, &globals, &params, parent.clone())?;
 490
 491        if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
 492            fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
 493        }
 494
 495        let viewport = globals
 496            .viewporter
 497            .as_ref()
 498            .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));
 499
 500        let this = Self(WaylandWindowStatePtr {
 501            state: Rc::new(RefCell::new(WaylandWindowState::new(
 502                handle,
 503                surface.clone(),
 504                surface_state,
 505                appearance,
 506                viewport,
 507                client,
 508                globals,
 509                gpu_context,
 510                params,
 511                parent,
 512            )?)),
 513            callbacks: Rc::new(RefCell::new(Callbacks::default())),
 514        });
 515
 516        // Kick things off
 517        surface.commit();
 518
 519        Ok((this, surface.id()))
 520    }
 521}
 522
 523impl WaylandWindowStatePtr {
 524    pub fn handle(&self) -> AnyWindowHandle {
 525        self.state.borrow().handle
 526    }
 527
 528    pub fn surface(&self) -> wl_surface::WlSurface {
 529        self.state.borrow().surface.clone()
 530    }
 531
 532    pub fn toplevel(&self) -> Option<xdg_toplevel::XdgToplevel> {
 533        self.state.borrow().surface_state.toplevel().cloned()
 534    }
 535
 536    pub fn ptr_eq(&self, other: &Self) -> bool {
 537        Rc::ptr_eq(&self.state, &other.state)
 538    }
 539
 540    pub fn add_child(&self, child: ObjectId) {
 541        let mut state = self.state.borrow_mut();
 542        state.children.insert(child);
 543    }
 544
 545    pub fn is_blocked(&self) -> bool {
 546        let state = self.state.borrow();
 547        !state.children.is_empty()
 548    }
 549
 550    pub fn frame(&self) {
 551        let mut state = self.state.borrow_mut();
 552        state.surface.frame(&state.globals.qh, state.surface.id());
 553        state.resize_throttle = false;
 554        drop(state);
 555
 556        let mut cb = self.callbacks.borrow_mut();
 557        if let Some(fun) = cb.request_frame.as_mut() {
 558            fun(Default::default());
 559        }
 560    }
 561
 562    pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {
 563        if let xdg_surface::Event::Configure { serial } = event {
 564            {
 565                let mut state = self.state.borrow_mut();
 566                if let Some(window_controls) = state.in_progress_window_controls.take() {
 567                    state.window_controls = window_controls;
 568
 569                    drop(state);
 570                    let mut callbacks = self.callbacks.borrow_mut();
 571                    if let Some(appearance_changed) = callbacks.appearance_changed.as_mut() {
 572                        appearance_changed();
 573                    }
 574                }
 575            }
 576            {
 577                let mut state = self.state.borrow_mut();
 578
 579                if let Some(mut configure) = state.in_progress_configure.take() {
 580                    let got_unmaximized = state.maximized && !configure.maximized;
 581                    state.fullscreen = configure.fullscreen;
 582                    state.maximized = configure.maximized;
 583                    state.tiling = configure.tiling;
 584                    // Limit interactive resizes to once per vblank
 585                    if configure.resizing && state.resize_throttle {
 586                        return;
 587                    } else if configure.resizing {
 588                        state.resize_throttle = true;
 589                    }
 590                    if !configure.fullscreen && !configure.maximized {
 591                        configure.size = if got_unmaximized {
 592                            Some(state.window_bounds.size)
 593                        } else {
 594                            compute_outer_size(state.inset(), configure.size, state.tiling)
 595                        };
 596                        if let Some(size) = configure.size {
 597                            state.window_bounds = Bounds {
 598                                origin: Point::default(),
 599                                size,
 600                            };
 601                        }
 602                    }
 603                    drop(state);
 604                    if let Some(size) = configure.size {
 605                        self.resize(size);
 606                    }
 607                }
 608            }
 609            let mut state = self.state.borrow_mut();
 610            state.surface_state.ack_configure(serial);
 611
 612            let window_geometry = inset_by_tiling(
 613                state.bounds.map_origin(|_| px(0.0)),
 614                state.inset(),
 615                state.tiling,
 616            )
 617            .map(|v| v.0 as i32)
 618            .map_size(|v| if v <= 0 { 1 } else { v });
 619
 620            state.surface_state.set_geometry(
 621                window_geometry.origin.x,
 622                window_geometry.origin.y,
 623                window_geometry.size.width,
 624                window_geometry.size.height,
 625            );
 626
 627            let request_frame_callback = !state.acknowledged_first_configure;
 628            if request_frame_callback {
 629                state.acknowledged_first_configure = true;
 630                drop(state);
 631                self.frame();
 632            }
 633        }
 634    }
 635
 636    pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {
 637        if let zxdg_toplevel_decoration_v1::Event::Configure { mode } = event {
 638            match mode {
 639                WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
 640                    self.state.borrow_mut().decorations = WindowDecorations::Server;
 641                    if let Some(mut appearance_changed) =
 642                        self.callbacks.borrow_mut().appearance_changed.as_mut()
 643                    {
 644                        appearance_changed();
 645                    }
 646                }
 647                WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
 648                    self.state.borrow_mut().decorations = WindowDecorations::Client;
 649                    // Update background to be transparent
 650                    if let Some(mut appearance_changed) =
 651                        self.callbacks.borrow_mut().appearance_changed.as_mut()
 652                    {
 653                        appearance_changed();
 654                    }
 655                }
 656                WEnum::Value(_) => {
 657                    log::warn!("Unknown decoration mode");
 658                }
 659                WEnum::Unknown(v) => {
 660                    log::warn!("Unknown decoration mode: {}", v);
 661                }
 662            }
 663        }
 664    }
 665
 666    pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {
 667        if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
 668            self.rescale(scale as f32 / 120.0);
 669        }
 670    }
 671
 672    pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
 673        match event {
 674            xdg_toplevel::Event::Configure {
 675                width,
 676                height,
 677                states,
 678            } => {
 679                let mut size = if width == 0 || height == 0 {
 680                    None
 681                } else {
 682                    Some(size(px(width as f32), px(height as f32)))
 683                };
 684
 685                let states = extract_states::<xdg_toplevel::State>(&states);
 686
 687                let mut tiling = Tiling::default();
 688                let mut fullscreen = false;
 689                let mut maximized = false;
 690                let mut resizing = false;
 691
 692                for state in states {
 693                    match state {
 694                        xdg_toplevel::State::Maximized => {
 695                            maximized = true;
 696                        }
 697                        xdg_toplevel::State::Fullscreen => {
 698                            fullscreen = true;
 699                        }
 700                        xdg_toplevel::State::Resizing => resizing = true,
 701                        xdg_toplevel::State::TiledTop => {
 702                            tiling.top = true;
 703                        }
 704                        xdg_toplevel::State::TiledLeft => {
 705                            tiling.left = true;
 706                        }
 707                        xdg_toplevel::State::TiledRight => {
 708                            tiling.right = true;
 709                        }
 710                        xdg_toplevel::State::TiledBottom => {
 711                            tiling.bottom = true;
 712                        }
 713                        _ => {
 714                            // noop
 715                        }
 716                    }
 717                }
 718
 719                if fullscreen || maximized {
 720                    tiling = Tiling::tiled();
 721                }
 722
 723                let mut state = self.state.borrow_mut();
 724                state.in_progress_configure = Some(InProgressConfigure {
 725                    size,
 726                    fullscreen,
 727                    maximized,
 728                    resizing,
 729                    tiling,
 730                });
 731
 732                false
 733            }
 734            xdg_toplevel::Event::Close => {
 735                let mut cb = self.callbacks.borrow_mut();
 736                if let Some(mut should_close) = cb.should_close.take() {
 737                    let result = (should_close)();
 738                    cb.should_close = Some(should_close);
 739                    if result {
 740                        drop(cb);
 741                        self.close();
 742                    }
 743                    result
 744                } else {
 745                    true
 746                }
 747            }
 748            xdg_toplevel::Event::WmCapabilities { capabilities } => {
 749                let mut window_controls = WindowControls::default();
 750
 751                let states = extract_states::<xdg_toplevel::WmCapabilities>(&capabilities);
 752
 753                for state in states {
 754                    match state {
 755                        xdg_toplevel::WmCapabilities::Maximize => {
 756                            window_controls.maximize = true;
 757                        }
 758                        xdg_toplevel::WmCapabilities::Minimize => {
 759                            window_controls.minimize = true;
 760                        }
 761                        xdg_toplevel::WmCapabilities::Fullscreen => {
 762                            window_controls.fullscreen = true;
 763                        }
 764                        xdg_toplevel::WmCapabilities::WindowMenu => {
 765                            window_controls.window_menu = true;
 766                        }
 767                        _ => {}
 768                    }
 769                }
 770
 771                let mut state = self.state.borrow_mut();
 772                state.in_progress_window_controls = Some(window_controls);
 773                false
 774            }
 775            _ => false,
 776        }
 777    }
 778
 779    pub fn handle_layersurface_event(&self, event: zwlr_layer_surface_v1::Event) -> bool {
 780        match event {
 781            zwlr_layer_surface_v1::Event::Configure {
 782                width,
 783                height,
 784                serial,
 785            } => {
 786                let mut size = if width == 0 || height == 0 {
 787                    None
 788                } else {
 789                    Some(size(px(width as f32), px(height as f32)))
 790                };
 791
 792                let mut state = self.state.borrow_mut();
 793                state.in_progress_configure = Some(InProgressConfigure {
 794                    size,
 795                    fullscreen: false,
 796                    maximized: false,
 797                    resizing: false,
 798                    tiling: Tiling::default(),
 799                });
 800                drop(state);
 801
 802                // just do the same thing we'd do as an xdg_surface
 803                self.handle_xdg_surface_event(xdg_surface::Event::Configure { serial });
 804
 805                false
 806            }
 807            zwlr_layer_surface_v1::Event::Closed => {
 808                // unlike xdg, we don't have a choice here: the surface is closing.
 809                true
 810            }
 811            _ => false,
 812        }
 813    }
 814
 815    #[allow(clippy::mutable_key_type)]
 816    pub fn handle_surface_event(
 817        &self,
 818        event: wl_surface::Event,
 819        outputs: HashMap<ObjectId, Output>,
 820    ) {
 821        let mut state = self.state.borrow_mut();
 822
 823        match event {
 824            wl_surface::Event::Enter { output } => {
 825                let id = output.id();
 826
 827                let Some(output) = outputs.get(&id) else {
 828                    return;
 829                };
 830
 831                state.outputs.insert(id, output.clone());
 832
 833                let scale = state.primary_output_scale();
 834
 835                // We use `PreferredBufferScale` instead to set the scale if it's available
 836                if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
 837                    state.surface.set_buffer_scale(scale);
 838                    drop(state);
 839                    self.rescale(scale as f32);
 840                }
 841            }
 842            wl_surface::Event::Leave { output } => {
 843                state.outputs.remove(&output.id());
 844
 845                let scale = state.primary_output_scale();
 846
 847                // We use `PreferredBufferScale` instead to set the scale if it's available
 848                if state.surface.version() < wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
 849                    state.surface.set_buffer_scale(scale);
 850                    drop(state);
 851                    self.rescale(scale as f32);
 852                }
 853            }
 854            wl_surface::Event::PreferredBufferScale { factor } => {
 855                // We use `WpFractionalScale` instead to set the scale if it's available
 856                if state.globals.fractional_scale_manager.is_none() {
 857                    state.surface.set_buffer_scale(factor);
 858                    drop(state);
 859                    self.rescale(factor as f32);
 860                }
 861            }
 862            _ => {}
 863        }
 864    }
 865
 866    pub fn handle_ime(&self, ime: ImeInput) {
 867        if self.is_blocked() {
 868            return;
 869        }
 870        let mut state = self.state.borrow_mut();
 871        if let Some(mut input_handler) = state.input_handler.take() {
 872            drop(state);
 873            match ime {
 874                ImeInput::InsertText(text) => {
 875                    input_handler.replace_text_in_range(None, &text);
 876                }
 877                ImeInput::SetMarkedText(text) => {
 878                    input_handler.replace_and_mark_text_in_range(None, &text, None);
 879                }
 880                ImeInput::UnmarkText => {
 881                    input_handler.unmark_text();
 882                }
 883                ImeInput::DeleteText => {
 884                    if let Some(marked) = input_handler.marked_text_range() {
 885                        input_handler.replace_text_in_range(Some(marked), "");
 886                    }
 887                }
 888            }
 889            self.state.borrow_mut().input_handler = Some(input_handler);
 890        }
 891    }
 892
 893    pub fn get_ime_area(&self) -> Option<Bounds<Pixels>> {
 894        let mut state = self.state.borrow_mut();
 895        let mut bounds: Option<Bounds<Pixels>> = None;
 896        if let Some(mut input_handler) = state.input_handler.take() {
 897            drop(state);
 898            if let Some(selection) = input_handler.marked_text_range() {
 899                bounds = input_handler.bounds_for_range(selection.start..selection.start);
 900            }
 901            self.state.borrow_mut().input_handler = Some(input_handler);
 902        }
 903        bounds
 904    }
 905
 906    pub fn set_size_and_scale(&self, size: Option<Size<Pixels>>, scale: Option<f32>) {
 907        let (size, scale) = {
 908            let mut state = self.state.borrow_mut();
 909            if size.is_none_or(|size| size == state.bounds.size)
 910                && scale.is_none_or(|scale| scale == state.scale)
 911            {
 912                return;
 913            }
 914            if let Some(size) = size {
 915                state.bounds.size = size;
 916            }
 917            if let Some(scale) = scale {
 918                state.scale = scale;
 919            }
 920            let device_bounds = state.bounds.to_device_pixels(state.scale);
 921            state.renderer.update_drawable_size(device_bounds.size);
 922            (state.bounds.size, state.scale)
 923        };
 924
 925        if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
 926            fun(size, scale);
 927        }
 928
 929        {
 930            let state = self.state.borrow();
 931            if let Some(viewport) = &state.viewport {
 932                viewport.set_destination(size.width.0 as i32, size.height.0 as i32);
 933            }
 934        }
 935    }
 936
 937    pub fn resize(&self, size: Size<Pixels>) {
 938        self.set_size_and_scale(Some(size), None);
 939    }
 940
 941    pub fn rescale(&self, scale: f32) {
 942        self.set_size_and_scale(None, Some(scale));
 943    }
 944
 945    pub fn close(&self) {
 946        let state = self.state.borrow();
 947        let client = state.client.get_client();
 948        #[allow(clippy::mutable_key_type)]
 949        let children = state.children.clone();
 950        drop(state);
 951
 952        for child in children {
 953            let mut client_state = client.borrow_mut();
 954            let window = get_window(&mut client_state, &child);
 955            drop(client_state);
 956
 957            if let Some(child) = window {
 958                child.close();
 959            }
 960        }
 961        let mut callbacks = self.callbacks.borrow_mut();
 962        if let Some(fun) = callbacks.close.take() {
 963            fun()
 964        }
 965    }
 966
 967    pub fn handle_input(&self, input: PlatformInput) {
 968        if self.is_blocked() {
 969            return;
 970        }
 971        if let Some(ref mut fun) = self.callbacks.borrow_mut().input
 972            && !fun(input.clone()).propagate
 973        {
 974            return;
 975        }
 976        if let PlatformInput::KeyDown(event) = input
 977            && event.keystroke.modifiers.is_subset_of(&Modifiers::shift())
 978            && let Some(key_char) = &event.keystroke.key_char
 979        {
 980            let mut state = self.state.borrow_mut();
 981            if let Some(mut input_handler) = state.input_handler.take() {
 982                drop(state);
 983                input_handler.replace_text_in_range(None, key_char);
 984                self.state.borrow_mut().input_handler = Some(input_handler);
 985            }
 986        }
 987    }
 988
 989    pub fn set_focused(&self, focus: bool) {
 990        self.state.borrow_mut().active = focus;
 991        if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
 992            fun(focus);
 993        }
 994    }
 995
 996    pub fn set_hovered(&self, focus: bool) {
 997        if let Some(ref mut fun) = self.callbacks.borrow_mut().hover_status_change {
 998            fun(focus);
 999        }
1000    }
1001
1002    pub fn set_appearance(&mut self, appearance: WindowAppearance) {
1003        self.state.borrow_mut().appearance = appearance;
1004
1005        let mut callbacks = self.callbacks.borrow_mut();
1006        if let Some(ref mut fun) = callbacks.appearance_changed {
1007            (fun)()
1008        }
1009    }
1010
1011    pub fn primary_output_scale(&self) -> i32 {
1012        self.state.borrow_mut().primary_output_scale()
1013    }
1014}
1015
1016fn extract_states<'a, S: TryFrom<u32> + 'a>(states: &'a [u8]) -> impl Iterator<Item = S> + 'a
1017where
1018    <S as TryFrom<u32>>::Error: 'a,
1019{
1020    states
1021        .chunks_exact(4)
1022        .flat_map(TryInto::<[u8; 4]>::try_into)
1023        .map(u32::from_ne_bytes)
1024        .flat_map(S::try_from)
1025}
1026
1027impl rwh::HasWindowHandle for WaylandWindow {
1028    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
1029        let surface = self.0.surface().id().as_ptr() as *mut libc::c_void;
1030        let c_ptr = NonNull::new(surface).ok_or(rwh::HandleError::Unavailable)?;
1031        let handle = rwh::WaylandWindowHandle::new(c_ptr);
1032        let raw_handle = rwh::RawWindowHandle::Wayland(handle);
1033        Ok(unsafe { rwh::WindowHandle::borrow_raw(raw_handle) })
1034    }
1035}
1036
1037impl rwh::HasDisplayHandle for WaylandWindow {
1038    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
1039        let display = self
1040            .0
1041            .surface()
1042            .backend()
1043            .upgrade()
1044            .ok_or(rwh::HandleError::Unavailable)?
1045            .display_ptr() as *mut libc::c_void;
1046
1047        let c_ptr = NonNull::new(display).ok_or(rwh::HandleError::Unavailable)?;
1048        let handle = rwh::WaylandDisplayHandle::new(c_ptr);
1049        let raw_handle = rwh::RawDisplayHandle::Wayland(handle);
1050        Ok(unsafe { rwh::DisplayHandle::borrow_raw(raw_handle) })
1051    }
1052}
1053
1054impl PlatformWindow for WaylandWindow {
1055    fn bounds(&self) -> Bounds<Pixels> {
1056        self.borrow().bounds
1057    }
1058
1059    fn is_maximized(&self) -> bool {
1060        self.borrow().maximized
1061    }
1062
1063    fn window_bounds(&self) -> WindowBounds {
1064        let state = self.borrow();
1065        if state.fullscreen {
1066            WindowBounds::Fullscreen(state.window_bounds)
1067        } else if state.maximized {
1068            WindowBounds::Maximized(state.window_bounds)
1069        } else {
1070            drop(state);
1071            WindowBounds::Windowed(self.bounds())
1072        }
1073    }
1074
1075    fn inner_window_bounds(&self) -> WindowBounds {
1076        let state = self.borrow();
1077        if state.fullscreen {
1078            WindowBounds::Fullscreen(state.window_bounds)
1079        } else if state.maximized {
1080            WindowBounds::Maximized(state.window_bounds)
1081        } else {
1082            let inset = state.inset();
1083            drop(state);
1084            WindowBounds::Windowed(self.bounds().inset(inset))
1085        }
1086    }
1087
1088    fn content_size(&self) -> Size<Pixels> {
1089        self.borrow().bounds.size
1090    }
1091
1092    fn resize(&mut self, size: Size<Pixels>) {
1093        let state = self.borrow();
1094        let state_ptr = self.0.clone();
1095
1096        // Keep window geometry consistent with configure handling. On Wayland, window geometry is
1097        // surface-local: resizing should not attempt to translate the window; the compositor
1098        // controls placement. We also account for client-side decoration insets and tiling.
1099        let window_geometry = inset_by_tiling(
1100            Bounds {
1101                origin: Point::default(),
1102                size,
1103            },
1104            state.inset(),
1105            state.tiling,
1106        )
1107        .map(|v| v.0 as i32)
1108        .map_size(|v| if v <= 0 { 1 } else { v });
1109
1110        state.surface_state.set_geometry(
1111            window_geometry.origin.x,
1112            window_geometry.origin.y,
1113            window_geometry.size.width,
1114            window_geometry.size.height,
1115        );
1116
1117        state
1118            .globals
1119            .executor
1120            .spawn(async move { state_ptr.resize(size) })
1121            .detach();
1122    }
1123
1124    fn scale_factor(&self) -> f32 {
1125        self.borrow().scale
1126    }
1127
1128    fn appearance(&self) -> WindowAppearance {
1129        self.borrow().appearance
1130    }
1131
1132    fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
1133        let state = self.borrow();
1134        state.display.as_ref().map(|(id, display)| {
1135            Rc::new(WaylandDisplay {
1136                id: id.clone(),
1137                name: display.name.clone(),
1138                bounds: display.bounds.to_pixels(state.scale),
1139            }) as Rc<dyn PlatformDisplay>
1140        })
1141    }
1142
1143    fn mouse_position(&self) -> Point<Pixels> {
1144        self.borrow()
1145            .client
1146            .get_client()
1147            .borrow()
1148            .mouse_location
1149            .unwrap_or_default()
1150    }
1151
1152    fn modifiers(&self) -> Modifiers {
1153        self.borrow().client.get_client().borrow().modifiers
1154    }
1155
1156    fn capslock(&self) -> Capslock {
1157        self.borrow().client.get_client().borrow().capslock
1158    }
1159
1160    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
1161        self.borrow_mut().input_handler = Some(input_handler);
1162    }
1163
1164    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
1165        self.borrow_mut().input_handler.take()
1166    }
1167
1168    fn prompt(
1169        &self,
1170        _level: PromptLevel,
1171        _msg: &str,
1172        _detail: Option<&str>,
1173        _answers: &[PromptButton],
1174    ) -> Option<Receiver<usize>> {
1175        None
1176    }
1177
1178    fn activate(&self) {
1179        // Try to request an activation token. Even though the activation is likely going to be rejected,
1180        // KWin and Mutter can use the app_id to visually indicate we're requesting attention.
1181        let state = self.borrow();
1182        if let (Some(activation), Some(app_id)) = (&state.globals.activation, state.app_id.clone())
1183        {
1184            state.client.set_pending_activation(state.surface.id());
1185            let token = activation.get_activation_token(&state.globals.qh, ());
1186            // The serial isn't exactly important here, since the activation is probably going to be rejected anyway.
1187            let serial = state.client.get_serial(SerialKind::MousePress);
1188            token.set_app_id(app_id);
1189            token.set_serial(serial, &state.globals.seat);
1190            token.set_surface(&state.surface);
1191            token.commit();
1192        }
1193    }
1194
1195    fn is_active(&self) -> bool {
1196        self.borrow().active
1197    }
1198
1199    fn is_hovered(&self) -> bool {
1200        self.borrow().hovered
1201    }
1202
1203    fn set_title(&mut self, title: &str) {
1204        if let Some(toplevel) = self.borrow().surface_state.toplevel() {
1205            toplevel.set_title(title.to_string());
1206        }
1207    }
1208
1209    fn set_app_id(&mut self, app_id: &str) {
1210        let mut state = self.borrow_mut();
1211        if let Some(toplevel) = state.surface_state.toplevel() {
1212            toplevel.set_app_id(app_id.to_owned());
1213        }
1214        state.app_id = Some(app_id.to_owned());
1215    }
1216
1217    fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
1218        let mut state = self.borrow_mut();
1219        state.background_appearance = background_appearance;
1220        update_window(state);
1221    }
1222
1223    fn minimize(&self) {
1224        if let Some(toplevel) = self.borrow().surface_state.toplevel() {
1225            toplevel.set_minimized();
1226        }
1227    }
1228
1229    fn zoom(&self) {
1230        let state = self.borrow();
1231        if let Some(toplevel) = state.surface_state.toplevel() {
1232            if !state.maximized {
1233                toplevel.set_maximized();
1234            } else {
1235                toplevel.unset_maximized();
1236            }
1237        }
1238    }
1239
1240    fn toggle_fullscreen(&self) {
1241        let mut state = self.borrow();
1242        if let Some(toplevel) = state.surface_state.toplevel() {
1243            if !state.fullscreen {
1244                toplevel.set_fullscreen(None);
1245            } else {
1246                toplevel.unset_fullscreen();
1247            }
1248        }
1249    }
1250
1251    fn is_fullscreen(&self) -> bool {
1252        self.borrow().fullscreen
1253    }
1254
1255    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
1256        self.0.callbacks.borrow_mut().request_frame = Some(callback);
1257    }
1258
1259    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
1260        self.0.callbacks.borrow_mut().input = Some(callback);
1261    }
1262
1263    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1264        self.0.callbacks.borrow_mut().active_status_change = Some(callback);
1265    }
1266
1267    fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1268        self.0.callbacks.borrow_mut().hover_status_change = Some(callback);
1269    }
1270
1271    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
1272        self.0.callbacks.borrow_mut().resize = Some(callback);
1273    }
1274
1275    fn on_moved(&self, callback: Box<dyn FnMut()>) {
1276        self.0.callbacks.borrow_mut().moved = Some(callback);
1277    }
1278
1279    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
1280        self.0.callbacks.borrow_mut().should_close = Some(callback);
1281    }
1282
1283    fn on_close(&self, callback: Box<dyn FnOnce()>) {
1284        self.0.callbacks.borrow_mut().close = Some(callback);
1285    }
1286
1287    fn on_hit_test_window_control(&self, _callback: Box<dyn FnMut() -> Option<WindowControlArea>>) {
1288    }
1289
1290    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1291        self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
1292    }
1293
1294    fn draw(&self, scene: &Scene) {
1295        let mut state = self.borrow_mut();
1296        state.renderer.draw(scene);
1297    }
1298
1299    fn completed_frame(&self) {
1300        let state = self.borrow();
1301        state.surface.commit();
1302    }
1303
1304    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1305        let state = self.borrow();
1306        state.renderer.sprite_atlas().clone()
1307    }
1308
1309    fn show_window_menu(&self, position: Point<Pixels>) {
1310        let state = self.borrow();
1311        let serial = state.client.get_serial(SerialKind::MousePress);
1312        if let Some(toplevel) = state.surface_state.toplevel() {
1313            toplevel.show_window_menu(
1314                &state.globals.seat,
1315                serial,
1316                position.x.0 as i32,
1317                position.y.0 as i32,
1318            );
1319        }
1320    }
1321
1322    fn start_window_move(&self) {
1323        let state = self.borrow();
1324        let serial = state.client.get_serial(SerialKind::MousePress);
1325        if let Some(toplevel) = state.surface_state.toplevel() {
1326            toplevel._move(&state.globals.seat, serial);
1327        }
1328    }
1329
1330    fn start_window_resize(&self, edge: crate::ResizeEdge) {
1331        let state = self.borrow();
1332        if let Some(toplevel) = state.surface_state.toplevel() {
1333            toplevel.resize(
1334                &state.globals.seat,
1335                state.client.get_serial(SerialKind::MousePress),
1336                edge.to_xdg(),
1337            )
1338        }
1339    }
1340
1341    fn window_decorations(&self) -> Decorations {
1342        let state = self.borrow();
1343        match state.decorations {
1344            WindowDecorations::Server => Decorations::Server,
1345            WindowDecorations::Client => Decorations::Client {
1346                tiling: state.tiling,
1347            },
1348        }
1349    }
1350
1351    fn request_decorations(&self, decorations: WindowDecorations) {
1352        let mut state = self.borrow_mut();
1353        match state.surface_state.decoration().as_ref() {
1354            Some(decoration) => {
1355                decoration.set_mode(decorations.to_xdg());
1356                state.decorations = decorations;
1357                update_window(state);
1358            }
1359            None => {
1360                if matches!(decorations, WindowDecorations::Server) {
1361                    log::info!(
1362                        "Server-side decorations requested, but the Wayland server does not support them. Falling back to client-side decorations."
1363                    );
1364                }
1365                state.decorations = WindowDecorations::Client;
1366                update_window(state);
1367            }
1368        }
1369    }
1370
1371    fn window_controls(&self) -> WindowControls {
1372        self.borrow().window_controls
1373    }
1374
1375    fn set_client_inset(&self, inset: Pixels) {
1376        let mut state = self.borrow_mut();
1377        if Some(inset) != state.client_inset {
1378            state.client_inset = Some(inset);
1379            update_window(state);
1380        }
1381    }
1382
1383    fn update_ime_position(&self, bounds: Bounds<Pixels>) {
1384        let state = self.borrow();
1385        state.client.update_ime_position(bounds);
1386    }
1387
1388    fn gpu_specs(&self) -> Option<GpuSpecs> {
1389        self.borrow().renderer.gpu_specs().into()
1390    }
1391}
1392
1393fn update_window(mut state: RefMut<WaylandWindowState>) {
1394    let opaque = !state.is_transparent();
1395
1396    state.renderer.update_transparency(!opaque);
1397    let mut opaque_area = state.window_bounds.map(|v| v.0 as i32);
1398    opaque_area.inset(state.inset().0 as i32);
1399
1400    let region = state
1401        .globals
1402        .compositor
1403        .create_region(&state.globals.qh, ());
1404    region.add(
1405        opaque_area.origin.x,
1406        opaque_area.origin.y,
1407        opaque_area.size.width,
1408        opaque_area.size.height,
1409    );
1410
1411    // Note that rounded corners make this rectangle API hard to work with.
1412    // As this is common when using CSD, let's just disable this API.
1413    if state.background_appearance == WindowBackgroundAppearance::Opaque
1414        && state.decorations == WindowDecorations::Server
1415    {
1416        // Promise the compositor that this region of the window surface
1417        // contains no transparent pixels. This allows the compositor to skip
1418        // updating whatever is behind the surface for better performance.
1419        state.surface.set_opaque_region(Some(&region));
1420    } else {
1421        state.surface.set_opaque_region(None);
1422    }
1423
1424    if let Some(ref blur_manager) = state.globals.blur_manager {
1425        if state.background_appearance == WindowBackgroundAppearance::Blurred {
1426            if state.blur.is_none() {
1427                let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
1428                state.blur = Some(blur);
1429            }
1430            state.blur.as_ref().unwrap().commit();
1431        } else {
1432            // It probably doesn't hurt to clear the blur for opaque windows
1433            blur_manager.unset(&state.surface);
1434            if let Some(b) = state.blur.take() {
1435                b.release()
1436            }
1437        }
1438    }
1439
1440    region.destroy();
1441}
1442
1443impl WindowDecorations {
1444    fn to_xdg(self) -> zxdg_toplevel_decoration_v1::Mode {
1445        match self {
1446            WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
1447            WindowDecorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
1448        }
1449    }
1450}
1451
1452impl ResizeEdge {
1453    fn to_xdg(self) -> xdg_toplevel::ResizeEdge {
1454        match self {
1455            ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
1456            ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
1457            ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
1458            ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
1459            ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
1460            ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
1461            ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
1462            ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
1463        }
1464    }
1465}
1466
1467/// The configuration event is in terms of the window geometry, which we are constantly
1468/// updating to account for the client decorations. But that's not the area we want to render
1469/// to, due to our intrusize CSD. So, here we calculate the 'actual' size, by adding back in the insets
1470fn compute_outer_size(
1471    inset: Pixels,
1472    new_size: Option<Size<Pixels>>,
1473    tiling: Tiling,
1474) -> Option<Size<Pixels>> {
1475    new_size.map(|mut new_size| {
1476        if !tiling.top {
1477            new_size.height += inset;
1478        }
1479        if !tiling.bottom {
1480            new_size.height += inset;
1481        }
1482        if !tiling.left {
1483            new_size.width += inset;
1484        }
1485        if !tiling.right {
1486            new_size.width += inset;
1487        }
1488
1489        new_size
1490    })
1491}
1492
1493fn inset_by_tiling(mut bounds: Bounds<Pixels>, inset: Pixels, tiling: Tiling) -> Bounds<Pixels> {
1494    if !tiling.top {
1495        bounds.origin.y += inset;
1496        bounds.size.height -= inset;
1497    }
1498    if !tiling.bottom {
1499        bounds.size.height -= inset;
1500    }
1501    if !tiling.left {
1502        bounds.origin.x += inset;
1503        bounds.size.width -= inset;
1504    }
1505    if !tiling.right {
1506        bounds.size.width -= inset;
1507    }
1508
1509    bounds
1510}