//! Utilities for graphics tablet support //! //! This module provides helpers to handle graphics tablets. //! //! ``` //! use smithay::{delegate_seat, delegate_tablet_manager}; //! # use smithay::delegate_compositor; //! use smithay::backend::input::TabletToolDescriptor; //! use smithay::input::{Seat, SeatState, SeatHandler, pointer::CursorImageStatus}; //! # use smithay::wayland::compositor::{CompositorHandler, CompositorState, CompositorClientState}; //! use smithay::wayland::tablet_manager::{TabletManagerState, TabletDescriptor, TabletSeatHandler}; //! use smithay::reexports::wayland_server::{Display, protocol::wl_surface::WlSurface}; //! # use smithay::reexports::wayland_server::Client; //! //! # struct State { seat_state: SeatState }; //! # let mut display = Display::::new().unwrap(); //! # let display_handle = display.handle(); //! //! let mut seat_state = SeatState::::new(); //! let tablet_state = TabletManagerState::new::(&display_handle); //! // add the seat state to your state //! // ... //! //! // create the seat //! let seat = seat_state.new_wl_seat( //! &display_handle, // the display //! "seat-0", // the name of the seat, will be advertized to clients //! ); //! //! use smithay::wayland::tablet_manager::TabletSeatTrait; //! //! seat //! .tablet_seat() // Get TabletSeat asosiated with this seat //! .add_tablet::( // Add a new tablet to a seat //! &display_handle, //! &TabletDescriptor { //! name: "Test".into(), //! usb_id: None, //! syspath: None, //! } //! ); //! //! // implement the required traits //! impl SeatHandler for State { //! type KeyboardFocus = WlSurface; //! type PointerFocus = WlSurface; //! type TouchFocus = WlSurface; //! fn seat_state(&mut self) -> &mut SeatState { //! &mut self.seat_state //! } //! fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { //! // ... //! } //! fn cursor_image(&mut self, seat: &Seat, image: CursorImageStatus) { //! // ... //! } //! } //! delegate_seat!(State); //! //! impl TabletSeatHandler for State { //! fn tablet_tool_image(&mut self, tool: &TabletToolDescriptor, image: CursorImageStatus) { //! // ... //! } //! } //! delegate_tablet_manager!(State); //! //! # impl CompositorHandler for State { //! # fn compositor_state(&mut self) -> &mut CompositorState { unimplemented!() } //! # fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { unimplemented!() } //! # fn commit(&mut self, surface: &WlSurface) {} //! # } //! # delegate_compositor!(State); //! ``` //! ```ignore //! // Init the manager global //! let state = TabletManagerState::new::(&display); //! //! // Init the seat //! let seat = Seat::::new( //! &display, //! "seat-0".into(), //! None //! ); //! //! use smithay::wyaldnd::tablet_manager::TabletSeatTrait; //! //! seat //! .tablet_seat() // Get TabletSeat asosiated with this seat //! .add_tablet( // Add a new tablet to a seat //! display //! &TabletDescriptor { //! name: "Test".into(), //! usb_id: None, //! syspath: None, //! } //! ); //! ``` use crate::input::{Seat, SeatHandler}; use wayland_protocols::wp::tablet::zv2::server::{ zwp_tablet_manager_v2::{self, ZwpTabletManagerV2}, zwp_tablet_seat_v2::ZwpTabletSeatV2, zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2, }; use wayland_server::{backend::GlobalId, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New}; const MANAGER_VERSION: u32 = 1; mod tablet; mod tablet_seat; pub(crate) mod tablet_tool; pub use tablet::{TabletDescriptor, TabletHandle, TabletUserData}; pub use tablet_seat::{TabletSeatHandle, TabletSeatHandler, TabletSeatUserData}; pub use tablet_tool::{TabletToolHandle, TabletToolUserData}; use super::compositor::CompositorHandler; /// Extends [Seat] with graphic tablet specific functionality pub trait TabletSeatTrait { /// Get tablet seat associated with this seat fn tablet_seat(&self) -> TabletSeatHandle; } impl TabletSeatTrait for Seat { fn tablet_seat(&self) -> TabletSeatHandle { let user_data = self.user_data(); user_data.insert_if_missing(TabletSeatHandle::default); user_data.get::().unwrap().clone() } } /// State of wp tablet protocol #[derive(Debug)] pub struct TabletManagerState { global: GlobalId, } impl TabletManagerState { /// Initialize a tablet manager global. pub fn new(display: &DisplayHandle) -> Self where D: GlobalDispatch, D: Dispatch, D: Dispatch, D: Dispatch, D: 'static, { let global = display.create_global::(MANAGER_VERSION, ()); Self { global } } /// Get the id of ZwpTabletManagerV2 global pub fn global(&self) -> GlobalId { self.global.clone() } } impl GlobalDispatch for TabletManagerState where D: GlobalDispatch, D: Dispatch, D: Dispatch, D: 'static, { fn bind( _: &mut D, _: &DisplayHandle, _: &Client, resource: New, _: &(), data_init: &mut DataInit<'_, D>, ) { data_init.init(resource, ()); } } impl Dispatch for TabletManagerState where D: Dispatch, D: Dispatch, D: Dispatch, D: Dispatch, D: SeatHandler + TabletSeatHandler + 'static, D: CompositorHandler, { fn request( state: &mut D, client: &Client, _: &ZwpTabletManagerV2, request: zwp_tablet_manager_v2::Request, _: &(), dh: &DisplayHandle, data_init: &mut DataInit<'_, D>, ) { match request { zwp_tablet_manager_v2::Request::GetTabletSeat { tablet_seat, seat } => { let seat = Seat::::from_resource(&seat).unwrap(); let user_data = seat.user_data(); user_data.insert_if_missing(TabletSeatHandle::default); let handle = user_data.get::().unwrap(); let instance = data_init.init( tablet_seat, TabletSeatUserData { handle: handle.clone(), }, ); handle.add_instance::(state, dh, &instance, client); } zwp_tablet_manager_v2::Request::Destroy => { // Nothing to do } _ => unreachable!(), } } } #[allow(missing_docs)] // TODO #[macro_export] macro_rules! delegate_tablet_manager { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::wp::tablet::zv2::server::zwp_tablet_manager_v2::ZwpTabletManagerV2: () ] => $crate::wayland::tablet_manager::TabletManagerState); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::wp::tablet::zv2::server::zwp_tablet_manager_v2::ZwpTabletManagerV2: () ] => $crate::wayland::tablet_manager::TabletManagerState); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::wp::tablet::zv2::server::zwp_tablet_seat_v2::ZwpTabletSeatV2: $crate::wayland::tablet_manager::TabletSeatUserData ] => $crate::wayland::tablet_manager::TabletManagerState); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::wp::tablet::zv2::server::zwp_tablet_tool_v2::ZwpTabletToolV2: $crate::wayland::tablet_manager::TabletToolUserData ] => $crate::wayland::tablet_manager::TabletManagerState); $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ $crate::reexports::wayland_protocols::wp::tablet::zv2::server::zwp_tablet_v2::ZwpTabletV2: $crate::wayland::tablet_manager::TabletUserData ] => $crate::wayland::tablet_manager::TabletManagerState); }; }