wayland.tablet: Add tablet manager protocol
This commit is contained in:
parent
1c9e3fe903
commit
90a62aeae7
|
@ -62,6 +62,7 @@ pub mod output;
|
|||
pub mod seat;
|
||||
pub mod shell;
|
||||
pub mod shm;
|
||||
pub mod tablet_manager;
|
||||
|
||||
/// A global [`SerialCounter`] for use in your compositor.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue