Apply suggestions from code review & Rebase

This commit is contained in:
Poly 2021-07-05 23:57:25 +02:00
parent c557adc6a6
commit 07743faad2
10 changed files with 145 additions and 136 deletions

View File

@ -16,7 +16,7 @@ use smithay::{
wayland::{ wayland::{
output::Mode, output::Mode,
seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState}, seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState},
tablet_manager::TabletSeatTrait, tablet_manager::{TabletDescriptor, TabletSeatTrait},
SERIAL_COUNTER as SCOUNTER, SERIAL_COUNTER as SCOUNTER,
}, },
}; };
@ -246,14 +246,16 @@ impl AnvilState<UdevData> {
InputEvent::TabletToolButton { event, .. } => self.on_tablet_button::<B>(event), InputEvent::TabletToolButton { event, .. } => self.on_tablet_button::<B>(event),
InputEvent::DeviceAdded { device } => { InputEvent::DeviceAdded { device } => {
if device.has_capability(DeviceCapability::TabletTool) { if device.has_capability(DeviceCapability::TabletTool) {
self.seat.tablet_seat().add_tablet(&device.into()); self.seat
.tablet_seat()
.add_tablet(&TabletDescriptor::from(&device));
} }
} }
InputEvent::DeviceRemoved { device } => { InputEvent::DeviceRemoved { device } => {
if device.has_capability(DeviceCapability::TabletTool) { if device.has_capability(DeviceCapability::TabletTool) {
let tablet_seat = self.seat.tablet_seat(); let tablet_seat = self.seat.tablet_seat();
tablet_seat.remove_tablet(&device.into()); tablet_seat.remove_tablet(&TabletDescriptor::from(&device));
// If there are no tablets in seat we can remove all tools // If there are no tablets in seat we can remove all tools
if tablet_seat.count_tablets() == 0 { if tablet_seat.count_tablets() == 0 {
@ -283,68 +285,84 @@ impl AnvilState<UdevData> {
} }
fn on_tablet_tool_axis<B: InputBackend>(&mut self, evt: B::TabletToolAxisEvent) { fn on_tablet_tool_axis<B: InputBackend>(&mut self, evt: B::TabletToolAxisEvent) {
if let Some(out) = self.backend_data.output_map.first() { let output_map = self.output_map.borrow();
self.pointer_location = evt.position_transformed(out.size); let pointer_location = &mut self.pointer_location;
let tablet_seat = self.seat.tablet_seat();
let window_map = self.window_map.borrow();
let under = self.window_map.borrow().get_surface_under(self.pointer_location); output_map
let tablet = self.seat.tablet_seat().get_tablet(&evt.device().into()); .with_primary(|_, rect| {
let tool = self.seat.tablet_seat().get_tool(&evt.tool()); pointer_location.0 = evt.x_transformed(rect.width as u32) + rect.x as f64;
pointer_location.1 = evt.y_transformed(rect.height as u32) + rect.y as f64;
if let (Some(tablet), Some(tool)) = (tablet, tool) { let under = window_map.get_surface_under(*pointer_location);
if evt.pressure_has_changed() { let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device()));
tool.pressure(evt.pressure()); let tool = tablet_seat.get_tool(&evt.tool());
}
if evt.distance_has_changed() {
tool.distance(evt.distance());
}
if evt.tilt_has_changed() {
tool.tilt(evt.tilt());
}
if evt.slider_has_changed() {
tool.slider_position(evt.slider_position());
}
if evt.rotation_has_changed() {
tool.rotation(evt.rotation());
}
if evt.wheel_has_changed() {
tool.wheel(evt.wheel_delta(), evt.wheel_delta_discrete());
}
tool.motion( if let (Some(tablet), Some(tool)) = (tablet, tool) {
self.pointer_location, if evt.pressure_has_changed() {
under, tool.pressure(evt.pressure());
&tablet, }
SCOUNTER.next_serial(), if evt.distance_has_changed() {
evt.time(), tool.distance(evt.distance());
); }
} if evt.tilt_has_changed() {
} tool.tilt(evt.tilt());
} }
if evt.slider_has_changed() {
tool.slider_position(evt.slider_position());
}
if evt.rotation_has_changed() {
tool.rotation(evt.rotation());
}
if evt.wheel_has_changed() {
tool.wheel(evt.wheel_delta(), evt.wheel_delta_discrete());
}
fn on_tablet_tool_proximity<B: InputBackend>(&mut self, evt: B::TabletToolProximityEvent) { tool.motion(
if let Some(out) = self.backend_data.output_map.first() { *pointer_location,
let tool = evt.tool();
self.seat.tablet_seat().add_tool(&tool);
self.pointer_location = evt.position_transformed(out.size);
let under = self.window_map.borrow().get_surface_under(self.pointer_location);
let tablet = self.seat.tablet_seat().get_tablet(&evt.device().into());
let tool = self.seat.tablet_seat().get_tool(&tool);
if let (Some(under), Some(tablet), Some(tool)) = (under, tablet, tool) {
match evt.state() {
ProximityState::In => tool.proximity_in(
self.pointer_location,
under, under,
&tablet, &tablet,
SCOUNTER.next_serial(), SCOUNTER.next_serial(),
evt.time(), evt.time(),
), );
ProximityState::Out => tool.proximity_out(evt.time()),
} }
} })
} .unwrap();
}
fn on_tablet_tool_proximity<B: InputBackend>(&mut self, evt: B::TabletToolProximityEvent) {
let output_map = self.output_map.borrow();
let pointer_location = &mut self.pointer_location;
let tablet_seat = self.seat.tablet_seat();
let window_map = self.window_map.borrow();
output_map
.with_primary(|_, rect| {
let tool = evt.tool();
tablet_seat.add_tool(&tool);
pointer_location.0 = evt.x_transformed(rect.width as u32) + rect.x as f64;
pointer_location.1 = evt.y_transformed(rect.height as u32) + rect.y as f64;
let under = window_map.get_surface_under(*pointer_location);
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device()));
let tool = tablet_seat.get_tool(&tool);
if let (Some(under), Some(tablet), Some(tool)) = (under, tablet, tool) {
match evt.state() {
ProximityState::In => tool.proximity_in(
*pointer_location,
under,
&tablet,
SCOUNTER.next_serial(),
evt.time(),
),
ProximityState::Out => tool.proximity_out(evt.time()),
}
}
})
.unwrap();
} }
fn on_tablet_tool_tip<B: InputBackend>(&mut self, evt: B::TabletToolTipEvent) { fn on_tablet_tool_tip<B: InputBackend>(&mut self, evt: B::TabletToolTipEvent) {

View File

@ -20,11 +20,8 @@ pub trait Device: PartialEq + Eq + std::hash::Hash {
/// Test if this device has a specific capability /// Test if this device has a specific capability
fn has_capability(&self, capability: DeviceCapability) -> bool; fn has_capability(&self, capability: DeviceCapability) -> bool;
/// Get the product ID for this device. /// Returns device USB (product,vendor) id
fn id_product(&self) -> Option<u32>; fn usb_id(&self) -> Option<(u32, u32)>;
/// Get the vendor ID for this device.
fn id_vendor(&self) -> Option<u32>;
/// Returns the syspath of the device. /// Returns the syspath of the device.
/// ///

View File

@ -1,4 +1,5 @@
use super::{ButtonState, Event, InputBackend, UnusedEvent}; use super::{ButtonState, Event, InputBackend, UnusedEvent};
use bitflags::bitflags;
/// Description of physical tablet tool /// Description of physical tablet tool
#[derive(Debug, Clone, Hash, Eq, PartialEq)] #[derive(Debug, Clone, Hash, Eq, PartialEq)]
@ -35,23 +36,24 @@ pub enum TabletToolType {
Totem, Totem,
} }
/// Describes extra capabilities on a tablet. bitflags! {
/// /// Describes extra capabilities on a tablet.
/// Any tool must provide x and y values, extra axes are device-specific. ///
#[derive(Debug, Clone, Hash, Eq, PartialEq)] /// Any tool must provide x and y values, extra axes are device-specific.
pub struct TabletToolCapabilitys { pub struct TabletToolCapabilitys: u32 {
/// Tilt axes /// Tilt axes
pub tilt: bool, const TILT = 1;
/// Pressure axis /// Pressure axis
pub pressure: bool, const PRESSURE = 2;
/// Distance axis /// Distance axis
pub distance: bool, const DISTANCE = 4;
/// Z-rotation axis /// Z-rotation axis
pub rotation: bool, const ROTATION = 16;
/// Slider axis /// Slider axis
pub slider: bool, const SLIDER = 32;
/// Wheel axis /// Wheel axis
pub wheel: bool, const WHEEL = 64;
}
} }
/// Tablet tool event /// Tablet tool event
@ -110,7 +112,7 @@ pub trait TabletToolEvent<B: InputBackend> {
/// Return the current absolute Y coordinate of the tablet tool event, transformed to screen coordinates. /// Return the current absolute Y coordinate of the tablet tool event, transformed to screen coordinates.
fn y_transformed(&self, height: u32) -> f64; fn y_transformed(&self, height: u32) -> f64;
/// Returns the current pressure being applied on the tool in use, normalized to the range [0, 1]. /// Returns the current distance from the tablet's sensor, normalized to the range [0, 1]
/// ///
/// If this axis does not exist on the current tool, this function returns 0. /// If this axis does not exist on the current tool, this function returns 0.
fn distance(&self) -> f64; fn distance(&self) -> f64;
@ -118,7 +120,7 @@ pub trait TabletToolEvent<B: InputBackend> {
/// Check if the distance axis was updated in this event. /// Check if the distance axis was updated in this event.
fn distance_has_changed(&self) -> bool; fn distance_has_changed(&self) -> bool;
/// Returns the current distance from the tablet's sensor, normalized to the range [0, 1] /// Returns the current pressure being applied on the tool in use, normalized to the range [0, 1].
/// ///
/// If this axis does not exist on the current tool, this function returns 0. /// If this axis does not exist on the current tool, this function returns 0.
fn pressure(&self) -> f64; fn pressure(&self) -> f64;
@ -168,7 +170,8 @@ pub trait TabletToolEvent<B: InputBackend> {
/// Returns the current z rotation of the tool in degrees, clockwise from the tool's logical neutral position. /// Returns the current z rotation of the tool in degrees, clockwise from the tool's logical neutral position.
/// ///
/// For tools of type Mouse and Lens the logical neutral position is pointing to the current logical north of the tablet. For tools of type Brush, the logical neutral position is with the buttons pointing up. /// For tools of type Mouse and Lens the logical neutral position is pointing to the current logical north of the tablet.
/// For tools of type Brush, the logical neutral position is with the buttons pointing up.
/// ///
/// If this axis does not exist on the current tool, this function returns 0. /// If this axis does not exist on the current tool, this function returns 0.
fn rotation(&self) -> f64; fn rotation(&self) -> f64;
@ -264,7 +267,8 @@ impl<B: InputBackend> TabletToolAxisEvent<B> for UnusedEvent {}
/// detectable distance of the tablet device. A tool that is out of proximity cannot /// detectable distance of the tablet device. A tool that is out of proximity cannot
/// generate events. /// generate events.
/// ///
/// On some hardware a tool goes out of proximity when it ceases to touch the surface. On /// other hardware, the tool is still detectable within a short distance (a few cm) off /// On some hardware a tool goes out of proximity when it ceases to touch the surface. On
/// other hardware, the tool is still detectable within a short distance (a few cm) off
/// the surface. /// the surface.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ProximityState { pub enum ProximityState {
@ -339,7 +343,8 @@ pub trait TabletToolButtonEvent<B: InputBackend>: TabletToolEvent<B> + Event<B>
/// Return the button that triggered this event. /// Return the button that triggered this event.
fn button(&self) -> u32; fn button(&self) -> u32;
/// For the button of a TabletToolButtonEvent, return the total number of buttons pressed on all devices on the associated seat after the the event was triggered. /// For the button of a TabletToolButtonEvent,
/// return the total number of buttons pressed on all devices on the associated seat after the the event was triggered.
fn seat_button_count(&self) -> u32; fn seat_button_count(&self) -> u32;
/// Return the button state of the event. /// Return the button state of the event.

View File

@ -97,12 +97,11 @@ impl backend::Device for libinput::Device {
libinput::Device::has_capability(self, capability.into()) libinput::Device::has_capability(self, capability.into())
} }
fn id_product(&self) -> Option<u32> { fn usb_id(&self) -> Option<(u32, u32)> {
Some(libinput::Device::id_product(self)) Some((
} libinput::Device::id_product(self),
libinput::Device::id_vendor(self),
fn id_vendor(&self) -> Option<u32> { ))
Some(libinput::Device::id_vendor(self))
} }
fn syspath(&self) -> Option<PathBuf> { fn syspath(&self) -> Option<PathBuf> {

View File

@ -9,16 +9,16 @@ use input::event::{tablet_tool, EventTrait};
use super::LibinputInputBackend; use super::LibinputInputBackend;
/// Marker for tablet tool events /// Marker for tablet tool events
pub trait IsTabetEvent: tablet_tool::TabletToolEventTrait + EventTrait {} pub trait IsTabletEvent: tablet_tool::TabletToolEventTrait + EventTrait {}
impl IsTabetEvent for tablet_tool::TabletToolAxisEvent {} impl IsTabletEvent for tablet_tool::TabletToolAxisEvent {}
impl IsTabetEvent for tablet_tool::TabletToolProximityEvent {} impl IsTabletEvent for tablet_tool::TabletToolProximityEvent {}
impl IsTabetEvent for tablet_tool::TabletToolTipEvent {} impl IsTabletEvent for tablet_tool::TabletToolTipEvent {}
impl IsTabetEvent for tablet_tool::TabletToolButtonEvent {} impl IsTabletEvent for tablet_tool::TabletToolButtonEvent {}
impl<E> backend::Event<LibinputInputBackend> for E impl<E> backend::Event<LibinputInputBackend> for E
where where
E: IsTabetEvent, E: IsTabletEvent,
{ {
fn time(&self) -> u32 { fn time(&self) -> u32 {
tablet_tool::TabletToolEventTrait::time(self) tablet_tool::TabletToolEventTrait::time(self)
@ -51,7 +51,7 @@ impl backend::TabletToolTipEvent<LibinputInputBackend> for tablet_tool::TabletTo
impl<E> backend::TabletToolEvent<LibinputInputBackend> for E impl<E> backend::TabletToolEvent<LibinputInputBackend> for E
where where
E: IsTabetEvent + event::EventTrait, E: IsTabletEvent + event::EventTrait,
{ {
fn tool(&self) -> TabletToolDescriptor { fn tool(&self) -> TabletToolDescriptor {
let tool = self.tool(); let tool = self.tool();
@ -70,14 +70,14 @@ where
let hardware_serial = tool.serial(); let hardware_serial = tool.serial();
let hardware_id_wacom = tool.tool_id(); let hardware_id_wacom = tool.tool_id();
let capabilitys = TabletToolCapabilitys { let mut capabilitys = TabletToolCapabilitys::empty();
tilt: tool.has_tilt(),
pressure: tool.has_pressure(), capabilitys.set(TabletToolCapabilitys::TILT, tool.has_tilt());
distance: tool.has_distance(), capabilitys.set(TabletToolCapabilitys::PRESSURE, tool.has_pressure());
rotation: tool.has_rotation(), capabilitys.set(TabletToolCapabilitys::DISTANCE, tool.has_distance());
slider: tool.has_slider(), capabilitys.set(TabletToolCapabilitys::ROTATION, tool.has_rotation());
wheel: tool.has_wheel(), capabilitys.set(TabletToolCapabilitys::SLIDER, tool.has_slider());
}; capabilitys.set(TabletToolCapabilitys::WHEEL, tool.has_wheel());
TabletToolDescriptor { TabletToolDescriptor {
tool_type, tool_type,

View File

@ -308,11 +308,7 @@ impl Device for WinitVirtualDevice {
) )
} }
fn id_product(&self) -> Option<u32> { fn usb_id(&self) -> Option<(u32, u32)> {
None
}
fn id_vendor(&self) -> Option<u32> {
None None
} }

View File

@ -26,8 +26,7 @@
//! .tablet_seat() // Get TabletSeat asosiated with this seat //! .tablet_seat() // Get TabletSeat asosiated with this seat
//! .add_tablet(&TabletDescriptor { // Add a new tablet to a seat //! .add_tablet(&TabletDescriptor { // Add a new tablet to a seat
//! name: "Test".into(), //! name: "Test".into(),
//! id_product: None, //! usb_id: None,
//! id_vendor: None,
//! syspath: None, //! syspath: None,
//! }); //! });
//! //!
@ -75,11 +74,9 @@ pub fn init_tablet_manager_global(display: &mut Display) -> Global<ZwpTabletMana
user_data.insert_if_missing(TabletSeatHandle::default); user_data.insert_if_missing(TabletSeatHandle::default);
let instance = tablet_seat; let instance = tablet_seat;
let tablet_seat = user_data.get::<TabletSeatHandle>(); let tablet_seat = user_data.get::<TabletSeatHandle>().unwrap();
if let Some(tablet_seat) = tablet_seat { tablet_seat.add_instance(instance);
tablet_seat.add_instance(instance);
}
} }
zwp_tablet_manager_v2::Request::Destroy => { zwp_tablet_manager_v2::Request::Destroy => {
// Nothing to do // Nothing to do

View File

@ -14,21 +14,18 @@ use crate::backend::input::Device;
pub struct TabletDescriptor { pub struct TabletDescriptor {
/// Tablet device name /// Tablet device name
pub name: String, pub name: String,
/// Tablet device USB product id /// Tablet device USB (product,vendor) id
pub id_product: Option<u32>, pub usb_id: Option<(u32, u32)>,
/// Tablet device USB vendor id
pub id_vendor: Option<u32>,
/// Path to the device /// Path to the device
pub syspath: Option<PathBuf>, pub syspath: Option<PathBuf>,
} }
impl<D: Device> From<D> for TabletDescriptor { impl<D: Device> From<&D> for TabletDescriptor {
fn from(device: D) -> Self { fn from(device: &D) -> Self {
TabletDescriptor { TabletDescriptor {
name: device.name(), name: device.name(),
syspath: device.syspath(), syspath: device.syspath(),
id_product: device.id_product(), usb_id: device.usb_id(),
id_vendor: device.id_vendor(),
} }
} }
} }
@ -67,7 +64,7 @@ impl TabletHandle {
wl_tablet.name(tablet.name.clone()); wl_tablet.name(tablet.name.clone());
if let (Some(id_product), Some(id_vendor)) = (tablet.id_product, tablet.id_vendor) { if let Some((id_product, id_vendor)) = tablet.usb_id {
wl_tablet.id(id_product, id_vendor); wl_tablet.id(id_product, id_vendor);
} }

View File

@ -43,7 +43,7 @@ impl fmt::Debug for TabletSeat {
/// ///
/// TabletSeat extends `Seat` with graphic tablet specyfic functionality /// TabletSeat extends `Seat` with graphic tablet specyfic functionality
/// ///
/// TabletSeatHandle can be used to advertise avalible graphics tablets and tools to wayland clients /// TabletSeatHandle can be used to advertise available graphics tablets and tools to wayland clients
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct TabletSeatHandle { pub struct TabletSeatHandle {
inner: Rc<RefCell<TabletSeat>>, inner: Rc<RefCell<TabletSeat>>,
@ -53,12 +53,12 @@ impl TabletSeatHandle {
pub(super) fn add_instance(&self, seat: Main<ZwpTabletSeatV2>) { pub(super) fn add_instance(&self, seat: Main<ZwpTabletSeatV2>) {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
// Notify new instance about avaluble tablets // Notify new instance about available tablets
for (desc, tablet) in inner.tablets.iter_mut() { for (desc, tablet) in inner.tablets.iter_mut() {
tablet.new_instance(seat.deref(), desc); tablet.new_instance(seat.deref(), desc);
} }
// Notify new instance about avalible tools // Notify new instance about available tools
for (desc, tool) in inner.tools.iter_mut() { for (desc, tool) in inner.tools.iter_mut() {
let inner = self.inner.clone(); let inner = self.inner.clone();
tool.new_instance(seat.deref(), desc, move |desc, status| { tool.new_instance(seat.deref(), desc, move |desc, status| {
@ -89,7 +89,7 @@ impl TabletSeatHandle {
/// Add a new tablet to a seat. /// Add a new tablet to a seat.
/// ///
/// You can either add tablet on [LibinputEvent::NewDevice](crate::backend::libinput::LibinputEvent::NewDevice) event, /// You can either add tablet on [input::Event::DeviceAdded](crate::backend::input::InputEvent::DeviceAdded) event,
/// or you can add tablet based on tool event, then clients will not know about devices that are not being used /// 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. /// Returns new [TabletHandle] if tablet was not know by this seat, if tablet was allready know it returns exsisting handle.
@ -124,7 +124,7 @@ impl TabletSeatHandle {
/// Remove tablet device /// Remove tablet device
/// ///
/// Called when tablet is no longer avalible /// Called when tablet is no longer avalible
/// For example on [LibinputEvent::RemovedDevice](crate::backend::libinput::LibinputEvent::RemovedDevice) event. /// For example on [input::Event::DeviceRemoved](crate::backend::input::InputEvent::DeviceRemoved) event.
pub fn remove_tablet(&self, tablet_desc: &TabletDescriptor) { pub fn remove_tablet(&self, tablet_desc: &TabletDescriptor) {
self.inner.borrow_mut().tablets.remove(tablet_desc); self.inner.borrow_mut().tablets.remove(tablet_desc);
} }

View File

@ -2,7 +2,7 @@ use std::ops::Deref as _;
use std::sync::Mutex; use std::sync::Mutex;
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::backend::input::{ButtonState, TabletToolDescriptor, TabletToolType}; use crate::backend::input::{ButtonState, TabletToolCapabilitys, TabletToolDescriptor, TabletToolType};
use crate::wayland::seat::{CursorImageAttributes, CursorImageStatus}; use crate::wayland::seat::{CursorImageAttributes, CursorImageStatus};
use wayland_protocols::unstable::tablet::v2::server::{ use wayland_protocols::unstable::tablet::v2::server::{
zwp_tablet_seat_v2::ZwpTabletSeatV2, zwp_tablet_seat_v2::ZwpTabletSeatV2,
@ -318,27 +318,27 @@ impl TabletToolHandle {
let low: u32 = tool.hardware_id_wacom as u32; let low: u32 = tool.hardware_id_wacom as u32;
wl_tool.hardware_id_wacom(high, low); wl_tool.hardware_id_wacom(high, low);
if tool.capabilitys.pressure { if tool.capabilitys.contains(TabletToolCapabilitys::PRESSURE) {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Pressure); wl_tool.capability(zwp_tablet_tool_v2::Capability::Pressure);
} }
if tool.capabilitys.distance { if tool.capabilitys.contains(TabletToolCapabilitys::DISTANCE) {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Distance); wl_tool.capability(zwp_tablet_tool_v2::Capability::Distance);
} }
if tool.capabilitys.tilt { if tool.capabilitys.contains(TabletToolCapabilitys::TILT) {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Tilt); wl_tool.capability(zwp_tablet_tool_v2::Capability::Tilt);
} }
if tool.capabilitys.slider { if tool.capabilitys.contains(TabletToolCapabilitys::SLIDER) {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Slider); wl_tool.capability(zwp_tablet_tool_v2::Capability::Slider);
} }
if tool.capabilitys.rotation { if tool.capabilitys.contains(TabletToolCapabilitys::ROTATION) {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Rotation); wl_tool.capability(zwp_tablet_tool_v2::Capability::Rotation);
} }
if tool.capabilitys.wheel { if tool.capabilitys.contains(TabletToolCapabilitys::WHEEL) {
wl_tool.capability(zwp_tablet_tool_v2::Capability::Wheel); wl_tool.capability(zwp_tablet_tool_v2::Capability::Wheel);
} }