2084 lines · 71.4 KB · Rust
RawDownload
use std::{    cell::{Cell, Ref, RefCell, RefMut},    ffi::c_void,    ptr::NonNull,    rc::Rc,    sync::Arc,}; use collections::{FxHashMap, HashMap};use futures::channel::oneshot::Receiver; use raw_window_handle as rwh;use wayland_backend::client::ObjectId;use wayland_client::WEnum;use wayland_client::{    Proxy,    protocol::{wl_output, wl_seat, wl_surface},};use wayland_protocols::wp::viewporter::client::wp_viewport;use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;use wayland_protocols::xdg::shell::client::xdg_popup;use wayland_protocols::xdg::shell::client::xdg_positioner;use wayland_protocols::xdg::shell::client::xdg_surface;use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};use wayland_protocols::{    wp::fractional_scale::v1::client::wp_fractional_scale_v1,    xdg::dialog::v1::client::xdg_dialog_v1::XdgDialogV1,};use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1; use crate::linux::wayland::{display::WaylandDisplay, serial::SerialKind};use crate::linux::{Globals, Output, WaylandClientStatePtr, get_window};use gpui::{    AnyWindowHandle, Bounds, Capslock, Decorations, DevicePixels, GpuSpecs, Modifiers, Pixels,    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,    PromptButton, PromptLevel, RequestFrameOptions, ResizeEdge, Scene, Size, Tiling,    WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls,    WindowDecorations, WindowKind, WindowParams,    layer_shell::{Anchor, LayerShellNotSupportedError},    popup::PopupOptions,    px, size,};use gpui_wgpu::{CompositorGpuHint, WgpuRenderer, WgpuSurfaceConfig, wgpu}; #[derive(Default)]pub(crate) struct Callbacks {    request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,    input: Option<Box<dyn FnMut(gpui::PlatformInput) -> gpui::DispatchEventResult>>,    active_status_change: Option<Box<dyn FnMut(bool)>>,    hover_status_change: Option<Box<dyn FnMut(bool)>>,    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,    moved: Option<Box<dyn FnMut()>>,    should_close: Option<Box<dyn FnMut() -> bool>>,    close: Option<Box<dyn FnOnce()>>,    appearance_changed: Option<Box<dyn FnMut()>>,    button_layout_changed: Option<Box<dyn FnMut()>>,} #[derive(Debug, Clone, Copy)]struct RawWindow {    window: *mut c_void,    display: *mut c_void,} // Safety: The raw pointers in RawWindow point to Wayland surface/display// which are valid for the window's lifetime. These are used only for// passing to wgpu which needs Send+Sync for surface creation.unsafe impl Send for RawWindow {}unsafe impl Sync for RawWindow {} impl rwh::HasWindowHandle for RawWindow {    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {        let window = NonNull::new(self.window).unwrap();        let handle = rwh::WaylandWindowHandle::new(window);        Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })    }}impl rwh::HasDisplayHandle for RawWindow {    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {        let display = NonNull::new(self.display).unwrap();        let handle = rwh::WaylandDisplayHandle::new(display);        Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })    }} #[derive(Debug)]struct InProgressConfigure {    size: Option<Size<Pixels>>,    fullscreen: bool,    maximized: bool,    resizing: bool,    tiling: Tiling,} pub struct WaylandWindowState {    surface_state: WaylandSurfaceState,    acknowledged_first_configure: bool,    parent: Option<WaylandWindowStatePtr>,    /// Child surfaces mapped to whether they block this window's input (dialogs    /// block, popups don't). Children are closed before this window closes.    children: FxHashMap<ObjectId, bool>,    pub surface: wl_surface::WlSurface,    app_id: Option<String>,    appearance: WindowAppearance,    blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,    viewport: Option<wp_viewport::WpViewport>,    outputs: HashMap<ObjectId, Output>,    display: Option<(ObjectId, Output)>,    globals: Globals,    renderer: WgpuRenderer,    bounds: Bounds<Pixels>,    scale: f32,    input_handler: Option<PlatformInputHandler>,    decorations: WindowDecorations,    background_appearance: WindowBackgroundAppearance,    fullscreen: bool,    maximized: bool,    tiling: Tiling,    window_bounds: Bounds<Pixels>,    client: WaylandClientStatePtr,    handle: AnyWindowHandle,    active: bool,    hovered: bool,    pub(crate) force_render_after_recovery: bool,    renderer_presented: bool,    in_progress_configure: Option<InProgressConfigure>,    resize_throttle: bool,    in_progress_window_controls: Option<WindowControls>,    window_controls: WindowControls,    client_inset: Option<Pixels>,    accesskit_adapter: Option<accesskit_unix::Adapter>,} pub enum WaylandSurfaceState {    Xdg(WaylandXdgSurfaceState),    LayerShell(WaylandLayerSurfaceState),    Popup(WaylandPopupSurfaceState),} impl WaylandSurfaceState {    fn new(        surface: &wl_surface::WlSurface,        globals: &Globals,        params: &WindowParams,        parent: Option<WaylandWindowStatePtr>,        popup_grab: Option<(u32, wl_seat::WlSeat)>,        target_output: Option<wl_output::WlOutput>,    ) -> anyhow::Result<Self> {        // For layer_shell windows, create a layer surface instead of an xdg surface        if let WindowKind::LayerShell(options) = &params.kind {            let Some(layer_shell) = globals.layer_shell.as_ref() else {                return Err(LayerShellNotSupportedError.into());            };             let layer_surface = layer_shell.get_layer_surface(                &surface,                target_output.as_ref(),                super::layer_shell::wayland_layer(options.layer),                options.namespace.clone(),                &globals.qh,                surface.id(),            );             let width = f32::from(params.bounds.size.width);            let height = f32::from(params.bounds.size.height);            layer_surface.set_size(width as u32, height as u32);             layer_surface.set_anchor(super::layer_shell::wayland_anchor(options.anchor));            layer_surface.set_keyboard_interactivity(                super::layer_shell::wayland_keyboard_interactivity(options.keyboard_interactivity),            );             if let Some(margin) = options.margin {                layer_surface.set_margin(                    f32::from(margin.0) as i32,                    f32::from(margin.1) as i32,                    f32::from(margin.2) as i32,                    f32::from(margin.3) as i32,                )            }             if let Some(exclusive_zone) = options.exclusive_zone {                layer_surface.set_exclusive_zone(f32::from(exclusive_zone) as i32);            }             if let Some(exclusive_edge) = options.exclusive_edge {                Self::apply_exclusive_edge(&layer_surface, options.anchor, exclusive_edge);            }             return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {                layer_surface,                anchor: options.anchor,            }));        }         if let WindowKind::AnchoredPopup(options) = &params.kind {            let Some(parent) = parent.as_ref() else {                return Err(anyhow::anyhow!("popup parent window not found"));            };             let positioner = build_popup_positioner(                globals,                options,                params.bounds.size,                parent.window_geometry(),            );             let xdg_surface = globals                .wm_base                .get_xdg_surface(&surface, &globals.qh, surface.id());             // A layer-shell parent takes a null xdg parent and is attached via the layer            // surface. Every other surface kind has an xdg_surface to parent to directly.            let xdg_popup = if let Some(parent_layer_surface) = parent.layer_surface() {                let xdg_popup = xdg_surface.get_popup(None, &positioner, &globals.qh, surface.id());                parent_layer_surface.get_popup(&xdg_popup);                xdg_popup            } else {                xdg_surface.get_popup(                    parent.xdg_surface().as_ref(),                    &positioner,                    &globals.qh,                    surface.id(),                )            };            positioner.destroy();             if let Some((serial, seat)) = popup_grab {                xdg_popup.grab(&seat, serial);            }             // Non-blocking: the parent keeps its input so it can dismiss the popup on            // clicks in its own window.            parent.add_child(surface.id(), false);             return Ok(WaylandSurfaceState::Popup(WaylandPopupSurfaceState {                xdg_surface,                xdg_popup,                options: options.clone(),                next_reposition_token: Cell::new(0),            }));        }         // All other WindowKinds result in a regular xdg surface        let xdg_surface = globals            .wm_base            .get_xdg_surface(&surface, &globals.qh, surface.id());         let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());        let xdg_parent = parent.as_ref().and_then(|w| w.toplevel());         if params.kind == WindowKind::Floating || params.kind == WindowKind::Dialog {            toplevel.set_parent(xdg_parent.as_ref());        }         let dialog = if params.kind == WindowKind::Dialog {            let dialog = globals.dialog.as_ref().map(|dialog| {                let xdg_dialog = dialog.get_xdg_dialog(&toplevel, &globals.qh, ());                xdg_dialog.set_modal();                xdg_dialog            });             if let Some(parent) = parent.as_ref() {                parent.add_child(surface.id(), true);            }             dialog        } else {            None        };         if let Some(size) = params.window_min_size {            toplevel.set_min_size(f32::from(size.width) as i32, f32::from(size.height) as i32);        }         // Attempt to set up window decorations based on the requested configuration        let decoration = globals            .decoration_manager            .as_ref()            .map(|decoration_manager| {                decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())            });         Ok(WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {            xdg_surface,            toplevel,            decoration,            dialog,        }))    }} pub struct WaylandXdgSurfaceState {    xdg_surface: xdg_surface::XdgSurface,    toplevel: xdg_toplevel::XdgToplevel,    decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,    dialog: Option<XdgDialogV1>,} pub struct WaylandLayerSurfaceState {    layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,    anchor: Anchor,} pub struct WaylandPopupSurfaceState {    xdg_surface: xdg_surface::XdgSurface,    xdg_popup: xdg_popup::XdgPopup,    // Kept so the popup can be re-anchored via `xdg_popup.reposition` when resized.    options: PopupOptions,    next_reposition_token: Cell<u32>,} fn build_popup_positioner(    globals: &Globals,    options: &PopupOptions,    size: Size<Pixels>,    parent_geometry: Bounds<Pixels>,) -> xdg_positioner::XdgPositioner {    let positioner = globals.wm_base.create_positioner(&globals.qh, ());    // A zero or negative size is a protocol error.    positioner.set_size(        f32::from(size.width).max(1.0) as i32,        f32::from(size.height).max(1.0) as i32,    );     // The protocol wants the anchor rect relative to the parent's window geometry, while    // `options.anchor_rect` is in gpui window coordinates (surface-local). A rect extending    // outside the geometry or with a zero size is a protocol error, so translate, then clamp    // to at least one pixel inside the geometry, pulling the origin inward at the edges.    let anchor_rect = Bounds {        origin: options.anchor_rect.origin - parent_geometry.origin,        size: options.anchor_rect.size,    };    let one = Point::new(px(1.0), px(1.0));    let geometry_bottom_right: Point<Pixels> = parent_geometry.size.into();    let top_left = anchor_rect        .origin        .min(&(geometry_bottom_right - one))        .max(&Point::default());    let bottom_right = anchor_rect        .bottom_right()        .min(&geometry_bottom_right)        .max(&(top_left + one));    let anchor_rect = Bounds::from_corners(top_left, bottom_right);    positioner.set_anchor_rect(        f32::from(anchor_rect.origin.x) as i32,        f32::from(anchor_rect.origin.y) as i32,        f32::from(anchor_rect.size.width) as i32,        f32::from(anchor_rect.size.height) as i32,    );     positioner.set_anchor(super::popup::wayland_anchor(options.anchor));    positioner.set_gravity(super::popup::wayland_gravity(options.gravity));    positioner.set_constraint_adjustment(super::popup::wayland_constraint_adjustment(        options.constraint_adjustment,    ));    positioner.set_offset(        f32::from(options.offset.x) as i32,        f32::from(options.offset.y) as i32,    );    positioner} impl WaylandSurfaceState {    fn ack_configure(&self, serial: u32) {        match self {            WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {                xdg_surface.ack_configure(serial);            }            WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {                layer_surface.ack_configure(serial);            }            WaylandSurfaceState::Popup(WaylandPopupSurfaceState { xdg_surface, .. }) => {                xdg_surface.ack_configure(serial);            }        }    }     fn decoration(&self) -> Option<&zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1> {        if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { decoration, .. }) = self {            decoration.as_ref()        } else {            None        }    }     fn toplevel(&self) -> Option<&xdg_toplevel::XdgToplevel> {        if let WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { toplevel, .. }) = self {            Some(toplevel)        } else {            None        }    }     fn xdg_surface(&self) -> Option<&xdg_surface::XdgSurface> {        match self {            WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {                Some(xdg_surface)            }            WaylandSurfaceState::Popup(WaylandPopupSurfaceState { xdg_surface, .. }) => {                Some(xdg_surface)            }            WaylandSurfaceState::LayerShell(_) => None,        }    }     fn layer_surface(&self) -> Option<&zwlr_layer_surface_v1::ZwlrLayerSurfaceV1> {        if let WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) =            self        {            Some(layer_surface)        } else {            None        }    }     fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {        match self {            WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, .. }) => {                xdg_surface.set_window_geometry(x, y, width, height);            }            WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) => {                // cannot set window position of a layer surface                layer_surface.set_size(width as u32, height as u32);            }            WaylandSurfaceState::Popup(WaylandPopupSurfaceState { xdg_surface, .. }) => {                xdg_surface.set_window_geometry(x, y, width, height);            }        }    }     // Re-anchors a mapped popup at a new size via `xdg_popup.reposition`. Repositioning an    // unmapped popup (before the first configure) is a protocol error.    fn reposition_popup(        &self,        globals: &Globals,        size: Size<Pixels>,        parent_geometry: Bounds<Pixels>,    ) {        if let WaylandSurfaceState::Popup(WaylandPopupSurfaceState {            xdg_popup,            options,            next_reposition_token,            ..        }) = self            && xdg_popup.version() >= xdg_popup::REQ_REPOSITION_SINCE        {            let token = next_reposition_token.get();            next_reposition_token.set(token.wrapping_add(1));             let positioner = build_popup_positioner(globals, options, size, parent_geometry);            xdg_popup.reposition(&positioner, token);            positioner.destroy();        }    }     fn set_exclusive_zone(&self, zone: i32) -> bool {        if let WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, .. }) =            self        {            layer_surface.set_exclusive_zone(zone);            true        } else {            false        }    }     /// An exclusive edge must be a single edge that the surface is anchored to,    /// otherwise the compositor raises a fatal `invalid_exclusive_edge` protocol    /// error. An invalid edge is logged and ignored. Returns whether it applied.    fn apply_exclusive_edge(        layer_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,        anchor: Anchor,        edge: Anchor,    ) -> bool {        if edge.bits().count_ones() == 1 && anchor.contains(edge) {            layer_surface.set_exclusive_edge(super::layer_shell::wayland_anchor(edge));            true        } else {            log::warn!(                "ignoring exclusive edge {edge:?}: must be a single edge of the surface anchor {anchor:?}"            );            false        }    }     fn set_exclusive_edge(&self, edge: Anchor) -> bool {        if let WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {            layer_surface,            anchor,            ..        }) = self        {            Self::apply_exclusive_edge(layer_surface, *anchor, edge)        } else {            false        }    } 

Lines 1–500 of 2084.