commit
70826c9d2c
|
@ -21,7 +21,7 @@ drm-fourcc = "^2.1.1"
|
||||||
drm = { version = "0.4.0", optional = true }
|
drm = { version = "0.4.0", optional = true }
|
||||||
drm-ffi = { version = "0.1.0", optional = true }
|
drm-ffi = { version = "0.1.0", optional = true }
|
||||||
gbm = { version = "0.6.0", optional = true, default-features = false, features = ["drm-support"] }
|
gbm = { version = "0.6.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||||
input = { version = "0.5", default-features = false, optional = true }
|
input = { version = "0.5", default-features = false, features=["libinput_1_14"], optional = true }
|
||||||
image = { version = "0.23.14", default-features = false, optional = true }
|
image = { version = "0.23.14", default-features = false, optional = true }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
libc = "0.2.70"
|
libc = "0.2.70"
|
||||||
|
@ -57,7 +57,7 @@ backend_gbm = ["gbm"]
|
||||||
backend_egl = ["gl_generator"]
|
backend_egl = ["gl_generator"]
|
||||||
backend_libinput = ["input"]
|
backend_libinput = ["input"]
|
||||||
backend_session = []
|
backend_session = []
|
||||||
backend_udev = ["udev"]
|
backend_udev = ["udev", "input/udev"]
|
||||||
backend_session_logind = ["dbus", "backend_session", "pkg-config"]
|
backend_session_logind = ["dbus", "backend_session", "pkg-config"]
|
||||||
backend_session_elogind = ["backend_session_logind"]
|
backend_session_elogind = ["backend_session_logind"]
|
||||||
backend_session_libseat = ["backend_session", "libseat"]
|
backend_session_libseat = ["backend_session", "libseat"]
|
||||||
|
|
|
@ -8,13 +8,15 @@ use crate::AnvilState;
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::input::{
|
backend::input::{
|
||||||
self, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent,
|
self, Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
|
||||||
PointerButtonEvent,
|
PointerAxisEvent, PointerButtonEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent,
|
||||||
|
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState,
|
||||||
},
|
},
|
||||||
reexports::wayland_server::protocol::wl_pointer,
|
reexports::wayland_server::protocol::wl_pointer,
|
||||||
wayland::{
|
wayland::{
|
||||||
output::Mode,
|
output::Mode,
|
||||||
seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState},
|
seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState},
|
||||||
|
tablet_manager::{TabletDescriptor, TabletSeatTrait},
|
||||||
SERIAL_COUNTER as SCOUNTER,
|
SERIAL_COUNTER as SCOUNTER,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -81,7 +83,7 @@ impl<Backend> AnvilState<Backend> {
|
||||||
input::MouseButton::Other(b) => b as u32,
|
input::MouseButton::Other(b) => b as u32,
|
||||||
};
|
};
|
||||||
let state = match evt.state() {
|
let state = match evt.state() {
|
||||||
input::MouseButtonState::Pressed => {
|
input::ButtonState::Pressed => {
|
||||||
// change the keyboard focus unless the pointer is grabbed
|
// change the keyboard focus unless the pointer is grabbed
|
||||||
if !self.pointer.is_grabbed() {
|
if !self.pointer.is_grabbed() {
|
||||||
let under = self
|
let under = self
|
||||||
|
@ -93,7 +95,7 @@ impl<Backend> AnvilState<Backend> {
|
||||||
}
|
}
|
||||||
wl_pointer::ButtonState::Pressed
|
wl_pointer::ButtonState::Pressed
|
||||||
}
|
}
|
||||||
input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
|
input::ButtonState::Released => wl_pointer::ButtonState::Released,
|
||||||
};
|
};
|
||||||
self.pointer.button(button, state, serial, evt.time());
|
self.pointer.button(button, state, serial, evt.time());
|
||||||
}
|
}
|
||||||
|
@ -238,6 +240,29 @@ impl AnvilState<UdevData> {
|
||||||
InputEvent::PointerMotion { event, .. } => self.on_pointer_move::<B>(event),
|
InputEvent::PointerMotion { event, .. } => self.on_pointer_move::<B>(event),
|
||||||
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
|
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
|
||||||
InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event),
|
InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event),
|
||||||
|
InputEvent::TabletToolAxis { event, .. } => self.on_tablet_tool_axis::<B>(event),
|
||||||
|
InputEvent::TabletToolProximity { event, .. } => self.on_tablet_tool_proximity::<B>(event),
|
||||||
|
InputEvent::TabletToolTip { event, .. } => self.on_tablet_tool_tip::<B>(event),
|
||||||
|
InputEvent::TabletToolButton { event, .. } => self.on_tablet_button::<B>(event),
|
||||||
|
InputEvent::DeviceAdded { device } => {
|
||||||
|
if device.has_capability(DeviceCapability::TabletTool) {
|
||||||
|
self.seat
|
||||||
|
.tablet_seat()
|
||||||
|
.add_tablet(&TabletDescriptor::from(&device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::DeviceRemoved { device } => {
|
||||||
|
if device.has_capability(DeviceCapability::TabletTool) {
|
||||||
|
let tablet_seat = self.seat.tablet_seat();
|
||||||
|
|
||||||
|
tablet_seat.remove_tablet(&TabletDescriptor::from(&device));
|
||||||
|
|
||||||
|
// If there are no tablets in seat we can remove all tools
|
||||||
|
if tablet_seat.count_tablets() == 0 {
|
||||||
|
tablet_seat.clear_tools();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// other events are not handled in anvil (yet)
|
// other events are not handled in anvil (yet)
|
||||||
}
|
}
|
||||||
|
@ -259,6 +284,127 @@ impl AnvilState<UdevData> {
|
||||||
.motion(self.pointer_location, under, serial, evt.time());
|
.motion(self.pointer_location, under, serial, evt.time());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_tablet_tool_axis<B: InputBackend>(&mut self, evt: B::TabletToolAxisEvent) {
|
||||||
|
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| {
|
||||||
|
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(&evt.tool());
|
||||||
|
|
||||||
|
if let (Some(tablet), Some(tool)) = (tablet, tool) {
|
||||||
|
if evt.pressure_has_changed() {
|
||||||
|
tool.pressure(evt.pressure());
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
*pointer_location,
|
||||||
|
under,
|
||||||
|
&tablet,
|
||||||
|
SCOUNTER.next_serial(),
|
||||||
|
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) {
|
||||||
|
let tool = self.seat.tablet_seat().get_tool(&evt.tool());
|
||||||
|
|
||||||
|
if let Some(tool) = tool {
|
||||||
|
match evt.tip_state() {
|
||||||
|
TabletToolTipState::Down => {
|
||||||
|
tool.tip_down(SCOUNTER.next_serial(), evt.time());
|
||||||
|
|
||||||
|
// change the keyboard focus unless the pointer is grabbed
|
||||||
|
if !self.pointer.is_grabbed() {
|
||||||
|
let under = self
|
||||||
|
.window_map
|
||||||
|
.borrow_mut()
|
||||||
|
.get_surface_and_bring_to_top(self.pointer_location);
|
||||||
|
|
||||||
|
let serial = SCOUNTER.next_serial();
|
||||||
|
self.keyboard
|
||||||
|
.set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TabletToolTipState::Up => {
|
||||||
|
tool.tip_up(evt.time());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_tablet_button<B: InputBackend>(&mut self, evt: B::TabletToolButtonEvent) {
|
||||||
|
let tool = self.seat.tablet_seat().get_tool(&evt.tool());
|
||||||
|
|
||||||
|
if let Some(tool) = tool {
|
||||||
|
tool.button(
|
||||||
|
evt.button(),
|
||||||
|
evt.button_state(),
|
||||||
|
SCOUNTER.next_serial(),
|
||||||
|
evt.time(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) {
|
fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) {
|
||||||
if self.output_map.borrow().is_empty() {
|
if self.output_map.borrow().is_empty() {
|
||||||
return pos;
|
return pos;
|
||||||
|
|
|
@ -16,6 +16,7 @@ use smithay::{
|
||||||
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
|
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
|
||||||
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
|
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
|
||||||
shm::init_shm_global,
|
shm::init_shm_global,
|
||||||
|
tablet_manager::{init_tablet_manager_global, TabletSeatTrait},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ pub struct AnvilState<BackendData> {
|
||||||
pub pointer_location: (f64, f64),
|
pub pointer_location: (f64, f64),
|
||||||
pub cursor_status: Arc<Mutex<CursorImageStatus>>,
|
pub cursor_status: Arc<Mutex<CursorImageStatus>>,
|
||||||
pub seat_name: String,
|
pub seat_name: String,
|
||||||
|
pub seat: Seat,
|
||||||
pub start_time: std::time::Instant,
|
pub start_time: std::time::Instant,
|
||||||
// things we must keep alive
|
// things we must keep alive
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
|
@ -121,6 +123,14 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
*cursor_status2.lock().unwrap() = new_status
|
*cursor_status2.lock().unwrap() = new_status
|
||||||
});
|
});
|
||||||
|
|
||||||
|
init_tablet_manager_global(&mut display.borrow_mut());
|
||||||
|
|
||||||
|
let cursor_status3 = cursor_status.clone();
|
||||||
|
seat.tablet_seat().on_cursor_surface(move |_tool, new_status| {
|
||||||
|
// TODO: tablet tools should have their own cursors
|
||||||
|
*cursor_status3.lock().unwrap() = new_status;
|
||||||
|
});
|
||||||
|
|
||||||
let keyboard = seat
|
let keyboard = seat
|
||||||
.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| {
|
.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| {
|
||||||
set_data_device_focus(seat, focus.and_then(|s| s.as_ref().client()))
|
set_data_device_focus(seat, focus.and_then(|s| s.as_ref().client()))
|
||||||
|
@ -159,6 +169,7 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
cursor_status,
|
cursor_status,
|
||||||
pointer_location: (0.0, 0.0),
|
pointer_location: (0.0, 0.0),
|
||||||
seat_name,
|
seat_name,
|
||||||
|
seat,
|
||||||
start_time: std::time::Instant::now(),
|
start_time: std::time::Instant::now(),
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
xwayland,
|
xwayland,
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
//! Common traits for input backends to receive input from.
|
//! Common traits for input backends to receive input from.
|
||||||
|
|
||||||
use std::error::Error;
|
use std::{error::Error, path::PathBuf};
|
||||||
|
|
||||||
|
mod tablet;
|
||||||
|
|
||||||
|
pub use tablet::{
|
||||||
|
ProximityState, TabletToolAxisEvent, TabletToolButtonEvent, TabletToolCapabilitys, TabletToolDescriptor,
|
||||||
|
TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TabletToolType,
|
||||||
|
};
|
||||||
|
|
||||||
/// Trait for generic functions every input device does provide
|
/// Trait for generic functions every input device does provide
|
||||||
pub trait Device: PartialEq + Eq + std::hash::Hash {
|
pub trait Device: PartialEq + Eq + std::hash::Hash {
|
||||||
|
@ -12,6 +19,14 @@ pub trait Device: PartialEq + Eq + std::hash::Hash {
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
/// 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;
|
||||||
|
|
||||||
|
/// Returns device USB (product,vendor) id
|
||||||
|
fn usb_id(&self) -> Option<(u32, u32)>;
|
||||||
|
|
||||||
|
/// Returns the syspath of the device.
|
||||||
|
///
|
||||||
|
/// The path is an absolute path and includes the sys mount point.
|
||||||
|
fn syspath(&self) -> Option<PathBuf>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of input types a device may provide
|
/// Set of input types a device may provide
|
||||||
|
@ -105,9 +120,9 @@ pub enum MouseButton {
|
||||||
Other(u8),
|
Other(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State of a button on a mouse. Either pressed or released
|
/// State of a button on a pointer device, like mouse or tablet tool. Either pressed or released
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum MouseButtonState {
|
pub enum ButtonState {
|
||||||
/// Button is released
|
/// Button is released
|
||||||
Released,
|
Released,
|
||||||
/// Button is pressed
|
/// Button is pressed
|
||||||
|
@ -119,7 +134,7 @@ pub trait PointerButtonEvent<B: InputBackend>: Event<B> {
|
||||||
/// Pressed button of the event
|
/// Pressed button of the event
|
||||||
fn button(&self) -> MouseButton;
|
fn button(&self) -> MouseButton;
|
||||||
/// State of the button
|
/// State of the button
|
||||||
fn state(&self) -> MouseButtonState;
|
fn state(&self) -> ButtonState;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: InputBackend> PointerButtonEvent<B> for UnusedEvent {
|
impl<B: InputBackend> PointerButtonEvent<B> for UnusedEvent {
|
||||||
|
@ -127,7 +142,7 @@ impl<B: InputBackend> PointerButtonEvent<B> for UnusedEvent {
|
||||||
match *self {}
|
match *self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> MouseButtonState {
|
fn state(&self) -> ButtonState {
|
||||||
match *self {}
|
match *self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,6 +496,14 @@ pub trait InputBackend: Sized {
|
||||||
type TouchCancelEvent: TouchCancelEvent<Self>;
|
type TouchCancelEvent: TouchCancelEvent<Self>;
|
||||||
/// Type representing touch frame events
|
/// Type representing touch frame events
|
||||||
type TouchFrameEvent: TouchFrameEvent<Self>;
|
type TouchFrameEvent: TouchFrameEvent<Self>;
|
||||||
|
/// Type representing axis events on tablet devices
|
||||||
|
type TabletToolAxisEvent: TabletToolAxisEvent<Self>;
|
||||||
|
/// Type representing proximity events on tablet devices
|
||||||
|
type TabletToolProximityEvent: TabletToolProximityEvent<Self>;
|
||||||
|
/// Type representing tip events on tablet devices
|
||||||
|
type TabletToolTipEvent: TabletToolTipEvent<Self>;
|
||||||
|
/// Type representing button events on tablet tool devices
|
||||||
|
type TabletToolButtonEvent: TabletToolButtonEvent<Self>;
|
||||||
|
|
||||||
/// Special events that are custom to this backend
|
/// Special events that are custom to this backend
|
||||||
type SpecialEvent;
|
type SpecialEvent;
|
||||||
|
@ -557,6 +580,31 @@ pub enum InputEvent<B: InputBackend> {
|
||||||
/// The touch frame event
|
/// The touch frame event
|
||||||
event: B::TouchFrameEvent,
|
event: B::TouchFrameEvent,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// A tablet tool axis was emited
|
||||||
|
TabletToolAxis {
|
||||||
|
/// The tablet tool axis event
|
||||||
|
event: B::TabletToolAxisEvent,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A tablet tool proximity was emited
|
||||||
|
TabletToolProximity {
|
||||||
|
/// The tablet tool proximity event
|
||||||
|
event: B::TabletToolProximityEvent,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A tablet tool tip event was emited
|
||||||
|
TabletToolTip {
|
||||||
|
/// The tablet tool axis event
|
||||||
|
event: B::TabletToolTipEvent,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A tablet tool button was pressed or released
|
||||||
|
TabletToolButton {
|
||||||
|
/// The pointer button event
|
||||||
|
event: B::TabletToolButtonEvent,
|
||||||
|
},
|
||||||
|
|
||||||
/// Special event specific of this backend
|
/// Special event specific of this backend
|
||||||
Special(B::SpecialEvent),
|
Special(B::SpecialEvent),
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
use super::{ButtonState, Event, InputBackend, UnusedEvent};
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
/// Description of physical tablet tool
|
||||||
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
|
pub struct TabletToolDescriptor {
|
||||||
|
/// The tool type is the high-level type of the tool and usually decides the interaction expected from this tool.
|
||||||
|
pub tool_type: TabletToolType,
|
||||||
|
/// Unique hardware serial number of the tool
|
||||||
|
pub hardware_serial: u64,
|
||||||
|
/// Hardware id in Wacom’s format
|
||||||
|
pub hardware_id_wacom: u64,
|
||||||
|
/// Tool capability
|
||||||
|
/// Notifies the client of any capabilities of this tool, beyond the main set of x/y axes and tip up/down detection
|
||||||
|
pub capabilitys: TabletToolCapabilitys,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes the physical type of a tool. The physical type of a tool generally defines its base usage.
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
|
pub enum TabletToolType {
|
||||||
|
/// A generic pen.
|
||||||
|
Pen,
|
||||||
|
/// Eraser
|
||||||
|
Eraser,
|
||||||
|
/// A paintbrush-like tool.
|
||||||
|
Brush,
|
||||||
|
/// Physical drawing tool, e.g. Wacom Inking Pen
|
||||||
|
Pencil,
|
||||||
|
/// An airbrush-like tool.
|
||||||
|
Airbrush,
|
||||||
|
/// A mouse bound to the tablet.
|
||||||
|
Mouse,
|
||||||
|
/// A mouse tool with a lens.
|
||||||
|
Lens,
|
||||||
|
/// A rotary device with positional and rotation data.
|
||||||
|
Totem,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Describes extra capabilities on a tablet.
|
||||||
|
///
|
||||||
|
/// Any tool must provide x and y values, extra axes are device-specific.
|
||||||
|
pub struct TabletToolCapabilitys: u32 {
|
||||||
|
/// Tilt axes
|
||||||
|
const TILT = 1;
|
||||||
|
/// Pressure axis
|
||||||
|
const PRESSURE = 2;
|
||||||
|
/// Distance axis
|
||||||
|
const DISTANCE = 4;
|
||||||
|
/// Z-rotation axis
|
||||||
|
const ROTATION = 16;
|
||||||
|
/// Slider axis
|
||||||
|
const SLIDER = 32;
|
||||||
|
/// Wheel axis
|
||||||
|
const WHEEL = 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tablet tool event
|
||||||
|
pub trait TabletToolEvent<B: InputBackend> {
|
||||||
|
/// Get tablet tool that caused this event
|
||||||
|
fn tool(&self) -> TabletToolDescriptor;
|
||||||
|
|
||||||
|
/// Delta between the last and new pointer device position interpreted as pixel movement
|
||||||
|
fn delta(&self) -> (f64, f64) {
|
||||||
|
(self.delta_x(), self.delta_y())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tool position in the device's native coordinate space
|
||||||
|
fn position(&self) -> (f64, f64) {
|
||||||
|
(self.x(), self.y())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tool position converted into the target coordinate space.
|
||||||
|
fn position_transformed(&self, coordinate_space: (u32, u32)) -> (f64, f64) {
|
||||||
|
(
|
||||||
|
self.x_transformed(coordinate_space.0),
|
||||||
|
self.y_transformed(coordinate_space.1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current tilt along the (X,Y) axis of the tablet's current logical
|
||||||
|
/// orientation, in degrees off the tablet's z axis.
|
||||||
|
///
|
||||||
|
/// That is, if the tool is perfectly orthogonal to the tablet, the tilt angle is 0.
|
||||||
|
/// When the top tilts towards the logical top/left of the tablet, the x/y tilt
|
||||||
|
/// angles are negative, if the top tilts towards the logical bottom/right of the
|
||||||
|
/// tablet, the x/y tilt angles are positive.
|
||||||
|
///
|
||||||
|
/// If this axis does not exist on the current tool, this function returns (0,0).
|
||||||
|
fn tilt(&self) -> (f64, f64) {
|
||||||
|
(self.tilt_x(), self.tilt_y())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the tilt was updated in this event.
|
||||||
|
fn tilt_has_changed(&self) -> bool {
|
||||||
|
self.tilt_x_has_changed() || self.tilt_y_has_changed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delta on the x axis between the last and new pointer device position interpreted as pixel movement
|
||||||
|
fn delta_x(&self) -> f64;
|
||||||
|
/// Delta on the y axis between the last and new pointer device position interpreted as pixel movement
|
||||||
|
fn delta_y(&self) -> f64;
|
||||||
|
|
||||||
|
/// Returns the x coordinate of the tablet tool, in mm from the top left corner of the tablet in its current logical orientation.
|
||||||
|
fn x(&self) -> f64;
|
||||||
|
/// Returns the y coordinate of the tablet tool, in mm from the top left corner of the tablet in its current logical orientation.
|
||||||
|
fn y(&self) -> f64;
|
||||||
|
|
||||||
|
/// Return the current absolute X coordinate of the tablet tool event, transformed to screen coordinates.
|
||||||
|
fn x_transformed(&self, width: u32) -> f64;
|
||||||
|
/// Return the current absolute Y coordinate of the tablet tool event, transformed to screen coordinates.
|
||||||
|
fn y_transformed(&self, height: u32) -> f64;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
fn distance(&self) -> f64;
|
||||||
|
|
||||||
|
/// Check if the distance axis was updated in this event.
|
||||||
|
fn distance_has_changed(&self) -> bool;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
fn pressure(&self) -> f64;
|
||||||
|
|
||||||
|
/// Check if the pressure axis was updated in this event.
|
||||||
|
fn pressure_has_changed(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns the current position of the slider on the tool, normalized to the range
|
||||||
|
/// [-1, 1].
|
||||||
|
///
|
||||||
|
/// The logical zero is the neutral position of the slider, or the logical center of
|
||||||
|
/// the axis. This axis is available on e.g. the Wacom Airbrush.
|
||||||
|
///
|
||||||
|
/// If this axis does not exist on the current tool, this function returns 0.
|
||||||
|
fn slider_position(&self) -> f64;
|
||||||
|
|
||||||
|
/// Check if the slider axis was updated in this event.
|
||||||
|
fn slider_has_changed(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns the current tilt along the X axis of the tablet's current logical
|
||||||
|
/// orientation, in degrees off the tablet's z axis.
|
||||||
|
///
|
||||||
|
/// That is, if the tool is perfectly orthogonal to the tablet, the tilt angle is 0.
|
||||||
|
/// When the top tilts towards the logical top/left of the tablet, the x/y tilt
|
||||||
|
/// angles are negative, if the top tilts towards the logical bottom/right of the
|
||||||
|
/// tablet, the x/y tilt angles are positive.
|
||||||
|
///
|
||||||
|
/// If this axis does not exist on the current tool, this function returns 0.
|
||||||
|
fn tilt_x(&self) -> f64;
|
||||||
|
|
||||||
|
/// Check if the tilt x axis was updated in this event.
|
||||||
|
fn tilt_x_has_changed(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns the current tilt along the Y axis of the tablet's current logical
|
||||||
|
/// orientation, in degrees off the tablet's z axis.
|
||||||
|
///
|
||||||
|
/// That is, if the tool is perfectly orthogonal to the tablet, the tilt angle is 0.
|
||||||
|
/// When the top tilts towards the logical top/left of the tablet, the x/y tilt
|
||||||
|
/// angles are negative, if the top tilts towards the logical bottom/right of the
|
||||||
|
/// tablet, the x/y tilt angles are positive.
|
||||||
|
///
|
||||||
|
/// If this axis does not exist on the current tool, this function returns 0.
|
||||||
|
fn tilt_y(&self) -> f64;
|
||||||
|
|
||||||
|
/// Check if the tilt y axis was updated in this event.
|
||||||
|
fn tilt_y_has_changed(&self) -> bool;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// If this axis does not exist on the current tool, this function returns 0.
|
||||||
|
fn rotation(&self) -> f64;
|
||||||
|
|
||||||
|
/// Check if the z-rotation axis was updated in this event.
|
||||||
|
fn rotation_has_changed(&self) -> bool;
|
||||||
|
|
||||||
|
/// Return the delta for the wheel in degrees.
|
||||||
|
fn wheel_delta(&self) -> f64;
|
||||||
|
/// Return the delta for the wheel in discrete steps (e.g. wheel clicks).
|
||||||
|
fn wheel_delta_discrete(&self) -> i32;
|
||||||
|
/// Check if the wheel axis was updated in this event.
|
||||||
|
fn wheel_has_changed(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: InputBackend> TabletToolEvent<B> for UnusedEvent {
|
||||||
|
fn tool(&self) -> TabletToolDescriptor {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn delta_x(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn delta_y(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn x_transformed(&self, _width: u32) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn y_transformed(&self, _height: u32) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn distance(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn distance_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn pressure(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn pressure_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn slider_position(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn slider_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn tilt_x(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn tilt_x_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn tilt_y(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn tilt_y_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn rotation(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn rotation_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn wheel_delta(&self) -> f64 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn wheel_delta_discrete(&self) -> i32 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
fn wheel_has_changed(&self) -> bool {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for axis tablet tool events.
|
||||||
|
pub trait TabletToolAxisEvent<B: InputBackend>: TabletToolEvent<B> + Event<B> {}
|
||||||
|
|
||||||
|
impl<B: InputBackend> TabletToolAxisEvent<B> for UnusedEvent {}
|
||||||
|
|
||||||
|
/// The state of proximity for a tool on a device.
|
||||||
|
///
|
||||||
|
/// The proximity of a tool is a binary state signalling whether the tool is within a
|
||||||
|
/// detectable distance of the tablet device. A tool that is out of proximity cannot
|
||||||
|
/// 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
|
||||||
|
/// the surface.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ProximityState {
|
||||||
|
/// Out of proximity
|
||||||
|
Out,
|
||||||
|
/// In proximity
|
||||||
|
In,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for tablet tool proximity events.
|
||||||
|
pub trait TabletToolProximityEvent<B: InputBackend>: TabletToolEvent<B> + Event<B> {
|
||||||
|
/// Returns the new proximity state of a tool from a proximity event.
|
||||||
|
///
|
||||||
|
/// Used to check whether or not a tool came in or out of proximity during an
|
||||||
|
/// `TabletToolProximityEvent`.
|
||||||
|
///
|
||||||
|
/// See [Handling of proximity events](https://wayland.freedesktop.org/libinput/doc/latest/tablet-support.html#tablet-fake-proximity)
|
||||||
|
/// for recommendations on proximity handling.
|
||||||
|
fn state(&self) -> ProximityState;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: InputBackend> TabletToolProximityEvent<B> for UnusedEvent {
|
||||||
|
fn state(&self) -> ProximityState {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The tip contact state for a tool on a device.
|
||||||
|
///
|
||||||
|
/// The tip contact state of a tool is a binary state signalling whether the tool is
|
||||||
|
/// touching the surface of the tablet device.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum TabletToolTipState {
|
||||||
|
/// Not touching the surface
|
||||||
|
Up,
|
||||||
|
/// Touching the surface
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signals that a tool has come in contact with the surface of a device with the
|
||||||
|
/// `DeviceCapability::TabletTool` capability.
|
||||||
|
///
|
||||||
|
/// On devices without distance proximity detection, the `TabletToolTipEvent` is sent
|
||||||
|
/// immediately after `TabletToolProximityEvent` for the tip down event, and
|
||||||
|
/// immediately before for the tip up event.
|
||||||
|
///
|
||||||
|
/// The decision when a tip touches the surface is device-dependent and may be
|
||||||
|
/// derived from pressure data or other means. If the tip state is changed by axes
|
||||||
|
/// changing state, the `TabletToolTipEvent` includes the changed axes and no
|
||||||
|
/// additional axis event is sent for this state change. In other words, a caller
|
||||||
|
/// must look at both `TabletToolAxisEvent` and `TabletToolTipEvent` events to know
|
||||||
|
/// the current state of the axes.
|
||||||
|
///
|
||||||
|
/// If a button state change occurs at the same time as a tip state change, the order
|
||||||
|
/// of events is device-dependent.
|
||||||
|
pub trait TabletToolTipEvent<B: InputBackend>: TabletToolEvent<B> + Event<B> {
|
||||||
|
/// Returns the new tip state of a tool from a tip event.
|
||||||
|
///
|
||||||
|
/// Used to check whether or not a tool came in contact with the tablet surface or
|
||||||
|
/// left contact with the tablet surface during an `TabletToolTipEvent`.
|
||||||
|
fn tip_state(&self) -> TabletToolTipState;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: InputBackend> TabletToolTipEvent<B> for UnusedEvent {
|
||||||
|
fn tip_state(&self) -> TabletToolTipState {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signals that a tool has changed a logical button state on a device with the DeviceCapability::TabletTool capability.
|
||||||
|
pub trait TabletToolButtonEvent<B: InputBackend>: TabletToolEvent<B> + Event<B> {
|
||||||
|
/// Return the button that triggered this event.
|
||||||
|
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.
|
||||||
|
fn seat_button_count(&self) -> u32;
|
||||||
|
|
||||||
|
/// Return the button state of the event.
|
||||||
|
fn button_state(&self) -> ButtonState;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: InputBackend> TabletToolButtonEvent<B> for UnusedEvent {
|
||||||
|
fn button(&self) -> u32 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seat_button_count(&self) -> u32 {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button_state(&self) -> ButtonState {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,12 +14,15 @@ use std::path::Path;
|
||||||
use std::{
|
use std::{
|
||||||
io::Error as IoError,
|
io::Error as IoError,
|
||||||
os::unix::io::{AsRawFd, RawFd},
|
os::unix::io::{AsRawFd, RawFd},
|
||||||
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use calloop::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
use calloop::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||||
|
|
||||||
use slog::{info, o};
|
use slog::{info, o};
|
||||||
|
|
||||||
|
mod tablet;
|
||||||
|
|
||||||
// No idea if this is the same across unix platforms
|
// No idea if this is the same across unix platforms
|
||||||
// Lets make this linux exclusive for now, once someone tries to build it for
|
// Lets make this linux exclusive for now, once someone tries to build it for
|
||||||
// any BSD-like system, they can verify if this is right and make a PR to change this.
|
// any BSD-like system, they can verify if this is right and make a PR to change this.
|
||||||
|
@ -93,6 +96,21 @@ impl backend::Device for libinput::Device {
|
||||||
fn has_capability(&self, capability: backend::DeviceCapability) -> bool {
|
fn has_capability(&self, capability: backend::DeviceCapability) -> bool {
|
||||||
libinput::Device::has_capability(self, capability.into())
|
libinput::Device::has_capability(self, capability.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn usb_id(&self) -> Option<(u32, u32)> {
|
||||||
|
Some((
|
||||||
|
libinput::Device::id_product(self),
|
||||||
|
libinput::Device::id_vendor(self),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syspath(&self) -> Option<PathBuf> {
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
return unsafe { libinput::Device::udev_device(self) }.map(|d| d.syspath().to_owned());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "udev"))]
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<backend::DeviceCapability> for libinput::DeviceCapability {
|
impl From<backend::DeviceCapability> for libinput::DeviceCapability {
|
||||||
|
@ -179,7 +197,7 @@ impl backend::PointerButtonEvent<LibinputInputBackend> for event::pointer::Point
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> backend::MouseButtonState {
|
fn state(&self) -> backend::ButtonState {
|
||||||
self.button_state().into()
|
self.button_state().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +373,10 @@ impl InputBackend for LibinputInputBackend {
|
||||||
type TouchMotionEvent = event::touch::TouchMotionEvent;
|
type TouchMotionEvent = event::touch::TouchMotionEvent;
|
||||||
type TouchCancelEvent = event::touch::TouchCancelEvent;
|
type TouchCancelEvent = event::touch::TouchCancelEvent;
|
||||||
type TouchFrameEvent = event::touch::TouchFrameEvent;
|
type TouchFrameEvent = event::touch::TouchFrameEvent;
|
||||||
|
type TabletToolAxisEvent = event::tablet_tool::TabletToolAxisEvent;
|
||||||
|
type TabletToolProximityEvent = event::tablet_tool::TabletToolProximityEvent;
|
||||||
|
type TabletToolTipEvent = event::tablet_tool::TabletToolTipEvent;
|
||||||
|
type TabletToolButtonEvent = event::tablet_tool::TabletToolButtonEvent;
|
||||||
|
|
||||||
type SpecialEvent = backend::UnusedEvent;
|
type SpecialEvent = backend::UnusedEvent;
|
||||||
|
|
||||||
|
@ -420,6 +442,20 @@ impl InputBackend for LibinputInputBackend {
|
||||||
callback(InputEvent::PointerButton { event: button_event });
|
callback(InputEvent::PointerButton { event: button_event });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
libinput::Event::Tablet(tablet_event) => match tablet_event {
|
||||||
|
event::TabletToolEvent::Axis(event) => {
|
||||||
|
callback(InputEvent::TabletToolAxis { event });
|
||||||
|
}
|
||||||
|
event::TabletToolEvent::Proximity(event) => {
|
||||||
|
callback(InputEvent::TabletToolProximity { event });
|
||||||
|
}
|
||||||
|
event::TabletToolEvent::Tip(event) => {
|
||||||
|
callback(InputEvent::TabletToolTip { event });
|
||||||
|
}
|
||||||
|
event::TabletToolEvent::Button(event) => {
|
||||||
|
callback(InputEvent::TabletToolButton { event });
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {} //FIXME: What to do with the rest.
|
_ => {} //FIXME: What to do with the rest.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,11 +501,11 @@ impl From<event::pointer::AxisSource> for backend::AxisSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<event::pointer::ButtonState> for backend::MouseButtonState {
|
impl From<event::pointer::ButtonState> for backend::ButtonState {
|
||||||
fn from(libinput: event::pointer::ButtonState) -> Self {
|
fn from(libinput: event::pointer::ButtonState) -> Self {
|
||||||
match libinput {
|
match libinput {
|
||||||
event::pointer::ButtonState::Pressed => backend::MouseButtonState::Pressed,
|
event::pointer::ButtonState::Pressed => backend::ButtonState::Pressed,
|
||||||
event::pointer::ButtonState::Released => backend::MouseButtonState::Released,
|
event::pointer::ButtonState::Released => backend::ButtonState::Released,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
use crate::backend::input::{
|
||||||
|
self as backend, TabletToolCapabilitys, TabletToolDescriptor, TabletToolTipState, TabletToolType,
|
||||||
|
};
|
||||||
|
|
||||||
|
use input as libinput;
|
||||||
|
use input::event;
|
||||||
|
use input::event::{tablet_tool, EventTrait};
|
||||||
|
|
||||||
|
use super::LibinputInputBackend;
|
||||||
|
|
||||||
|
/// Marker for tablet tool events
|
||||||
|
pub trait IsTabletEvent: tablet_tool::TabletToolEventTrait + EventTrait {}
|
||||||
|
|
||||||
|
impl IsTabletEvent for tablet_tool::TabletToolAxisEvent {}
|
||||||
|
impl IsTabletEvent for tablet_tool::TabletToolProximityEvent {}
|
||||||
|
impl IsTabletEvent for tablet_tool::TabletToolTipEvent {}
|
||||||
|
impl IsTabletEvent for tablet_tool::TabletToolButtonEvent {}
|
||||||
|
|
||||||
|
impl<E> backend::Event<LibinputInputBackend> for E
|
||||||
|
where
|
||||||
|
E: IsTabletEvent,
|
||||||
|
{
|
||||||
|
fn time(&self) -> u32 {
|
||||||
|
tablet_tool::TabletToolEventTrait::time(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device(&self) -> libinput::Device {
|
||||||
|
event::EventTrait::device(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl backend::TabletToolAxisEvent<LibinputInputBackend> for tablet_tool::TabletToolAxisEvent {}
|
||||||
|
|
||||||
|
impl backend::TabletToolProximityEvent<LibinputInputBackend> for tablet_tool::TabletToolProximityEvent {
|
||||||
|
fn state(&self) -> backend::ProximityState {
|
||||||
|
match tablet_tool::TabletToolProximityEvent::proximity_state(self) {
|
||||||
|
tablet_tool::ProximityState::In => backend::ProximityState::In,
|
||||||
|
tablet_tool::ProximityState::Out => backend::ProximityState::Out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl backend::TabletToolTipEvent<LibinputInputBackend> for tablet_tool::TabletToolTipEvent {
|
||||||
|
fn tip_state(&self) -> TabletToolTipState {
|
||||||
|
match tablet_tool::TabletToolTipEvent::tip_state(self) {
|
||||||
|
tablet_tool::TipState::Up => backend::TabletToolTipState::Up,
|
||||||
|
tablet_tool::TipState::Down => backend::TabletToolTipState::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> backend::TabletToolEvent<LibinputInputBackend> for E
|
||||||
|
where
|
||||||
|
E: IsTabletEvent + event::EventTrait,
|
||||||
|
{
|
||||||
|
fn tool(&self) -> TabletToolDescriptor {
|
||||||
|
let tool = self.tool();
|
||||||
|
|
||||||
|
let tool_type = match tool.tool_type() {
|
||||||
|
tablet_tool::TabletToolType::Pen => TabletToolType::Pen,
|
||||||
|
tablet_tool::TabletToolType::Eraser => TabletToolType::Eraser,
|
||||||
|
tablet_tool::TabletToolType::Brush => TabletToolType::Brush,
|
||||||
|
tablet_tool::TabletToolType::Pencil => TabletToolType::Pencil,
|
||||||
|
tablet_tool::TabletToolType::Airbrush => TabletToolType::Airbrush,
|
||||||
|
tablet_tool::TabletToolType::Mouse => TabletToolType::Mouse,
|
||||||
|
tablet_tool::TabletToolType::Lens => TabletToolType::Lens,
|
||||||
|
tablet_tool::TabletToolType::Totem => TabletToolType::Totem,
|
||||||
|
};
|
||||||
|
|
||||||
|
let hardware_serial = tool.serial();
|
||||||
|
let hardware_id_wacom = tool.tool_id();
|
||||||
|
|
||||||
|
let mut capabilitys = TabletToolCapabilitys::empty();
|
||||||
|
|
||||||
|
capabilitys.set(TabletToolCapabilitys::TILT, tool.has_tilt());
|
||||||
|
capabilitys.set(TabletToolCapabilitys::PRESSURE, tool.has_pressure());
|
||||||
|
capabilitys.set(TabletToolCapabilitys::DISTANCE, tool.has_distance());
|
||||||
|
capabilitys.set(TabletToolCapabilitys::ROTATION, tool.has_rotation());
|
||||||
|
capabilitys.set(TabletToolCapabilitys::SLIDER, tool.has_slider());
|
||||||
|
capabilitys.set(TabletToolCapabilitys::WHEEL, tool.has_wheel());
|
||||||
|
|
||||||
|
TabletToolDescriptor {
|
||||||
|
tool_type,
|
||||||
|
hardware_serial,
|
||||||
|
hardware_id_wacom,
|
||||||
|
capabilitys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delta_x(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::dx(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delta_y(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::dy(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn x(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::x(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::y(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn x_transformed(&self, width: u32) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::x_transformed(self, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y_transformed(&self, height: u32) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::y_transformed(self, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distance(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::distance(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distance_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::distance_has_changed(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pressure(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::pressure(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pressure_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::pressure_has_changed(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slider_position(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::slider_position(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slider_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::slider_has_changed(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tilt_x(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::dx(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tilt_x_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::tilt_x_has_changed(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tilt_y(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::dy(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tilt_y_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::tilt_y_has_changed(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotation(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::rotation(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotation_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::rotation_has_changed(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wheel_delta(&self) -> f64 {
|
||||||
|
tablet_tool::TabletToolEventTrait::wheel_delta(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wheel_delta_discrete(&self) -> i32 {
|
||||||
|
// I have no idea why f64 is returend by this fn, in libinput's api wheel clicks are always i32
|
||||||
|
tablet_tool::TabletToolEventTrait::wheel_delta_discrete(self) as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wheel_has_changed(&self) -> bool {
|
||||||
|
tablet_tool::TabletToolEventTrait::wheel_has_changed(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl backend::TabletToolButtonEvent<LibinputInputBackend> for tablet_tool::TabletToolButtonEvent {
|
||||||
|
fn button(&self) -> u32 {
|
||||||
|
tablet_tool::TabletToolButtonEvent::button(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seat_button_count(&self) -> u32 {
|
||||||
|
tablet_tool::TabletToolButtonEvent::seat_button_count(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button_state(&self) -> backend::ButtonState {
|
||||||
|
tablet_tool::TabletToolButtonEvent::button_state(&self).into()
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,8 +23,8 @@ use crate::backend::egl::display::EGLDisplay;
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
|
egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
|
||||||
input::{
|
input::{
|
||||||
Axis, AxisSource, Device, DeviceCapability, Event as BackendEvent, InputBackend, InputEvent,
|
Axis, AxisSource, ButtonState, Device, DeviceCapability, Event as BackendEvent, InputBackend,
|
||||||
KeyState, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
|
InputEvent, KeyState, KeyboardKeyEvent, MouseButton, PointerAxisEvent, PointerButtonEvent,
|
||||||
PointerMotionAbsoluteEvent, TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot,
|
PointerMotionAbsoluteEvent, TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot,
|
||||||
TouchUpEvent, UnusedEvent,
|
TouchUpEvent, UnusedEvent,
|
||||||
},
|
},
|
||||||
|
@ -33,7 +33,7 @@ use crate::backend::{
|
||||||
Bind, Renderer, Transform, Unbind,
|
Bind, Renderer, Transform, Unbind,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
use std::{cell::RefCell, path::PathBuf, rc::Rc, time::Instant};
|
||||||
use wayland_egl as wegl;
|
use wayland_egl as wegl;
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
||||||
|
@ -307,6 +307,14 @@ impl Device for WinitVirtualDevice {
|
||||||
DeviceCapability::Keyboard | DeviceCapability::Pointer | DeviceCapability::Touch
|
DeviceCapability::Keyboard | DeviceCapability::Pointer | DeviceCapability::Touch
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn usb_id(&self) -> Option<(u32, u32)> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syspath(&self) -> Option<PathBuf> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that may happen when driving the event loop of [`WinitInputBackend`]
|
/// Errors that may happen when driving the event loop of [`WinitInputBackend`]
|
||||||
|
@ -460,7 +468,7 @@ impl PointerButtonEvent<WinitInputBackend> for WinitMouseInputEvent {
|
||||||
self.button.into()
|
self.button.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> MouseButtonState {
|
fn state(&self) -> ButtonState {
|
||||||
self.state.into()
|
self.state.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -619,6 +627,10 @@ impl InputBackend for WinitInputBackend {
|
||||||
type TouchMotionEvent = WinitTouchMovedEvent;
|
type TouchMotionEvent = WinitTouchMovedEvent;
|
||||||
type TouchCancelEvent = WinitTouchCancelledEvent;
|
type TouchCancelEvent = WinitTouchCancelledEvent;
|
||||||
type TouchFrameEvent = UnusedEvent;
|
type TouchFrameEvent = UnusedEvent;
|
||||||
|
type TabletToolAxisEvent = UnusedEvent;
|
||||||
|
type TabletToolProximityEvent = UnusedEvent;
|
||||||
|
type TabletToolTipEvent = UnusedEvent;
|
||||||
|
type TabletToolButtonEvent = UnusedEvent;
|
||||||
|
|
||||||
type SpecialEvent = WinitEvent;
|
type SpecialEvent = WinitEvent;
|
||||||
|
|
||||||
|
@ -846,11 +858,11 @@ impl From<ElementState> for KeyState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ElementState> for MouseButtonState {
|
impl From<ElementState> for ButtonState {
|
||||||
fn from(state: ElementState) -> Self {
|
fn from(state: ElementState) -> Self {
|
||||||
match state {
|
match state {
|
||||||
ElementState::Pressed => MouseButtonState::Pressed,
|
ElementState::Pressed => ButtonState::Pressed,
|
||||||
ElementState::Released => MouseButtonState::Released,
|
ElementState::Released => ButtonState::Released,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
//! 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(),
|
||||||
|
//! usb_id: 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>().unwrap();
|
||||||
|
|
||||||
|
tablet_seat.add_instance(instance);
|
||||||
|
}
|
||||||
|
zwp_tablet_manager_v2::Request::Destroy => {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
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,vendor) id
|
||||||
|
pub usb_id: Option<(u32, 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(),
|
||||||
|
usb_id: device.usb_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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, id_vendor)) = tablet.usb_id {
|
||||||
|
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 available 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 available tablets
|
||||||
|
for (desc, tablet) in inner.tablets.iter_mut() {
|
||||||
|
tablet.new_instance(seat.deref(), desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify new instance about available 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 [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
|
||||||
|
///
|
||||||
|
/// 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 [input::Event::DeviceRemoved](crate::backend::input::InputEvent::DeviceRemoved) 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, TabletToolCapabilitys, 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.contains(TabletToolCapabilitys::PRESSURE) {
|
||||||
|
wl_tool.capability(zwp_tablet_tool_v2::Capability::Pressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tool.capabilitys.contains(TabletToolCapabilitys::DISTANCE) {
|
||||||
|
wl_tool.capability(zwp_tablet_tool_v2::Capability::Distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tool.capabilitys.contains(TabletToolCapabilitys::TILT) {
|
||||||
|
wl_tool.capability(zwp_tablet_tool_v2::Capability::Tilt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tool.capabilitys.contains(TabletToolCapabilitys::SLIDER) {
|
||||||
|
wl_tool.capability(zwp_tablet_tool_v2::Capability::Slider);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tool.capabilitys.contains(TabletToolCapabilitys::ROTATION) {
|
||||||
|
wl_tool.capability(zwp_tablet_tool_v2::Capability::Rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tool.capabilitys.contains(TabletToolCapabilitys::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