From aad0cbd79a511d0e8203511d31c191913aa95aea Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 5 May 2023 20:14:23 +0300 Subject: [PATCH] chore: use `wayland-csd-frame` for CSD interop smithay-client-toolkit could have various breaking changes, to avoid dealing with the ecosystem fallout the client side decorations frame was extracted to another repository and provides more or less stable interface, so client-toolkit could be bumped without in other libraries using that crate without introducing a breaking update. --- CHANGELOG.md | 1 + Cargo.toml | 1 + examples/themed_window.rs | 38 +++-- src/lib.rs | 1 + src/seat/keyboard/repeat.rs | 4 + src/shell/xdg/{frame => }/fallback_frame.rs | 17 +-- src/shell/xdg/frame/mod.rs | 146 -------------------- src/shell/xdg/mod.rs | 17 +-- src/shell/xdg/window/mod.rs | 70 +--------- 9 files changed, 59 insertions(+), 236 deletions(-) rename src/shell/xdg/{frame => }/fallback_frame.rs (97%) delete mode 100644 src/shell/xdg/frame/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d5eb42c5..6f19d48c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `SeatState:get_pointer_with_them*` now takes `Shm` and `WlSurface` for the themed cursor. - `ThemedPointer` now automatically releases the associated `WlPointer`. - `CursorIcon` from `cursor-icon` crate is now used for `set_cursor` and `Frame`. +- `wayland-csd-frame` is now used for CSD types like `WindowState`, `DecorationsFrame`, etc. ## 0.17.0 - 2023-03-28 diff --git a/Cargo.toml b/Cargo.toml index 28e14f639..61718869f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ wayland-cursor = "0.30.0" wayland-protocols = { version = "0.30.0", features = ["client", "unstable"] } wayland-protocols-wlr = { version = "0.1.0", features = ["client"] } wayland-scanner = "0.30.0" +wayland-csd-frame = "0.1.0" xkbcommon = { version = "0.5", optional = true, features = ["wayland"] } calloop = { version = "0.10.5", optional = true } diff --git a/examples/themed_window.rs b/examples/themed_window.rs index 7c38c75d5..2c5175529 100644 --- a/examples/themed_window.rs +++ b/examples/themed_window.rs @@ -1,6 +1,15 @@ use std::sync::Arc; use std::{convert::TryInto, num::NonZeroU32}; +use smithay_client_toolkit::reexports::client::{ + globals::registry_queue_init, + protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface}, + Connection, Proxy, QueueHandle, +}; +use smithay_client_toolkit::reexports::csd_frame::{ + DecorationsFrame, FrameAction, FrameClick, ResizeEdge, +}; +use smithay_client_toolkit::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge; use smithay_client_toolkit::seat::keyboard::keysyms; use smithay_client_toolkit::{ compositor::{CompositorHandler, CompositorState}, @@ -19,8 +28,7 @@ use smithay_client_toolkit::{ }, shell::{ xdg::{ - frame::fallback_frame::FallbackFrame, - frame::{DecorationsFrame, FrameAction, FrameClick}, + fallback_frame::FallbackFrame, window::{DecorationMode, Window, WindowConfigure, WindowDecorations, WindowHandler}, XdgShell, XdgSurface, }, @@ -32,11 +40,6 @@ use smithay_client_toolkit::{ }, subcompositor::SubcompositorState, }; -use wayland_client::{ - globals::registry_queue_init, - protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface}, - Connection, Proxy, QueueHandle, -}; // Cursor shapes. const CURSORS: &[CursorIcon] = &[ @@ -471,7 +474,7 @@ impl PointerHandler for SimpleWindow { self.decorations_cursor = self .window_frame .as_mut() - .and_then(|frame| frame.click_point_moved(&event.surface, x, y)); + .and_then(|frame| frame.click_point_moved(&event.surface.id(), x, y)); } Leave { .. } => { if &event.surface != self.window.wl_surface() { @@ -484,7 +487,7 @@ impl PointerHandler for SimpleWindow { if let Some(new_cursor) = self .window_frame .as_mut() - .and_then(|frame| frame.click_point_moved(&event.surface, x, y)) + .and_then(|frame| frame.click_point_moved(&event.surface.id(), x, y)) { self.set_cursor = true; self.decorations_cursor = Some(new_cursor); @@ -526,8 +529,23 @@ impl SimpleWindow { FrameAction::Maximize => self.window.set_maximized(), FrameAction::UnMaximize => self.window.unset_maximized(), FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)), - FrameAction::Resize(edge) => self.window.resize(seat, serial, edge), + FrameAction::Resize(edge) => { + let edge = match edge { + ResizeEdge::None => XdgResizeEdge::None, + ResizeEdge::Top => XdgResizeEdge::Top, + ResizeEdge::Bottom => XdgResizeEdge::Bottom, + ResizeEdge::Left => XdgResizeEdge::Left, + ResizeEdge::TopLeft => XdgResizeEdge::TopLeft, + ResizeEdge::BottomLeft => XdgResizeEdge::BottomLeft, + ResizeEdge::Right => XdgResizeEdge::Right, + ResizeEdge::TopRight => XdgResizeEdge::TopRight, + ResizeEdge::BottomRight => XdgResizeEdge::BottomRight, + _ => return, + }; + self.window.resize(seat, serial, edge); + } FrameAction::Move => self.window.move_(seat, serial), + _ => return, } } } diff --git a/src/lib.rs b/src/lib.rs index ef32656ed..d0deb891f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod reexports { #[cfg(feature = "calloop")] pub use calloop; pub use wayland_client as client; + pub use wayland_csd_frame as csd_frame; pub use wayland_protocols as protocols; pub use wayland_protocols_wlr as protocols_wlr; } diff --git a/src/seat/keyboard/repeat.rs b/src/seat/keyboard/repeat.rs index 79d35c337..c74e3cc70 100644 --- a/src/seat/keyboard/repeat.rs +++ b/src/seat/keyboard/repeat.rs @@ -52,6 +52,8 @@ impl SeatState { /// ## Errors /// /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a keyboard. + /// + /// [`EventSource`]: calloop::EventSource pub fn get_keyboard_with_repeat( &mut self, qh: &QueueHandle, @@ -84,6 +86,8 @@ impl SeatState { /// ## Errors /// /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a keyboard. + /// + /// [`EventSource`]: calloop::EventSource pub fn get_keyboard_with_repeat_with_data( &mut self, qh: &QueueHandle, diff --git a/src/shell/xdg/frame/fallback_frame.rs b/src/shell/xdg/fallback_frame.rs similarity index 97% rename from src/shell/xdg/frame/fallback_frame.rs rename to src/shell/xdg/fallback_frame.rs index 1a13b1dc9..f7b8b2b6e 100644 --- a/src/shell/xdg/frame/fallback_frame.rs +++ b/src/shell/xdg/fallback_frame.rs @@ -7,18 +7,19 @@ use crate::reexports::client::{ protocol::{wl_shm, wl_subsurface::WlSubsurface, wl_surface::WlSurface}, Dispatch, Proxy, QueueHandle, }; -use crate::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge; +use crate::reexports::csd_frame::{ + DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowManagerCapabilities, WindowState, +}; use crate::{ compositor::SurfaceData, seat::pointer::CursorIcon, - shell::xdg::{WindowManagerCapabilities, WindowState}, shell::WaylandSurface, shm::{slot::SlotPool, Shm}, subcompositor::{SubcompositorState, SubsurfaceData}, }; -use super::{DecorationsFrame, FrameAction, FrameClick}; +use wayland_backend::client::ObjectId; /// The size of the header bar. const HEADER_SIZE: u32 = 24; @@ -170,8 +171,8 @@ where } #[inline] - fn part_index_for_surface(&mut self, surface: &WlSurface) -> Option { - self.render_data.as_ref()?.parts.iter().position(|part| &part.surface == surface) + fn part_index_for_surface(&mut self, surface_id: &ObjectId) -> Option { + self.render_data.as_ref()?.parts.iter().position(|part| &part.surface.id() == surface_id) } fn draw_buttons( @@ -316,7 +317,7 @@ impl DecorationsFrame for FallbackFrame where State: Dispatch + Dispatch + 'static, { - fn on_click(&mut self, click: FrameClick, pressed: bool) -> Option { + fn on_click(&mut self, click: FrameClick, pressed: bool) -> Option { // Handle alternate click before everything else. if click == FrameClick::Alternate { return if Location::Head != self.mouse_location @@ -358,8 +359,8 @@ where } } - fn click_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option { - let part_index = self.part_index_for_surface(surface)?; + fn click_point_moved(&mut self, surface_id: &ObjectId, x: f64, y: f64) -> Option { + let part_index = self.part_index_for_surface(surface_id)?; let location = match part_index { LEFT_BORDER => Location::Left, RIGHT_BORDER => Location::Right, diff --git a/src/shell/xdg/frame/mod.rs b/src/shell/xdg/frame/mod.rs deleted file mode 100644 index 8d00ddf1a..000000000 --- a/src/shell/xdg/frame/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! The frame to use with XDG shell window. - -use std::num::NonZeroU32; - -use crate::reexports::client::protocol::wl_surface::WlSurface; -use crate::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge; - -use crate::seat::pointer::CursorIcon; -use crate::shell::xdg::window::{WindowManagerCapabilities, WindowState}; - -pub mod fallback_frame; - -/// The interface for the client side decorations. -pub trait DecorationsFrame: Sized { - /// Emulate click on the decorations. - /// - /// The `click` is a variant of click to use, see [`FrameClick`] for more information. - /// - /// The return value is a [`FrameAction`] you should apply, this action could be - /// ignored. - /// - /// The location of the click is the one passed to [`Self::click_point_moved`]. - fn on_click(&mut self, click: FrameClick, pressed: bool) -> Option; - - /// Emulate pointer moved event on the decorations frame. - /// - /// The `x` and `y` are location in the surface local coordinates relative to the `surface`. - /// - /// The return value is the new cursor icon you should apply to provide better visual - /// feedback for the user. However, you might want to ignore it, if you're using touch events - /// to drive the movements. - fn click_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option; - - /// All clicks left the decorations. - /// - /// This function should be called when input leaves the decorations. - fn click_point_left(&mut self); - - /// Update the state of the frame. - /// - /// The state is usually obtained from the [`WindowConfigure`] event. - /// - /// [`WindowConfigure`]: crate::shell::window::WindowConfigure - fn update_state(&mut self, state: WindowState); - - /// Update the window manager capabilites. - /// - /// The capabilites are usually obtained from the [`WindowConfigure`] event. - /// - /// [`WindowConfigure`]: crate::shell::window::WindowConfigure - fn update_wm_capabilities(&mut self, wm_capabilities: WindowManagerCapabilities); - - /// Resize the window to the new size. - /// - /// The size must be without the borders, as in [`Self::subtract_borders]` were used on it. - /// - /// **Note:** The [`Self::update_state`] and [`Self::update_wm_capabilities`] **must be** - /// applied before calling this function. - /// - /// # Panics - /// - /// Panics when resizing the hidden frame. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32); - - /// Return the coordinates of the top-left corner of the borders relative to the content. - /// - /// Values **must** thus be negative. - fn location(&self) -> (i32, i32); - - /// Subtract the borders from the given `width` and `height`. - /// - /// `None` will be returned for the particular dimension when the given - /// value for it was too small. - fn subtract_borders( - &self, - width: NonZeroU32, - height: NonZeroU32, - ) -> (Option, Option); - - /// Add the borders to the given `width` and `height`. - /// - /// Passing zero for both width and height could be used to get the size - /// of the decorations frame. - fn add_borders(&self, width: u32, height: u32) -> (u32, u32); - - /// Whether the given frame is dirty and should be redrawn. - fn is_dirty(&self) -> bool; - - /// Set the frame as hidden. - /// - /// The frame **must be** visible by default. - fn set_hidden(&mut self, hidden: bool); - - /// Get the frame hidden state. - /// - /// Get the state of the last [`DecorationsFrame::set_hidden`]. - fn is_hidden(&self) -> bool; - - /// Mark the frame as resizable. - /// - /// By default the frame is resizable. - fn set_resizable(&mut self, resizable: bool); - - /// Draw the decorations frame. - /// - /// The user of the frame **must** commit the base surface afterwards. - fn draw(&mut self); - - /// Set the frames title. - fn set_title(&mut self, title: impl Into); -} - -/// The Frame action user should perform in responce to mouse click events. -#[derive(Debug, Clone, Copy)] -pub enum FrameAction { - /// The window should be minimized. - Minimize, - /// The window should be maximized. - Maximize, - /// The window should be unmaximized. - UnMaximize, - /// The window should be closed. - Close, - /// An interactive move should be started. - Move, - /// An interactive resize should be started with the provided edge. - Resize(ResizeEdge), - /// Show window menu. - /// - /// The coordinates are relative to the base surface, as in should be directly passed - /// to the `xdg_toplevel::show_window_menu`. - ShowMenu(i32, i32), -} - -/// The user clicked or touched the decoractions frame. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum FrameClick { - /// The user done normal click, likely with left mouse button or single finger touch. - Normal, - - /// The user done right mouse click or some touch sequence that was treated as alternate click. - /// - /// The alternate click exists solely to provide alternative action, like show window - /// menu when doing right mouse button cilck on the header decorations, nothing more. - Alternate, -} diff --git a/src/shell/xdg/mod.rs b/src/shell/xdg/mod.rs index fb9842ea0..91265c385 100644 --- a/src/shell/xdg/mod.rs +++ b/src/shell/xdg/mod.rs @@ -2,14 +2,16 @@ // TODO: Examples use std::sync::{Arc, Mutex}; -use wayland_client::globals::{BindError, GlobalList}; -use wayland_client::Connection; -use wayland_client::{protocol::wl_surface, Dispatch, Proxy, QueueHandle}; -use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::Mode; -use wayland_protocols::xdg::decoration::zv1::client::{ + +use crate::reexports::client::globals::{BindError, GlobalList}; +use crate::reexports::client::Connection; +use crate::reexports::client::{protocol::wl_surface, Dispatch, Proxy, QueueHandle}; +use crate::reexports::csd_frame::{WindowManagerCapabilities, WindowState}; +use crate::reexports::protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::Mode; +use crate::reexports::protocols::xdg::decoration::zv1::client::{ zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1, }; -use wayland_protocols::xdg::shell::client::{ +use crate::reexports::protocols::xdg::shell::client::{ xdg_positioner, xdg_surface, xdg_toplevel, xdg_wm_base, }; @@ -21,12 +23,11 @@ use crate::registry::GlobalProxy; use self::window::inner::WindowInner; use self::window::{ DecorationMode, Window, WindowConfigure, WindowData, WindowDecorations, WindowHandler, - WindowManagerCapabilities, WindowState, }; use super::WaylandSurface; -pub mod frame; +pub mod fallback_frame; pub mod popup; pub mod window; diff --git a/src/shell/xdg/window/mod.rs b/src/shell/xdg/window/mod.rs index b459a2f4d..f33459673 100644 --- a/src/shell/xdg/window/mod.rs +++ b/src/shell/xdg/window/mod.rs @@ -5,13 +5,12 @@ use std::{ sync::{Arc, Weak}, }; -use bitflags::bitflags; - -use wayland_client::{ +use crate::reexports::client::{ protocol::{wl_output, wl_seat, wl_surface}, Connection, Proxy, QueueHandle, }; -use wayland_protocols::{ +use crate::reexports::csd_frame::{WindowManagerCapabilities, WindowState}; +use crate::reexports::protocols::{ xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{self, Mode}, xdg::shell::client::{xdg_surface, xdg_toplevel}, }; @@ -95,12 +94,12 @@ pub struct WindowConfigure { /// The capabilities supported by the compositor. /// - /// For more see [`WindowManagerCapabilites`] documentation on the flag values. + /// For more see [`WindowManagerCapabilities`] documentation on the flag values. pub capabilities: WindowManagerCapabilities, } impl WindowConfigure { - /// Is [`State::Maximized`] state is set. + /// Is [`WindowState::MAXIMIZED`] state is set. #[inline] pub fn is_maximized(&self) -> bool { self.state.contains(WindowState::MAXIMIZED) @@ -112,7 +111,7 @@ impl WindowConfigure { self.state.contains(WindowState::FULLSCREEN) } - /// Is [`WindowState::Resizing`] state is set. + /// Is [`WindowState::RESIZING`] state is set. #[inline] pub fn is_resizing(&self) -> bool { self.state.contains(WindowState::RESIZING) @@ -155,46 +154,6 @@ impl WindowConfigure { } } -bitflags! { - /// The configured state of the window. - pub struct WindowState: u16 { - /// The surface is maximized. The window geometry specified in the - /// configure event must be obeyed by the client. The client should - /// draw without shadow or other decoration outside of the window - /// geometry. - const MAXIMIZED = 0b0000_0000_0000_0001; - /// The surface is fullscreen. The window geometry specified in the - /// configure event is a maximum; the client cannot resize beyond it. - /// For a surface to cover the whole fullscreened area, the geometry - /// dimensions must be obeyed by the client. For more details, see - /// xdg_toplevel.set_fullscreen. - const FULLSCREEN = 0b0000_0000_0000_0010; - /// The surface is being resized. The window geometry specified in the - /// configure event is a maximum; the client cannot resize beyond it. - /// Clients that have aspect ratio or cell sizing configuration can use - /// a smaller size, however. - const RESIZING = 0b0000_0000_0000_0100; - /// Client window decorations should be painted as if the window is - /// active. Do not assume this means that the window actually has - /// keyboard or pointer focus. - const ACTIVATED = 0b0000_0000_0000_1000; - /// The window is currently in a tiled layout and the left edge is - /// considered to be adjacent to another part of the tiling grid. - const TILED_LEFT = 0b0000_0000_0001_0000; - /// The window is currently in a tiled layout and the right edge is - /// considered to be adjacent to another part of the tiling grid. - const TILED_RIGHT = 0b0000_0000_0010_0000; - /// The window is currently in a tiled layout and the top edge is - /// considered to be adjacent to another part of the tiling grid. - const TILED_TOP = 0b0000_0000_0100_0000; - /// The window is currently in a tiled layout and the bottom edge is - /// considered to be adjacent to another part of the tiling grid. - const TILED_BOTTOM = 0b0000_0000_1000_0000; - /// The window has any of the mentioned tiled bits set. - const TILED = Self::TILED_TOP.bits | Self::TILED_LEFT.bits | Self::TILED_RIGHT.bits | Self::TILED_BOTTOM.bits; - } -} - /// Decorations a window is created with. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WindowDecorations { @@ -223,23 +182,6 @@ pub enum WindowDecorations { None, } -bitflags! { - /// The capabilities of the window manager. - /// - /// This is a hint to hide UI elements which provide functionality - /// not supported by compositor. - pub struct WindowManagerCapabilities : u16 { - /// `show_window_menu` is available. - const WINDOW_MENU = 0b0000_0000_0000_0001; - /// Window can be maximized and unmaximized. - const MAXIMIZE = 0b0000_0000_0000_0010; - /// Window can be fullscreened and unfullscreened. - const FULLSCREEN = 0b0000_0000_0000_0100; - /// Window could be minimized. - const MINIMIZE = 0b0000_0000_0000_1000; - } -} - #[derive(Debug, Clone)] pub struct Window(pub(super) Arc);