use std::{ cell::{Ref, RefCell, RefMut}, ffi::c_void, ptr::NonNull, rc::Rc, sync::Arc,}; use collections::{FxHashSet, 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_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_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::LayerShellNotSupportedError, px, size,};use gpui_wgpu::{CompositorGpuHint, WgpuRenderer, WgpuSurfaceConfig}; #[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()>>,} 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>, children: FxHashSet<ObjectId>, 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, in_progress_configure: Option<InProgressConfigure>, resize_throttle: bool, in_progress_window_controls: Option<WindowControls>, window_controls: WindowControls, client_inset: Option<Pixels>,} pub enum WaylandSurfaceState { Xdg(WaylandXdgSurfaceState), LayerShell(WaylandLayerSurfaceState),} impl WaylandSurfaceState { fn new( surface: &wl_surface::WlSurface, globals: &Globals, params: &WindowParams, parent: Option<WaylandWindowStatePtr>, 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 { layer_surface .set_exclusive_edge(super::layer_shell::wayland_anchor(exclusive_edge)); } return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, })); } // 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()); } 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,} 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); } } } 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 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); } } } fn destroy(&mut self) { match self { WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, toplevel, decoration: _decoration, dialog, }) => { // drop the dialog before toplevel so compositor can explicitly unapply it's effects if let Some(dialog) = dialog { dialog.destroy(); } // The role object (toplevel) must always be destroyed before the xdg_surface. // See https://wayland.app/protocols/xdg-shell#xdg_surface:request:destroy toplevel.destroy(); xdg_surface.destroy(); } WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface }) => { layer_surface.destroy(); } } }} #[derive(Clone)]pub struct WaylandWindowStatePtr { state: Rc<RefCell<WaylandWindowState>>, callbacks: Rc<RefCell<Callbacks>>,} impl WaylandWindowState { pub(crate) fn new( handle: AnyWindowHandle, surface: wl_surface::WlSurface, surface_state: WaylandSurfaceState, appearance: WindowAppearance, viewport: Option<wp_viewport::WpViewport>, client: WaylandClientStatePtr, globals: Globals, gpu_context: gpui_wgpu::GpuContext, compositor_gpu: Option<CompositorGpuHint>, options: WindowParams, parent: Option<WaylandWindowStatePtr>, ) -> anyhow::Result<Self> { let renderer = { let raw_window = RawWindow { window: surface.id().as_ptr().cast::<c_void>(), display: surface .backend() .upgrade() .unwrap() .display_ptr() .cast::<c_void>(), }; let config = WgpuSurfaceConfig { size: Size { width: DevicePixels(f32::from(options.bounds.size.width) as i32), height: DevicePixels(f32::from(options.bounds.size.height) as i32), }, transparent: true, }; WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)? }; if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state { if let Some(title) = options.titlebar.and_then(|titlebar| titlebar.title) { xdg_state.toplevel.set_title(title.to_string()); } // Set max window size based on the GPU's maximum texture dimension. // This prevents the window from being resized larger than what the GPU can render. let max_texture_size = renderer.max_texture_size() as i32; xdg_state .toplevel .set_max_size(max_texture_size, max_texture_size); } Ok(Self { surface_state, acknowledged_first_configure: false, parent, children: FxHashSet::default(), surface, app_id: None, blur: None, viewport, globals, outputs: HashMap::default(), display: None, renderer, bounds: options.bounds, scale: 1.0, input_handler: None, decorations: WindowDecorations::Client, background_appearance: WindowBackgroundAppearance::Opaque, fullscreen: false, maximized: false, tiling: Tiling::default(), window_bounds: options.bounds, in_progress_configure: None, resize_throttle: false, client, appearance, handle, active: false, hovered: false, in_progress_window_controls: None, window_controls: WindowControls::default(), client_inset: None, }) } pub fn is_transparent(&self) -> bool { self.decorations == WindowDecorations::Client || self.background_appearance != WindowBackgroundAppearance::Opaque } pub fn primary_output_scale(&mut self) -> i32 { let mut scale = 1; let mut current_output = self.display.take(); for (id, output) in self.outputs.iter() { if let Some((_, output_data)) = ¤t_output { if output.scale > output_data.scale { current_output = Some((id.clone(), output.clone())); } } else { current_output = Some((id.clone(), output.clone())); } scale = scale.max(output.scale); } self.display = current_output; scale } pub fn inset(&self) -> Pixels { match self.decorations { WindowDecorations::Server => px(0.0), WindowDecorations::Client => self.client_inset.unwrap_or(px(0.0)), } }} pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);pub enum ImeInput { InsertText(String), SetMarkedText(String), UnmarkText, DeleteText,} impl Drop for WaylandWindow { fn drop(&mut self) { let mut state = self.0.state.borrow_mut(); let surface_id = state.surface.id(); if let Some(parent) = state.parent.as_ref() { parent.state.borrow_mut().children.remove(&surface_id); } let client = state.client.clone(); state.renderer.destroy(); // Destroy blur first, this has no dependencies. if let Some(blur) = &state.blur { blur.release(); } // Decorations must be destroyed before the xdg state. // See https://wayland.app/protocols/xdg-decoration-unstable-v1#zxdg_toplevel_decoration_v1 if let Some(decoration) = &state.surface_state.decoration() { decoration.destroy(); } // Surface state might contain xdg_toplevel/xdg_surface which can be destroyed now that // decorations are gone. layer_surface has no dependencies. state.surface_state.destroy(); // Viewport must be destroyed before the wl_surface. // See https://wayland.app/protocols/viewporter#wp_viewport if let Some(viewport) = &state.viewport { viewport.destroy(); } // The wl_surface itself should always be destroyed last. state.surface.destroy(); let state_ptr = self.0.clone(); state .globals .executor .spawn(async move { state_ptr.close(); client.drop_window(&surface_id) }) .detach(); drop(state); }} impl WaylandWindow { fn borrow(&self) -> Ref<'_, WaylandWindowState> { self.0.state.borrow() } fn borrow_mut(&self) -> RefMut<'_, WaylandWindowState> { self.0.state.borrow_mut() } pub fn new( handle: AnyWindowHandle, globals: Globals, gpu_context: gpui_wgpu::GpuContext, compositor_gpu: Option<CompositorGpuHint>, client: WaylandClientStatePtr, params: WindowParams, appearance: WindowAppearance, parent: Option<WaylandWindowStatePtr>,Lines 1–500 of 1595. Load more
use std::{ cell::{Ref, RefCell, RefMut}, ffi::c_void, ptr::NonNull, rc::Rc, sync::Arc, }; use collections::{FxHashSet, 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_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_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::LayerShellNotSupportedError, px, size, }; use gpui_wgpu::{CompositorGpuHint, WgpuRenderer, WgpuSurfaceConfig}; #[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()>>, } 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>, children: FxHashSet<ObjectId>, 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, in_progress_configure: Option<InProgressConfigure>, resize_throttle: bool, in_progress_window_controls: Option<WindowControls>, window_controls: WindowControls, client_inset: Option<Pixels>, } pub enum WaylandSurfaceState { Xdg(WaylandXdgSurfaceState), LayerShell(WaylandLayerSurfaceState), } impl WaylandSurfaceState { fn new( surface: &wl_surface::WlSurface, globals: &Globals, params: &WindowParams, parent: Option<WaylandWindowStatePtr>, 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 { layer_surface .set_exclusive_edge(super::layer_shell::wayland_anchor(exclusive_edge)); } return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface, })); } // 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()); } 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, } 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); } } } 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 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); } } } fn destroy(&mut self) { match self { WaylandSurfaceState::Xdg(WaylandXdgSurfaceState { xdg_surface, toplevel, decoration: _decoration, dialog, }) => { // drop the dialog before toplevel so compositor can explicitly unapply it's effects if let Some(dialog) = dialog { dialog.destroy(); } // The role object (toplevel) must always be destroyed before the xdg_surface. // See https://wayland.app/protocols/xdg-shell#xdg_surface:request:destroy toplevel.destroy(); xdg_surface.destroy(); } WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface }) => { layer_surface.destroy(); } } } } #[derive(Clone)] pub struct WaylandWindowStatePtr { state: Rc<RefCell<WaylandWindowState>>, callbacks: Rc<RefCell<Callbacks>>, } impl WaylandWindowState { pub(crate) fn new( handle: AnyWindowHandle, surface: wl_surface::WlSurface, surface_state: WaylandSurfaceState, appearance: WindowAppearance, viewport: Option<wp_viewport::WpViewport>, client: WaylandClientStatePtr, globals: Globals, gpu_context: gpui_wgpu::GpuContext, compositor_gpu: Option<CompositorGpuHint>, options: WindowParams, parent: Option<WaylandWindowStatePtr>, ) -> anyhow::Result<Self> { let renderer = { let raw_window = RawWindow { window: surface.id().as_ptr().cast::<c_void>(), display: surface .backend() .upgrade() .unwrap() .display_ptr() .cast::<c_void>(), }; let config = WgpuSurfaceConfig { size: Size { width: DevicePixels(f32::from(options.bounds.size.width) as i32), height: DevicePixels(f32::from(options.bounds.size.height) as i32), }, transparent: true, }; WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)? }; if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state { if let Some(title) = options.titlebar.and_then(|titlebar| titlebar.title) { xdg_state.toplevel.set_title(title.to_string()); } // Set max window size based on the GPU's maximum texture dimension. // This prevents the window from being resized larger than what the GPU can render. let max_texture_size = renderer.max_texture_size() as i32; xdg_state .toplevel .set_max_size(max_texture_size, max_texture_size); } Ok(Self { surface_state, acknowledged_first_configure: false, parent, children: FxHashSet::default(), surface, app_id: None, blur: None, viewport, globals, outputs: HashMap::default(), display: None, renderer, bounds: options.bounds, scale: 1.0, input_handler: None, decorations: WindowDecorations::Client, background_appearance: WindowBackgroundAppearance::Opaque, fullscreen: false, maximized: false, tiling: Tiling::default(), window_bounds: options.bounds, in_progress_configure: None, resize_throttle: false, client, appearance, handle, active: false, hovered: false, in_progress_window_controls: None, window_controls: WindowControls::default(), client_inset: None, }) } pub fn is_transparent(&self) -> bool { self.decorations == WindowDecorations::Client || self.background_appearance != WindowBackgroundAppearance::Opaque } pub fn primary_output_scale(&mut self) -> i32 { let mut scale = 1; let mut current_output = self.display.take(); for (id, output) in self.outputs.iter() { if let Some((_, output_data)) = ¤t_output { if output.scale > output_data.scale { current_output = Some((id.clone(), output.clone())); } } else { current_output = Some((id.clone(), output.clone())); } scale = scale.max(output.scale); } self.display = current_output; scale } pub fn inset(&self) -> Pixels { match self.decorations { WindowDecorations::Server => px(0.0), WindowDecorations::Client => self.client_inset.unwrap_or(px(0.0)), } } } pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr); pub enum ImeInput { InsertText(String), SetMarkedText(String), UnmarkText, DeleteText, } impl Drop for WaylandWindow { fn drop(&mut self) { let mut state = self.0.state.borrow_mut(); let surface_id = state.surface.id(); if let Some(parent) = state.parent.as_ref() { parent.state.borrow_mut().children.remove(&surface_id); } let client = state.client.clone(); state.renderer.destroy(); // Destroy blur first, this has no dependencies. if let Some(blur) = &state.blur { blur.release(); } // Decorations must be destroyed before the xdg state. // See https://wayland.app/protocols/xdg-decoration-unstable-v1#zxdg_toplevel_decoration_v1 if let Some(decoration) = &state.surface_state.decoration() { decoration.destroy(); } // Surface state might contain xdg_toplevel/xdg_surface which can be destroyed now that // decorations are gone. layer_surface has no dependencies. state.surface_state.destroy(); // Viewport must be destroyed before the wl_surface. // See https://wayland.app/protocols/viewporter#wp_viewport if let Some(viewport) = &state.viewport { viewport.destroy(); } // The wl_surface itself should always be destroyed last. state.surface.destroy(); let state_ptr = self.0.clone(); state .globals .executor .spawn(async move { state_ptr.close(); client.drop_window(&surface_id) }) .detach(); drop(state); } } impl WaylandWindow { fn borrow(&self) -> Ref<'_, WaylandWindowState> { self.0.state.borrow() } fn borrow_mut(&self) -> RefMut<'_, WaylandWindowState> { self.0.state.borrow_mut() } pub fn new( handle: AnyWindowHandle, globals: Globals, gpu_context: gpui_wgpu::GpuContext, compositor_gpu: Option<CompositorGpuHint>, client: WaylandClientStatePtr, params: WindowParams, appearance: WindowAppearance, parent: Option<WaylandWindowStatePtr>,