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) = ¶ms.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) = ¶ms.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. Load more
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) = ¶ms.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) = ¶ms.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 } }