1
0
Fork 0
forked from wry/wry

Merge pull request #435 from mahkoh/jorth/tablet-v2

tablet: implement version 2
This commit is contained in:
mahkoh 2025-04-22 23:01:52 +02:00 committed by GitHub
commit 4b9e5e3d25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 427 additions and 34 deletions

View file

@ -188,7 +188,7 @@ Jay supports the following wayland protocols:
| zwp_pointer_gestures_v1 | 3 | |
| zwp_primary_selection_device_manager_v1 | 1 | |
| zwp_relative_pointer_manager_v1 | 1 | |
| zwp_tablet_manager_v2 | 1 | |
| zwp_tablet_manager_v2 | 2 | |
| zwp_text_input_manager_v3 | 1 | |
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
| zxdg_decoration_manager_v1 | 1 | |

View file

@ -3,6 +3,7 @@
- Floating windows can now be configured to be shown above fullscreen windows
by using the `enable-float-above-fullscreen` action.
- Implement xdg-toplevel-tag-v1.
- Implement tablet-v2 version 2.
# 1.10.0 (2025-04-22)

View file

@ -430,6 +430,12 @@ pub enum InputEvent {
source: Option<TabletStripEventSource>,
position: Option<f64>,
},
TabletPadDial {
time_usec: u64,
pad: TabletPadId,
dial: u32,
value120: i32,
},
TouchDown {
time_usec: u64,
id: i32,

View file

@ -697,6 +697,7 @@ impl InputDevice for MetalInputDevice {
name: dev.name(),
pid: dev.product(),
vid: dev.vendor(),
bustype: dev.bustype(),
path: self.syspath.as_bytes().as_bstr().to_string(),
}))
}
@ -718,6 +719,7 @@ impl InputDevice for MetalInputDevice {
let buttons = dev.pad_num_buttons();
let strips = dev.pad_num_strips();
let rings = dev.pad_num_rings();
let dials = dev.pad_num_dials();
let mut groups = vec![];
for n in 0..dev.pad_num_mode_groups() {
let Some(group) = dev.pad_mode_group(n) else {
@ -727,6 +729,7 @@ impl InputDevice for MetalInputDevice {
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(),
dials: (0..dials).filter(|b| group.has_dial(*b)).collect(),
modes: group.num_modes(),
mode: group.mode(),
});
@ -738,6 +741,7 @@ impl InputDevice for MetalInputDevice {
buttons,
strips,
rings,
dials,
groups,
}))
}

View file

@ -71,7 +71,7 @@ impl MetalBackend {
break;
}
Ok(n) if n.intersects(c::POLLERR | c::POLLHUP) => {
log::error!("libinput fd fd is in an error state");
log::error!("libinput fd is in an error state");
break;
}
_ => {}
@ -121,6 +121,7 @@ impl MetalBackend {
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),
c::LIBINPUT_EVENT_TABLET_PAD_DIAL => self.handle_tablet_pad_dial(event),
c::LIBINPUT_EVENT_TOUCH_DOWN => self.handle_touch_down(event),
c::LIBINPUT_EVENT_TOUCH_UP => self.handle_touch_up(event),
c::LIBINPUT_EVENT_TOUCH_MOTION => self.handle_touch_motion(event),
@ -545,6 +546,25 @@ impl MetalBackend {
});
}
fn handle_tablet_pad_dial(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, tablet_pad_event);
let Some(dial) = event.dial_number() else {
return;
};
let Some(value120) = event.dial_delta_v120() else {
return;
};
dev.event(InputEvent::TabletPadDial {
time_usec: event.time_usec(),
pad: match dev.tablet_pad_id.get() {
None => return,
Some(id) => id,
},
dial,
value120: value120 as _,
});
}
fn handle_touch_down(self: &Rc<Self>, event: LibInputEvent) {
let (event, dev) = unpack!(self, event, touch_event);
dev.event(InputEvent::TouchDown {

View file

@ -12,13 +12,13 @@ use {
Axis120, AxisFrame, AxisInverted, AxisPx, AxisSource, AxisStop, Button, HoldBegin,
HoldEnd, Key, Modifiers, PinchBegin, PinchEnd, PinchUpdate, PointerAbs, PointerRel,
SwipeBegin, SwipeEnd, SwipeUpdate, SwitchEvent, TabletPadButton,
TabletPadModeSwitch, TabletPadRingAngle, TabletPadRingFrame, TabletPadRingSource,
TabletPadRingStop, TabletPadStripFrame, TabletPadStripPosition,
TabletPadStripSource, TabletPadStripStop, TabletToolButton, TabletToolDistance,
TabletToolDown, TabletToolFrame, TabletToolMotion, TabletToolPressure,
TabletToolProximityIn, TabletToolProximityOut, TabletToolRotation,
TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel, TouchCancel,
TouchDown, TouchMotion, TouchUp,
TabletPadDialDelta, TabletPadDialFrame, TabletPadModeSwitch, TabletPadRingAngle,
TabletPadRingFrame, TabletPadRingSource, TabletPadRingStop, TabletPadStripFrame,
TabletPadStripPosition, TabletPadStripSource, TabletPadStripStop, TabletToolButton,
TabletToolDistance, TabletToolDown, TabletToolFrame, TabletToolMotion,
TabletToolPressure, TabletToolProximityIn, TabletToolProximityOut,
TabletToolRotation, TabletToolSlider, TabletToolTilt, TabletToolUp,
TabletToolWheel, TouchCancel, TouchDown, TouchMotion, TouchUp,
},
},
},
@ -82,6 +82,11 @@ pub struct PendingTabletPadRing {
stop: bool,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct PendingTabletPadDial {
value120: Option<i32>,
}
async fn run(seat_test: Rc<SeatTest>) {
let tc = &seat_test.tc;
let comp = tc.jay_compositor().await;
@ -586,6 +591,30 @@ async fn run(seat_test: Rc<SeatTest>) {
}
println!();
});
let tt = Rc::new(RefCell::new(PendingTabletPadDial::default()));
TabletPadDialDelta::handle(tc, se, tt.clone(), move |tt, ev| {
tt.borrow_mut().value120 = Some(ev.value120);
});
let st = seat_test.clone();
TabletPadDialFrame::handle(tc, se, tt.clone(), move |tt, ev| {
let tt = tt.take();
if !all && ev.seat != seat {
return;
}
if all {
print!("Seat: {}, ", st.name(ev.seat));
}
print!(
"Time: {:.4}, Device: {}, Dial: {}",
time(ev.time_usec),
ev.input_device,
ev.dial,
);
if let Some(val) = tt.value120 {
print!(", delta: {val}/120");
}
println!();
});
let st = seat_test.clone();
TouchDown::handle(tc, se, (), move |_, ev| {
if all || ev.seat == seat {

View file

@ -470,6 +470,27 @@ impl JaySeatEvents {
});
}
pub fn send_tablet_pad_dial(
&self,
seat: SeatId,
pad: InputDeviceId,
time_usec: u64,
value120: i32,
dial: u32,
) {
self.client.event(TabletPadDialDelta {
self_id: self.id,
value120,
});
self.client.event(TabletPadDialFrame {
self_id: self.id,
seat: seat.raw(),
time_usec,
input_device: pad.raw(),
dial,
});
}
pub fn send_touch_down(&self, seat: SeatId, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
self.client.event(TouchDown {
self_id: self.id,

View file

@ -266,6 +266,7 @@ impl WlSeatGlobal {
| InputEvent::TabletPadModeSwitch { time_usec, .. }
| InputEvent::TabletPadRing { time_usec, .. }
| InputEvent::TabletPadStrip { time_usec, .. }
| InputEvent::TabletPadDial { time_usec, .. }
| InputEvent::TouchFrame { time_usec, .. } => {
self.last_input_usec.set(time_usec);
if self.idle_notifications.is_not_empty() {
@ -314,6 +315,7 @@ impl WlSeatGlobal {
InputEvent::TabletPadModeSwitch { .. } => {}
InputEvent::TabletPadRing { .. } => {}
InputEvent::TabletPadStrip { .. } => {}
InputEvent::TabletPadDial { .. } => {}
InputEvent::TouchDown { .. } => {}
InputEvent::TouchUp { .. } => {}
InputEvent::TouchMotion { .. } => {}
@ -458,6 +460,12 @@ impl WlSeatGlobal {
source,
position,
} => self.tablet_event_pad_strip(pad, strip, source, position, time_usec),
InputEvent::TabletPadDial {
time_usec,
pad,
dial,
value120,
} => self.tablet_event_pad_dial(pad, dial, value120, time_usec),
InputEvent::TouchDown {
time_usec,
id,

View file

@ -7,7 +7,8 @@ use {
WlSeatGlobal,
tablet::{
pad_owner::PadOwnerHolder, tablet_bindings::TabletBindings,
tool_owner::ToolOwnerHolder, zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
tool_owner::ToolOwnerHolder, zwp_tablet_pad_dial_v2::ZwpTabletPadDialV2,
zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2,
zwp_tablet_pad_v2::ZwpTabletPadV2, zwp_tablet_seat_v2::ZwpTabletSeatV2,
@ -35,6 +36,7 @@ mod tablet_bindings;
mod tool;
pub mod tool_owner;
pub mod zwp_tablet_manager_v2;
pub mod zwp_tablet_pad_dial_v2;
pub mod zwp_tablet_pad_group_v2;
pub mod zwp_tablet_pad_ring_v2;
pub mod zwp_tablet_pad_strip_v2;
@ -58,6 +60,7 @@ pub struct TabletInit {
pub name: String,
pub pid: u32,
pub vid: u32,
pub bustype: Option<u32>,
pub path: String,
}
@ -79,6 +82,7 @@ pub struct TabletPadInit {
pub buttons: u32,
pub strips: u32,
pub rings: u32,
pub dials: u32,
pub groups: Vec<TabletPadGroupInit>,
}
@ -87,6 +91,7 @@ pub struct TabletPadGroupInit {
pub buttons: Vec<u32>,
pub rings: Vec<u32>,
pub strips: Vec<u32>,
pub dials: Vec<u32>,
pub modes: u32,
pub mode: u32,
}
@ -112,6 +117,7 @@ pub struct Tablet {
name: String,
pid: u32,
vid: u32,
bustype: Option<u32>,
path: String,
bindings: TabletBindings<ZwpTabletV2>,
tools: CopyHashMap<TabletToolId, Rc<TabletTool>>,
@ -186,6 +192,7 @@ pub struct TabletPad {
groups: Vec<Rc<TabletPadGroup>>,
strips: Vec<Rc<TabletPadStrip>>,
rings: Vec<Rc<TabletPadRing>>,
dials: Vec<Rc<TabletPadDial>>,
node: CloneCell<Rc<dyn Node>>,
pub(super) pad_owner: PadOwnerHolder,
}
@ -196,6 +203,7 @@ pub struct TabletPadGroup {
modes: u32,
rings: Vec<u32>,
strips: Vec<u32>,
dials: Vec<u32>,
bindings: TabletBindings<ZwpTabletPadGroupV2>,
}
@ -207,6 +215,10 @@ pub struct TabletPadRing {
bindings: TabletBindings<ZwpTabletPadRingV2>,
}
pub struct TabletPadDial {
bindings: TabletBindings<ZwpTabletPadDialV2>,
}
#[derive(Copy, Clone, Debug)]
pub enum TabletRingEventSource {
Finger,
@ -269,6 +281,7 @@ impl WlSeatGlobal {
name: init.name.clone(),
pid: init.pid,
vid: init.vid,
bustype: init.bustype,
path: init.path.clone(),
bindings: Default::default(),
tools: Default::default(),
@ -319,6 +332,9 @@ impl WlSeatGlobal {
for strips in &pad.strips {
strips.bindings.clear();
}
for dials in &pad.dials {
dials.bindings.clear();
}
}
}

View file

@ -6,9 +6,10 @@ use {
wl_seat::{
WlSeatGlobal,
tablet::{
PadButtonState, TabletPad, TabletPadGroup, TabletPadId, TabletPadInit,
TabletPadRing, TabletPadStrip, TabletRingEventSource, TabletStripEventSource,
normalizeu, zwp_tablet_pad_v2::ZwpTabletPadV2, zwp_tablet_v2::ZwpTabletV2,
PadButtonState, TabletPad, TabletPadDial, TabletPadGroup, TabletPadId,
TabletPadInit, TabletPadRing, TabletPadStrip, TabletRingEventSource,
TabletStripEventSource, normalizeu, zwp_tablet_pad_v2::ZwpTabletPadV2,
zwp_tablet_v2::ZwpTabletV2,
},
},
wl_surface::WlSurface,
@ -33,6 +34,12 @@ impl WlSeatGlobal {
bindings: Default::default(),
}));
}
let mut dials = Vec::new();
for _ in 0..init.dials {
dials.push(Rc::new(TabletPadDial {
bindings: Default::default(),
}));
}
let mut groups = Vec::new();
for group_init in &init.groups {
groups.push(Rc::new(TabletPadGroup {
@ -41,6 +48,7 @@ impl WlSeatGlobal {
modes: group_init.modes,
rings: group_init.rings.clone(),
strips: group_init.strips.clone(),
dials: group_init.dials.clone(),
bindings: Default::default(),
}));
}
@ -56,6 +64,7 @@ impl WlSeatGlobal {
groups,
strips,
rings,
dials,
node: CloneCell::new(self.state.root.clone()),
pad_owner: Default::default(),
});
@ -161,6 +170,26 @@ impl WlSeatGlobal {
}
}
}
pub fn tablet_event_pad_dial(
self: &Rc<Self>,
pad: TabletPadId,
dial: u32,
value120: i32,
time_usec: u64,
) {
if let Some(pad) = self.tablet.pads.get(&pad) {
self.state.for_each_seat_tester(|t| {
t.send_tablet_pad_dial(self.id, pad.dev, time_usec, value120, dial)
});
if pad.tablet.is_some() {
if let Some(dial) = pad.dials.get(dial as usize) {
let node = self.keyboard_node.get();
node.node_on_tablet_pad_dial(&pad, dial, value120, time_usec);
}
}
}
}
}
impl TabletPad {
@ -235,6 +264,22 @@ impl TabletPad {
});
}
pub fn surface_dial(
self: &Rc<Self>,
n: &WlSurface,
dial: &Rc<TabletPadDial>,
value120: i32,
time_usec: u64,
) {
let time = usec_to_msec(time_usec);
self.seat.tablet_for_each_seat(n, |s| {
if let Some(dial) = dial.bindings.get(&s) {
dial.send_delta(value120);
dial.send_frame(time);
}
});
}
pub fn surface_strip(
self: &Rc<Self>,
n: &WlSurface,

View file

@ -57,7 +57,7 @@ impl Global for ZwpTabletManagerV2Global {
}
fn version(&self) -> u32 {
1
2
}
}

View file

@ -0,0 +1,74 @@
use {
crate::{
client::{Client, ClientError},
ifs::wl_seat::tablet::{TabletPadDial, zwp_tablet_seat_v2::ZwpTabletSeatV2},
leaks::Tracker,
object::{Object, Version},
wire::{ZwpTabletPadDialV2Id, zwp_tablet_pad_dial_v2::*},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwpTabletPadDialV2 {
pub id: ZwpTabletPadDialV2Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub seat: Rc<ZwpTabletSeatV2>,
pub dial: Rc<TabletPadDial>,
}
impl ZwpTabletPadDialV2 {
pub fn detach(&self) {
self.dial.bindings.remove(&self.seat);
}
pub fn send_delta(&self, value120: i32) {
self.client.event(Delta {
self_id: self.id,
value120,
});
}
pub fn send_frame(&self, time: u32) {
self.client.event(Frame {
self_id: self.id,
time,
});
}
}
impl ZwpTabletPadDialV2RequestHandler for ZwpTabletPadDialV2 {
type Error = ZwpTabletPadDialV2Error;
fn set_feedback(&self, _req: SetFeedback<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.detach();
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = ZwpTabletPadDialV2;
version = self.version;
}
impl Object for ZwpTabletPadDialV2 {
fn break_loops(&self) {
self.detach();
}
}
simple_add_obj!(ZwpTabletPadDialV2);
#[derive(Debug, Error)]
pub enum ZwpTabletPadDialV2Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(ZwpTabletPadDialV2Error, ClientError);

View file

@ -2,7 +2,8 @@ use {
crate::{
client::{Client, ClientError},
ifs::wl_seat::tablet::{
TabletPadGroup, zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
TabletPadGroup, zwp_tablet_pad_dial_v2::ZwpTabletPadDialV2,
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_seat_v2::ZwpTabletSeatV2,
},
leaks::Tracker,
@ -48,6 +49,13 @@ impl ZwpTabletPadGroupV2 {
});
}
pub fn send_dial(&self, dial: &ZwpTabletPadDialV2) {
self.client.event(Dial {
self_id: self.id,
dial: dial.id,
});
}
pub fn send_modes(&self, modes: u32) {
self.client.event(Modes {
self_id: self.id,

View file

@ -4,7 +4,8 @@ use {
ifs::wl_seat::{
WlSeatGlobal,
tablet::{
Tablet, TabletPad, TabletTool, zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
Tablet, TabletPad, TabletTool, zwp_tablet_pad_dial_v2::ZwpTabletPadDialV2,
zwp_tablet_pad_group_v2::ZwpTabletPadGroupV2,
zwp_tablet_pad_ring_v2::ZwpTabletPadRingV2,
zwp_tablet_pad_strip_v2::ZwpTabletPadStripV2, zwp_tablet_pad_v2::ZwpTabletPadV2,
zwp_tablet_tool_v2::ZwpTabletToolV2, zwp_tablet_v2::ZwpTabletV2,
@ -18,6 +19,9 @@ use {
thiserror::Error,
};
const BUSTYPE_SINCE: Version = Version(2);
const DIALS_SINCE: Version = Version(2);
pub struct ZwpTabletSeatV2 {
pub id: ZwpTabletSeatV2Id,
pub client: Rc<Client>,
@ -53,6 +57,11 @@ impl ZwpTabletSeatV2 {
obj.send_name(&tablet.name);
obj.send_id(tablet.vid, tablet.pid);
obj.send_path(&tablet.path);
if obj.version >= BUSTYPE_SINCE {
if let Some(bustype) = tablet.bustype {
obj.send_bustype(bustype);
}
}
obj.send_done();
tablet.bindings.add(self, &obj);
}
@ -161,6 +170,25 @@ impl ZwpTabletSeatV2 {
group_obj.send_strip(&strip_obj);
strip.bindings.add(self, &strip_obj);
}
if self.version >= DIALS_SINCE {
for dial in &group.dials {
let Some(dial) = pad.dials.get(*dial as usize) else {
continue;
};
let dial_obj = Rc::new(ZwpTabletPadDialV2 {
id: id!(),
client: self.client.clone(),
seat: self.clone(),
tracker: Default::default(),
version: self.version,
dial: dial.clone(),
});
track!(self.client, dial_obj);
self.client.add_server_obj(&dial_obj);
group_obj.send_dial(&dial_obj);
dial.bindings.add(self, &dial_obj);
}
}
group_obj.send_done();
}
obj.send_done();

View file

@ -53,6 +53,13 @@ impl ZwpTabletV2 {
pub fn send_removed(&self) {
self.client.event(Removed { self_id: self.id });
}
pub fn send_bustype(&self, bustype: u32) {
self.client.event(Bustype {
self_id: self.id,
bustype,
});
}
}
impl ZwpTabletV2RequestHandler for ZwpTabletV2 {

View file

@ -39,9 +39,9 @@ use {
wl_seat::{
Dnd, NodeSeatState, SeatId, WlSeatGlobal,
tablet::{
PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip,
TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges,
ToolButtonState,
PadButtonState, TabletPad, TabletPadDial, TabletPadGroup, TabletPadRing,
TabletPadStrip, TabletRingEventSource, TabletStripEventSource, TabletTool,
TabletToolChanges, ToolButtonState,
},
text_input::TextInputConnection,
wl_pointer::PendingScroll,
@ -2016,6 +2016,16 @@ impl Node for WlSurface {
pad.surface_strip(self, strip, source, position, time_usec);
}
fn node_on_tablet_pad_dial(
&self,
pad: &Rc<TabletPad>,
dial: &Rc<TabletPadDial>,
value120: i32,
time_usec: u64,
) {
pad.surface_dial(self, dial, value120, time_usec);
}
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, time_usec: u64) {
tool.surface_leave(self, time_usec);
}

View file

@ -140,6 +140,7 @@ cenum! {
LIBINPUT_EVENT_TABLET_PAD_RING = 701,
LIBINPUT_EVENT_TABLET_PAD_STRIP = 702,
LIBINPUT_EVENT_TABLET_PAD_KEY = 703,
LIBINPUT_EVENT_TABLET_PAD_DIAL = 704,
LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800,
LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE = 801,
LIBINPUT_EVENT_GESTURE_SWIPE_END = 802,

View file

@ -25,18 +25,19 @@ use {
libinput_device_config_tap_set_drag_enabled,
libinput_device_config_tap_set_drag_lock_enabled,
libinput_device_config_tap_set_enabled, libinput_device_get_device_group,
libinput_device_get_id_product, libinput_device_get_id_vendor,
libinput_device_get_name, libinput_device_get_user_data, libinput_device_group,
libinput_device_group_get_user_data, libinput_device_group_set_user_data,
libinput_device_has_capability, libinput_device_set_user_data,
libinput_device_tablet_pad_get_mode_group, libinput_device_tablet_pad_get_num_buttons,
libinput_device_get_id_bustype, libinput_device_get_id_product,
libinput_device_get_id_vendor, libinput_device_get_name, libinput_device_get_user_data,
libinput_device_group, libinput_device_group_get_user_data,
libinput_device_group_set_user_data, libinput_device_has_capability,
libinput_device_set_user_data, libinput_device_tablet_pad_get_mode_group,
libinput_device_tablet_pad_get_num_buttons, libinput_device_tablet_pad_get_num_dials,
libinput_device_tablet_pad_get_num_mode_groups,
libinput_device_tablet_pad_get_num_rings, libinput_device_tablet_pad_get_num_strips,
libinput_device_unref, libinput_path_remove_device, libinput_tablet_pad_mode_group,
libinput_tablet_pad_mode_group_get_index, libinput_tablet_pad_mode_group_get_mode,
libinput_tablet_pad_mode_group_get_num_modes,
libinput_tablet_pad_mode_group_has_button, libinput_tablet_pad_mode_group_has_ring,
libinput_tablet_pad_mode_group_has_strip,
libinput_tablet_pad_mode_group_has_button, libinput_tablet_pad_mode_group_has_dial,
libinput_tablet_pad_mode_group_has_ring, libinput_tablet_pad_mode_group_has_strip,
},
},
bstr::ByteSlice,
@ -223,6 +224,10 @@ impl<'a> LibInputDevice<'a> {
unsafe { libinput_device_get_id_vendor(self.dev) as u32 }
}
pub fn bustype(&self) -> Option<u32> {
libinput_device_get_id_bustype.map(|f| unsafe { f(self.dev) as u32 })
}
pub fn pad_num_buttons(&self) -> u32 {
match unsafe { libinput_device_tablet_pad_get_num_buttons(self.dev) } {
-1 => 0,
@ -244,6 +249,17 @@ impl<'a> LibInputDevice<'a> {
}
}
pub fn pad_num_dials(&self) -> u32 {
match unsafe {
libinput_device_tablet_pad_get_num_dials
.map(|f| f(self.dev))
.unwrap_or_default()
} {
-1 => 0,
n => n as u32,
}
}
pub fn pad_num_mode_groups(&self) -> u32 {
match unsafe { libinput_device_tablet_pad_get_num_mode_groups(self.dev) } {
-1 => 0,
@ -316,6 +332,15 @@ impl<'a> LibInputTabletPadModeGroup<'a> {
pub fn has_strip(&self, strip: u32) -> bool {
unsafe { libinput_tablet_pad_mode_group_has_strip(self.group, strip as _) != 0 }
}
pub fn has_dial(&self, dial: u32) -> bool {
unsafe {
libinput_tablet_pad_mode_group_has_dial
.map(|f| f(self.group, dial as _))
.unwrap_or_default()
!= 0
}
}
}
impl RegisteredDevice {

View file

@ -27,7 +27,9 @@ use {
libinput_event_switch, libinput_event_switch_get_switch,
libinput_event_switch_get_switch_state, libinput_event_switch_get_time_usec,
libinput_event_tablet_pad, libinput_event_tablet_pad_get_button_number,
libinput_event_tablet_pad_get_button_state, libinput_event_tablet_pad_get_mode,
libinput_event_tablet_pad_get_button_state,
libinput_event_tablet_pad_get_dial_delta_v120,
libinput_event_tablet_pad_get_dial_number, libinput_event_tablet_pad_get_mode,
libinput_event_tablet_pad_get_mode_group, libinput_event_tablet_pad_get_ring_number,
libinput_event_tablet_pad_get_ring_position, libinput_event_tablet_pad_get_ring_source,
libinput_event_tablet_pad_get_strip_number,
@ -459,6 +461,14 @@ impl<'a> LibInputEventTabletPad<'a> {
unsafe { TabletPadStripAxisSource(libinput_event_tablet_pad_get_strip_source(self.event)) }
}
pub fn dial_number(&self) -> Option<u32> {
libinput_event_tablet_pad_get_dial_number.map(|f| unsafe { f(self.event) as u32 })
}
pub fn dial_delta_v120(&self) -> Option<f64> {
libinput_event_tablet_pad_get_dial_delta_v120.map(|f| unsafe { f(self.event) })
}
pub fn button_number(&self) -> u32 {
unsafe { libinput_event_tablet_pad_get_button_number(self.event) }
}

View file

@ -1,4 +1,4 @@
use uapi::c;
use {libloading::os::unix::Library, std::sync::LazyLock, uapi::c};
include!(concat!(env!("OUT_DIR"), "/libinput_tys.rs"));
@ -393,3 +393,38 @@ pub struct libinput_interface {
) -> c::c_int,
pub close_restricted: unsafe extern "C" fn(fd: c::c_int, user_data: *mut c::c_void),
}
macro_rules! dynload {
(
$(
fn $name:ident($($arg:ident: $ty:ty),* $(,)?) -> $ret:ty;
)*
) => {
$(
#[expect(non_upper_case_globals)]
pub static $name: LazyLock<Option<unsafe extern "C" fn($($arg: $ty),*) -> $ret>> = LazyLock::new(|| {
unsafe {
Library::this()
.get(concat!(stringify!($name), "\0").as_bytes())
.ok()
.map(|sym| *sym)
}
});
)*
};
}
dynload! {
fn libinput_device_get_id_bustype(device: *mut libinput_device) -> c::c_uint;
fn libinput_event_tablet_pad_get_dial_delta_v120(event: *mut libinput_event_tablet_pad) -> f64;
fn libinput_event_tablet_pad_get_dial_number(event: *mut libinput_event_tablet_pad) -> c::c_uint;
fn libinput_device_tablet_pad_get_num_dials(device: *mut libinput_device) -> c::c_int;
fn libinput_tablet_pad_mode_group_has_dial(
group: *mut libinput_tablet_pad_mode_group,
dial: c::c_uint,
) -> c::c_int;
}

View file

@ -7,9 +7,9 @@ use {
wl_seat::{
Dnd, NodeSeatState, WlSeatGlobal,
tablet::{
PadButtonState, TabletPad, TabletPadGroup, TabletPadRing, TabletPadStrip,
TabletRingEventSource, TabletStripEventSource, TabletTool, TabletToolChanges,
ToolButtonState,
PadButtonState, TabletPad, TabletPadDial, TabletPadGroup, TabletPadRing,
TabletPadStrip, TabletRingEventSource, TabletStripEventSource, TabletTool,
TabletToolChanges, ToolButtonState,
},
wl_pointer::PendingScroll,
},
@ -464,6 +464,19 @@ pub trait Node: 'static {
let _ = position;
}
fn node_on_tablet_pad_dial(
&self,
pad: &Rc<TabletPad>,
dial: &Rc<TabletPadDial>,
value120: i32,
time_usec: u64,
) {
let _ = pad;
let _ = time_usec;
let _ = dial;
let _ = value120;
}
fn node_on_tablet_tool_leave(&self, tool: &Rc<TabletTool>, time_usec: u64) {
let _ = tool;
let _ = time_usec;

View file

@ -100,9 +100,7 @@ impl Action {
SimpleCommand::SetFloatAboveFullscreen(bool) => {
B::new(move || set_float_above_fullscreen(bool))
}
SimpleCommand::ToggleFloatAboveFullscreen => {
B::new(|| toggle_float_above_fullscreen())
}
SimpleCommand::ToggleFloatAboveFullscreen => B::new(toggle_float_above_fullscreen),
},
Action::Multi { actions } => {
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();

View file

@ -267,3 +267,14 @@ event touch_cancel {
time_usec: pod(u64),
id: i32,
}
event tablet_pad_dial_delta {
value120: i32,
}
event tablet_pad_dial_frame {
seat: u32,
time_usec: pod(u64),
input_device: u32,
dial: u32,
}

View file

@ -0,0 +1,15 @@
request set_feedback {
description: str,
serial: u32,
}
request destroy {
}
event delta {
value120: i32,
}
event frame {
time: u32,
}

View file

@ -25,3 +25,7 @@ event mode_switch {
serial: u32,
mode: u32,
}
event dial {
dial: id(zwp_tablet_pad_dial_v2),
}

View file

@ -19,3 +19,7 @@ event done {
event removed {
}
event bustype {
bustype: u32,
}