1
0
Fork 0
forked from wry/wry

wayland: implement tablet-v2

This commit is contained in:
Julian Orth 2024-05-01 00:09:16 +02:00
parent 86e283d255
commit 7ed499eabd
62 changed files with 5174 additions and 318 deletions

View file

@ -6,8 +6,8 @@ use {
crate::{
async_engine::SpawnedFuture,
backend::{
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId,
InputEvent, KeyState, TransformMatrix,
Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability,
InputDeviceGroupId, InputDeviceId, InputEvent, KeyState, TransformMatrix,
},
backends::metal::video::{
MetalDrmDeviceData, MetalLeaseData, MetalRenderContext, PendingDrmDevice,
@ -15,12 +15,16 @@ use {
dbus::{DbusError, SignalHandler},
drm_feedback::DrmFeedback,
gfx_api::GfxError,
ifs::wl_seat::tablet::{
TabletId, TabletInit, TabletPadGroupInit, TabletPadId, TabletPadInit,
},
libinput::{
consts::{
AccelProfile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, LIBINPUT_DEVICE_CAP_TABLET_PAD,
LIBINPUT_DEVICE_CAP_TABLET_TOOL,
},
device::RegisteredDevice,
device::{LibInputDevice, RegisteredDevice},
LibInput, LibInputAdapter, LibInputError,
},
logind::{LogindError, Session},
@ -41,6 +45,7 @@ use {
gbm::GbmError,
},
},
bstr::ByteSlice,
std::{
any::Any,
cell::{Cell, RefCell},
@ -335,6 +340,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<MetalBackend>, MetalError> {
}
struct MetalInputDevice {
state: Rc<State>,
slot: usize,
id: InputDeviceId,
devnum: c::dev_t,
@ -342,11 +348,14 @@ struct MetalInputDevice {
inputdev: CloneCell<Option<Rc<RegisteredDevice>>>,
devnode: CString,
_sysname: CString,
syspath: CString,
removed: Cell<bool>,
events: SyncQueue<InputEvent>,
cb: CloneCell<Option<Rc<dyn Fn()>>>,
name: CloneCell<Rc<String>>,
transform_matrix: Cell<Option<TransformMatrix>>,
tablet_id: Cell<Option<TabletId>>,
tablet_pad_id: Cell<Option<TabletPadId>>,
// state
pressed_keys: SmallMap<u32, (), 5>,
@ -646,6 +655,71 @@ impl InputDevice for MetalInputDevice {
fn natural_scrolling_enabled(&self) -> Option<bool> {
self.effective.natural_scrolling_enabled.get()
}
fn tablet_info(&self) -> Option<Box<TabletInit>> {
let dev = self.inputdev.get()?;
let dev = dev.device();
if !dev.has_cap(LIBINPUT_DEVICE_CAP_TABLET_TOOL) {
return None;
}
let id = match self.tablet_id.get() {
Some(id) => id,
None => {
let id = self.state.tablet_ids.next();
self.tablet_id.set(Some(id));
id
}
};
Some(Box::new(TabletInit {
id,
group: self.get_device_group(&dev),
name: dev.name(),
pid: dev.product(),
vid: dev.vendor(),
path: self.syspath.as_bytes().as_bstr().to_string(),
}))
}
fn tablet_pad_info(&self) -> Option<Box<TabletPadInit>> {
let dev = self.inputdev.get()?;
let dev = dev.device();
if !dev.has_cap(LIBINPUT_DEVICE_CAP_TABLET_PAD) {
return None;
}
let id = match self.tablet_pad_id.get() {
Some(id) => id,
None => {
let id = self.state.tablet_pad_ids.next();
self.tablet_pad_id.set(Some(id));
id
}
};
let buttons = dev.pad_num_buttons();
let strips = dev.pad_num_strips();
let rings = dev.pad_num_rings();
let mut groups = vec![];
for n in 0..dev.pad_num_mode_groups() {
let Some(group) = dev.pad_mode_group(n) else {
break;
};
groups.push(TabletPadGroupInit {
buttons: (0..buttons).filter(|b| group.has_button(*b)).collect(),
rings: (0..rings).filter(|b| group.has_ring(*b)).collect(),
strips: (0..strips).filter(|b| group.has_strip(*b)).collect(),
modes: group.num_modes(),
mode: group.mode(),
});
}
Some(Box::new(TabletPadInit {
id,
group: self.get_device_group(&dev),
path: self.syspath.as_bytes().as_bstr().to_string(),
buttons,
strips,
rings,
groups,
}))
}
}
impl MetalInputDevice {
@ -655,4 +729,14 @@ impl MetalInputDevice {
cb();
}
}
fn get_device_group(&self, dev: &LibInputDevice) -> InputDeviceGroupId {
let group = dev.device_group();
let mut id = group.user_data();
if id == 0 {
id = self.state.input_device_group_ids.next().raw();
group.set_user_data(id);
}
InputDeviceGroupId::from_raw(id)
}
}

View file

@ -3,14 +3,25 @@ use {
backend::{AxisSource, InputEvent, KeyState, ScrollAxis},
backends::metal::MetalBackend,
fixed::Fixed,
ifs::wl_seat::tablet::{
PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange,
TabletToolCapability, TabletToolChanges, TabletToolId, TabletToolInit,
TabletToolPositionChange, TabletToolType, TabletToolWheelChange, ToolButtonState,
},
libinput::{
consts::{
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED,
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON,
LIBINPUT_SWITCH_TABLET_MODE,
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_BUTTON_STATE_RELEASED,
LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_SWITCH_LID,
LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON, LIBINPUT_SWITCH_TABLET_MODE,
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, LIBINPUT_TABLET_TOOL_TIP_DOWN,
LIBINPUT_TABLET_TOOL_TIP_UP, LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH,
LIBINPUT_TABLET_TOOL_TYPE_BRUSH, LIBINPUT_TABLET_TOOL_TYPE_ERASER,
LIBINPUT_TABLET_TOOL_TYPE_LENS, LIBINPUT_TABLET_TOOL_TYPE_MOUSE,
LIBINPUT_TABLET_TOOL_TYPE_PEN, LIBINPUT_TABLET_TOOL_TYPE_PENCIL,
},
event::LibInputEvent,
event::{LibInputEvent, LibInputEventTabletTool},
},
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt},
},
@ -103,6 +114,13 @@ impl MetalBackend {
c::LIBINPUT_EVENT_GESTURE_HOLD_BEGIN => self.handle_gesture_hold_begin(event),
c::LIBINPUT_EVENT_GESTURE_HOLD_END => self.handle_gesture_hold_end(event),
c::LIBINPUT_EVENT_SWITCH_TOGGLE => self.handle_switch_toggle(event),
c::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY => self.handle_tablet_tool_proximity(event),
c::LIBINPUT_EVENT_TABLET_TOOL_AXIS => self.handle_tablet_tool_axis(event),
c::LIBINPUT_EVENT_TABLET_TOOL_BUTTON => self.handle_tablet_tool_button(event),
c::LIBINPUT_EVENT_TABLET_TOOL_TIP => self.handle_tablet_tool_tip(event),
c::LIBINPUT_EVENT_TABLET_PAD_BUTTON => self.handle_tablet_pad_button(event),
c::LIBINPUT_EVENT_TABLET_PAD_RING => self.handle_tablet_pad_ring(event),
c::LIBINPUT_EVENT_TABLET_PAD_STRIP => self.handle_tablet_pad_strip(event),
_ => {}
}
}
@ -320,4 +338,211 @@ impl MetalBackend {
event: switch_event,
});
}
fn get_tool_id(&self, event: &LibInputEventTabletTool) -> TabletToolId {
let tool = event.tool();
let mut user_data = tool.user_data();
if user_data == 0 {
user_data = self.state.tablet_tool_ids.next().raw();
tool.set_user_data(user_data);
}
TabletToolId::from_raw(user_data)
}
fn build_tablet_tool_changed(
&self,
event: &LibInputEventTabletTool,
down: Option<bool>,
) -> InputEvent {
let mut changes = Box::<TabletToolChanges>::default();
changes.down = down;
if event.x_has_changed() || event.y_has_changed() {
changes.pos = Some(TabletTool2dChange {
x: TabletToolPositionChange {
x: event.x_transformed(1),
dx: event.dx(),
},
y: TabletToolPositionChange {
x: event.y_transformed(1),
dx: event.dy(),
},
})
}
if event.pressure_has_changed() {
changes.pressure = Some(event.pressure());
}
if event.distance_has_changed() {
changes.distance = Some(event.distance());
}
if event.tilt_x_has_changed() || event.tilt_y_has_changed() {
changes.tilt = Some(TabletTool2dChange {
x: event.tilt_x(),
y: event.tilt_y(),
});
}
if event.rotation_has_changed() {
changes.rotation = Some(event.rotation());
}
if event.slider_has_changed() {
changes.slider = Some(event.slider_position());
}
if event.wheel_has_changed() {
changes.wheel = Some(TabletToolWheelChange {
degrees: event.wheel_delta(),
clicks: event.wheel_delta_discrete(),
});
}
InputEvent::TabletToolChanged {
time_usec: event.time_usec(),
id: self.get_tool_id(event),
changes,
}
}
fn handle_tablet_tool_proximity(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_tool_event);
let id = self.get_tool_id(&event);
if event.proximity_state() == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN {
let Some(tablet_id) = dev.tablet_id.get() else {
return;
};
let tool = event.tool();
dev.event(InputEvent::TabletToolAdded {
time_usec: event.time_usec(),
init: Box::new(TabletToolInit {
tablet_id,
id,
type_: match tool.type_() {
LIBINPUT_TABLET_TOOL_TYPE_PEN => TabletToolType::Pen,
LIBINPUT_TABLET_TOOL_TYPE_ERASER => TabletToolType::Eraser,
LIBINPUT_TABLET_TOOL_TYPE_BRUSH => TabletToolType::Brush,
LIBINPUT_TABLET_TOOL_TYPE_PENCIL => TabletToolType::Pencil,
LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH => TabletToolType::Airbrush,
LIBINPUT_TABLET_TOOL_TYPE_MOUSE => TabletToolType::Mouse,
LIBINPUT_TABLET_TOOL_TYPE_LENS => TabletToolType::Lens,
_ => return,
},
hardware_serial: tool.serial(),
hardware_id_wacom: tool.tool_id(),
capabilities: {
let mut caps = vec![];
macro_rules! add_cap {
($f:ident, $cap:ident) => {
if tool.$f() {
caps.push(TabletToolCapability::$cap);
}
};
}
add_cap!(has_tilt, Tilt);
add_cap!(has_pressure, Pressure);
add_cap!(has_distance, Distance);
add_cap!(has_rotation, Rotation);
add_cap!(has_slider, Slider);
add_cap!(has_wheel, Wheel);
caps
},
}),
});
dev.event(self.build_tablet_tool_changed(&event, None));
} else {
dev.event(InputEvent::TabletToolRemoved {
time_usec: event.time_usec(),
id,
});
}
}
fn handle_tablet_tool_tip(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_tool_event);
let down = match event.tip_state() {
LIBINPUT_TABLET_TOOL_TIP_UP => false,
LIBINPUT_TABLET_TOOL_TIP_DOWN => true,
_ => return,
};
dev.event(self.build_tablet_tool_changed(&event, Some(down)));
}
fn handle_tablet_tool_axis(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_tool_event);
dev.event(self.build_tablet_tool_changed(&event, None));
}
fn handle_tablet_tool_button(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_tool_event);
dev.event(InputEvent::TabletToolButton {
time_usec: event.time_usec(),
id: self.get_tool_id(&event),
button: event.button(),
state: match event.button_state() {
LIBINPUT_BUTTON_STATE_RELEASED => ToolButtonState::Released,
LIBINPUT_BUTTON_STATE_PRESSED => ToolButtonState::Pressed,
_ => return,
},
});
}
fn handle_tablet_pad_button(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_pad_event);
let id = match dev.tablet_pad_id.get() {
None => return,
Some(id) => id,
};
let state = match event.button_state() {
LIBINPUT_BUTTON_STATE_RELEASED => PadButtonState::Released,
LIBINPUT_BUTTON_STATE_PRESSED => PadButtonState::Pressed,
_ => return,
};
dev.event(InputEvent::TabletPadModeSwitch {
time_usec: event.time_usec(),
pad: id,
group: event.mode_group().index(),
mode: event.mode(),
});
dev.event(InputEvent::TabletPadButton {
time_usec: event.time_usec(),
id,
button: event.button_number(),
state,
});
}
fn handle_tablet_pad_ring(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_pad_event);
dev.event(InputEvent::TabletPadRing {
time_usec: event.time_usec(),
pad: match dev.tablet_pad_id.get() {
None => return,
Some(id) => id,
},
ring: event.ring_number(),
source: match event.ring_source() {
LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER => Some(TabletRingEventSource::Finger),
_ => None,
},
angle: match event.ring_position() {
n if n == -1.0 => None,
n => Some(n),
},
});
}
fn handle_tablet_pad_strip(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_pad_event);
dev.event(InputEvent::TabletPadStrip {
time_usec: event.time_usec(),
pad: match dev.tablet_pad_id.get() {
None => return,
Some(id) => id,
},
strip: event.strip_number(),
source: match event.strip_source() {
LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER => Some(TabletStripEventSource::Finger),
_ => None,
},
position: match event.strip_position() {
n if n == -1.0 => None,
n => Some(n),
},
});
}
}

View file

@ -293,6 +293,7 @@ impl MetalBackend {
let devnum = dev.devnum();
let devnode = dev.devnode()?;
let sysname = dev.sysname()?;
let syspath = dev.syspath()?;
log::info!("Device added: {}", devnode.to_bytes().as_bstr());
let mut slots = self.device_holder.input_devices.borrow_mut();
let slot = 'slot: {
@ -305,6 +306,7 @@ impl MetalBackend {
slots.len() - 1
};
let dev = Rc::new(MetalInputDevice {
state: self.state.clone(),
slot,
id: device_id,
devnum,
@ -312,6 +314,7 @@ impl MetalBackend {
inputdev: Default::default(),
devnode: devnode.to_owned(),
_sysname: sysname.to_owned(),
syspath: syspath.to_owned(),
removed: Cell::new(false),
events: Default::default(),
cb: Default::default(),
@ -321,6 +324,8 @@ impl MetalBackend {
desired: Default::default(),
transform_matrix: Default::default(),
effective: Default::default(),
tablet_id: Default::default(),
tablet_pad_id: Default::default(),
});
slots[slot] = Some(dev.clone());
self.device_holder