commit
70826c9d2c
|
@ -21,7 +21,7 @@ drm-fourcc = "^2.1.1"
|
|||
drm = { version = "0.4.0", optional = true }
|
||||
drm-ffi = { version = "0.1.0", optional = true }
|
||||
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 }
|
||||
lazy_static = "1"
|
||||
libc = "0.2.70"
|
||||
|
@ -57,7 +57,7 @@ backend_gbm = ["gbm"]
|
|||
backend_egl = ["gl_generator"]
|
||||
backend_libinput = ["input"]
|
||||
backend_session = []
|
||||
backend_udev = ["udev"]
|
||||
backend_udev = ["udev", "input/udev"]
|
||||
backend_session_logind = ["dbus", "backend_session", "pkg-config"]
|
||||
backend_session_elogind = ["backend_session_logind"]
|
||||
backend_session_libseat = ["backend_session", "libseat"]
|
||||
|
|
|
@ -8,13 +8,15 @@ use crate::AnvilState;
|
|||
|
||||
use smithay::{
|
||||
backend::input::{
|
||||
self, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent,
|
||||
PointerButtonEvent,
|
||||
self, Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
|
||||
PointerAxisEvent, PointerButtonEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent,
|
||||
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState,
|
||||
},
|
||||
reexports::wayland_server::protocol::wl_pointer,
|
||||
wayland::{
|
||||
output::Mode,
|
||||
seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState},
|
||||
tablet_manager::{TabletDescriptor, TabletSeatTrait},
|
||||
SERIAL_COUNTER as SCOUNTER,
|
||||
},
|
||||
};
|
||||
|
@ -81,7 +83,7 @@ impl<Backend> AnvilState<Backend> {
|
|||
input::MouseButton::Other(b) => b as u32,
|
||||
};
|
||||
let state = match evt.state() {
|
||||
input::MouseButtonState::Pressed => {
|
||||
input::ButtonState::Pressed => {
|
||||
// change the keyboard focus unless the pointer is grabbed
|
||||
if !self.pointer.is_grabbed() {
|
||||
let under = self
|
||||
|
@ -93,7 +95,7 @@ impl<Backend> AnvilState<Backend> {
|
|||
}
|
||||
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());
|
||||
}
|
||||
|
@ -238,6 +240,29 @@ impl AnvilState<UdevData> {
|
|||
InputEvent::PointerMotion { event, .. } => self.on_pointer_move::<B>(event),
|
||||
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<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)
|
||||
}
|
||||
|
@ -259,6 +284,127 @@ impl AnvilState<UdevData> {
|
|||
.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) {
|
||||
if self.output_map.borrow().is_empty() {
|
||||
return pos;
|
||||
|
|
|
@ -16,6 +16,7 @@ use smithay::{
|
|||
data_device::{default_action_chooser, init_data_device, set_data_device_focus, DataDeviceEvent},
|
||||
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
|
||||
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 cursor_status: Arc<Mutex<CursorImageStatus>>,
|
||||
pub seat_name: String,
|
||||
pub seat: Seat,
|
||||
pub start_time: std::time::Instant,
|
||||
// things we must keep alive
|
||||
#[cfg(feature = "xwayland")]
|
||||
|
@ -121,6 +123,14 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
|||
*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
|
||||
.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| {
|
||||
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,
|
||||
pointer_location: (0.0, 0.0),
|
||||
seat_name,
|
||||
seat,
|
||||
start_time: std::time::Instant::now(),
|
||||
#[cfg(feature = "xwayland")]
|
||||
xwayland,
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
//! 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
|
||||
pub trait Device: PartialEq + Eq + std::hash::Hash {
|
||||
|
@ -12,6 +19,14 @@ pub trait Device: PartialEq + Eq + std::hash::Hash {
|
|||
fn name(&self) -> String;
|
||||
/// Test if this device has a specific capability
|
||||
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
|
||||
|
@ -105,9 +120,9 @@ pub enum MouseButton {
|
|||
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)]
|
||||
pub enum MouseButtonState {
|
||||
pub enum ButtonState {
|
||||
/// Button is released
|
||||
Released,
|
||||
/// Button is pressed
|
||||
|
@ -119,7 +134,7 @@ pub trait PointerButtonEvent<B: InputBackend>: Event<B> {
|
|||
/// Pressed button of the event
|
||||
fn button(&self) -> MouseButton;
|
||||
/// State of the button
|
||||
fn state(&self) -> MouseButtonState;
|
||||
fn state(&self) -> ButtonState;
|
||||
}
|
||||
|
||||
impl<B: InputBackend> PointerButtonEvent<B> for UnusedEvent {
|
||||
|
@ -127,7 +142,7 @@ impl<B: InputBackend> PointerButtonEvent<B> for UnusedEvent {
|
|||
match *self {}
|
||||
}
|
||||
|
||||
fn state(&self) -> MouseButtonState {
|
||||
fn state(&self) -> ButtonState {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
@ -481,6 +496,14 @@ pub trait InputBackend: Sized {
|
|||
type TouchCancelEvent: TouchCancelEvent<Self>;
|
||||
/// Type representing touch frame events
|
||||
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
|
||||
type SpecialEvent;
|
||||
|
@ -557,6 +580,31 @@ pub enum InputEvent<B: InputBackend> {
|
|||
/// The touch frame event
|
||||
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(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::{
|
||||
io::Error as IoError,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use calloop::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
use slog::{info, o};
|
||||
|
||||
mod tablet;
|
||||
|
||||
// No idea if this is the same across unix platforms
|
||||
// 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.
|
||||
|
@ -93,6 +96,21 @@ impl backend::Device for libinput::Device {
|
|||
fn has_capability(&self, capability: backend::DeviceCapability) -> bool {
|
||||
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 {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -355,6 +373,10 @@ impl InputBackend for LibinputInputBackend {
|
|||
type TouchMotionEvent = event::touch::TouchMotionEvent;
|
||||
type TouchCancelEvent = event::touch::TouchCancelEvent;
|
||||
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;
|
||||
|
||||
|
@ -420,6 +442,20 @@ impl InputBackend for LibinputInputBackend {
|
|||
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.
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
match libinput {
|
||||
event::pointer::ButtonState::Pressed => backend::MouseButtonState::Pressed,
|
||||
event::pointer::ButtonState::Released => backend::MouseButtonState::Released,
|
||||
event::pointer::ButtonState::Pressed => backend::ButtonState::Pressed,
|
||||
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::{
|
||||
egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
|
||||
input::{
|
||||
Axis, AxisSource, Device, DeviceCapability, Event as BackendEvent, InputBackend, InputEvent,
|
||||
KeyState, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
|
||||
Axis, AxisSource, ButtonState, Device, DeviceCapability, Event as BackendEvent, InputBackend,
|
||||
InputEvent, KeyState, KeyboardKeyEvent, MouseButton, PointerAxisEvent, PointerButtonEvent,
|
||||
PointerMotionAbsoluteEvent, TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot,
|
||||
TouchUpEvent, UnusedEvent,
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ use crate::backend::{
|
|||
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 winit::{
|
||||
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
||||
|
@ -307,6 +307,14 @@ impl Device for WinitVirtualDevice {
|
|||
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`]
|
||||
|
@ -460,7 +468,7 @@ impl PointerButtonEvent<WinitInputBackend> for WinitMouseInputEvent {
|
|||
self.button.into()
|
||||
}
|
||||
|
||||
fn state(&self) -> MouseButtonState {
|
||||
fn state(&self) -> ButtonState {
|
||||
self.state.into()
|
||||
}
|
||||
}
|
||||
|
@ -619,6 +627,10 @@ impl InputBackend for WinitInputBackend {
|
|||
type TouchMotionEvent = WinitTouchMovedEvent;
|
||||
type TouchCancelEvent = WinitTouchCancelledEvent;
|
||||
type TouchFrameEvent = UnusedEvent;
|
||||
type TabletToolAxisEvent = UnusedEvent;
|
||||
type TabletToolProximityEvent = UnusedEvent;
|
||||
type TabletToolTipEvent = UnusedEvent;
|
||||
type TabletToolButtonEvent = UnusedEvent;
|
||||
|
||||
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 {
|
||||
match state {
|
||||
ElementState::Pressed => MouseButtonState::Pressed,
|
||||
ElementState::Released => MouseButtonState::Released,
|
||||
ElementState::Pressed => ButtonState::Pressed,
|
||||
ElementState::Released => ButtonState::Released,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ pub mod output;
|
|||
pub mod seat;
|
||||
pub mod shell;
|
||||
pub mod shm;
|
||||
pub mod tablet_manager;
|
||||
|
||||
/// A global [`SerialCounter`] for use in your compositor.
|
||||
///
|
||||
|
|
|
@ -0,0 +1,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