wayland: implement tablet-v2
This commit is contained in:
parent
86e283d255
commit
7ed499eabd
62 changed files with 5174 additions and 318 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue