From 504a4e8ef7a1486dc2295f3dd1bf244ff8f86c40 Mon Sep 17 00:00:00 2001 From: Max Guppy Date: Tue, 30 May 2023 14:30:22 +1000 Subject: [PATCH 1/2] Add wlr layer-shell windows, configurable at creation. --- src/platform/wayland.rs | 54 +- src/platform_impl/linux/mod.rs | 23 + src/platform_impl/linux/wayland/state.rs | 53 +- src/platform_impl/linux/wayland/window/mod.rs | 186 +++-- .../linux/wayland/window/state.rs | 713 ++++++++++++------ 5 files changed, 757 insertions(+), 272 deletions(-) diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs index 3e59184d70..7561a725ae 100644 --- a/src/platform/wayland.rs +++ b/src/platform/wayland.rs @@ -1,6 +1,9 @@ use std::os::raw; -use sctk::reexports::client::Proxy; +use sctk::{ + reexports::client::Proxy, + shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}, +}; use crate::{ event_loop::{EventLoopBuilder, EventLoopWindowTarget}, @@ -122,6 +125,22 @@ pub trait WindowBuilderExtWayland { /// For details about application ID conventions, see the /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) fn with_name(self, general: impl Into, instance: impl Into) -> Self; + + // TODO(theonlymrcat): Users shouldn't need to pull sctk in. Reexport? or Redefine? + + /// Create this window using the WLR Layer Shell protocol. + /// + /// Building this window will fail if the compositor does not support the `zwlr_layer_shell_v1` + /// protocol. + fn with_layer_shell(self, layer: Layer) -> Self; + + fn with_anchor(self, anchor: Anchor) -> Self; + + fn with_exclusive_zone(self, exclusive_zone: i32) -> Self; + + fn with_margin(self, top: i32, right: i32, bottom: i32, left: i32) -> Self; + + fn with_keyboard_interactivity(self, keyboard_interactivity: KeyboardInteractivity) -> Self; } impl WindowBuilderExtWayland for WindowBuilder { @@ -130,6 +149,39 @@ impl WindowBuilderExtWayland for WindowBuilder { self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into())); self } + + #[inline] + fn with_layer_shell(mut self, layer: Layer) -> Self { + self.platform_specific.layer_shell = Some(layer); + self + } + + #[inline] + fn with_anchor(mut self, anchor: Anchor) -> Self { + self.platform_specific.anchor = Some(anchor); + self + } + + #[inline] + fn with_exclusive_zone(mut self, exclusive_zone: i32) -> Self { + self.platform_specific.exclusive_zone = Some(exclusive_zone); + self + } + + #[inline] + fn with_margin(mut self, top: i32, right: i32, bottom: i32, left: i32) -> Self { + self.platform_specific.margin = Some((top, right, bottom, left)); + self + } + + #[inline] + fn with_keyboard_interactivity( + mut self, + keyboard_interactivity: KeyboardInteractivity, + ) -> Self { + self.platform_specific.keyboard_interactivity = Some(keyboard_interactivity); + self + } } /// Additional methods on `MonitorHandle` that are specific to Wayland. diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 03db21ed3e..b92e60fb8e 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -40,6 +40,9 @@ use crate::{ }, }; +#[cfg(wayland_platform)] +use sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}; + pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(self) use crate::platform_impl::Fullscreen; @@ -97,6 +100,16 @@ pub struct PlatformSpecificWindowBuilderAttributes { pub override_redirect: bool, #[cfg(x11_platform)] pub x11_window_types: Vec, + #[cfg(wayland_platform)] + pub layer_shell: Option, + #[cfg(wayland_platform)] + pub anchor: Option, + #[cfg(wayland_platform)] + pub exclusive_zone: Option, + #[cfg(wayland_platform)] + pub margin: Option<(i32, i32, i32, i32)>, + #[cfg(wayland_platform)] + pub keyboard_interactivity: Option, } impl Default for PlatformSpecificWindowBuilderAttributes { @@ -113,6 +126,16 @@ impl Default for PlatformSpecificWindowBuilderAttributes { override_redirect: false, #[cfg(x11_platform)] x11_window_types: vec![XWindowType::Normal], + #[cfg(wayland_platform)] + layer_shell: None, + #[cfg(wayland_platform)] + anchor: None, + #[cfg(wayland_platform)] + exclusive_zone: None, + #[cfg(wayland_platform)] + margin: None, + #[cfg(wayland_platform)] + keyboard_interactivity: None, } } } diff --git a/src/platform_impl/linux/wayland/state.rs b/src/platform_impl/linux/wayland/state.rs index f9b7c4afca..6e9d655204 100644 --- a/src/platform_impl/linux/wayland/state.rs +++ b/src/platform_impl/linux/wayland/state.rs @@ -16,6 +16,7 @@ use sctk::output::{OutputHandler, OutputState}; use sctk::registry::{ProvidesRegistryState, RegistryState}; use sctk::seat::pointer::ThemedPointer; use sctk::seat::SeatState; +use sctk::shell::wlr_layer::{LayerShell, LayerShellHandler, LayerSurface, LayerSurfaceConfigure}; use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler}; use sctk::shell::xdg::XdgShell; use sctk::shell::WaylandSurface; @@ -59,6 +60,9 @@ pub struct WinitState { /// The XDG shell that is used for widnows. pub xdg_shell: XdgShell, + /// The layer shell for layer surfaces + pub layer_shell: LayerShell, + /// The currently present windows. pub windows: RefCell>>>, @@ -148,6 +152,8 @@ impl WinitState { xdg_shell: XdgShell::bind(globals, queue_handle)?, xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(), + layer_shell: LayerShell::bind(globals, queue_handle)?, + windows: Default::default(), window_requests: Default::default(), window_compositor_updates: Vec::new(), @@ -273,12 +279,56 @@ impl WindowHandler for WinitState { .expect("got configure for dead window.") .lock() .unwrap() - .configure(configure, &self.shm, &self.subcompositor_state); + .configure_xdg(configure, &self.shm, &self.subcompositor_state); self.window_compositor_updates[pos].size = Some(new_size); } } +impl LayerShellHandler for WinitState { + fn closed(&mut self, _: &Connection, _: &QueueHandle, layer: &LayerSurface) { + let window_id = super::make_wid(layer.wl_surface()); + Self::queue_close(&mut self.window_compositor_updates, window_id); + } + + fn configure( + &mut self, + _: &Connection, + _: &QueueHandle, + layer: &LayerSurface, + configure: LayerSurfaceConfigure, + _serial: u32, + ) { + let window_id = super::make_wid(layer.wl_surface()); + + let pos = if let Some(pos) = self + .window_compositor_updates + .iter() + .position(|update| update.window_id == window_id) + { + pos + } else { + self.window_compositor_updates + .push(WindowCompositorUpdate::new(window_id)); + self.window_compositor_updates.len() - 1 + }; + + // Populate the configure to the window. + // + // XXX the size on the window will be updated right before dispatching the size to the user. + let new_size = self + .windows + .get_mut() + .get_mut(&window_id) + .expect("got configure for dead window.") + .lock() + .unwrap() + .configure_layer(configure); + + // self.window_compositor_updates[pos].size = Some(new_size); + } +} + impl OutputHandler for WinitState { fn output_state(&mut self) -> &mut OutputState { &mut self.output_state @@ -366,3 +416,4 @@ sctk::delegate_registry!(WinitState); sctk::delegate_shm!(WinitState); sctk::delegate_xdg_shell!(WinitState); sctk::delegate_xdg_window!(WinitState); +sctk::delegate_layer!(WinitState); diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 76168f3620..a5dd09ec30 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -15,9 +15,11 @@ use sctk::reexports::client::QueueHandle; use sctk::compositor::{CompositorState, Region, SurfaceData}; use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1; +use sctk::shell::wlr_layer::{Anchor, LayerSurface}; use sctk::shell::xdg::window::Window as SctkWindow; use sctk::shell::xdg::window::WindowDecorations; use sctk::shell::WaylandSurface; +use wayland_client::protocol::wl_output::WlOutput; use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; @@ -43,13 +45,12 @@ pub use state::WindowState; /// The Wayland window. pub struct Window { - /// Reference to the underlying SCTK window. - window: SctkWindow, + /// The underlying window shell and state + window: WindowShell, /// Window id. window_id: WindowId, - /// The state of the window. window_state: Arc>, /// Compositor to handle WlRegion stuff. @@ -105,37 +106,79 @@ impl Window { .map(|size| size.to_logical::(1.)) .unwrap_or((800, 600).into()); - let window = state.xdg_shell.create_window( - surface.clone(), - WindowDecorations::ServerDefault, - &queue_handle, - ); - - let mut window_state = WindowState::new( - event_loop_window_target.connection.clone(), - &event_loop_window_target.queue_handle, - &state, - size, - window.clone(), - attributes.preferred_theme, - ); - - // Set the app_id. - if let Some(name) = platform_attributes.name.map(|name| name.general) { - window.set_app_id(name); - } + let (window, mut window_state) = if let Some(layer) = platform_attributes.layer_shell { + let layer_surface = state.layer_shell.create_layer_surface( + &queue_handle, + surface.clone(), + layer, + // TODO(theonlymrcat): Is this where app id should go? + None::, + None, + ); + + let window_state = WindowState::new_layer( + event_loop_window_target.connection.clone(), + &event_loop_window_target.queue_handle, + &state, + size, + layer_surface.clone(), + attributes.preferred_theme, + ); + + layer_surface.set_size(size.width, size.height); + if let Some(anchor) = platform_attributes.anchor { + layer_surface.set_anchor(anchor); + } + if let Some(exclusive_zone) = platform_attributes.exclusive_zone { + layer_surface.set_exclusive_zone(exclusive_zone) + } + if let Some((top, right, bottom, left)) = platform_attributes.margin { + layer_surface.set_margin(top, right, bottom, left); + } + if let Some(keyboard_interactivity) = platform_attributes.keyboard_interactivity { + layer_surface.set_keyboard_interactivity(keyboard_interactivity); + } - // Set the window title. - window_state.set_title(attributes.title); + ( + WindowShell::WlrLayer { + surface: layer_surface, + }, + window_state, + ) + } else { + let window = state.xdg_shell.create_window( + surface.clone(), + WindowDecorations::ServerDefault, + &queue_handle, + ); + + let mut window_state = WindowState::new_xdg( + event_loop_window_target.connection.clone(), + &event_loop_window_target.queue_handle, + &state, + size, + window.clone(), + attributes.preferred_theme, + ); + + // Set the app_id. + if let Some(name) = platform_attributes.name.map(|name| name.general) { + window.set_app_id(name); + } - // Set the min and max sizes. - let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.)); - let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.)); - window_state.set_min_inner_size(min_size); - window_state.set_max_inner_size(max_size); + // Set the window title. + window_state.set_title(attributes.title); - // Non-resizable implies that the min and max sizes are set to the same value. - window_state.set_resizable(attributes.resizable); + // Set the min and max sizes. + let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.)); + let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.)); + window_state.set_min_inner_size(min_size); + window_state.set_max_inner_size(max_size); + + // Non-resizable implies that the min and max sizes are set to the same value. + window_state.set_resizable(attributes.resizable); + (WindowShell::Xdg { window }, window_state) + }; // Set startup mode. match attributes.fullscreen.map(Into::into) { @@ -151,12 +194,12 @@ impl Window { window.set_fullscreen(output.as_ref()) } - _ if attributes.maximized => window.set_maximized(), + _ if attributes.maximized => window.set_maximized(true), _ => (), }; // XXX Do initial commit. - window.commit(); + window.wl_surface().commit(); // Add the window and window requests into the state. let window_state = Arc::new(Mutex::new(window_state)); @@ -228,7 +271,7 @@ impl Window { #[inline] pub fn set_title(&self, title: impl ToString) { let new_title = title.to_string(); - self.window_state.lock().unwrap().set_title(new_title); + self.window_state.lock().unwrap().set_title(new_title) } #[inline] @@ -396,34 +439,17 @@ impl Window { #[inline] pub fn is_maximized(&self) -> bool { - self.window_state - .lock() - .unwrap() - .last_configure - .as_ref() - .map(|last_configure| last_configure.is_maximized()) - .unwrap_or_default() + self.window_state.lock().unwrap().is_maximized() } #[inline] pub fn set_maximized(&self, maximized: bool) { - if maximized { - self.window.set_maximized() - } else { - self.window.unset_maximized() - } + self.window.set_maximized(maximized); } #[inline] pub(crate) fn fullscreen(&self) -> Option { - let is_fullscreen = self - .window_state - .lock() - .unwrap() - .last_configure - .as_ref() - .map(|last_configure| last_configure.is_fullscreen()) - .unwrap_or_default(); + let is_fullscreen = self.window_state.lock().unwrap().is_fullscreen(); if is_fullscreen { let current_monitor = self.current_monitor().map(PlatformMonitorHandle::Wayland); @@ -623,6 +649,58 @@ impl Drop for Window { } } +enum WindowShell { + Xdg { window: SctkWindow }, + WlrLayer { surface: LayerSurface }, +} + +impl WindowShell { + pub fn set_maximized(&self, maximized: bool) { + match self { + WindowShell::Xdg { window } => { + if maximized { + window.set_maximized() + } else { + window.unset_maximized() + } + } + WindowShell::WlrLayer { .. } => { + warn!("Maximizing is ignored for layer_shell windows") + } + } + } + + pub fn set_minimized(&self) { + match self { + WindowShell::Xdg { window } => window.set_minimized(), + WindowShell::WlrLayer { .. } => warn!("Minimizing is ignored for layer_shell windows"), + } + } + + pub fn set_fullscreen(&self, output: Option<&WlOutput>) { + match self { + WindowShell::Xdg { window } => { + window.set_fullscreen(output); + } + WindowShell::WlrLayer { .. } => warn!("Fullscreen is ignored for layer_shell windows"), + } + } + + pub fn unset_fullscreen(&self) { + match self { + WindowShell::Xdg { window } => window.unset_fullscreen(), + WindowShell::WlrLayer { .. } => warn!("Fullscreen is ignored for layer_shell windows"), + } + } + + pub fn wl_surface(&self) -> &WlSurface { + match self { + WindowShell::Xdg { window } => window.wl_surface(), + WindowShell::WlrLayer { surface } => surface.wl_surface(), + } + } +} + /// The request from the window to the event loop. #[derive(Debug)] pub struct WindowRequests { diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index 12d55c9646..a33f000c87 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -17,6 +17,7 @@ use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge; use sctk::compositor::{CompositorState, Region, SurfaceData}; use sctk::seat::pointer::ThemedPointer; +use sctk::shell::wlr_layer::{LayerSurface, LayerSurfaceConfigure}; use sctk::shell::xdg::frame::{DecorationsFrame, FrameAction, FrameClick}; use sctk::shell::xdg::window::{DecorationMode, Window, WindowConfigure}; use sctk::shell::xdg::XdgSurface; @@ -47,18 +48,9 @@ pub struct WindowState { /// The connection to Wayland server. pub connection: Connection, - /// The underlying SCTK window. - pub window: ManuallyDrop, - - /// The window frame, which is created from the configure request. - frame: Option, - /// The `Shm` to set cursor. pub shm: WlShm, - /// The last received configure. - pub last_configure: Option, - /// The pointers observed on the window. pub pointers: Vec>>, @@ -74,15 +66,15 @@ pub struct WindowState { /// Queue handle. pub queue_handle: QueueHandle, + /// State that differes based on being an XDG shell or a WLR layer shell + shell_specific: ShellSpecificState, + /// Theme varaint. theme: Option, /// The current window title. title: String, - /// Whether the frame is resizable. - resizable: bool, - /// Whether the window has focus. has_focus: bool, @@ -110,20 +102,41 @@ pub struct WindowState { /// The inner size of the window, as in without client side decorations. size: LogicalSize, - /// Whether the CSD fail to create, so we don't try to create them on each iteration. - csd_fails: bool, + viewport: Option, + fractional_scale: Option, +} - /// Min size. - min_inner_size: LogicalSize, - max_inner_size: Option>, +enum ShellSpecificState { + Xdg { + /// The underlying SCTK window. + window: ManuallyDrop, - /// The size of the window when no states were applied to it. The primary use for it - /// is to fallback to original window size, before it was maximized, if the compositor - /// sends `None` for the new size in the configure. - stateless_size: LogicalSize, + /// The last received configure. + last_configure: Option, - viewport: Option, - fractional_scale: Option, + /// Whether the frame is resizable. + resizable: bool, + + /// The window frame, which is created from the configure request. + frame: Option, + + /// Whether the CSD fail to create, so we don't try to create them on each iteration. + csd_fails: bool, + + /// The size of the window when no states were applied to it. The primary use for it + /// is to fallback to original window size, before it was maximized, if the compositor + /// sends `None` for the new size in the configure. + stateless_size: LogicalSize, + + /// Min size. + min_inner_size: LogicalSize, + max_inner_size: Option>, + }, + WlrLayer { + surface: ManuallyDrop, + + last_configure: Option, + }, } /// The state of the cursor grabs. @@ -160,73 +173,112 @@ impl WindowState { }) } - pub fn configure( + fn wl_surface(&self) -> &WlSurface { + match &self.shell_specific { + ShellSpecificState::Xdg { window, .. } => window.wl_surface(), + ShellSpecificState::WlrLayer { surface, .. } => surface.wl_surface(), + } + } + + pub fn configure_xdg( &mut self, configure: WindowConfigure, shm: &Shm, subcompositor: &Arc, ) -> LogicalSize { - if configure.decoration_mode == DecorationMode::Client - && self.frame.is_none() - && !self.csd_fails - { - match WinitFrame::new( - &*self.window, - shm, - subcompositor.clone(), - self.queue_handle.clone(), - #[cfg(feature = "sctk-adwaita")] - into_sctk_adwaita_config(self.theme), - ) { - Ok(mut frame) => { - frame.set_title(&self.title); - // Ensure that the frame is not hidden. - frame.set_hidden(false); - self.frame = Some(frame); - } - Err(err) => { - warn!("Failed to create client side decorations frame: {err}"); - self.csd_fails = true; + match self.shell_specific { + ShellSpecificState::Xdg { + ref window, + ref mut last_configure, + ref mut frame, + ref mut csd_fails, + stateless_size, + .. + } => { + if configure.decoration_mode == DecorationMode::Client + && frame.is_none() + && !*csd_fails + { + match WinitFrame::new( + &**window, + shm, + subcompositor.clone(), + self.queue_handle.clone(), + #[cfg(feature = "sctk-adwaita")] + into_sctk_adwaita_config(self.theme), + ) { + Ok(mut new_frame) => { + new_frame.set_title(&self.title); + // Ensure that the frame is not hidden. + new_frame.set_hidden(false); + *frame = Some(new_frame); + } + Err(err) => { + warn!("Failed to create client side decorations frame: {err}"); + *csd_fails = true; + } + } + } else if configure.decoration_mode == DecorationMode::Server { + // Drop the frame for server side decorations to save resources. + *frame = None; } + + let stateless = Self::is_stateless(&configure); + + let new_size = if let Some(frame) = frame.as_mut() { + // Configure the window states. + frame.update_state(configure.state); + + match configure.new_size { + (Some(width), Some(height)) => { + let (width, height) = frame.subtract_borders(width, height); + ( + width.map(|w| w.get()).unwrap_or(1), + height.map(|h| h.get()).unwrap_or(1), + ) + .into() + } + (_, _) if stateless => stateless_size, + _ => self.size, + } + } else { + match configure.new_size { + (Some(width), Some(height)) => (width.get(), height.get()).into(), + _ if stateless => stateless_size, + _ => self.size, + } + }; + + // XXX Set the configure before doing a resize. + *last_configure = Some(configure); + + // XXX Update the new size right away. + self.resize(new_size); + + new_size } - } else if configure.decoration_mode == DecorationMode::Server { - // Drop the frame for server side decorations to save resources. - self.frame = None; + ShellSpecificState::WlrLayer { .. } => unreachable!(), // TODO(theonlymrcat): Replace this match with let...else } + } - let stateless = Self::is_stateless(&configure); - - let new_size = if let Some(frame) = self.frame.as_mut() { - // Configure the window states. - frame.update_state(configure.state); - - match configure.new_size { - (Some(width), Some(height)) => { - let (width, height) = frame.subtract_borders(width, height); - ( - width.map(|w| w.get()).unwrap_or(1), - height.map(|h| h.get()).unwrap_or(1), - ) - .into() - } - (_, _) if stateless => self.stateless_size, - _ => self.size, + pub fn configure_layer(&mut self, configure: LayerSurfaceConfigure) { + match &mut self.shell_specific { + ShellSpecificState::WlrLayer { last_configure, .. } => { + let new_size = match configure.new_size { + (0, 0) => self.size, + (0, height) => (self.size.width, height).into(), + (width, 0) => (width, self.size.height).into(), + (width, height) => (width, height).into(), + }; + + // XXX Set the configuration before doing a resize. + *last_configure = Some(configure); + + // XXX Update the new size right away. + self.resize(new_size); } - } else { - match configure.new_size { - (Some(width), Some(height)) => (width.get(), height.get()).into(), - _ if stateless => self.stateless_size, - _ => self.size, - } - }; - - // XXX Set the configure before doing a resize. - self.last_configure = Some(configure); - - // XXX Update the new size right away. - self.resize(new_size); - - new_size + ShellSpecificState::Xdg { .. } => unreachable!(), // TODO(theonlymrcat): Replace this match with let...else + } } #[inline] @@ -236,27 +288,37 @@ impl WindowState { /// Start interacting drag resize. pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> { - let xdg_toplevel = self.window.xdg_toplevel(); - - // TODO(kchibisov) handle touch serials. - self.apply_on_poiner(|_, data| { - let serial = data.latest_button_serial(); - let seat = data.seat(); - xdg_toplevel.resize(seat, serial, direction.into()); - }); + match &self.shell_specific { + ShellSpecificState::Xdg { window, .. } => { + let xdg_toplevel = window.xdg_toplevel(); + + // TODO(kchibisov) handle touch serials. + self.apply_on_poiner(|_, data| { + let serial = data.latest_button_serial(); + let seat = data.seat(); + xdg_toplevel.resize(seat, serial, direction.into()); + }); + } + ShellSpecificState::WlrLayer { .. } => {} + } Ok(()) } /// Start the window drag. pub fn drag_window(&self) -> Result<(), ExternalError> { - let xdg_toplevel = self.window.xdg_toplevel(); - // TODO(kchibisov) handle touch serials. - self.apply_on_poiner(|_, data| { - let serial = data.latest_button_serial(); - let seat = data.seat(); - xdg_toplevel._move(seat, serial); - }); + match &self.shell_specific { + ShellSpecificState::Xdg { window, .. } => { + let xdg_toplevel = window.xdg_toplevel(); + // TODO(kchibisov) handle touch serials. + self.apply_on_poiner(|_, data| { + let serial = data.latest_button_serial(); + let seat = data.seat(); + xdg_toplevel._move(seat, serial); + }); + } + ShellSpecificState::WlrLayer { .. } => {} // TODO(theonlymrcat): This match should be replaced with let...else + } Ok(()) } @@ -271,48 +333,80 @@ impl WindowState { window_id: WindowId, updates: &mut Vec, ) -> Option { - match self.frame.as_mut()?.on_click(click, pressed)? { - FrameAction::Minimize => self.window.set_minimized(), - FrameAction::Maximize => self.window.set_maximized(), - FrameAction::UnMaximize => self.window.unset_maximized(), - FrameAction::Close => WinitState::queue_close(updates, window_id), - FrameAction::Move => self.window.move_(seat, serial), - FrameAction::Resize(edge) => self.window.resize(seat, serial, edge), - FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)), - }; + match &mut self.shell_specific { + ShellSpecificState::Xdg { window, frame, .. } => { + match frame.as_mut()?.on_click(click, pressed)? { + FrameAction::Minimize => window.set_minimized(), + FrameAction::Maximize => window.set_maximized(), + FrameAction::UnMaximize => window.unset_maximized(), + FrameAction::Close => WinitState::queue_close(updates, window_id), + FrameAction::Move => window.move_(seat, serial), + FrameAction::Resize(edge) => window.resize(seat, serial, edge), + FrameAction::ShowMenu(x, y) => window.show_window_menu(seat, serial, (x, y)), + }; + } + ShellSpecificState::WlrLayer { .. } => {} // TODO(theonlymrcat): This match should be replaced with let...else + } Some(false) } pub fn frame_point_left(&mut self) { - if let Some(frame) = self.frame.as_mut() { - frame.click_point_left(); + match &mut self.shell_specific { + ShellSpecificState::Xdg { ref mut frame, .. } => { + if let Some(frame) = frame.as_mut() { + frame.click_point_left(); + } + } + ShellSpecificState::WlrLayer { .. } => {} // TODO(theonlymrcat): This match should be replaced with let...else } } // Move the point over decorations. pub fn frame_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option<&str> { - if let Some(frame) = self.frame.as_mut() { - frame.click_point_moved(surface, x, y) - } else { - None + match &mut self.shell_specific { + ShellSpecificState::Xdg { frame, .. } => { + if let Some(frame) = frame.as_mut() { + frame.click_point_moved(surface, x, y) + } else { + None + } + } + ShellSpecificState::WlrLayer { .. } => None, // TODO(theonlymrcat): This match should be replaced with let...else } } /// Get the stored resizable state. #[inline] pub fn resizable(&self) -> bool { - self.resizable + match self.shell_specific { + ShellSpecificState::Xdg { resizable, .. } => resizable, + ShellSpecificState::WlrLayer { .. } => false, + } } /// Set the resizable state on the window. #[inline] pub fn set_resizable(&mut self, resizable: bool) { - if self.resizable == resizable { - return; + match &mut self.shell_specific { + ShellSpecificState::Xdg { + resizable: state_resizable, + .. + } => { + if *state_resizable == resizable { + return; + } + + *state_resizable = resizable; + } + ShellSpecificState::WlrLayer { .. } => { + if resizable { + warn!("Resizable is ignored for layer_shell windows"); + } + return; + } } - self.resizable = resizable; if resizable { // Restore min/max sizes of the window. self.reload_min_max_hints(); @@ -322,8 +416,14 @@ impl WindowState { } // Reload the state on the frame as well. - if let Some(frame) = self.frame.as_mut() { - frame.set_resizable(resizable); + match &mut self.shell_specific { + ShellSpecificState::Xdg { + frame: Some(frame), .. + } => { + frame.set_resizable(resizable); + } + ShellSpecificState::Xdg { frame: None, .. } => {} + ShellSpecificState::WlrLayer { .. } => unreachable!(), } } @@ -348,26 +448,37 @@ impl WindowState { /// Whether the window received initial configure event from the compositor. #[inline] pub fn is_configured(&self) -> bool { - self.last_configure.is_some() + match &self.shell_specific { + ShellSpecificState::Xdg { last_configure, .. } => last_configure.is_some(), + ShellSpecificState::WlrLayer { last_configure, .. } => last_configure.is_some(), + } } #[inline] pub fn is_decorated(&mut self) -> bool { - let csd = self - .last_configure - .as_ref() - .map(|configure| configure.decoration_mode == DecorationMode::Client) - .unwrap_or(false); - if let Some(frame) = csd.then_some(self.frame.as_ref()).flatten() { - frame.is_hidden() - } else { - // Server side decorations. - true + match &self.shell_specific { + ShellSpecificState::Xdg { + last_configure, + frame, + .. + } => { + let csd = last_configure + .as_ref() + .map(|configure| configure.decoration_mode == DecorationMode::Client) + .unwrap_or(false); + if let Some(frame) = csd.then_some(frame.as_ref()).flatten() { + frame.is_hidden() + } else { + // Server side decorations. + true + } + } + ShellSpecificState::WlrLayer { .. } => false, } } /// Create new window state. - pub fn new( + pub fn new_xdg( connection: Connection, queue_handle: &QueueHandle, winit_state: &WinitState, @@ -390,41 +501,93 @@ impl WindowState { compositor, connection, theme, - csd_fails: false, cursor_grab_mode: GrabState::new(), cursor_icon: CursorIcon::Default, cursor_visible: true, fractional_scale, - frame: None, has_focus: false, ime_allowed: false, ime_purpose: ImePurpose::Normal, - last_configure: None, - max_inner_size: None, - min_inner_size: MIN_WINDOW_SIZE, pointer_constraints, pointers: Default::default(), queue_handle: queue_handle.clone(), scale_factor: 1., shm: winit_state.shm.wl_shm().clone(), + shell_specific: ShellSpecificState::Xdg { + csd_fails: false, + frame: None, + last_configure: None, + max_inner_size: None, + min_inner_size: MIN_WINDOW_SIZE, + resizable: true, + stateless_size: size, + window: ManuallyDrop::new(window), + }, + size, + text_inputs: Vec::new(), + title: String::default(), + transparent: false, + viewport, + } + } + + pub fn new_layer( + connection: Connection, + queue_handle: &QueueHandle, + winit_state: &WinitState, + size: LogicalSize, + layer_surface: LayerSurface, + theme: Option, + ) -> Self { + let compositor = winit_state.compositor_state.clone(); + let pointer_constraints = winit_state.pointer_constraints.clone(); + let viewport = winit_state + .viewporter_state + .as_ref() + .map(|state| state.get_viewport(layer_surface.wl_surface(), queue_handle)); + let fractional_scale = winit_state + .fractional_scaling_manager + .as_ref() + .map(|fsm| fsm.fractional_scaling(layer_surface.wl_surface(), queue_handle)); + + Self { + compositor, + connection, + theme, + cursor_grab_mode: GrabState::new(), + cursor_icon: CursorIcon::Default, + cursor_visible: true, + fractional_scale, + has_focus: false, + ime_allowed: false, + ime_purpose: ImePurpose::Normal, + pointer_constraints, + pointers: Default::default(), + queue_handle: queue_handle.clone(), + scale_factor: 1., + shm: winit_state.shm.wl_shm().clone(), + shell_specific: ShellSpecificState::WlrLayer { + surface: ManuallyDrop::new(layer_surface), + last_configure: None, + }, size, - stateless_size: size, text_inputs: Vec::new(), title: String::default(), transparent: false, - resizable: true, viewport, - window: ManuallyDrop::new(window), } } /// Get the outer size of the window. #[inline] pub fn outer_size(&self) -> LogicalSize { - self.frame - .as_ref() - .map(|frame| frame.add_borders(self.size.width, self.size.height).into()) - .unwrap_or(self.size) + match &self.shell_specific { + ShellSpecificState::Xdg { frame, .. } => frame + .as_ref() + .map(|frame| frame.add_borders(self.size.width, self.size.height).into()) + .unwrap_or(self.size), + ShellSpecificState::WlrLayer { .. } => self.size, + } } /// Register pointer on the top-level. @@ -452,14 +615,18 @@ impl WindowState { /// Refresh the decorations frame if it's present returning whether the client should redraw. pub fn refresh_frame(&mut self) -> bool { - if let Some(frame) = self.frame.as_mut() { - let dirty = frame.is_dirty(); - if dirty { - frame.draw(); + match self.shell_specific { + ShellSpecificState::Xdg { + frame: Some(ref mut frame), + .. + } => { + let dirty = frame.is_dirty(); + if dirty { + frame.draw(); + } + dirty } - dirty - } else { - false + _ => false, } } @@ -474,7 +641,7 @@ impl WindowState { /// Reissue the transparency hint to the compositor. pub fn reload_transparency_hint(&self) { - let surface = self.window.wl_surface(); + let surface = self.wl_surface(); if self.transparent { surface.set_opaque_region(None); @@ -491,38 +658,58 @@ impl WindowState { self.size = inner_size; // Update the stateless size. - if Some(true) == self.last_configure.as_ref().map(Self::is_stateless) { - self.stateless_size = inner_size; + match &mut self.shell_specific { + ShellSpecificState::Xdg { + last_configure, + stateless_size, + .. + } => { + if Some(true) == last_configure.as_ref().map(Self::is_stateless) { + *stateless_size = inner_size; + } + } + ShellSpecificState::WlrLayer { .. } => {} } // Update the inner frame. - let ((x, y), outer_size) = if let Some(frame) = self.frame.as_mut() { - // Resize only visible frame. - if !frame.is_hidden() { - frame.resize( - NonZeroU32::new(self.size.width).unwrap(), - NonZeroU32::new(self.size.height).unwrap(), - ); - } + let ((x, y), outer_size) = match self.shell_specific { + ShellSpecificState::Xdg { + frame: Some(ref mut frame), + .. + } => { + // Resize only visible frame. + if !frame.is_hidden() { + frame.resize( + NonZeroU32::new(self.size.width).unwrap(), + NonZeroU32::new(self.size.height).unwrap(), + ); + } - ( - frame.location(), - frame.add_borders(self.size.width, self.size.height).into(), - ) - } else { - ((0, 0), self.size) + ( + frame.location(), + frame.add_borders(self.size.width, self.size.height).into(), + ) + } + _ => ((0, 0), self.size), }; // Reload the hint. self.reload_transparency_hint(); // Set the window geometry. - self.window.xdg_surface().set_window_geometry( - x, - y, - outer_size.width as i32, - outer_size.height as i32, - ); + match &self.shell_specific { + ShellSpecificState::Xdg { window, .. } => { + window.xdg_surface().set_window_geometry( + x, + y, + outer_size.width as i32, + outer_size.height as i32, + ); + } + ShellSpecificState::WlrLayer { surface, .. } => { + surface.set_size(outer_size.width, outer_size.height) + } + } // Update the target viewport, this is used if and only if fractional scaling is in use. if let Some(viewport) = self.viewport.as_ref() { @@ -566,50 +753,105 @@ impl WindowState { }) } + pub fn is_maximized(&self) -> bool { + match &self.shell_specific { + ShellSpecificState::Xdg { last_configure, .. } => last_configure + .as_ref() + .map(|last_configure| last_configure.is_maximized()) + .unwrap_or_default(), + ShellSpecificState::WlrLayer { .. } => false, + } + } + + pub fn is_fullscreen(&self) -> bool { + match &self.shell_specific { + ShellSpecificState::Xdg { last_configure, .. } => last_configure + .as_ref() + .map(|last_configure| last_configure.is_fullscreen()) + .unwrap_or_default(), + ShellSpecificState::WlrLayer { .. } => false, + } + } + /// Set maximum inner window size. pub fn set_min_inner_size(&mut self, size: Option>) { - // Ensure that the window has the right minimum size. - let mut size = size.unwrap_or(MIN_WINDOW_SIZE); - size.width = size.width.max(MIN_WINDOW_SIZE.width); - size.height = size.height.max(MIN_WINDOW_SIZE.height); - - // Add the borders. - let size = self - .frame - .as_ref() - .map(|frame| frame.add_borders(size.width, size.height).into()) - .unwrap_or(size); - - self.min_inner_size = size; - self.window.set_min_size(Some(size.into())); + match &mut self.shell_specific { + ShellSpecificState::Xdg { + window, + frame, + min_inner_size, + .. + } => { + // Ensure that the window has the right minimum size. + let mut size = size.unwrap_or(MIN_WINDOW_SIZE); + size.width = size.width.max(MIN_WINDOW_SIZE.width); + size.height = size.height.max(MIN_WINDOW_SIZE.height); + + // Add the borders. + let size = frame + .as_ref() + .map(|frame| frame.add_borders(size.width, size.height).into()) + .unwrap_or(size); + + *min_inner_size = size; + window.set_min_size(Some(size.into())); + } + ShellSpecificState::WlrLayer { .. } => { + warn!("Minimum size is ignored for layer_shell windows") + } + } } /// Set maximum inner window size. pub fn set_max_inner_size(&mut self, size: Option>) { - let size = size.map(|size| { - self.frame - .as_ref() - .map(|frame| frame.add_borders(size.width, size.height).into()) - .unwrap_or(size) - }); - - self.max_inner_size = size; - self.window.set_max_size(size.map(Into::into)); + match &mut self.shell_specific { + ShellSpecificState::Xdg { + window, + frame, + max_inner_size, + .. + } => { + let size = size.map(|size| { + frame + .as_ref() + .map(|frame| frame.add_borders(size.width, size.height).into()) + .unwrap_or(size) + }); + + *max_inner_size = size; + window.set_max_size(size.map(Into::into)); + } + ShellSpecificState::WlrLayer { .. } => { + warn!("Maximum size is ignored for layer_shell windows") + } + } } /// Set the CSD theme. pub fn set_theme(&mut self, theme: Option) { - self.theme = theme; - #[cfg(feature = "sctk-adwaita")] - if let Some(frame) = self.frame.as_mut() { - frame.set_config(into_sctk_adwaita_config(theme)) + match &mut self.shell_specific { + ShellSpecificState::Xdg { frame, .. } => { + self.theme = theme; + #[cfg(feature = "sctk-adwaita")] + if let Some(frame) = frame.as_mut() { + frame.set_config(into_sctk_adwaita_config(theme)) + } + } + ShellSpecificState::WlrLayer { .. } => { + if theme.is_some() { + warn!("Theme is ignored for layer_shell windows") + } + } } } /// The current theme for CSD decorations. #[inline] pub fn theme(&self) -> Option { - self.theme + match &self.shell_specific { + ShellSpecificState::Xdg { .. } => self.theme, + ShellSpecificState::WlrLayer { .. } => None, + } } /// Set the cursor grabbing state on the top-level. @@ -621,8 +863,17 @@ impl WindowState { /// Reload the hints for minimum and maximum sizes. pub fn reload_min_max_hints(&mut self) { - self.set_min_inner_size(Some(self.min_inner_size)); - self.set_max_inner_size(self.max_inner_size); + match self.shell_specific { + ShellSpecificState::Xdg { + min_inner_size, + max_inner_size, + .. + } => { + self.set_min_inner_size(Some(min_inner_size)); + self.set_max_inner_size(max_inner_size); + } + ShellSpecificState::WlrLayer { .. } => {} + } } /// Set the grabbing state on the surface. @@ -646,7 +897,7 @@ impl WindowState { } } - let surface = self.window.wl_surface(); + let surface = self.wl_surface(); match mode { CursorGrabMode::Locked => self.apply_on_poiner(|pointer, data| { let pointer = pointer.pointer(); @@ -706,10 +957,21 @@ impl WindowState { /// Whether show or hide client side decorations. #[inline] pub fn set_decorate(&mut self, decorate: bool) { - if let Some(frame) = self.frame.as_mut() { - frame.set_hidden(!decorate); - // Force the resize. - self.resize(self.size); + match self.shell_specific { + ShellSpecificState::Xdg { + frame: Some(ref mut frame), + .. + } => { + frame.set_hidden(!decorate); + // Force the resize. + self.resize(self.size); + } + ShellSpecificState::Xdg { frame: None, .. } => {} + ShellSpecificState::WlrLayer { .. } => { + if decorate { + warn!("Client-side decorations are ignored for layer_shell windows"); + } + } } } @@ -774,7 +1036,7 @@ impl WindowState { // XXX when fractional scaling is not used update the buffer scale. if self.fractional_scale.is_none() { - let _ = self.window.set_buffer_scale(self.scale_factor as _); + let _ = self.wl_surface().set_buffer_scale(self.scale_factor as _); } } @@ -792,12 +1054,16 @@ impl WindowState { title.truncate(new_len); } - // Update the CSD title. - if let Some(frame) = self.frame.as_mut() { - frame.set_title(&title); + match &mut self.shell_specific { + ShellSpecificState::Xdg { window, frame, .. } => { + // Update the CSD title. + if let Some(frame) = frame.as_mut() { + frame.set_title(&title); + } + window.set_title(&title); + } + ShellSpecificState::WlrLayer { .. } => {} } - - self.window.set_title(&title); self.title = title; } @@ -833,12 +1099,27 @@ impl WindowState { impl Drop for WindowState { fn drop(&mut self) { - let surface = self.window.wl_surface().clone(); - unsafe { - ManuallyDrop::drop(&mut self.window); - } + match &mut self.shell_specific { + ShellSpecificState::Xdg { window, .. } => { + let surface = window.wl_surface().clone(); + unsafe { + ManuallyDrop::drop(window); + } - surface.destroy(); + surface.destroy(); + } + ShellSpecificState::WlrLayer { + surface: layer_surface, + .. + } => { + let surface = layer_surface.wl_surface().clone(); + unsafe { + ManuallyDrop::drop(layer_surface); + } + + surface.destroy() + } + } } } From c8e0bf49199e271d449b87f8ca0d8a2170f29e5e Mon Sep 17 00:00:00 2001 From: Max Guppy Date: Thu, 1 Jun 2023 19:26:21 +1000 Subject: [PATCH 2/2] Add layer shell methods to `WindowExtWayland` --- src/platform/wayland.rs | 50 ++++++++++++++ src/platform_impl/linux/wayland/state.rs | 2 +- src/platform_impl/linux/wayland/window/mod.rs | 67 ++++++++++++++++++- .../linux/wayland/window/state.rs | 4 +- 4 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs index 7561a725ae..24f40fdc5c 100644 --- a/src/platform/wayland.rs +++ b/src/platform/wayland.rs @@ -93,6 +93,16 @@ pub trait WindowExtWayland { /// /// The pointer will become invalid when the [`Window`] is destroyed. fn wayland_display(&self) -> Option<*mut raw::c_void>; + + fn set_anchor(&self, anchor: Anchor); + + fn set_exclusive_zone(&self, exclusive_zone: i32); + + fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32); + + fn set_keyboard_interactivity(&self, keyboard_interactivity: KeyboardInteractivity); + + fn set_layer(&self, anchor: Layer); } impl WindowExtWayland for Window { @@ -113,6 +123,46 @@ impl WindowExtWayland for Window { _ => None, } } + + fn set_anchor(&self, anchor: Anchor) { + match self.window { + LinuxWindow::Wayland(ref w) => w.set_anchor(anchor), + #[cfg(x11_platform)] + _ => {} + } + } + + fn set_exclusive_zone(&self, exclusive_zone: i32) { + match self.window { + LinuxWindow::Wayland(ref w) => w.set_exclusive_zone(exclusive_zone), + #[cfg(x11_platform)] + _ => {} + } + } + + fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32) { + match self.window { + LinuxWindow::Wayland(ref w) => w.set_margin(top, right, bottom, left), + #[cfg(x11_platform)] + _ => {} + } + } + + fn set_keyboard_interactivity(&self, keyboard_interactivity: KeyboardInteractivity) { + match self.window { + LinuxWindow::Wayland(ref w) => w.set_keyboard_interactivity(keyboard_interactivity), + #[cfg(x11_platform)] + _ => {} + } + } + + fn set_layer(&self, layer: Layer) { + match self.window { + LinuxWindow::Wayland(ref w) => w.set_layer(layer), + #[cfg(x11_platform)] + _ => {} + } + } } /// Additional methods on [`WindowBuilder`] that are specific to Wayland. diff --git a/src/platform_impl/linux/wayland/state.rs b/src/platform_impl/linux/wayland/state.rs index 6e9d655204..f74dd66103 100644 --- a/src/platform_impl/linux/wayland/state.rs +++ b/src/platform_impl/linux/wayland/state.rs @@ -325,7 +325,7 @@ impl LayerShellHandler for WinitState { .unwrap() .configure_layer(configure); - // self.window_compositor_updates[pos].size = Some(new_size); + self.window_compositor_updates[pos].size = Some(new_size); } } diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index a5dd09ec30..af10d58664 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -15,7 +15,7 @@ use sctk::reexports::client::QueueHandle; use sctk::compositor::{CompositorState, Region, SurfaceData}; use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1; -use sctk::shell::wlr_layer::{Anchor, LayerSurface}; +use sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer, LayerSurface}; use sctk::shell::xdg::window::Window as SctkWindow; use sctk::shell::xdg::window::WindowDecorations; use sctk::shell::WaylandSurface; @@ -106,7 +106,7 @@ impl Window { .map(|size| size.to_logical::(1.)) .unwrap_or((800, 600).into()); - let (window, mut window_state) = if let Some(layer) = platform_attributes.layer_shell { + let (window, window_state) = if let Some(layer) = platform_attributes.layer_shell { let layer_surface = state.layer_shell.create_layer_surface( &queue_handle, surface.clone(), @@ -640,6 +640,32 @@ impl Window { pub fn title(&self) -> String { self.window_state.lock().unwrap().title().to_owned() } + + #[inline] + pub fn set_anchor(&self, anchor: Anchor) { + self.window.set_anchor(anchor); + } + + #[inline] + pub fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32) { + self.window.set_margin(top, right, bottom, left); + } + + #[inline] + pub fn set_exclusive_zone(&self, exclusive_zone: i32) { + self.window.set_exclusive_zone(exclusive_zone); + } + + #[inline] + pub fn set_keyboard_interactivity(&self, keyboard_interactivity: KeyboardInteractivity) { + self.window + .set_keyboard_interactivity(keyboard_interactivity); + } + + #[inline] + pub fn set_layer(&self, layer: Layer) { + self.window.set_layer(layer); + } } impl Drop for Window { @@ -699,6 +725,43 @@ impl WindowShell { WindowShell::WlrLayer { surface } => surface.wl_surface(), } } + + pub fn set_anchor(&self, anchor: Anchor) { + match self { + WindowShell::WlrLayer { surface } => surface.set_anchor(anchor), + WindowShell::Xdg { .. } => warn!("Anchor is ignored for XDG windows"), + } + } + + pub fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32) { + match self { + WindowShell::WlrLayer { surface } => surface.set_margin(top, right, bottom, left), + WindowShell::Xdg { .. } => warn!("Margin is ignored for XDG windows"), + } + } + + pub fn set_exclusive_zone(&self, exclusive_zone: i32) { + match self { + WindowShell::WlrLayer { surface } => surface.set_exclusive_zone(exclusive_zone), + WindowShell::Xdg { .. } => warn!("Exclusive zone is ignored for XDG windows"), + } + } + + pub fn set_keyboard_interactivity(&self, keyboard_interactivity: KeyboardInteractivity) { + match self { + WindowShell::WlrLayer { surface } => { + surface.set_keyboard_interactivity(keyboard_interactivity) + } + WindowShell::Xdg { .. } => warn!("Keyboard interactivity is ignored for XDG windows"), + } + } + + pub fn set_layer(&self, layer: Layer) { + match self { + WindowShell::WlrLayer { surface } => surface.set_layer(layer), + WindowShell::Xdg { .. } => warn!("Layer is ignored for XDG windows"), + } + } } /// The request from the window to the event loop. diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index a33f000c87..0aabd9c1a0 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -261,7 +261,7 @@ impl WindowState { } } - pub fn configure_layer(&mut self, configure: LayerSurfaceConfigure) { + pub fn configure_layer(&mut self, configure: LayerSurfaceConfigure) -> LogicalSize { match &mut self.shell_specific { ShellSpecificState::WlrLayer { last_configure, .. } => { let new_size = match configure.new_size { @@ -276,6 +276,8 @@ impl WindowState { // XXX Update the new size right away. self.resize(new_size); + + new_size } ShellSpecificState::Xdg { .. } => unreachable!(), // TODO(theonlymrcat): Replace this match with let...else }