input: Add pointer-emulation grab for tablet tools
This commit is contained in:
parent
6467f79025
commit
9125f8a2a7
3 changed files with 351 additions and 23 deletions
100
src/input/mod.rs
100
src/input/mod.rs
|
|
@ -9,7 +9,10 @@ use crate::{
|
|||
cosmic_modifiers_from_smithay,
|
||||
},
|
||||
},
|
||||
input::gestures::{GestureState, SwipeAction},
|
||||
input::{
|
||||
gestures::{GestureState, SwipeAction},
|
||||
tablet_emu::PointerEmulationGrab,
|
||||
},
|
||||
shell::{
|
||||
SeatExt, Trigger,
|
||||
focus::{
|
||||
|
|
@ -52,7 +55,7 @@ use smithay::{
|
|||
keyboard::KeyboardHandle,
|
||||
keyboard::{FilterResult, KeyboardSource, KeysymHandle, ModifiersState},
|
||||
pointer::{
|
||||
AxisFrame, ButtonEvent as PointerButtonEvent, GestureHoldBeginEvent,
|
||||
AxisFrame, ButtonEvent as PointerButtonEvent, Focus, GestureHoldBeginEvent,
|
||||
GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent,
|
||||
GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent,
|
||||
GestureSwipeUpdateEvent, MotionEvent as PointerMotionEvent, PointerGrab, PointerHandle,
|
||||
|
|
@ -89,6 +92,7 @@ use std::{
|
|||
|
||||
pub mod actions;
|
||||
pub mod gestures;
|
||||
pub mod tablet_emu;
|
||||
|
||||
/// Identifies the input backend instance an event came from, used to disambiguate device ids
|
||||
/// (which are only unique within a single backend instance, see
|
||||
|
|
@ -1609,6 +1613,26 @@ impl State {
|
|||
let tool = tablet_seat.get_tool(&event.tool());
|
||||
|
||||
if let Some(tool) = tool {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
if !tool.is_grabbed()
|
||||
&& under
|
||||
.as_ref()
|
||||
.is_some_and(|(target, _)| !target.supports_tool(&tool))
|
||||
{
|
||||
let start_data = tool::GrabStartData {
|
||||
focus: under.clone(),
|
||||
trigger: tool::GrabTrigger::Proximity,
|
||||
location: position.as_logical(),
|
||||
};
|
||||
tool.set_grab(
|
||||
self,
|
||||
PointerEmulationGrab::new(start_data, seat.clone()),
|
||||
event.time(),
|
||||
serial,
|
||||
Focus::Keep,
|
||||
);
|
||||
}
|
||||
|
||||
let frame = tool::AxisFrame {
|
||||
pressure: event.pressure_has_changed().then(|| event.pressure()),
|
||||
distance: event.distance_has_changed().then(|| event.distance()),
|
||||
|
|
@ -1629,7 +1653,7 @@ impl State {
|
|||
under,
|
||||
&tool::MotionEvent {
|
||||
location: position.as_logical(),
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
serial,
|
||||
time: event.time(),
|
||||
},
|
||||
);
|
||||
|
|
@ -1683,6 +1707,25 @@ impl State {
|
|||
if let Some(tablet) = tablet {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
if !tool.is_grabbed()
|
||||
&& under
|
||||
.as_ref()
|
||||
.is_some_and(|(target, _)| !target.supports_tool(&tool))
|
||||
{
|
||||
let start_data = tool::GrabStartData {
|
||||
focus: under.clone(),
|
||||
trigger: tool::GrabTrigger::Proximity,
|
||||
location: position.as_logical(),
|
||||
};
|
||||
tool.set_grab(
|
||||
self,
|
||||
PointerEmulationGrab::new(start_data, seat.clone()),
|
||||
event.time(),
|
||||
serial,
|
||||
Focus::Keep,
|
||||
);
|
||||
}
|
||||
|
||||
let frame = tool::AxisFrame {
|
||||
pressure: event.pressure_has_changed().then(|| event.pressure()),
|
||||
distance: event.distance_has_changed().then(|| event.distance()),
|
||||
|
|
@ -1697,24 +1740,39 @@ impl State {
|
|||
};
|
||||
|
||||
match event.state() {
|
||||
ProximityState::In => tool.proximity_in(
|
||||
self,
|
||||
under,
|
||||
tablet,
|
||||
&tool::ProximityInEvent {
|
||||
location: position.as_logical(),
|
||||
axis: Some(frame),
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: event.time(),
|
||||
},
|
||||
),
|
||||
ProximityState::Out => tool.proximity_out(
|
||||
self,
|
||||
&tool::ProximityOutEvent {
|
||||
serial,
|
||||
time: event.time(),
|
||||
},
|
||||
),
|
||||
ProximityState::In => {
|
||||
tool.proximity_in(
|
||||
self,
|
||||
under,
|
||||
tablet,
|
||||
&tool::ProximityInEvent {
|
||||
location: position.as_logical(),
|
||||
axis: Some(frame),
|
||||
serial,
|
||||
time: event.time(),
|
||||
},
|
||||
);
|
||||
}
|
||||
ProximityState::Out => {
|
||||
tool.proximity_out(
|
||||
self,
|
||||
&tool::ProximityOutEvent {
|
||||
serial,
|
||||
time: event.time(),
|
||||
},
|
||||
);
|
||||
if let Some(pointer) = seat.get_pointer() {
|
||||
pointer.motion(
|
||||
self,
|
||||
None,
|
||||
&PointerMotionEvent {
|
||||
location: position.as_logical(),
|
||||
serial,
|
||||
time: event.time(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool.frame(self, event.time());
|
||||
|
|
|
|||
260
src/input/tablet_emu.rs
Normal file
260
src/input/tablet_emu.rs
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
//! Pointer Emulating TabletTool grab.
|
||||
|
||||
use crate::{shell::focus::target::PointerFocusTarget, state::State};
|
||||
use smithay::{
|
||||
backend::input::{ButtonState, InputTime, MouseButton},
|
||||
input::{
|
||||
Seat, pointer,
|
||||
tablet::{
|
||||
self,
|
||||
tool::{
|
||||
AxisFrame, ButtonEvent, DownEvent, GrabStartData, ProximityInEvent,
|
||||
ProximityOutEvent, TabletToolGrab, TabletToolInnerHandle, UpEvent,
|
||||
},
|
||||
},
|
||||
},
|
||||
utils::{Logical, Point, SERIAL_COUNTER},
|
||||
};
|
||||
|
||||
// from https://gitlab.freedesktop.org/libinput/libinput/-/blob/main/include/linux/linux/input-event-codes.h
|
||||
const BTN_STYLUS: u32 = 0x14b;
|
||||
const BTN_STYLUS_2: u32 = 0x14c;
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
const BTN_RIGHT: u32 = 0x111;
|
||||
const BTN_MIDDLE: u32 = 0x112;
|
||||
const BTN_FORWARD: u32 = 0x115;
|
||||
const BTN_BACK: u32 = 0x116;
|
||||
|
||||
pub struct PointerEmulationGrab {
|
||||
start_data: GrabStartData<State>,
|
||||
seat: Seat<State>,
|
||||
|
||||
above_source: bool,
|
||||
tip_down: bool,
|
||||
button_down: Vec<u32>,
|
||||
}
|
||||
|
||||
impl PointerEmulationGrab {
|
||||
pub fn new(start_data: GrabStartData<State>, seat: Seat<State>) -> Self {
|
||||
Self {
|
||||
start_data,
|
||||
seat,
|
||||
|
||||
above_source: true,
|
||||
tip_down: false,
|
||||
button_down: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn button_to_mouse(&self, button: u32) -> Option<MouseButton> {
|
||||
// FIXME: This should be configurable.
|
||||
if button == BTN_STYLUS {
|
||||
Some(MouseButton::Right)
|
||||
} else if button == BTN_STYLUS_2 {
|
||||
Some(MouseButton::Middle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_button(&self, data: &mut State, button: MouseButton, state: ButtonState) {
|
||||
if let Some(pointer) = self.seat.get_pointer() {
|
||||
let button = match button {
|
||||
MouseButton::Left => BTN_LEFT,
|
||||
MouseButton::Right => BTN_RIGHT,
|
||||
MouseButton::Middle => BTN_MIDDLE,
|
||||
MouseButton::Back => BTN_BACK,
|
||||
MouseButton::Forward => BTN_FORWARD,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
pointer.button(
|
||||
data,
|
||||
&pointer::ButtonEvent {
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
button,
|
||||
state,
|
||||
time: InputTime::now(),
|
||||
},
|
||||
);
|
||||
|
||||
pointer.frame(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Type = tablet::tool::MotionEvent;
|
||||
|
||||
impl TabletToolGrab<State> for PointerEmulationGrab {
|
||||
fn proximity_in(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &ProximityInEvent,
|
||||
) {
|
||||
handle.proximity_in(data, focus.clone(), event);
|
||||
|
||||
if !self.above_source && !self.tip_down && self.button_down.is_empty() {
|
||||
handle.unset_grab(
|
||||
self,
|
||||
data,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
InputTime::now(),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
if let Some(pointer) = self.seat.get_pointer() {
|
||||
pointer.motion(
|
||||
data,
|
||||
focus,
|
||||
&pointer::MotionEvent {
|
||||
location: event.location,
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: InputTime::now(),
|
||||
},
|
||||
);
|
||||
pointer.frame(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn proximity_out(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
event: &ProximityOutEvent,
|
||||
) {
|
||||
handle.proximity_out(data, event);
|
||||
|
||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||
}
|
||||
|
||||
fn down(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
event: &DownEvent,
|
||||
) {
|
||||
handle.down(data, event);
|
||||
self.tip_down = true;
|
||||
|
||||
self.pointer_button(data, MouseButton::Left, ButtonState::Pressed);
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
event: &UpEvent,
|
||||
) {
|
||||
handle.up(data, event);
|
||||
|
||||
self.pointer_button(data, MouseButton::Left, ButtonState::Released);
|
||||
self.tip_down = false;
|
||||
|
||||
if !self.above_source && !self.tip_down && self.button_down.is_empty() {
|
||||
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||
}
|
||||
}
|
||||
|
||||
fn motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
|
||||
event: &Type,
|
||||
) {
|
||||
self.above_source = focus.as_ref().map(|(target, _)| target)
|
||||
== self.start_data.focus.as_ref().map(|(target, _)| target);
|
||||
handle.motion(data, self.start_data.focus.clone(), event);
|
||||
|
||||
if !self.above_source && !self.tip_down && self.button_down.is_empty() {
|
||||
handle.unset_grab(
|
||||
self,
|
||||
data,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
InputTime::now(),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
if let Some(pointer) = self.seat.get_pointer() {
|
||||
pointer.motion(
|
||||
data,
|
||||
focus,
|
||||
&pointer::MotionEvent {
|
||||
location: event.location,
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: InputTime::now(),
|
||||
},
|
||||
);
|
||||
pointer.frame(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn button(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
event: &ButtonEvent,
|
||||
) {
|
||||
handle.button(data, event);
|
||||
|
||||
self.button_down.retain(|b| b != &event.button);
|
||||
if matches!(event.state, ButtonState::Pressed) {
|
||||
self.button_down.push(event.button);
|
||||
}
|
||||
|
||||
if let Some(button) = self.button_to_mouse(event.button) {
|
||||
self.pointer_button(data, button, event.state);
|
||||
}
|
||||
|
||||
if !self.above_source && !self.tip_down && self.button_down.is_empty() {
|
||||
handle.unset_grab(
|
||||
self,
|
||||
data,
|
||||
SERIAL_COUNTER.next_serial(),
|
||||
InputTime::now(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn axis(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
frame: AxisFrame,
|
||||
) {
|
||||
handle.axis(data, frame);
|
||||
}
|
||||
|
||||
fn frame(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut TabletToolInnerHandle<'_, State>,
|
||||
time: InputTime,
|
||||
) {
|
||||
handle.frame(data, time);
|
||||
}
|
||||
|
||||
fn unset(&mut self, data: &mut State) {
|
||||
if self.tip_down {
|
||||
self.pointer_button(data, MouseButton::Left, ButtonState::Released);
|
||||
}
|
||||
|
||||
let buttons = std::mem::take(&mut self.button_down);
|
||||
for button in buttons.into_iter() {
|
||||
if let Some(button) = self.button_to_mouse(button) {
|
||||
self.pointer_button(data, button, ButtonState::Released);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &GrabStartData<State> {
|
||||
&self.start_data
|
||||
}
|
||||
}
|
||||
|
|
@ -33,8 +33,8 @@ use smithay::{
|
|||
Tablet,
|
||||
tool::{
|
||||
AxisFrame as ToolAxisFrame, ButtonEvent as ToolButtonEvent,
|
||||
DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolTarget,
|
||||
UpEvent as ToolUpEvent,
|
||||
DownEvent as ToolDownEvent, MotionEvent as ToolMotionEvent, TabletToolHandle,
|
||||
TabletToolTarget, UpEvent as ToolUpEvent,
|
||||
},
|
||||
},
|
||||
touch::{
|
||||
|
|
@ -306,6 +306,16 @@ impl PointerFocusTarget {
|
|||
session.set_cursor_hotspot(cursor_hotspot);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn supports_tool(&self, tool_handle: &TabletToolHandle<State>) -> bool {
|
||||
match self {
|
||||
Self::WlSurface { surface, .. } if surface.client().is_some() => tool_handle
|
||||
.client_tools(&surface.client().unwrap())
|
||||
.next()
|
||||
.is_some(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardFocusTarget {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue