diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f1b66e3..0d5eb42c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +#### Breaking Changes + +- `ThemedPointer::set_cursor` now takes only `Connection` and `&str`. +- `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`. + ## 0.17.0 - 2023-03-28 #### Breaking Changes diff --git a/Cargo.toml b/Cargo.toml index fe4f7e8f8..28e14f639 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,17 +17,18 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] bitflags = "1.0" -nix = { version = "0.26.1", default-features = false, features = ["fs", "mman"] } +cursor-icon = "1.0.0" dlib = "0.5" lazy_static = "1.0" -memmap2 = "0.5.0" log = "0.4" +memmap2 = "0.5.0" +nix = { version = "0.26.1", default-features = false, features = ["fs", "mman"] } thiserror = "1.0.30" wayland-backend = "0.1.0" wayland-client = "0.30.1" +wayland-cursor = "0.30.0" wayland-protocols = { version = "0.30.0", features = ["client", "unstable"] } wayland-protocols-wlr = { version = "0.1.0", features = ["client"] } -wayland-cursor = "0.30.0" wayland-scanner = "0.30.0" xkbcommon = { version = "0.5", optional = true, features = ["wayland"] } diff --git a/examples/themed_window.rs b/examples/themed_window.rs index 35d12cb86..7c38c75d5 100644 --- a/examples/themed_window.rs +++ b/examples/themed_window.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use std::{convert::TryInto, num::NonZeroU32}; +use smithay_client_toolkit::seat::keyboard::keysyms; use smithay_client_toolkit::{ compositor::{CompositorHandler, CompositorState}, delegate_compositor, delegate_keyboard, delegate_output, delegate_pointer, delegate_registry, @@ -11,7 +12,8 @@ use smithay_client_toolkit::{ seat::{ keyboard::{KeyEvent, KeyboardHandler, Modifiers}, pointer::{ - PointerData, PointerEvent, PointerEventKind, PointerHandler, ThemeSpec, ThemedPointer, + CursorIcon, PointerData, PointerEvent, PointerEventKind, PointerHandler, ThemeSpec, + ThemedPointer, }, Capability, SeatHandler, SeatState, }, @@ -36,6 +38,44 @@ use wayland_client::{ Connection, Proxy, QueueHandle, }; +// Cursor shapes. +const CURSORS: &[CursorIcon] = &[ + CursorIcon::Default, + CursorIcon::Crosshair, + CursorIcon::Pointer, + CursorIcon::Move, + CursorIcon::Text, + CursorIcon::Wait, + CursorIcon::Help, + CursorIcon::Progress, + CursorIcon::NotAllowed, + CursorIcon::ContextMenu, + CursorIcon::Cell, + CursorIcon::VerticalText, + CursorIcon::Alias, + CursorIcon::Copy, + CursorIcon::NoDrop, + CursorIcon::Grab, + CursorIcon::Grabbing, + CursorIcon::AllScroll, + CursorIcon::ZoomIn, + CursorIcon::ZoomOut, + CursorIcon::EResize, + CursorIcon::NResize, + CursorIcon::NeResize, + CursorIcon::NwResize, + CursorIcon::SResize, + CursorIcon::SeResize, + CursorIcon::SwResize, + CursorIcon::WResize, + CursorIcon::EwResize, + CursorIcon::NsResize, + CursorIcon::NeswResize, + CursorIcon::NwseResize, + CursorIcon::ColResize, + CursorIcon::RowResize, +]; + fn main() { env_logger::init(); @@ -60,7 +100,6 @@ fn main() { .expect("Failed to create pool"); let window_surface = compositor_state.create_surface(&qh); - let pointer_surface = compositor_state.create_surface(&qh); let window = xdg_shell_state.create_window(window_surface, WindowDecorations::ServerDefault, &qh); @@ -76,11 +115,13 @@ fn main() { // the correct options. window.commit(); + println!("Press `n` to cycle through cursor icons."); + let mut simple_window = SimpleWindow { registry_state, seat_state, output_state, - _compositor_state: compositor_state, + compositor_state, subcompositor_state: Arc::new(subcompositor_state), shm_state, _xdg_shell_state: xdg_shell_state, @@ -94,21 +135,20 @@ fn main() { buffer: None, window, window_frame: None, - pointer_surface, keyboard: None, keyboard_focus: false, themed_pointer: None, set_cursor: false, - cursor_icon: String::from("diamond_cross"), + window_cursor_icon_idx: 0, + decorations_cursor: None, }; // We don't draw immediately, the configure will notify us when to first draw. - loop { event_queue.blocking_dispatch(&mut simple_window).unwrap(); if simple_window.exit { - println!("exiting example"); + println!("Exiting example."); break; } } @@ -118,7 +158,7 @@ struct SimpleWindow { registry_state: RegistryState, seat_state: SeatState, output_state: OutputState, - _compositor_state: CompositorState, + compositor_state: CompositorState, subcompositor_state: Arc, shm_state: Shm, _xdg_shell_state: XdgShell, @@ -132,12 +172,12 @@ struct SimpleWindow { buffer: Option, window: Window, window_frame: Option>, - pointer_surface: wl_surface::WlSurface, keyboard: Option, keyboard_focus: bool, themed_pointer: Option, set_cursor: bool, - cursor_icon: String, + window_cursor_icon_idx: usize, + decorations_cursor: Option, } impl CompositorHandler for SimpleWindow { @@ -247,7 +287,7 @@ impl WindowHandler for SimpleWindow { let width = width.unwrap_or(NonZeroU32::new(1).unwrap()); let height = height.unwrap_or(NonZeroU32::new(1).unwrap()); - println!("{width}, {height}"); + println!("New dimentions: {width}, {height}"); window_frame.resize(width, height); let (x, y) = window_frame.location(); @@ -311,10 +351,16 @@ impl SeatHandler for SimpleWindow { if capability == Capability::Pointer && self.themed_pointer.is_none() { println!("Set pointer capability"); - println!("Creating pointer theme"); + let surface = self.compositor_state.create_surface(qh); let themed_pointer = self .seat_state - .get_pointer_with_theme(qh, &seat, ThemeSpec::default()) + .get_pointer_with_theme( + qh, + &seat, + self.shm_state.wl_shm(), + surface, + ThemeSpec::default(), + ) .expect("Failed to create pointer"); self.themed_pointer.replace(themed_pointer); } @@ -367,7 +413,6 @@ impl KeyboardHandler for SimpleWindow { _: u32, ) { if self.window.wl_surface() == surface { - println!("Release keyboard focus on window"); self.keyboard_focus = false; } } @@ -380,7 +425,12 @@ impl KeyboardHandler for SimpleWindow { _: u32, event: KeyEvent, ) { - println!("Key press: {event:?}"); + if event.keysym == keysyms::XKB_KEY_n { + // Cycle through cursor icons. + self.window_cursor_icon_idx = (self.window_cursor_icon_idx + 1) % CURSORS.len(); + println!("Setting cursor icon to: {}", CURSORS[self.window_cursor_icon_idx].name()); + self.set_cursor = true; + } } fn release_key( @@ -389,9 +439,8 @@ impl KeyboardHandler for SimpleWindow { _: &QueueHandle, _: &wl_keyboard::WlKeyboard, _: u32, - event: KeyEvent, + _: KeyEvent, ) { - println!("Key release: {event:?}"); } fn update_modifiers( @@ -400,9 +449,8 @@ impl KeyboardHandler for SimpleWindow { _: &QueueHandle, _: &wl_keyboard::WlKeyboard, _serial: u32, - modifiers: Modifiers, + _: Modifiers, ) { - println!("Update modifiers: {modifiers:?}"); } } @@ -420,16 +468,10 @@ impl PointerHandler for SimpleWindow { match event.kind { Enter { .. } => { self.set_cursor = true; - self.cursor_icon = self + self.decorations_cursor = self .window_frame .as_mut() - .and_then(|frame| frame.click_point_moved(&event.surface, x, y)) - .unwrap_or("diamond_cross") - .to_owned(); - - if &event.surface == self.window.wl_surface() { - println!("Pointer entered @{:?}", event.position); - } + .and_then(|frame| frame.click_point_moved(&event.surface, x, y)); } Leave { .. } => { if &event.surface != self.window.wl_surface() { @@ -437,7 +479,6 @@ impl PointerHandler for SimpleWindow { window_frame.click_point_left(); } } - println!("Pointer left"); } Motion { .. } => { if let Some(new_cursor) = self @@ -446,11 +487,11 @@ impl PointerHandler for SimpleWindow { .and_then(|frame| frame.click_point_moved(&event.surface, x, y)) { self.set_cursor = true; - self.cursor_icon = new_cursor.to_owned(); + self.decorations_cursor = Some(new_cursor); } } Press { button, serial, .. } | Release { button, serial, .. } => { - let pressed = if matches!(event.kind, Press { .. }) { true } else { false }; + let pressed = matches!(event.kind, Press { .. }); if &event.surface != self.window.wl_surface() { let click = match button { 0x110 => FrameClick::Normal, @@ -466,19 +507,15 @@ impl PointerHandler for SimpleWindow { self.frame_action(pointer, serial, action); } } else if pressed { - println!("Press {:x} @ {:?}", button, event.position); self.shift = self.shift.xor(Some(0)); } } - Axis { horizontal, vertical, .. } => { - if &event.surface == self.window.wl_surface() { - println!("Scroll H:{horizontal:?}, V:{vertical:?}"); - } - } + Axis { .. } => {} } } } } + impl SimpleWindow { fn frame_action(&mut self, pointer: &wl_pointer::WlPointer, serial: u32, action: FrameAction) { let pointer_data = pointer.data::().unwrap(); @@ -504,13 +541,9 @@ impl ShmHandler for SimpleWindow { impl SimpleWindow { pub fn draw(&mut self, conn: &Connection, qh: &QueueHandle) { if self.set_cursor { - let _ = self.themed_pointer.as_mut().unwrap().set_cursor( - conn, - &self.cursor_icon, - self.shm_state.wl_shm(), - &self.pointer_surface, - 1, - ); + let cursor_icon = + self.decorations_cursor.unwrap_or(CURSORS[self.window_cursor_icon_idx]); + let _ = self.themed_pointer.as_mut().unwrap().set_cursor(conn, cursor_icon); self.set_cursor = false; } @@ -562,11 +595,11 @@ impl SimpleWindow { } // Draw the decorations frame. - self.window_frame.as_mut().map(|frame| { + if let Some(frame) = self.window_frame.as_mut() { if frame.is_dirty() && !frame.is_hidden() { frame.draw(); } - }); + } // Damage the entire window self.window.wl_surface().damage_buffer(0, 0, width as i32, height as i32); diff --git a/src/seat/mod.rs b/src/seat/mod.rs index 0a1ffb3ea..6f9bf117a 100644 --- a/src/seat/mod.rs +++ b/src/seat/mod.rs @@ -15,11 +15,14 @@ use std::{ use wayland_client::{ globals::GlobalList, - protocol::{wl_pointer, wl_seat, wl_touch}, + protocol::{wl_pointer, wl_seat, wl_shm, wl_surface, wl_touch}, Connection, Dispatch, Proxy, QueueHandle, }; -use crate::registry::{ProvidesRegistryState, RegistryHandler}; +use crate::{ + compositor::SurfaceDataExt, + registry::{ProvidesRegistryState, RegistryHandler}, +}; use self::{ pointer::{PointerData, PointerDataExt, PointerHandler, ThemeSpec, ThemedPointer, Themes}, @@ -130,16 +133,29 @@ impl SeatState { /// ## Errors /// /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a pointer. - pub fn get_pointer_with_theme( + pub fn get_pointer_with_theme( &mut self, qh: &QueueHandle, seat: &wl_seat::WlSeat, + shm: &wl_shm::WlShm, + surface: wl_surface::WlSurface, theme: ThemeSpec, ) -> Result, SeatError> where - D: Dispatch + PointerHandler + 'static, + D: Dispatch + + Dispatch + + PointerHandler + + 'static, + S: SurfaceDataExt + 'static, { - self.get_pointer_with_theme_and_data(qh, seat, theme, PointerData::new(seat.clone())) + self.get_pointer_with_theme_and_data( + qh, + seat, + shm, + surface, + theme, + PointerData::new(seat.clone()), + ) } /// Creates a pointer from a seat. @@ -172,15 +188,21 @@ impl SeatState { /// ## Errors /// /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a pointer. - pub fn get_pointer_with_theme_and_data( + pub fn get_pointer_with_theme_and_data( &mut self, qh: &QueueHandle, seat: &wl_seat::WlSeat, + shm: &wl_shm::WlShm, + surface: wl_surface::WlSurface, theme: ThemeSpec, pointer_data: U, ) -> Result, SeatError> where - D: Dispatch + PointerHandler + 'static, + D: Dispatch + + Dispatch + + PointerHandler + + 'static, + S: SurfaceDataExt + 'static, U: PointerDataExt + 'static, { let inner = @@ -194,7 +216,10 @@ impl SeatState { Ok(ThemedPointer { themes: Arc::new(Mutex::new(Themes::new(theme))), pointer: wl_ptr, + shm: shm.clone(), + surface, _marker: std::marker::PhantomData, + _surface_data: std::marker::PhantomData, }) } diff --git a/src/seat/pointer/mod.rs b/src/seat/pointer/mod.rs index 5db8e118c..6ed8ed143 100644 --- a/src/seat/pointer/mod.rs +++ b/src/seat/pointer/mod.rs @@ -3,18 +3,29 @@ use std::{ env, mem, sync::{Arc, Mutex}, }; -use wayland_backend::{client::InvalidId, smallvec::SmallVec}; +use wayland_backend::{client::InvalidId, smallvec::SmallVec}; use wayland_client::{ - protocol::{wl_pointer, wl_seat::WlSeat, wl_shm, wl_surface}, + protocol::{ + wl_pointer::{self, WlPointer}, + wl_seat::WlSeat, + wl_shm::WlShm, + wl_surface::WlSurface, + }, Connection, Dispatch, Proxy, QueueHandle, WEnum, }; use wayland_cursor::{Cursor, CursorTheme}; -use crate::error::GlobalError; +use crate::{ + compositor::{SurfaceData, SurfaceDataExt}, + error::GlobalError, +}; use super::SeatState; +#[doc(inline)] +pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError}; + /* From linux/input-event-codes.h - the buttons usually used by mice */ pub const BTN_LEFT: u32 = 0x110; pub const BTN_RIGHT: u32 = 0x111; @@ -64,7 +75,7 @@ impl AxisScroll { /// A single pointer event. #[derive(Debug, Clone)] pub struct PointerEvent { - pub surface: wl_surface::WlSurface, + pub surface: WlSurface, pub position: (f64, f64), pub kind: PointerEventKind, } @@ -109,7 +120,7 @@ pub trait PointerHandler: Sized { &mut self, conn: &Connection, qh: &QueueHandle, - pointer: &wl_pointer::WlPointer, + pointer: &WlPointer, events: &[PointerEvent], ); } @@ -130,10 +141,16 @@ impl PointerData { &self.seat } - /// The latest serial from the `Enter` event. + /// Serial from the latest [`PointerEventKind::Enter`] event. pub fn latest_enter_serial(&self) -> Option { self.inner.lock().unwrap().latest_enter } + + /// Serial from the latest button [`PointerEventKind::Press`] and + /// [`PointerEventKind::Release`] events. + pub fn latest_button_serial(&self) -> Option { + self.inner.lock().unwrap().latest_btn + } } pub trait PointerDataExt: Send + Sync { @@ -169,7 +186,7 @@ macro_rules! delegate_pointer { #[derive(Debug, Default)] pub(crate) struct PointerDataInner { /// Surface the pointer most recently entered - pub(crate) surface: Option, + pub(crate) surface: Option, /// Position relative to the surface pub(crate) position: (f64, f64), @@ -178,16 +195,19 @@ pub(crate) struct PointerDataInner { /// The serial of the latest enter event for the pointer pub(crate) latest_enter: Option, + + /// The serial of the latest enter event for the pointer + pub(crate) latest_btn: Option, } -impl Dispatch for SeatState +impl Dispatch for SeatState where - D: Dispatch + PointerHandler, + D: Dispatch + PointerHandler, U: PointerDataExt, { fn event( data: &mut D, - pointer: &wl_pointer::WlPointer, + pointer: &WlPointer, event: wl_pointer::Event, udata: &U, conn: &Connection, @@ -220,20 +240,22 @@ where PointerEventKind::Motion { time } } - wl_pointer::Event::Button { time, button, state, serial } => match state { - WEnum::Value(wl_pointer::ButtonState::Pressed) => { - PointerEventKind::Press { time, button, serial } - } - WEnum::Value(wl_pointer::ButtonState::Released) => { - PointerEventKind::Release { time, button, serial } - } - WEnum::Unknown(unknown) => { - log::warn!(target: "sctk", "{}: invalid pointer button state: {:x}", pointer.id(), unknown); - return; + wl_pointer::Event::Button { time, button, state, serial } => { + guard.latest_btn.replace(serial); + match state { + WEnum::Value(wl_pointer::ButtonState::Pressed) => { + PointerEventKind::Press { time, button, serial } + } + WEnum::Value(wl_pointer::ButtonState::Released) => { + PointerEventKind::Release { time, button, serial } + } + WEnum::Unknown(unknown) => { + log::warn!(target: "sctk", "{}: invalid pointer button state: {:x}", pointer.id(), unknown); + return; + } + _ => unreachable!(), } - _ => unreachable!(), - }, - + } // Axis logical events. wl_pointer::Event::Axis { time, axis, value } => match axis { WEnum::Value(axis) => { @@ -372,25 +394,27 @@ where /// Pointer themeing #[derive(Debug)] -pub struct ThemedPointer { +pub struct ThemedPointer { pub(super) themes: Arc>, - pub(super) pointer: wl_pointer::WlPointer, + /// The underlying wl_pointer. + pub(super) pointer: WlPointer, + pub(super) shm: WlShm, + /// The surface owned by the cursor to present the icon. + pub(super) surface: WlSurface, pub(super) _marker: std::marker::PhantomData, + pub(super) _surface_data: std::marker::PhantomData, } -impl ThemedPointer { - pub fn set_cursor( - &self, - conn: &Connection, - name: &str, - shm: &wl_shm::WlShm, - surface: &wl_surface::WlSurface, - scale: i32, - ) -> Result<(), PointerThemeError> { +impl ThemedPointer { + /// Set the cursor to the given [`CursorIcon`]. + /// + /// The cursor icon should be reloaded on every [`PointerEventKind::Enter`] event. + pub fn set_cursor(&self, conn: &Connection, icon: CursorIcon) -> Result<(), PointerThemeError> { let mut themes = self.themes.lock().unwrap(); + let scale = self.surface.data::().unwrap().surface_data().scale_factor(); let cursor = themes - .get_cursor(conn, name, scale as u32, shm) + .get_cursor(conn, icon.name(), scale as u32, &self.shm) .map_err(PointerThemeError::InvalidId)? .ok_or(PointerThemeError::CursorNotFound)?; @@ -398,33 +422,65 @@ impl ThemedPointer { let (w, h) = image.dimensions(); let (hx, hy) = image.hotspot(); - surface.set_buffer_scale(scale); - surface.attach(Some(image), 0, 0); + self.surface.set_buffer_scale(scale); + self.surface.attach(Some(image), 0, 0); - if surface.version() >= 4 { - surface.damage_buffer(0, 0, w as i32, h as i32); + if self.surface.version() >= 4 { + self.surface.damage_buffer(0, 0, w as i32, h as i32); } else { - // surface is old and does not support damage_buffer, so we damage - // in surface coordinates and hope it is not rescaled - surface.damage(0, 0, w as i32 / scale, h as i32 / scale); + // Fallback for the old old surface. + self.surface.damage(0, 0, w as i32 / scale, h as i32 / scale); } // Commit the surface to place the cursor image in the compositor's memory. - surface.commit(); + self.surface.commit(); // Set the pointer surface to change the pointer. let data = self.pointer.data::(); if let Some(serial) = data.and_then(|data| data.pointer_data().latest_enter_serial()) { - self.pointer.set_cursor(serial, Some(surface), hx as i32 / scale, hy as i32 / scale); + self.pointer.set_cursor( + serial, + Some(&self.surface), + hx as i32 / scale, + hy as i32 / scale, + ); + Ok(()) + } else { + Err(PointerThemeError::MissingEnterSerial) + } + } + + /// Hide the cursor by providing empty surface for it. + /// + /// The cursor should be hidden on every [`PointerEventKind::Enter`] event. + pub fn hide_cursor(&self) -> Result<(), PointerThemeError> { + let data = self.pointer.data::(); + if let Some(serial) = data.and_then(|data| data.pointer_data().latest_enter_serial()) { + self.pointer.set_cursor(serial, None, 0, 0); Ok(()) } else { Err(PointerThemeError::MissingEnterSerial) } } - pub fn pointer(&self) -> &wl_pointer::WlPointer { + /// The [`WlPointer`] associated with this [`ThemedPointer`]. + pub fn pointer(&self) -> &WlPointer { &self.pointer } + + /// The associated [`WlSurface`] with this [`ThemedPointer`]. + pub fn surface(&self) -> &WlSurface { + &self.surface + } +} + +impl Drop for ThemedPointer { + fn drop(&mut self) { + if self.pointer.version() >= 3 { + self.pointer.release(); + } + self.surface.destroy(); + } } /// Specifies which cursor theme should be used by the theme manager. @@ -509,7 +565,7 @@ impl Themes { conn: &Connection, name: &str, scale: u32, - shm: &wl_shm::WlShm, + shm: &WlShm, ) -> Result, InvalidId> { // Check if the theme has been initialized at the specified scale. if let Entry::Vacant(e) = self.themes.entry(scale) { diff --git a/src/shell/xdg/frame/fallback_frame.rs b/src/shell/xdg/frame/fallback_frame.rs index 333004be3..1a13b1dc9 100644 --- a/src/shell/xdg/frame/fallback_frame.rs +++ b/src/shell/xdg/frame/fallback_frame.rs @@ -11,6 +11,7 @@ use crate::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge; use crate::{ compositor::SurfaceData, + seat::pointer::CursorIcon, shell::xdg::{WindowManagerCapabilities, WindowState}, shell::WaylandSurface, shm::{slot::SlotPool, Shm}, @@ -357,7 +358,7 @@ where } } - fn click_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option<&str> { + fn click_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option { let part_index = self.part_index_for_surface(surface)?; let location = match part_index { LEFT_BORDER => Location::Left, @@ -383,15 +384,15 @@ where && old_location != self.mouse_location; Some(match self.mouse_location { - Location::Top => "top_side", - Location::TopRight => "top_right_corner", - Location::Right => "right_side", - Location::BottomRight => "bottom_right_corner", - Location::Bottom => "bottom_side", - Location::BottomLeft => "bottom_left_corner", - Location::Left => "left_side", - Location::TopLeft => "top_left_corner", - _ => "left_ptr", + Location::Top => CursorIcon::NResize, + Location::TopRight => CursorIcon::NeResize, + Location::Right => CursorIcon::EResize, + Location::BottomRight => CursorIcon::SeResize, + Location::Bottom => CursorIcon::SResize, + Location::BottomLeft => CursorIcon::SwResize, + Location::Left => CursorIcon::WResize, + Location::TopLeft => CursorIcon::NwResize, + _ => CursorIcon::Default, }) } diff --git a/src/shell/xdg/frame/mod.rs b/src/shell/xdg/frame/mod.rs index d97192c93..8d00ddf1a 100644 --- a/src/shell/xdg/frame/mod.rs +++ b/src/shell/xdg/frame/mod.rs @@ -5,6 +5,7 @@ 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; @@ -28,7 +29,7 @@ pub trait DecorationsFrame: Sized { /// 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<&str>; + fn click_point_moved(&mut self, surface: &WlSurface, x: f64, y: f64) -> Option; /// All clicks left the decorations. ///