diff --git a/anvil/src/shell/element.rs b/anvil/src/shell/element.rs index ba2b9c573fce..f315b2125109 100644 --- a/anvil/src/shell/element.rs +++ b/anvil/src/shell/element.rs @@ -22,7 +22,7 @@ use smithay::{ }, output::Output, reexports::{ - wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, + wayland_protocols::{wp::presentation_time::server::wp_presentation_feedback, xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode}, wayland_server::protocol::wl_surface::WlSurface, }, render_elements, @@ -38,7 +38,7 @@ use smithay::{ xwayland::X11Surface, }; -use super::ssd::HEADER_BAR_HEIGHT; +use super::ssd::{BUTTON_WIDTH, HEADER_BAR_HEIGHT}; use crate::AnvilState; #[derive(Debug, Clone, PartialEq)] @@ -194,11 +194,11 @@ impl IsAlive for WindowElement { impl PointerTarget> for WindowElement { fn enter(&self, seat: &Seat>, data: &mut AnvilState, event: &MotionEvent) { let mut state = self.decoration_state(); - if state.is_ssd { + if state.ssd_mode == Mode::ServerSide { if event.location.y < HEADER_BAR_HEIGHT as f64 { - state.header_bar.pointer_enter(event.location); + state.decorations.pointer_enter(event.location); } else { - state.header_bar.pointer_leave(); + state.decorations.pointer_leave(); let mut event = event.clone(); event.location.y -= HEADER_BAR_HEIGHT as f64; match self { @@ -208,6 +208,18 @@ impl PointerTarget> for Wind }; state.ptr_entered_window = true; } + } else if state.ssd_mode == Mode::ServerSideOverlay { + if event.location.y < HEADER_BAR_HEIGHT as f64 && event.location.x > state.decorations.width as f64 - BUTTON_WIDTH as f64 * 2. { + state.decorations.pointer_enter(event.location); + } else { + state.decorations.pointer_leave(); + match self { + WindowElement::Wayland(w) => PointerTarget::enter(w, seat, data, &event), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => PointerTarget::enter(w, seat, data, &event), + }; + state.ptr_entered_window = true; + } } else { state.ptr_entered_window = true; match self { @@ -219,7 +231,7 @@ impl PointerTarget> for Wind } fn motion(&self, seat: &Seat>, data: &mut AnvilState, event: &MotionEvent) { let mut state = self.decoration_state(); - if state.is_ssd { + if state.ssd_mode == Mode::ServerSide { if event.location.y < HEADER_BAR_HEIGHT as f64 { match self { WindowElement::Wayland(w) => { @@ -229,12 +241,40 @@ impl PointerTarget> for Wind WindowElement::X11(w) => PointerTarget::leave(w, seat, data, event.serial, event.time), }; state.ptr_entered_window = false; - state.header_bar.pointer_enter(event.location); + state.decorations.pointer_enter(event.location); } else { - state.header_bar.pointer_leave(); + state.decorations.pointer_leave(); let mut event = event.clone(); event.location.y -= HEADER_BAR_HEIGHT as f64; - if state.ptr_entered_window { + if state.ptr_entered_window { // TODO: clean + match self { + WindowElement::Wayland(w) => PointerTarget::motion(w, seat, data, &event), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => PointerTarget::motion(w, seat, data, &event), + }; + } else { + state.ptr_entered_window = true; + match self { + WindowElement::Wayland(w) => PointerTarget::enter(w, seat, data, &event), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => PointerTarget::enter(w, seat, data, &event), + }; + } + } + } else if state.ssd_mode == Mode::ServerSideOverlay { + if event.location.y < HEADER_BAR_HEIGHT as f64 && event.location.x > state.decorations.width as f64 - BUTTON_WIDTH as f64 * 2. { + match self { + WindowElement::Wayland(w) => { + PointerTarget::leave(w, seat, data, event.serial, event.time) + } + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => PointerTarget::leave(w, seat, data, event.serial, event.time), + }; + state.ptr_entered_window = false; + state.decorations.pointer_enter(event.location); + } else { + state.decorations.pointer_leave(); + if state.ptr_entered_window { // TODO: clean match self { WindowElement::Wayland(w) => PointerTarget::motion(w, seat, data, &event), #[cfg(feature = "xwayland")] @@ -264,7 +304,7 @@ impl PointerTarget> for Wind event: &RelativeMotionEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::relative_motion(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -274,7 +314,17 @@ impl PointerTarget> for Wind } fn button(&self, seat: &Seat>, data: &mut AnvilState, event: &ButtonEvent) { let mut state = self.decoration_state(); - if state.is_ssd { + if state.ssd_mode == Mode::ServerSide { + if state.ptr_entered_window { + match self { + WindowElement::Wayland(w) => PointerTarget::button(w, seat, data, event), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => PointerTarget::button(w, seat, data, event), + }; + } else { + state.decorations.clicked(seat, data, self, event.serial); + } + } else if state.ssd_mode == Mode::ServerSideOverlay { if state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::button(w, seat, data, event), @@ -282,7 +332,7 @@ impl PointerTarget> for Wind WindowElement::X11(w) => PointerTarget::button(w, seat, data, event), }; } else { - state.header_bar.clicked(seat, data, self, event.serial); + state.decorations.clicked(seat, data, self, event.serial); } } else { match self { @@ -294,7 +344,7 @@ impl PointerTarget> for Wind } fn axis(&self, seat: &Seat>, data: &mut AnvilState, frame: AxisFrame) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::axis(w, seat, data, frame), #[cfg(feature = "xwayland")] @@ -304,7 +354,7 @@ impl PointerTarget> for Wind } fn frame(&self, seat: &Seat>, data: &mut AnvilState) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::frame(w, seat, data), #[cfg(feature = "xwayland")] @@ -320,8 +370,8 @@ impl PointerTarget> for Wind time: u32, ) { let mut state = self.decoration_state(); - if state.is_ssd { - state.header_bar.pointer_leave(); + if state.ssd_mode != Mode::ClientSide { + state.decorations.pointer_leave(); if state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::leave(w, seat, data, serial, time), @@ -346,7 +396,7 @@ impl PointerTarget> for Wind event: &GestureSwipeBeginEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_swipe_begin(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -361,7 +411,7 @@ impl PointerTarget> for Wind event: &GestureSwipeUpdateEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_swipe_update(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -376,7 +426,7 @@ impl PointerTarget> for Wind event: &GestureSwipeEndEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_swipe_end(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -391,7 +441,7 @@ impl PointerTarget> for Wind event: &GesturePinchBeginEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_pinch_begin(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -406,7 +456,7 @@ impl PointerTarget> for Wind event: &GesturePinchUpdateEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_pinch_update(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -421,7 +471,7 @@ impl PointerTarget> for Wind event: &GesturePinchEndEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_pinch_end(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -436,7 +486,7 @@ impl PointerTarget> for Wind event: &GestureHoldBeginEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_hold_begin(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -451,7 +501,7 @@ impl PointerTarget> for Wind event: &GestureHoldEndEvent, ) { let state = self.decoration_state(); - if !state.is_ssd || state.ptr_entered_window { + if state.ssd_mode != Mode::ServerSide || state.ptr_entered_window { match self { WindowElement::Wayland(w) => PointerTarget::gesture_hold_end(w, seat, data, event), #[cfg(feature = "xwayland")] @@ -519,7 +569,7 @@ impl SpaceElement for WindowElement { #[cfg(feature = "xwayland")] WindowElement::X11(w) => SpaceElement::geometry(w), }; - if self.decoration_state().is_ssd { + if self.decoration_state().ssd_mode == Mode::ServerSide { geo.size.h += HEADER_BAR_HEIGHT; } geo @@ -530,13 +580,13 @@ impl SpaceElement for WindowElement { #[cfg(feature = "xwayland")] WindowElement::X11(w) => SpaceElement::bbox(w), }; - if self.decoration_state().is_ssd { + if self.decoration_state().ssd_mode == Mode::ServerSide { bbox.size.h += HEADER_BAR_HEIGHT; } bbox } fn is_in_input_region(&self, point: &Point) -> bool { - if self.decoration_state().is_ssd { + if self.decoration_state().ssd_mode == Mode::ServerSide { point.y < HEADER_BAR_HEIGHT as f64 || match self { WindowElement::Wayland(w) => SpaceElement::is_in_input_region( @@ -549,6 +599,19 @@ impl SpaceElement for WindowElement { &(*point - Point::from((0.0, HEADER_BAR_HEIGHT as f64))), ), } + } else if self.decoration_state().ssd_mode == Mode::ServerSideOverlay { + (point.y < HEADER_BAR_HEIGHT as f64 && point.x > self.decoration_state().decorations.width as f64 - BUTTON_WIDTH as f64 * 2.) + || match self { + WindowElement::Wayland(w) => SpaceElement::is_in_input_region( + w, + &(*point - Point::from((0.0, HEADER_BAR_HEIGHT as f64))), + ), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => SpaceElement::is_in_input_region( + w, + &(*point - Point::from((0.0, HEADER_BAR_HEIGHT as f64))), + ), + } } else { match self { WindowElement::Wayland(w) => SpaceElement::is_in_input_region(w, point), @@ -632,7 +695,7 @@ where WindowElement::X11(w) => SpaceElement::bbox(w), }; - if self.decoration_state().is_ssd && !window_bbox.is_empty() { + if self.decoration_state().ssd_mode == Mode::ServerSide && !window_bbox.is_empty() { let window_geo = match self { WindowElement::Wayland(w) => SpaceElement::geometry(w), #[cfg(feature = "xwayland")] @@ -641,9 +704,9 @@ where let mut state = self.decoration_state(); let width = window_geo.size.w; - state.header_bar.redraw(width as u32); + state.decorations.redraw(width as u32); let mut vec = AsRenderElements::::render_elements::>( - &state.header_bar, + &state.decorations, renderer, location, scale, @@ -652,6 +715,37 @@ where location.y += (scale.y * HEADER_BAR_HEIGHT as f64) as i32; + let window_elements = match self { + WindowElement::Wayland(xdg) => { + AsRenderElements::::render_elements::>( + xdg, renderer, location, scale, alpha, + ) + } + #[cfg(feature = "xwayland")] + WindowElement::X11(x11) => AsRenderElements::::render_elements::>( + x11, renderer, location, scale, alpha, + ), + }; + vec.extend(window_elements); + vec.into_iter().map(C::from).collect() + } else if self.decoration_state().ssd_mode == Mode::ServerSideOverlay && !window_bbox.is_empty() { + let window_geo = match self { + WindowElement::Wayland(w) => SpaceElement::geometry(w), + #[cfg(feature = "xwayland")] + WindowElement::X11(w) => SpaceElement::geometry(w), + }; + + let mut state = self.decoration_state(); + let width = window_geo.size.w; + state.decorations.redraw(width as u32); + let mut vec = AsRenderElements::::render_elements::>( + &state.decorations, + renderer, + location, + scale, + alpha, + ); + let window_elements = match self { WindowElement::Wayland(xdg) => { AsRenderElements::::render_elements::>( diff --git a/anvil/src/shell/ssd.rs b/anvil/src/shell/ssd.rs index d3574ddd6553..f6f550b90f11 100644 --- a/anvil/src/shell/ssd.rs +++ b/anvil/src/shell/ssd.rs @@ -5,10 +5,7 @@ use smithay::{ AsRenderElements, Kind, }, Renderer, - }, - input::Seat, - utils::{Logical, Point, Serial}, - wayland::shell::xdg::XdgShellHandler, + }, input::Seat, reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode, utils::{Logical, Point, Serial}, wayland::shell::xdg::XdgShellHandler }; use std::cell::{RefCell, RefMut}; @@ -18,18 +15,18 @@ use crate::AnvilState; use super::WindowElement; pub struct WindowState { - pub is_ssd: bool, + pub ssd_mode: Mode, pub ptr_entered_window: bool, - pub header_bar: HeaderBar, + pub decorations: Decorations, } #[derive(Debug, Clone)] -pub struct HeaderBar { +pub struct Decorations { pub pointer_loc: Option>, pub width: u32, pub close_button_hover: bool, pub maximize_button_hover: bool, - pub background: SolidColorBuffer, + pub background: Option, pub close_button: SolidColorBuffer, pub maximize_button: SolidColorBuffer, } @@ -42,9 +39,9 @@ const CLOSE_COLOR_HOVER: [f32; 4] = [0.75f32, 0.11f32, 0.016f32, 1f32]; pub const HEADER_BAR_HEIGHT: i32 = 32; const BUTTON_HEIGHT: u32 = HEADER_BAR_HEIGHT as u32; -const BUTTON_WIDTH: u32 = 32; +pub const BUTTON_WIDTH: u32 = 32; -impl HeaderBar { +impl Decorations { pub fn pointer_enter(&mut self, loc: Point) { self.pointer_loc = Some(loc); } @@ -110,8 +107,9 @@ impl HeaderBar { return; } - self.background - .update((width as i32, HEADER_BAR_HEIGHT), BG_COLOR); + if let Some(background) = &mut self.background { + background.update((width as i32, HEADER_BAR_HEIGHT), BG_COLOR); + } let mut needs_redraw_buttons = false; if width != self.width { @@ -165,7 +163,7 @@ impl HeaderBar { } } -impl AsRenderElements for HeaderBar { +impl AsRenderElements for Decorations { type RenderElement = SolidColorRenderElement; fn render_elements>( @@ -178,7 +176,7 @@ impl AsRenderElements for HeaderBar { let header_end_offset: Point = Point::from((self.width as i32, 0)); let button_offset: Point = Point::from((BUTTON_WIDTH as i32, 0)); - vec![ + let mut elements = vec![ SolidColorRenderElement::from_buffer( &self.close_button, location + (header_end_offset - button_offset).to_physical_precise_round(scale), @@ -194,10 +192,23 @@ impl AsRenderElements for HeaderBar { alpha, Kind::Unspecified, ) - .into(), - SolidColorRenderElement::from_buffer(&self.background, location, scale, alpha, Kind::Unspecified) + .into() + ]; + + if let Some(background) = &self.background { + elements.push( + SolidColorRenderElement::from_buffer( + background, + location, + scale, + alpha, + Kind::Unspecified, + ) .into(), - ] + ); + } + + elements } } @@ -205,14 +216,14 @@ impl WindowElement { pub fn decoration_state(&self) -> RefMut<'_, WindowState> { self.user_data().insert_if_missing(|| { RefCell::new(WindowState { - is_ssd: false, + ssd_mode: Mode::ClientSide, ptr_entered_window: false, - header_bar: HeaderBar { + decorations: Decorations { pointer_loc: None, width: 0, close_button_hover: false, maximize_button_hover: false, - background: SolidColorBuffer::default(), + background: Some(SolidColorBuffer::default()), close_button: SolidColorBuffer::default(), maximize_button: SolidColorBuffer::default(), }, @@ -225,7 +236,19 @@ impl WindowElement { .borrow_mut() } - pub fn set_ssd(&self, ssd: bool) { - self.decoration_state().is_ssd = ssd; + pub fn set_no_ssd(&self) { + self.decoration_state().ssd_mode = Mode::ClientSide; + } + + pub fn set_ssd(&self) { + let mut state = self.decoration_state(); + state.ssd_mode = Mode::ServerSide; + state.decorations.background = Some(SolidColorBuffer::default()); + } + + pub fn set_ssd_overlay(&self) { + let mut state = self.decoration_state(); + state.ssd_mode = Mode::ServerSideOverlay; + state.decorations.background = None; } } diff --git a/anvil/src/shell/x11.rs b/anvil/src/shell/x11.rs index fc7b2103235c..bdf0003b374e 100644 --- a/anvil/src/shell/x11.rs +++ b/anvil/src/shell/x11.rs @@ -64,7 +64,11 @@ impl XwmHandler for CalloopData { unreachable!() }; xsurface.configure(Some(bbox)).unwrap(); - window.set_ssd(!xsurface.is_decorated()); + if xsurface.is_decorated() { + window.set_no_ssd(); + } else { + window.set_ssd(); + } } fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { @@ -175,7 +179,7 @@ impl XwmHandler for CalloopData { let geometry = self.state.space.output_geometry(output).unwrap(); window.set_fullscreen(true).unwrap(); - elem.set_ssd(false); + elem.set_no_ssd(); window.configure(geometry).unwrap(); output.user_data().insert_if_missing(FullscreenSurface::default); output @@ -195,7 +199,11 @@ impl XwmHandler for CalloopData { .find(|e| matches!(e, WindowElement::X11(w) if w == &window)) { window.set_fullscreen(false).unwrap(); - elem.set_ssd(!window.is_decorated()); + if window.is_decorated() { + elem.set_no_ssd(); + } else { + elem.set_ssd(); + } if let Some(output) = self.state.space.outputs().find(|o| { o.user_data() .get::() diff --git a/anvil/src/shell/xdg.rs b/anvil/src/shell/xdg.rs index 82458194127f..21d136838700 100644 --- a/anvil/src/shell/xdg.rs +++ b/anvil/src/shell/xdg.rs @@ -193,12 +193,21 @@ impl XdgShellHandler for AnvilState { .find(|element| element.wl_surface().as_ref() == Some(&surface)); if let Some(window) = window { use xdg_decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode; - let is_ssd = configure + match configure .state .decoration_mode - .map(|mode| mode == Mode::ServerSide) - .unwrap_or(false); - window.set_ssd(is_ssd); + .unwrap_or(Mode::ClientSide) { + Mode::ServerSide => { + window.set_ssd(); + } + Mode::ClientSide => { + window.set_no_ssd(); + } + Mode::ServerSideOverlay => { + window.set_ssd_overlay(); + } + _ => { panic!() } + } } } } diff --git a/anvil/src/state.rs b/anvil/src/state.rs index c5626b36cf2b..fa6ecb5b9e46 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -71,8 +71,8 @@ use smithay::{ shell::{ wlr_layer::WlrLayerShellState, xdg::{ - decoration::{XdgDecorationHandler, XdgDecorationState}, - ToplevelSurface, XdgShellState, XdgToplevelSurfaceData, + decoration::{XdgDecorationHandler, XdgDecorationState}, DecorationOverlayArea, + ToplevelSurface, XdgShellState, XdgToplevelSurfaceData }, }, shm::{ShmHandler, ShmState}, @@ -89,7 +89,7 @@ use smithay::{ #[cfg(feature = "xwayland")] use crate::cursor::Cursor; -use crate::{focus::FocusTarget, shell::WindowElement}; +use crate::{focus::FocusTarget, shell::{ssd::{BUTTON_WIDTH, HEADER_BAR_HEIGHT}, WindowElement}}; #[cfg(feature = "xwayland")] use smithay::{ delegate_xwayland_keyboard_grab, @@ -372,16 +372,16 @@ impl XdgDecorationHandler for AnvilState { // Set the default to client side toplevel.with_pending_state(|state| { state.decoration_mode = Some(Mode::ClientSide); + state.decoration_overlay = Some(DecorationOverlayArea { + location: xdg_decoration::zv1::server::zxdg_toplevel_decoration_v1::OverlayLocation::Right, + width: BUTTON_WIDTH * 2, + height: HEADER_BAR_HEIGHT as u32 + }) }); } fn request_mode(&mut self, toplevel: ToplevelSurface, mode: DecorationMode) { - use xdg_decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode; - toplevel.with_pending_state(|state| { - state.decoration_mode = Some(match mode { - DecorationMode::ServerSide => Mode::ServerSide, - _ => Mode::ClientSide, - }); + state.decoration_mode = Some(mode) }); let initial_configure_sent = with_states(toplevel.wl_surface(), |states| { diff --git a/src/wayland/shell/xdg/decoration.rs b/src/wayland/shell/xdg/decoration.rs index 9576991efaf4..37806b880043 100644 --- a/src/wayland/shell/xdg/decoration.rs +++ b/src/wayland/shell/xdg/decoration.rs @@ -94,7 +94,7 @@ use wayland_server::{ backend::GlobalId, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum, }; -use super::{ToplevelSurface, XdgShellHandler}; +use super::{DecorationOverlayArea, ToplevelSurface, XdgShellHandler}; use crate::wayland::shell::xdg::XdgShellSurfaceUserData; /// Delegate type for handling xdg decoration events. @@ -191,6 +191,13 @@ pub(super) fn send_decoration_configure( id.configure(mode) } +pub(super) fn send_decoration_overlay_configure( + id: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, + area: DecorationOverlayArea +) { + id.configure_overlay(area.location, area.width, area.height) +} + impl GlobalDispatch for XdgDecorationState where diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs index 5f439b679eb0..75595f9a1257 100644 --- a/src/wayland/shell/xdg/mod.rs +++ b/src/wayland/shell/xdg/mod.rs @@ -838,6 +838,9 @@ pub struct ToplevelState { /// The xdg decoration mode of the surface pub decoration_mode: Option, + /// The location of the decoration overlay + pub decoration_overlay: Option, + /// The wm capabilities for this toplevel pub capabilities: WmCapabilitySet, } @@ -850,6 +853,7 @@ impl Clone for ToplevelState { size: self.size, bounds: self.bounds, decoration_mode: self.decoration_mode, + decoration_overlay: self.decoration_overlay.clone(), capabilities: self.capabilities.clone(), } } @@ -962,6 +966,28 @@ impl From for Vec { } } +/// Container holding the configuration of a decoration overlay for a +/// toplevel +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct DecorationOverlayArea { + /// The location where the decoration overlay should be placed + pub location: zxdg_toplevel_decoration_v1::OverlayLocation, + /// The width of the decoration overlay + pub width: u32, + /// The height of the decoration overlay + pub height: u32 +} + +impl Default for DecorationOverlayArea { + fn default() -> Self { + Self { + location: zxdg_toplevel_decoration_v1::OverlayLocation::Right, + width: 0, + height: 0 + } + } +} + /// Container holding the [`xdg_toplevel::WmCapabilities`] for a toplevel #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct WmCapabilitySet { @@ -1435,7 +1461,7 @@ impl ToplevelSurface { let shell_surface_data = self.shell_surface.data::(); let decoration = shell_surface_data.and_then(|data| data.decoration.lock().unwrap().as_ref().cloned()); - let (configure, decoration_mode_changed, bounds_changed, capabilities_changed) = + let (configure, decoration_mode_changed, decoration_overlay_changed, bounds_changed, capabilities_changed) = compositor::with_states(&self.wl_surface, |states| { let mut attributes = states .data_map @@ -1455,6 +1481,16 @@ impl ToplevelSurface { // or we never sent it let decoration_mode_changed = !attributes.initial_decoration_configure_sent || (pending.decoration_mode != current.decoration_mode); + + // test if we should send the overlay decoration location, either because it, + // changed, we never sent it, or the decoration mode changed to overlay + let decoration_overlay_changed = + pending.decoration_mode == Some(zxdg_toplevel_decoration_v1::Mode::ServerSideOverlay) && + ( + !attributes.initial_decoration_configure_sent || + pending.decoration_overlay != current.decoration_overlay || + pending.decoration_mode != current.decoration_mode + ); // test if we should send a bounds configure event, either because the // bounds changed or we never sent one @@ -1479,6 +1515,7 @@ impl ToplevelSurface { ( configure, decoration_mode_changed, + decoration_overlay_changed, bounds_changed, capabilities_changed, ) @@ -1486,12 +1523,27 @@ impl ToplevelSurface { if decoration_mode_changed { if let Some(decoration) = &decoration { + let mode = configure + .state + .decoration_mode + .unwrap_or(zxdg_toplevel_decoration_v1::Mode::ClientSide); self::decoration::send_decoration_configure( decoration, - configure - .state - .decoration_mode - .unwrap_or(zxdg_toplevel_decoration_v1::Mode::ClientSide), + mode + ); + } + } + + if decoration_overlay_changed { + if let Some(decoration) = &decoration { + let overlay = configure + .state + .decoration_overlay + .clone() + .unwrap_or_default(); + self::decoration::send_decoration_overlay_configure( + decoration, + overlay ); } }