wayland.tablet: Add tablet manager protocol

This commit is contained in:
Poly 2021-06-30 17:21:54 +02:00
parent 1c9e3fe903
commit 90a62aeae7
5 changed files with 857 additions and 0 deletions

View File

@ -62,6 +62,7 @@ pub mod output;
pub mod seat; pub mod seat;
pub mod shell; pub mod shell;
pub mod shm; pub mod shm;
pub mod tablet_manager;
/// A global [`SerialCounter`] for use in your compositor. /// A global [`SerialCounter`] for use in your compositor.
/// ///

View File

@ -0,0 +1,92 @@
//! Utilities for graphics tablet support
//!
//! This module provides helpers to handle graphics tablets.
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! # use smithay::wayland::compositor::compositor_init;
//!
//! use smithay::wayland::seat::{Seat};
//! use smithay::wayland::tablet_manager::{init_tablet_manager_global, TabletSeatTrait, TabletDescriptor};
//!
//! # let mut display = wayland_server::Display::new();
//! # compositor_init(&mut display, |_, _| {}, None);
//! // First we nee a reguler seat
//! let (seat, seat_global) = Seat::new(
//! &mut display,
//! "seat-0".into(),
//! None
//! );
//!
//! // Insert the manager global into your event loop
//! init_tablet_manager_global(&mut display);
//!
//! seat
//! .tablet_seat() // Get TabletSeat asosiated with this seat
//! .add_tablet(&TabletDescriptor { // Add a new tablet to a seat
//! name: "Test".into(),
//! id_product: None,
//! id_vendor: None,
//! syspath: None,
//! });
//!
//! ```
use crate::wayland::seat::Seat;
use wayland_protocols::unstable::tablet::v2::server::zwp_tablet_manager_v2::{self, ZwpTabletManagerV2};
use wayland_server::{Display, Filter, Global, Main};
const MANAGER_VERSION: u32 = 1;
mod tablet;
mod tablet_seat;
mod tablet_tool;
pub use tablet::{TabletDescriptor, TabletHandle};
pub use tablet_seat::TabletSeatHandle;
pub use tablet_tool::TabletToolHandle;
/// Extends [Seat] with graphic tablet specyfic functionality
pub trait TabletSeatTrait {
/// Get tablet seat asosiated 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::<TabletSeatHandle>().unwrap().clone()
}
}
/// Initialize a tablet manager global.
pub fn init_tablet_manager_global(display: &mut Display) -> Global<ZwpTabletManagerV2> {
display.create_global::<ZwpTabletManagerV2, _>(
MANAGER_VERSION,
Filter::new(
move |(manager, _version): (Main<ZwpTabletManagerV2>, u32), _, _| {
manager.quick_assign(|_manager, req, _| match req {
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 instance = tablet_seat;
let tablet_seat = user_data.get::<TabletSeatHandle>();
if let Some(tablet_seat) = tablet_seat {
tablet_seat.add_instance(instance);
}
}
zwp_tablet_manager_v2::Request::Destroy => {
// Nothing to do
}
_ => {}
});
},
),
)
}

View File

@ -0,0 +1,98 @@
use std::ops::Deref as _;
use std::path::PathBuf;
use std::{cell::RefCell, rc::Rc};
use wayland_protocols::unstable::tablet::v2::server::zwp_tablet_seat_v2::ZwpTabletSeatV2;
use wayland_protocols::unstable::tablet::v2::server::zwp_tablet_v2::ZwpTabletV2;
use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::Filter;
use crate::backend::input::Device;
/// Description of graphics tablet device
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct TabletDescriptor {
/// Tablet device name
pub name: String,
/// Tablet device USB product id
pub id_product: Option<u32>,
/// Tablet device USB vendor id
pub id_vendor: Option<u32>,
/// Path to the device
pub syspath: Option<PathBuf>,
}
impl<D: Device> From<D> for TabletDescriptor {
fn from(device: D) -> Self {
TabletDescriptor {
name: device.name(),
syspath: device.syspath(),
id_product: device.id_product(),
id_vendor: device.id_vendor(),
}
}
}
#[derive(Debug, Default)]
struct Tablet {
instances: Vec<ZwpTabletV2>,
}
/// Handle to a tablet device
///
/// Tablet represents one graphics tablet device
#[derive(Debug, Default, Clone)]
pub struct TabletHandle {
inner: Rc<RefCell<Tablet>>,
}
impl TabletHandle {
pub(super) fn new_instance(&mut self, seat: &ZwpTabletSeatV2, tablet: &TabletDescriptor) {
if let Some(client) = seat.as_ref().client() {
let wl_tablet = client
.create_resource::<ZwpTabletV2>(seat.as_ref().version())
.unwrap();
wl_tablet.quick_assign(|_, _req, _| {});
let inner = self.inner.clone();
wl_tablet.assign_destructor(Filter::new(move |instance: ZwpTabletV2, _, _| {
inner
.borrow_mut()
.instances
.retain(|i| !i.as_ref().equals(&instance.as_ref()));
}));
seat.tablet_added(&wl_tablet);
wl_tablet.name(tablet.name.clone());
if let (Some(id_product), Some(id_vendor)) = (tablet.id_product, tablet.id_vendor) {
wl_tablet.id(id_product, id_vendor);
}
if let Some(syspath) = tablet.syspath.as_ref().and_then(|p| p.to_str()) {
wl_tablet.path(syspath.to_owned());
}
wl_tablet.done();
self.inner.borrow_mut().instances.push(wl_tablet.deref().clone());
}
}
pub(super) fn with_focused_tablet<F>(&self, focus: &WlSurface, cb: F)
where
F: Fn(&ZwpTabletV2),
{
if let Some(instance) = self
.inner
.borrow()
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.as_ref()))
{
cb(instance);
}
}
}

View File

@ -0,0 +1,189 @@
use wayland_protocols::unstable::tablet::v2::server::zwp_tablet_seat_v2::ZwpTabletSeatV2;
use wayland_server::{Filter, Main};
use crate::backend::input::TabletToolDescriptor;
use crate::wayland::seat::CursorImageStatus;
use super::tablet::{TabletDescriptor, TabletHandle};
use super::tablet_tool::TabletToolHandle;
use std::convert::AsRef;
use std::fmt;
use std::ops::Deref as _;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
#[derive(Default)]
struct TabletSeat {
instances: Vec<ZwpTabletSeatV2>,
tablets: HashMap<TabletDescriptor, TabletHandle>,
tools: HashMap<TabletToolDescriptor, TabletToolHandle>,
cursor_callback: Option<Box<dyn FnMut(&TabletToolDescriptor, CursorImageStatus)>>,
}
impl fmt::Debug for TabletSeat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TabletSeat")
.field("instances", &self.instances)
.field("tablets", &self.tablets)
.field("tools", &self.tools)
.field(
"cursor_callback",
if self.cursor_callback.is_some() {
&"Some(...)"
} else {
&"None"
},
)
.finish()
}
}
/// Handle to a tablet seat
///
/// TabletSeat extends `Seat` with graphic tablet specyfic functionality
///
/// TabletSeatHandle can be used to advertise avalible graphics tablets and tools to wayland clients
#[derive(Default, Debug, Clone)]
pub struct TabletSeatHandle {
inner: Rc<RefCell<TabletSeat>>,
}
impl TabletSeatHandle {
pub(super) fn add_instance(&self, seat: Main<ZwpTabletSeatV2>) {
let mut inner = self.inner.borrow_mut();
// Notify new instance about avaluble tablets
for (desc, tablet) in inner.tablets.iter_mut() {
tablet.new_instance(seat.deref(), desc);
}
// Notify new instance about avalible tools
for (desc, tool) in inner.tools.iter_mut() {
let inner = self.inner.clone();
tool.new_instance(seat.deref(), desc, move |desc, status| {
if let Some(ref mut cursor_callback) = inner.borrow_mut().cursor_callback {
cursor_callback(desc, status);
}
});
}
inner.instances.push(seat.deref().clone());
let inner = self.inner.clone();
seat.assign_destructor(Filter::new(move |seat: ZwpTabletSeatV2, _, _| {
inner
.borrow_mut()
.instances
.retain(|i| !i.as_ref().equals(&seat.as_ref()));
}));
}
/// Add a callback to SetCursor event
pub fn on_cursor_surface<F>(&mut self, cb: F)
where
F: FnMut(&TabletToolDescriptor, CursorImageStatus) + 'static,
{
self.inner.borrow_mut().cursor_callback = Some(Box::new(cb));
}
/// Add a new tablet to a seat.
///
/// You can either add tablet on [LibinputEvent::NewDevice](crate::backend::libinput::LibinputEvent::NewDevice) event,
/// or you can add tablet based on tool event, then clients will not know about devices that are not being used
///
/// Returns new [TabletHandle] if tablet was not know by this seat, if tablet was allready know it returns exsisting handle.
pub fn add_tablet(&self, tablet_desc: &TabletDescriptor) -> TabletHandle {
let inner = &mut *self.inner.borrow_mut();
let tablets = &mut inner.tablets;
let instances = &inner.instances;
let tablet = tablets.entry(tablet_desc.clone()).or_insert_with(|| {
let mut tablet = TabletHandle::default();
// Create new tablet instance for every seat instance
for seat in instances.iter() {
tablet.new_instance(seat, tablet_desc);
}
tablet
});
tablet.clone()
}
/// Get a handle to a tablet
pub fn get_tablet(&self, tablet_desc: &TabletDescriptor) -> Option<TabletHandle> {
self.inner.borrow().tablets.get(tablet_desc).cloned()
}
/// Count all tablet devices
pub fn count_tablets(&self) -> usize {
self.inner.borrow_mut().tablets.len()
}
/// Remove tablet device
///
/// Called when tablet is no longer avalible
/// For example on [LibinputEvent::RemovedDevice](crate::backend::libinput::LibinputEvent::RemovedDevice) event.
pub fn remove_tablet(&self, tablet_desc: &TabletDescriptor) {
self.inner.borrow_mut().tablets.remove(tablet_desc);
}
/// Remove all tablet devices
pub fn clear_tablets(&self) {
self.inner.borrow_mut().tablets.clear();
}
/// Add a new tool to a seat.
///
/// Tool is usually added on [TabletToolProximityEvent](crate::backend::input::InputEvent::TabletToolProximity) event.
///
/// Returns new [TabletToolHandle] if tool was not know by this seat, if tool was allready know it returns exsisting handle,
/// it allows you to send tool input events to clients.
pub fn add_tool(&self, tool_desc: &TabletToolDescriptor) -> TabletToolHandle {
let inner = &mut *self.inner.borrow_mut();
let tools = &mut inner.tools;
let instances = &inner.instances;
let tool = tools.entry(tool_desc.clone()).or_insert_with(|| {
let mut tool = TabletToolHandle::default();
// Create new tool instance for every seat instance
for seat in instances.iter() {
let inner = self.inner.clone();
tool.new_instance(seat.deref(), tool_desc, move |desc, status| {
if let Some(ref mut cursor_callback) = inner.borrow_mut().cursor_callback {
cursor_callback(desc, status);
}
});
}
tool
});
tool.clone()
}
/// Get a handle to a tablet tool
pub fn get_tool(&self, tool_desc: &TabletToolDescriptor) -> Option<TabletToolHandle> {
self.inner.borrow().tools.get(tool_desc).cloned()
}
/// Count all tablet tool devices
pub fn count_tools(&self) -> usize {
self.inner.borrow_mut().tools.len()
}
/// Remove tablet tool device
///
/// Policy of tool removal is a compositor-specific.
///
/// One posible policy would be to remove a tool when all tablets the tool was used on are removed.
pub fn remove_tool(&self, tool_desc: &TabletToolDescriptor) {
self.inner.borrow_mut().tools.remove(tool_desc);
}
/// Remove all tablet tool devices
pub fn clear_tools(&self) {
self.inner.borrow_mut().tools.clear();
}
}

View File

@ -0,0 +1,477 @@
use std::ops::Deref as _;
use std::sync::Mutex;
use std::{cell::RefCell, rc::Rc};
use crate::backend::input::{ButtonState, TabletToolDescriptor, TabletToolType};
use crate::wayland::seat::{CursorImageAttributes, CursorImageStatus};
use wayland_protocols::unstable::tablet::v2::server::{
zwp_tablet_seat_v2::ZwpTabletSeatV2,
zwp_tablet_tool_v2::{self, ZwpTabletToolV2},
};
use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::Filter;
use crate::wayland::{compositor, Serial};
use super::tablet::TabletHandle;
static CURSOR_IMAGE_ROLE: &str = "cursor_image";
#[derive(Debug, Default)]
struct TabletTool {
instances: Vec<ZwpTabletToolV2>,
focus: Option<WlSurface>,
is_down: bool,
pending_pressure: Option<f64>,
pending_distance: Option<f64>,
pending_tilt: Option<(f64, f64)>,
pending_slider: Option<f64>,
pending_rotation: Option<f64>,
pending_wheel: Option<(f64, i32)>,
}
impl TabletTool {
fn proximity_in(
&mut self,
(x, y): (f64, f64),
(focus, (sx, sy)): (WlSurface, (f64, f64)),
tablet: &TabletHandle,
serial: Serial,
time: u32,
) {
let wl_tool = self
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.as_ref()));
if let Some(wl_tool) = wl_tool {
tablet.with_focused_tablet(&focus, |wl_tablet| {
wl_tool.proximity_in(serial.into(), wl_tablet, &focus);
// proximity_in has to be followed by motion event (required by protocol)
wl_tool.motion(x - sx, y - sy);
wl_tool.frame(time);
});
}
self.focus = Some(focus.clone());
}
fn proximity_out(&mut self, time: u32) {
if let Some(ref focus) = self.focus {
let wl_tool = self
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.as_ref()));
if let Some(wl_tool) = wl_tool {
if self.is_down {
wl_tool.up();
self.is_down = false;
}
wl_tool.proximity_out();
wl_tool.frame(time);
}
}
self.focus = None;
}
fn tip_down(&mut self, serial: Serial, time: u32) {
if let Some(ref focus) = self.focus {
if let Some(wl_tool) = self
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.as_ref()))
{
if !self.is_down {
wl_tool.down(serial.into());
wl_tool.frame(time);
}
}
}
self.is_down = true;
}
fn tip_up(&mut self, time: u32) {
if let Some(ref focus) = self.focus {
if let Some(wl_tool) = self
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.as_ref()))
{
if self.is_down {
wl_tool.up();
wl_tool.frame(time);
}
}
}
self.is_down = false;
}
fn motion(
&mut self,
pos: (f64, f64),
focus: Option<(WlSurface, (f64, f64))>,
tablet: &TabletHandle,
serial: Serial,
time: u32,
) {
match (focus, self.focus.as_ref()) {
(Some(focus), Some(prev_focus)) => {
if &focus.0 == prev_focus {
if let Some(wl_tool) = self
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.0.as_ref()))
{
let (x, y) = pos;
let (sx, sy) = focus.1;
wl_tool.motion(x - sx, y - sy);
if let Some(pressure) = self.pending_pressure.take() {
wl_tool.pressure((pressure * 65535.0).round() as u32);
}
if let Some(distance) = self.pending_distance.take() {
wl_tool.distance((distance * 65535.0).round() as u32);
}
if let Some((x, y)) = self.pending_tilt.take() {
wl_tool.tilt(x, y);
}
if let Some(slider) = self.pending_slider.take() {
wl_tool.slider((slider * 65535.0).round() as i32);
}
if let Some(rotation) = self.pending_rotation.take() {
wl_tool.rotation(rotation);
}
if let Some((degrees, clicks)) = self.pending_wheel.take() {
wl_tool.wheel(degrees, clicks)
}
wl_tool.frame(time);
}
} else {
// If surface has changed
// Unfocus previous surface
self.proximity_out(time);
// Focuss a new one
self.proximity_in(pos, focus, tablet, serial, time)
}
}
// New surface in focus
(Some(focus), None) => self.proximity_in(pos, focus, tablet, serial, time),
// No surface in focus
(None, _) => self.proximity_out(time),
}
}
fn pressure(&mut self, pressure: f64) {
self.pending_pressure = Some(pressure);
}
fn distance(&mut self, distance: f64) {
self.pending_distance = Some(distance);
}
fn tilt(&mut self, tilt: (f64, f64)) {
self.pending_tilt = Some(tilt);
}
fn rotation(&mut self, rotation: f64) {
self.pending_rotation = Some(rotation);
}
fn slider_position(&mut self, slider: f64) {
self.pending_slider = Some(slider);
}
fn wheel(&mut self, degrees: f64, clicks: i32) {
self.pending_wheel = Some((degrees, clicks));
}
/// Sent whenever a button on the tool is pressed or released.
fn button(&self, button: u32, state: ButtonState, serial: Serial, time: u32) {
if let Some(ref focus) = self.focus {
if let Some(wl_tool) = self
.instances
.iter()
.find(|i| i.as_ref().same_client_as(focus.as_ref()))
{
wl_tool.button(serial.into(), button, state.into());
wl_tool.frame(time);
}
}
}
}
impl Drop for TabletTool {
fn drop(&mut self) {
for instance in self.instances.iter() {
// This event is sent when the tool is removed from the system and will send no further events.
instance.removed();
}
}
}
/// Handle to a tablet tool device
///
/// TabletTool represents a physical tool that has been, or is currently in use with a tablet in seat.
///
/// A TabletTool relation to a physical tool depends on the tablet's ability to report serial numbers. If the tablet supports this capability, then the object represents a specific physical tool and can be identified even when used on multiple tablets.
#[derive(Debug, Default, Clone)]
pub struct TabletToolHandle {
inner: Rc<RefCell<TabletTool>>,
}
impl TabletToolHandle {
pub(super) fn new_instance<F>(&mut self, seat: &ZwpTabletSeatV2, tool: &TabletToolDescriptor, mut cb: F)
where
F: FnMut(&TabletToolDescriptor, CursorImageStatus) + 'static,
{
if let Some(client) = seat.as_ref().client() {
let wl_tool = client
.create_resource::<ZwpTabletToolV2>(seat.as_ref().version())
.unwrap();
let desc = tool.clone();
let inner = self.inner.clone();
wl_tool.quick_assign(move |tool, req, _| {
use wayland_protocols::unstable::tablet::v2::server::zwp_tablet_tool_v2::Request;
match req {
Request::SetCursor {
surface,
hotspot_x,
hotspot_y,
..
} => {
let inner = inner.borrow();
if let Some(ref focus) = inner.focus {
if focus.as_ref().same_client_as(&tool.as_ref()) {
if let Some(surface) = surface {
// tolerate re-using the same surface
if compositor::give_role(&surface, CURSOR_IMAGE_ROLE).is_err()
&& compositor::get_role(&surface) != Some(CURSOR_IMAGE_ROLE)
{
tool.as_ref().post_error(
zwp_tablet_tool_v2::Error::Role as u32,
"Given wl_surface has another role.".into(),
);
return;
}
compositor::with_states(&surface, |states| {
states.data_map.insert_if_missing_threadsafe(|| {
Mutex::new(CursorImageAttributes { hotspot: (0, 0) })
});
states
.data_map
.get::<Mutex<CursorImageAttributes>>()
.unwrap()
.lock()
.unwrap()
.hotspot = (hotspot_x, hotspot_y);
})
.unwrap();
cb(&desc, CursorImageStatus::Image(surface));
} else {
cb(&desc, CursorImageStatus::Hidden);
};
}
}
}
Request::Destroy => {
// Handled by our destructor
}
_ => {}
}
});
let inner = self.inner.clone();
wl_tool.assign_destructor(Filter::new(move |instance: ZwpTabletToolV2, _, _| {
inner
.borrow_mut()
.instances
.retain(|i| !i.as_ref().equals(&instance.as_ref()));
}));
seat.tool_added(&wl_tool);
wl_tool._type(tool.tool_type.into());
let high: u32 = (tool.hardware_serial >> 16) as u32;
let low: u32 = tool.hardware_serial as u32;
wl_tool.hardware_serial(high, low);
let high: u32 = (tool.hardware_id_wacom >> 16) as u32;
let low: u32 = tool.hardware_id_wacom as u32;
wl_tool.hardware_id_wacom(high, low);
if tool.capabilitys.pressure {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Pressure);
}
if tool.capabilitys.distance {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Distance);
}
if tool.capabilitys.tilt {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Tilt);
}
if tool.capabilitys.slider {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Slider);
}
if tool.capabilitys.rotation {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Rotation);
}
if tool.capabilitys.wheel {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Wheel);
}
wl_tool.done();
self.inner.borrow_mut().instances.push(wl_tool.deref().clone());
}
}
/// Notifify that this tool is focused on a certain surface.
///
/// You provide the location of the tool, in the form of:
///
/// - The coordinates of the tool in the global compositor space
/// - The surface on top of which the tool is, and the coordinates of its
/// origin in the global compositor space.
pub fn proximity_in(
&self,
pos: (f64, f64),
focus: (WlSurface, (f64, f64)),
tablet: &TabletHandle,
serial: Serial,
time: u32,
) {
self.inner
.borrow_mut()
.proximity_in(pos, focus, tablet, serial, time)
}
/// Notifify that this tool has left proximity.
pub fn proximity_out(&self, time: u32) {
self.inner.borrow_mut().proximity_out(time);
}
/// Tablet tool is making contact
pub fn tip_down(&self, serial: Serial, time: u32) {
self.inner.borrow_mut().tip_down(serial, time);
}
/// Tablet tool is no longer making contact
pub fn tip_up(&self, time: u32) {
self.inner.borrow_mut().tip_up(time);
}
/// Notify that the tool moved
///
/// You provide the new location of the tool, in the form of:
///
/// - The coordinates of the tool in the global compositor space
/// - The surface on top of which the tool is, and the coordinates of its
/// origin in the global compositor space (or `None` of the pointer is not
/// on top of a client surface).
///
/// This will internally take care of notifying the appropriate client objects
/// of proximity_in/proximity_out events.
pub fn motion(
&self,
pos: (f64, f64),
focus: Option<(WlSurface, (f64, f64))>,
tablet: &TabletHandle,
serial: Serial,
time: u32,
) {
self.inner.borrow_mut().motion(pos, focus, tablet, serial, time)
}
/// Queue tool pressure update
///
/// It will be sent alongside next motion event
pub fn pressure(&self, pressure: f64) {
self.inner.borrow_mut().pressure(pressure);
}
/// Queue tool distance update
///
/// It will be sent alongside next motion event
pub fn distance(&self, distance: f64) {
self.inner.borrow_mut().distance(distance);
}
/// Queue tool tilt update
///
/// It will be sent alongside next motion event
pub fn tilt(&self, tilt: (f64, f64)) {
self.inner.borrow_mut().tilt(tilt);
}
/// Queue tool rotation update
///
/// It will be sent alongside next motion event
pub fn rotation(&self, rotation: f64) {
self.inner.borrow_mut().rotation(rotation);
}
/// Queue tool slider update
///
/// It will be sent alongside next motion event
pub fn slider_position(&self, slider: f64) {
self.inner.borrow_mut().slider_position(slider);
}
/// Queue tool wheel update
///
/// It will be sent alongside next motion event
pub fn wheel(&self, degrees: f64, clicks: i32) {
self.inner.borrow_mut().wheel(degrees, clicks);
}
/// Button on the tool was pressed or released
pub fn button(&self, button: u32, state: ButtonState, serial: Serial, time: u32) {
self.inner.borrow().button(button, state, serial, time);
}
}
impl From<TabletToolType> for zwp_tablet_tool_v2::Type {
fn from(from: TabletToolType) -> zwp_tablet_tool_v2::Type {
match from {
TabletToolType::Pen => zwp_tablet_tool_v2::Type::Pen,
TabletToolType::Eraser => zwp_tablet_tool_v2::Type::Eraser,
TabletToolType::Brush => zwp_tablet_tool_v2::Type::Brush,
TabletToolType::Pencil => zwp_tablet_tool_v2::Type::Pencil,
TabletToolType::Airbrush => zwp_tablet_tool_v2::Type::Airbrush,
TabletToolType::Mouse => zwp_tablet_tool_v2::Type::Mouse,
TabletToolType::Lens => zwp_tablet_tool_v2::Type::Lens,
_ => zwp_tablet_tool_v2::Type::Pen,
}
}
}
impl From<ButtonState> for zwp_tablet_tool_v2::ButtonState {
fn from(from: ButtonState) -> zwp_tablet_tool_v2::ButtonState {
match from {
ButtonState::Pressed => zwp_tablet_tool_v2::ButtonState::Pressed,
ButtonState::Released => zwp_tablet_tool_v2::ButtonState::Released,
}
}
}