@@ -7354,8 +7354,9 @@ dependencies = [
"wayland-backend",
"wayland-client",
"wayland-cursor",
- "wayland-protocols",
+ "wayland-protocols 0.31.2",
"wayland-protocols-plasma",
+ "wayland-protocols-wlr",
"windows 0.61.1",
"windows-core 0.61.0",
"windows-numerics",
@@ -18369,9 +18370,9 @@ dependencies = [
[[package]]
name = "wayland-backend"
-version = "0.3.8"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf"
+checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
dependencies = [
"cc",
"downcast-rs",
@@ -18383,9 +18384,9 @@ dependencies = [
[[package]]
name = "wayland-client"
-version = "0.31.8"
+version = "0.31.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f"
+checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
dependencies = [
"bitflags 2.9.0",
"rustix 0.38.44",
@@ -18416,6 +18417,18 @@ dependencies = [
"wayland-scanner",
]
+[[package]]
+name = "wayland-protocols"
+version = "0.32.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
+dependencies = [
+ "bitflags 2.9.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
[[package]]
name = "wayland-protocols-plasma"
version = "0.2.0"
@@ -18425,7 +18438,20 @@ dependencies = [
"bitflags 2.9.0",
"wayland-backend",
"wayland-client",
- "wayland-protocols",
+ "wayland-protocols 0.31.2",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
+dependencies = [
+ "bitflags 2.9.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.32.8",
"wayland-scanner",
]
@@ -61,6 +61,7 @@ use wayland_protocols::xdg::decoration::zv1::client::{
};
use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
+use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
use xkbcommon::xkb::{self, KEYMAP_COMPILE_NO_FLAGS, Keycode};
@@ -114,6 +115,7 @@ pub struct Globals {
pub fractional_scale_manager:
Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
+ pub layer_shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
pub blur_manager: Option<org_kde_kwin_blur_manager::OrgKdeKwinBlurManager>,
pub text_input_manager: Option<zwp_text_input_manager_v3::ZwpTextInputManagerV3>,
pub executor: ForegroundExecutor,
@@ -151,6 +153,7 @@ impl Globals {
viewporter: globals.bind(&qh, 1..=1, ()).ok(),
fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
+ layer_shell: globals.bind(&qh, 1..=1, ()).ok(),
blur_manager: globals.bind(&qh, 1..=1, ()).ok(),
text_input_manager: globals.bind(&qh, 1..=1, ()).ok(),
executor,
@@ -929,6 +932,7 @@ delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
delegate_noop!(WaylandClientStatePtr: ignore wl_region::WlRegion);
delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
+delegate_noop!(WaylandClientStatePtr: ignore zwlr_layer_shell_v1::ZwlrLayerShellV1);
delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur_manager::OrgKdeKwinBlurManager);
delegate_noop!(WaylandClientStatePtr: ignore zwp_text_input_manager_v3::ZwpTextInputManagerV3);
delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur::OrgKdeKwinBlur);
@@ -1074,6 +1078,31 @@ impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
}
}
+impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ObjectId> for WaylandClientStatePtr {
+ fn event(
+ this: &mut Self,
+ _: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
+ event: <zwlr_layer_surface_v1::ZwlrLayerSurfaceV1 as Proxy>::Event,
+ surface_id: &ObjectId,
+ _: &Connection,
+ _: &QueueHandle<Self>,
+ ) {
+ let client = this.get_client();
+ let mut state = client.borrow_mut();
+ let Some(window) = get_window(&mut state, surface_id) else {
+ return;
+ };
+ drop(state);
+
+ let should_close = window.handle_layersurface_event(event);
+
+ if should_close {
+ // The close logic will be handled in drop_window()
+ window.close();
+ }
+ }
+}
+
impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
fn event(
_: &mut Self,
@@ -1,3 +1,6 @@
+use blade_graphics as gpu;
+use collections::HashMap;
+use futures::channel::oneshot::Receiver;
use std::{
cell::{Ref, RefCell, RefMut},
ffi::c_void,
@@ -6,9 +9,14 @@ use std::{
sync::Arc,
};
-use blade_graphics as gpu;
-use collections::HashMap;
-use futures::channel::oneshot::Receiver;
+use crate::{
+ Capslock,
+ platform::{
+ PlatformAtlas, PlatformInputHandler, PlatformWindow,
+ blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
+ linux::wayland::{display::WaylandDisplay, serial::SerialKind},
+ },
+};
use raw_window_handle as rwh;
use wayland_backend::client::ObjectId;
@@ -20,6 +28,8 @@ 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_plasma::blur::client::org_kde_kwin_blur;
+use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer;
+use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1;
use crate::scene::Scene;
use crate::{
@@ -27,15 +37,7 @@ use crate::{
PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
ResizeEdge, ScaledPixels, Size, Tiling, WaylandClientStatePtr, WindowAppearance,
WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
- WindowParams, px, size,
-};
-use crate::{
- Capslock,
- platform::{
- PlatformAtlas, PlatformInputHandler, PlatformWindow,
- blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
- linux::wayland::{display::WaylandDisplay, serial::SerialKind},
- },
+ WindowKind, WindowParams, px, size,
};
#[derive(Default)]
@@ -81,14 +83,12 @@ struct InProgressConfigure {
}
pub struct WaylandWindowState {
- xdg_surface: xdg_surface::XdgSurface,
+ surface_state: WaylandSurfaceState,
acknowledged_first_configure: bool,
pub surface: wl_surface::WlSurface,
- decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
app_id: Option<String>,
appearance: WindowAppearance,
blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
- toplevel: xdg_toplevel::XdgToplevel,
viewport: Option<wp_viewport::WpViewport>,
outputs: HashMap<ObjectId, Output>,
display: Option<(ObjectId, Output)>,
@@ -114,6 +114,78 @@ pub struct WaylandWindowState {
client_inset: Option<Pixels>,
}
+pub enum WaylandSurfaceState {
+ Xdg(WaylandXdgSurfaceState),
+ LayerShell(WaylandLayerSurfaceState),
+}
+
+pub struct WaylandXdgSurfaceState {
+ xdg_surface: xdg_surface::XdgSurface,
+ toplevel: xdg_toplevel::XdgToplevel,
+ decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
+}
+
+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,
+ }) => {
+ toplevel.destroy();
+ xdg_surface.destroy();
+ }
+ WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_surface }) => {
+ layer_surface.destroy();
+ }
+ }
+ }
+}
+
#[derive(Clone)]
pub struct WaylandWindowStatePtr {
state: Rc<RefCell<WaylandWindowState>>,
@@ -124,9 +196,7 @@ impl WaylandWindowState {
pub(crate) fn new(
handle: AnyWindowHandle,
surface: wl_surface::WlSurface,
- xdg_surface: xdg_surface::XdgSurface,
- toplevel: xdg_toplevel::XdgToplevel,
- decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
+ surface_state: WaylandSurfaceState,
appearance: WindowAppearance,
viewport: Option<wp_viewport::WpViewport>,
client: WaylandClientStatePtr,
@@ -156,13 +226,11 @@ impl WaylandWindowState {
};
Ok(Self {
- xdg_surface,
+ surface_state,
acknowledged_first_configure: false,
surface,
- decoration,
app_id: None,
blur: None,
- toplevel,
viewport,
globals,
outputs: HashMap::default(),
@@ -235,17 +303,16 @@ impl Drop for WaylandWindow {
let client = state.client.clone();
state.renderer.destroy();
- if let Some(decoration) = &state.decoration {
+ if let Some(decoration) = &state.surface_state.decoration() {
decoration.destroy();
}
if let Some(blur) = &state.blur {
blur.release();
}
- state.toplevel.destroy();
+ state.surface_state.destroy();
if let Some(viewport) = &state.viewport {
viewport.destroy();
}
- state.xdg_surface.destroy();
state.surface.destroy();
let state_ptr = self.0.clone();
@@ -279,27 +346,65 @@ impl WaylandWindow {
appearance: WindowAppearance,
) -> anyhow::Result<(Self, ObjectId)> {
let surface = globals.compositor.create_surface(&globals.qh, ());
- let xdg_surface = globals
- .wm_base
- .get_xdg_surface(&surface, &globals.qh, surface.id());
- let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());
- if let Some(size) = params.window_min_size {
- toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
- }
+ let surface_state = match (params.kind, globals.layer_shell.as_ref()) {
+ // Matching on layer_shell here means that if kind is Overlay, but the compositor doesn't support layer_shell,
+ // we end up defaulting to xdg_surface anyway
+ (WindowKind::Overlay, Some(layer_shell)) => {
+ let layer_surface = layer_shell.get_layer_surface(
+ &surface,
+ None,
+ Layer::Overlay,
+ "".to_string(),
+ &globals.qh,
+ surface.id(),
+ );
+
+ let width = params.bounds.size.width.0;
+ let height = params.bounds.size.height.0;
+ layer_surface.set_size(width as u32, height as u32);
+ layer_surface.set_keyboard_interactivity(
+ zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand,
+ );
+
+ WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState { layer_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());
+
+ if let Some(size) = params.window_min_size {
+ toplevel.set_min_size(size.width.0 as i32, size.height.0 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(),
+ )
+ });
+
+ WaylandSurfaceState::Xdg(WaylandXdgSurfaceState {
+ xdg_surface,
+ toplevel,
+ decoration,
+ })
+ }
+ };
if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {
fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());
}
- // 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())
- });
-
let viewport = globals
.viewporter
.as_ref()
@@ -309,9 +414,7 @@ impl WaylandWindow {
state: Rc::new(RefCell::new(WaylandWindowState::new(
handle,
surface.clone(),
- xdg_surface,
- toplevel,
- decoration,
+ surface_state,
appearance,
viewport,
client,
@@ -403,7 +506,7 @@ impl WaylandWindowStatePtr {
}
}
let mut state = self.state.borrow_mut();
- state.xdg_surface.ack_configure(serial);
+ state.surface_state.ack_configure(serial);
let window_geometry = inset_by_tiling(
state.bounds.map_origin(|_| px(0.0)),
@@ -413,7 +516,7 @@ impl WaylandWindowStatePtr {
.map(|v| v.0 as i32)
.map_size(|v| if v <= 0 { 1 } else { v });
- state.xdg_surface.set_window_geometry(
+ state.surface_state.set_geometry(
window_geometry.origin.x,
window_geometry.origin.y,
window_geometry.size.width,
@@ -578,6 +681,42 @@ impl WaylandWindowStatePtr {
}
}
+ pub fn handle_layersurface_event(&self, event: zwlr_layer_surface_v1::Event) -> bool {
+ match event {
+ zwlr_layer_surface_v1::Event::Configure {
+ width,
+ height,
+ serial,
+ } => {
+ let mut size = if width == 0 || height == 0 {
+ None
+ } else {
+ Some(size(px(width as f32), px(height as f32)))
+ };
+
+ let mut state = self.state.borrow_mut();
+ state.in_progress_configure = Some(InProgressConfigure {
+ size,
+ fullscreen: false,
+ maximized: false,
+ resizing: false,
+ tiling: Tiling::default(),
+ });
+ drop(state);
+
+ // just do the same thing we'd do as an xdg_surface
+ self.handle_xdg_surface_event(xdg_surface::Event::Configure { serial });
+
+ false
+ }
+ zwlr_layer_surface_v1::Event::Closed => {
+ // unlike xdg, we don't have a choice here: the surface is closing.
+ true
+ }
+ _ => false,
+ }
+ }
+
#[allow(clippy::mutable_key_type)]
pub fn handle_surface_event(
&self,
@@ -840,7 +979,7 @@ impl PlatformWindow for WaylandWindow {
let state_ptr = self.0.clone();
let dp_size = size.to_device_pixels(self.scale_factor());
- state.xdg_surface.set_window_geometry(
+ state.surface_state.set_geometry(
state.bounds.origin.x.0 as i32,
state.bounds.origin.y.0 as i32,
dp_size.width.0,
@@ -934,12 +1073,16 @@ impl PlatformWindow for WaylandWindow {
}
fn set_title(&mut self, title: &str) {
- self.borrow().toplevel.set_title(title.to_string());
+ if let Some(toplevel) = self.borrow().surface_state.toplevel() {
+ toplevel.set_title(title.to_string());
+ }
}
fn set_app_id(&mut self, app_id: &str) {
let mut state = self.borrow_mut();
- state.toplevel.set_app_id(app_id.to_owned());
+ if let Some(toplevel) = self.borrow().surface_state.toplevel() {
+ toplevel.set_app_id(app_id.to_owned());
+ }
state.app_id = Some(app_id.to_owned());
}
@@ -950,24 +1093,30 @@ impl PlatformWindow for WaylandWindow {
}
fn minimize(&self) {
- self.borrow().toplevel.set_minimized();
+ if let Some(toplevel) = self.borrow().surface_state.toplevel() {
+ toplevel.set_minimized();
+ }
}
fn zoom(&self) {
let state = self.borrow();
- if !state.maximized {
- state.toplevel.set_maximized();
- } else {
- state.toplevel.unset_maximized();
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ if !state.maximized {
+ toplevel.set_maximized();
+ } else {
+ toplevel.unset_maximized();
+ }
}
}
fn toggle_fullscreen(&self) {
- let mut state = self.borrow_mut();
- if !state.fullscreen {
- state.toplevel.set_fullscreen(None);
- } else {
- state.toplevel.unset_fullscreen();
+ let mut state = self.borrow();
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ if !state.fullscreen {
+ toplevel.set_fullscreen(None);
+ } else {
+ toplevel.unset_fullscreen();
+ }
}
}
@@ -1032,27 +1181,33 @@ impl PlatformWindow for WaylandWindow {
fn show_window_menu(&self, position: Point<Pixels>) {
let state = self.borrow();
let serial = state.client.get_serial(SerialKind::MousePress);
- state.toplevel.show_window_menu(
- &state.globals.seat,
- serial,
- position.x.0 as i32,
- position.y.0 as i32,
- );
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ toplevel.show_window_menu(
+ &state.globals.seat,
+ serial,
+ position.x.0 as i32,
+ position.y.0 as i32,
+ );
+ }
}
fn start_window_move(&self) {
let state = self.borrow();
let serial = state.client.get_serial(SerialKind::MousePress);
- state.toplevel._move(&state.globals.seat, serial);
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ toplevel._move(&state.globals.seat, serial);
+ }
}
fn start_window_resize(&self, edge: crate::ResizeEdge) {
let state = self.borrow();
- state.toplevel.resize(
- &state.globals.seat,
- state.client.get_serial(SerialKind::MousePress),
- edge.to_xdg(),
- )
+ if let Some(toplevel) = state.surface_state.toplevel() {
+ toplevel.resize(
+ &state.globals.seat,
+ state.client.get_serial(SerialKind::MousePress),
+ edge.to_xdg(),
+ )
+ }
}
fn window_decorations(&self) -> Decorations {
@@ -1068,7 +1223,7 @@ impl PlatformWindow for WaylandWindow {
fn request_decorations(&self, decorations: WindowDecorations) {
let mut state = self.borrow_mut();
state.decorations = decorations;
- if let Some(decoration) = state.decoration.as_ref() {
+ if let Some(decoration) = state.surface_state.decoration() {
decoration.set_mode(decorations.to_xdg());
update_window(state);
}