seat: support for curstom cursor images
This commit is contained in:
parent
60bb5e8d5a
commit
f3a68fb1af
|
@ -9,6 +9,7 @@ use rand;
|
|||
use smithay::{
|
||||
wayland::{
|
||||
compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent},
|
||||
seat::CursorImageRole,
|
||||
shell::{
|
||||
legacy::{
|
||||
wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind, ShellSurfaceRole,
|
||||
|
@ -27,7 +28,11 @@ use smithay::{
|
|||
|
||||
use window_map::{Kind as SurfaceKind, WindowMap};
|
||||
|
||||
define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] [ ShellSurface, ShellSurfaceRole<()>] );
|
||||
define_roles!(Roles =>
|
||||
[ XdgSurface, XdgSurfaceRole ]
|
||||
[ ShellSurface, ShellSurfaceRole<()>]
|
||||
[ CursorImage, CursorImageRole ]
|
||||
);
|
||||
|
||||
pub type MyWindowMap =
|
||||
WindowMap<SurfaceData, Roles, (), (), fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>>;
|
||||
|
|
|
@ -146,9 +146,14 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<()>, log: Logger
|
|||
/*
|
||||
* Initialize wayland input object
|
||||
*/
|
||||
let (mut w_seat, _) = Seat::new(&mut display.borrow_mut(), session.seat(), log.clone());
|
||||
let (mut w_seat, _) = Seat::new(
|
||||
&mut display.borrow_mut(),
|
||||
session.seat(),
|
||||
compositor_token.clone(),
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
let pointer = w_seat.add_pointer();
|
||||
let pointer = w_seat.add_pointer(compositor_token.clone(), |_| {});
|
||||
let keyboard = w_seat
|
||||
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
|
||||
set_data_device_focus(seat, focus.and_then(|s| s.client()))
|
||||
|
|
|
@ -52,9 +52,9 @@ pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop<()>, log: Log
|
|||
|
||||
init_data_device(display, |_| {}, default_action_chooser, log.clone());
|
||||
|
||||
let (mut seat, _) = Seat::new(display, "winit".into(), log.clone());
|
||||
let (mut seat, _) = Seat::new(display, "winit".into(), compositor_token.clone(), log.clone());
|
||||
|
||||
let pointer = seat.add_pointer();
|
||||
let pointer = seat.add_pointer(compositor_token.clone(), |_| {});
|
||||
|
||||
let keyboard = seat
|
||||
.add_keyboard(XkbConfig::default(), 1000, 500, |seat, focus| {
|
||||
|
|
|
@ -10,15 +10,22 @@
|
|||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! use smithay::wayland::seat::Seat;
|
||||
//! use smithay::wayland::seat::{Seat, CursorImageRole};
|
||||
//! # use smithay::wayland::compositor::compositor_init;
|
||||
//!
|
||||
//! // You need to insert the `CursorImageRole` into your roles, to handle requests from clients
|
||||
//! // to set a surface as a cursor image
|
||||
//! define_roles!(Roles => [CursorImage, CursorImageRole]);
|
||||
//!
|
||||
//! # fn main(){
|
||||
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
|
||||
//! # let mut display = wayland_server::Display::new(event_loop.handle());
|
||||
//! # let (compositor_token, _, _) = compositor_init::<(), Roles, _, _>(&mut display, |_, _, _| {}, None);
|
||||
//! // insert the seat:
|
||||
//! let (seat, seat_global) = Seat::new(
|
||||
//! &mut display, // the display
|
||||
//! "seat-0".into(), // the name of the seat, will be advertized to clients
|
||||
//! compositor_token.clone(), // the compositor token
|
||||
//! None // insert a logger here
|
||||
//! );
|
||||
//! # }
|
||||
|
@ -31,27 +38,8 @@
|
|||
//! Currently, only pointer and keyboard capabilities are supported by
|
||||
//! smithay.
|
||||
//!
|
||||
//! You can add these capabilities via methods of the `Seat` struct:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate wayland_server;
|
||||
//! # #[macro_use] extern crate smithay;
|
||||
//! #
|
||||
//! # use smithay::wayland::seat::Seat;
|
||||
//! #
|
||||
//! # fn main(){
|
||||
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
|
||||
//! # let mut display = wayland_server::Display::new(event_loop.handle());
|
||||
//! # let (mut seat, seat_global) = Seat::new(
|
||||
//! # &mut display,
|
||||
//! # "seat-0".into(),
|
||||
//! # None
|
||||
//! # );
|
||||
//! let pointer_handle = seat.add_pointer();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! These handles can be cloned and sent across thread, so you can keep one around
|
||||
//! You can add these capabilities via methods of the `Seat` struct: `add_keyboard`, `add_pointer`.
|
||||
//! These methods return handles that can be cloned and sent across thread, so you can keep one around
|
||||
//! in your event-handling code to forward inputs to your clients.
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -61,9 +49,13 @@ mod pointer;
|
|||
|
||||
pub use self::{
|
||||
keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState, XkbConfig},
|
||||
pointer::{AxisFrame, PointerGrab, PointerHandle, PointerInnerHandle},
|
||||
pointer::{
|
||||
AxisFrame, CursorImageRole, CursorImageStatus, PointerGrab, PointerHandle, PointerInnerHandle,
|
||||
},
|
||||
};
|
||||
|
||||
use wayland::compositor::{roles::Role, CompositorToken};
|
||||
|
||||
use wayland_commons::utils::UserDataMap;
|
||||
|
||||
use wayland_server::{
|
||||
|
@ -129,8 +121,15 @@ impl Seat {
|
|||
/// You are provided with the state token to retrieve it (allowing
|
||||
/// you to add or remove capabilities from it), and the global handle,
|
||||
/// in case you want to remove it.
|
||||
pub fn new<L>(display: &mut Display, name: String, logger: L) -> (Seat, Global<wl_seat::WlSeat>)
|
||||
pub fn new<U, R, L>(
|
||||
display: &mut Display,
|
||||
name: String,
|
||||
token: CompositorToken<U, R>,
|
||||
logger: L,
|
||||
) -> (Seat, Global<wl_seat::WlSeat>)
|
||||
where
|
||||
U: 'static,
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = ::slog_or_stdlog(logger);
|
||||
|
@ -146,7 +145,7 @@ impl Seat {
|
|||
});
|
||||
let seat = Seat { arc: arc.clone() };
|
||||
let global = display.create_global(5, move |new_seat, _version| {
|
||||
let seat = implement_seat(new_seat, arc.clone());
|
||||
let seat = implement_seat(new_seat, arc.clone(), token.clone());
|
||||
let mut inner = arc.inner.lock().unwrap();
|
||||
if seat.version() >= 2 {
|
||||
seat.send(wl_seat::Event::Name {
|
||||
|
@ -179,9 +178,44 @@ impl Seat {
|
|||
/// Calling this method on a seat that already has a pointer capability
|
||||
/// will overwrite it, and will be seen by the clients as if the
|
||||
/// mouse was unplugged and a new one was plugged.
|
||||
pub fn add_pointer(&mut self) -> PointerHandle {
|
||||
///
|
||||
/// You need to provide a compositor token, as well as a callback that will be notified
|
||||
/// whenever a client requests to set a custom cursor image.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate wayland_server;
|
||||
/// # #[macro_use] extern crate smithay;
|
||||
/// #
|
||||
/// # use smithay::wayland::{seat::{Seat, CursorImageRole}, compositor::compositor_init};
|
||||
/// #
|
||||
/// # define_roles!(Roles => [CursorImage, CursorImageRole]);
|
||||
/// #
|
||||
/// # fn main(){
|
||||
/// # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
|
||||
/// # let mut display = wayland_server::Display::new(event_loop.handle());
|
||||
/// # let (compositor_token, _, _) = compositor_init::<(), Roles, _, _>(&mut display, |_, _, _| {}, None);
|
||||
/// # let (mut seat, seat_global) = Seat::new(
|
||||
/// # &mut display,
|
||||
/// # "seat-0".into(),
|
||||
/// # compositor_token.clone(),
|
||||
/// # None
|
||||
/// # );
|
||||
/// let pointer_handle = seat.add_pointer(
|
||||
/// compositor_token.clone(),
|
||||
/// |new_status| { /* a closure handling requests from clients tot change the cursor icon */ }
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn add_pointer<U, R, F>(&mut self, token: CompositorToken<U, R>, cb: F) -> PointerHandle
|
||||
where
|
||||
U: 'static,
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
F: FnMut(CursorImageStatus) + Send + 'static,
|
||||
{
|
||||
let mut inner = self.arc.inner.lock().unwrap();
|
||||
let pointer = self::pointer::create_pointer_handler();
|
||||
let pointer = self::pointer::create_pointer_handler(token, cb);
|
||||
if inner.pointer.is_some() {
|
||||
// there is already a pointer, remove it and notify the clients
|
||||
// of the change
|
||||
|
@ -303,7 +337,15 @@ impl ::std::cmp::PartialEq for Seat {
|
|||
}
|
||||
}
|
||||
|
||||
fn implement_seat(new_seat: NewResource<wl_seat::WlSeat>, arc: Arc<SeatArc>) -> Resource<wl_seat::WlSeat> {
|
||||
fn implement_seat<U, R>(
|
||||
new_seat: NewResource<wl_seat::WlSeat>,
|
||||
arc: Arc<SeatArc>,
|
||||
token: CompositorToken<U, R>,
|
||||
) -> Resource<wl_seat::WlSeat>
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
U: 'static,
|
||||
{
|
||||
let dest_arc = arc.clone();
|
||||
new_seat.implement(
|
||||
move |request, seat| {
|
||||
|
@ -311,7 +353,7 @@ fn implement_seat(new_seat: NewResource<wl_seat::WlSeat>, arc: Arc<SeatArc>) ->
|
|||
let inner = arc.inner.lock().unwrap();
|
||||
match request {
|
||||
wl_seat::Request::GetPointer { id } => {
|
||||
let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref());
|
||||
let pointer = self::pointer::implement_pointer(id, inner.pointer.as_ref(), token.clone());
|
||||
if let Some(ref ptr_handle) = inner.pointer {
|
||||
ptr_handle.new_pointer(pointer);
|
||||
} else {
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
use wayland_server::{
|
||||
protocol::{
|
||||
wl_pointer::{Axis, AxisSource, ButtonState, Event, Request, WlPointer},
|
||||
wl_pointer::{self, Axis, AxisSource, ButtonState, Event, Request, WlPointer},
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
NewResource, Resource,
|
||||
};
|
||||
|
||||
// TODO: handle pointer surface role
|
||||
use wayland::compositor::{roles::Role, CompositorToken};
|
||||
|
||||
/// The role representing a surface set as the pointer cursor
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct CursorImageRole {
|
||||
/// Location of the hotspot of the pointer in the surface
|
||||
pub hotspot: (i32, i32),
|
||||
}
|
||||
|
||||
/// Possible status of a cursor as requested by clients
|
||||
#[derive(Clone)]
|
||||
pub enum CursorImageStatus {
|
||||
/// The cursor should be hidden
|
||||
Hidden,
|
||||
/// The compositor should draw its cursor
|
||||
Default,
|
||||
/// The cursor should be drawn using this surface as an image
|
||||
Image(Resource<WlSurface>),
|
||||
}
|
||||
|
||||
enum GrabStatus {
|
||||
None,
|
||||
|
@ -22,10 +40,35 @@ struct PointerInternal {
|
|||
location: (f64, f64),
|
||||
grab: GrabStatus,
|
||||
pressed_buttons: Vec<u32>,
|
||||
image_callback: Box<FnMut(CursorImageStatus) + Send>,
|
||||
}
|
||||
|
||||
impl PointerInternal {
|
||||
fn new() -> PointerInternal {
|
||||
fn new<F, U, R>(token: CompositorToken<U, R>, mut cb: F) -> PointerInternal
|
||||
where
|
||||
U: 'static,
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
F: FnMut(CursorImageStatus) + Send + 'static,
|
||||
{
|
||||
let mut old_status = CursorImageStatus::Default;
|
||||
let wrapper = move |new_status: CursorImageStatus| {
|
||||
if let CursorImageStatus::Image(surface) =
|
||||
::std::mem::replace(&mut old_status, new_status.clone())
|
||||
{
|
||||
match new_status {
|
||||
CursorImageStatus::Image(ref new_surface) if new_surface == &surface => {
|
||||
// don't remove the role, we are just re-binding the same surface
|
||||
}
|
||||
_ => {
|
||||
if surface.is_alive() {
|
||||
token.remove_role::<CursorImageRole>(&surface).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cb(new_status)
|
||||
};
|
||||
|
||||
PointerInternal {
|
||||
known_pointers: Vec::new(),
|
||||
focus: None,
|
||||
|
@ -33,6 +76,7 @@ impl PointerInternal {
|
|||
location: (0.0, 0.0),
|
||||
grab: GrabStatus::None,
|
||||
pressed_buttons: Vec::new(),
|
||||
image_callback: Box::new(wrapper) as Box<_>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,6 +341,7 @@ impl<'a> PointerInnerHandle<'a> {
|
|||
}
|
||||
});
|
||||
self.inner.focus = None;
|
||||
(self.inner.image_callback)(CursorImageStatus::Default);
|
||||
}
|
||||
|
||||
// do we enter one ?
|
||||
|
@ -431,6 +476,7 @@ pub struct AxisFrame {
|
|||
}
|
||||
|
||||
impl AxisFrame {
|
||||
/// Create a new frame of axis events
|
||||
pub fn new(time: u32) -> Self {
|
||||
AxisFrame {
|
||||
source: None,
|
||||
|
@ -501,34 +547,83 @@ impl AxisFrame {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_pointer_handler() -> PointerHandle {
|
||||
pub(crate) fn create_pointer_handler<F, U, R>(token: CompositorToken<U, R>, cb: F) -> PointerHandle
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
U: 'static,
|
||||
F: FnMut(CursorImageStatus) + Send + 'static,
|
||||
{
|
||||
PointerHandle {
|
||||
inner: Arc::new(Mutex::new(PointerInternal::new())),
|
||||
inner: Arc::new(Mutex::new(PointerInternal::new(token, cb))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn implement_pointer(
|
||||
pub(crate) fn implement_pointer<U, R>(
|
||||
new_pointer: NewResource<WlPointer>,
|
||||
handle: Option<&PointerHandle>,
|
||||
) -> Resource<WlPointer> {
|
||||
let destructor = match handle {
|
||||
Some(h) => {
|
||||
let inner = h.inner.clone();
|
||||
Some(move |pointer: Resource<_>| {
|
||||
token: CompositorToken<U, R>,
|
||||
) -> Resource<WlPointer>
|
||||
where
|
||||
R: Role<CursorImageRole> + 'static,
|
||||
U: 'static,
|
||||
{
|
||||
let inner = handle.map(|h| h.inner.clone());
|
||||
let destructor = match inner.clone() {
|
||||
Some(inner) => Some(move |pointer: Resource<_>| {
|
||||
inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.known_pointers
|
||||
.retain(|p| !p.equals(&pointer))
|
||||
})
|
||||
}
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
new_pointer.implement(
|
||||
|request, _pointer| {
|
||||
move |request, pointer| {
|
||||
match request {
|
||||
Request::SetCursor { .. } => {
|
||||
// TODO
|
||||
Request::SetCursor {
|
||||
serial: _,
|
||||
surface,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
} => {
|
||||
if let Some(ref inner) = inner {
|
||||
let mut guard = inner.lock().unwrap();
|
||||
// only allow setting the cursor icon if the current pointer focus
|
||||
// is of the same client
|
||||
let PointerInternal {
|
||||
ref mut image_callback,
|
||||
ref focus,
|
||||
..
|
||||
} = *guard;
|
||||
if let Some((ref focus, _)) = *focus {
|
||||
if focus.same_client_as(&pointer) {
|
||||
match surface {
|
||||
Some(surface) => {
|
||||
let role_data = CursorImageRole {
|
||||
hotspot: (hotspot_x, hotspot_y),
|
||||
};
|
||||
// we gracefully tolerate the client to provide a surface that
|
||||
// already had the "CursorImage" role, as most clients will
|
||||
// always reuse the same surface (and they are right to do so!)
|
||||
if token.with_role_data(&surface, |data| *data = role_data).is_err()
|
||||
&& token.give_role_with(&surface, role_data).is_err()
|
||||
{
|
||||
pointer.post_error(
|
||||
wl_pointer::Error::Role as u32,
|
||||
"Given wl_surface has another role.".into(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
image_callback(CursorImageStatus::Image(surface));
|
||||
}
|
||||
None => {
|
||||
image_callback(CursorImageStatus::Hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Request::Release => {
|
||||
// Our destructors already handle it
|
||||
|
|
Loading…
Reference in New Issue