From dee0066f1addb390a0ae057d402112293441d739 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 22 Apr 2025 22:22:31 +0200 Subject: [PATCH] tablet: implement version 2 --- docs/features.md | 2 +- release-notes.md | 1 + src/backend.rs | 6 ++ src/backends/metal.rs | 4 + src/backends/metal/input.rs | 22 +++++- src/cli/seat_test.rs | 43 +++++++++-- src/ifs/jay_seat_events.rs | 21 ++++++ src/ifs/wl_seat/event_handling.rs | 8 ++ src/ifs/wl_seat/tablet.rs | 18 ++++- src/ifs/wl_seat/tablet/pad.rs | 51 ++++++++++++- .../wl_seat/tablet/zwp_tablet_manager_v2.rs | 2 +- .../wl_seat/tablet/zwp_tablet_pad_dial_v2.rs | 74 +++++++++++++++++++ .../wl_seat/tablet/zwp_tablet_pad_group_v2.rs | 10 ++- src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs | 30 +++++++- src/ifs/wl_seat/tablet/zwp_tablet_v2.rs | 7 ++ src/ifs/wl_surface.rs | 16 +++- src/libinput/consts.rs | 1 + src/libinput/device.rs | 39 ++++++++-- src/libinput/event.rs | 12 ++- src/libinput/sys.rs | 37 +++++++++- src/tree.rs | 19 ++++- wire/jay_seat_events.txt | 11 +++ wire/zwp_tablet_pad_dial_v2.txt | 15 ++++ wire/zwp_tablet_pad_group_v2.txt | 4 + wire/zwp_tablet_v2.txt | 4 + 25 files changed, 426 insertions(+), 31 deletions(-) create mode 100644 src/ifs/wl_seat/tablet/zwp_tablet_pad_dial_v2.rs create mode 100644 wire/zwp_tablet_pad_dial_v2.txt diff --git a/docs/features.md b/docs/features.md index 0d931716..902cc15e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -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 | | diff --git a/release-notes.md b/release-notes.md index 06b5bce8..44409299 100644 --- a/release-notes.md +++ b/release-notes.md @@ -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) diff --git a/src/backend.rs b/src/backend.rs index 6d70597e..fa3ad5ce 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -430,6 +430,12 @@ pub enum InputEvent { source: Option, position: Option, }, + TabletPadDial { + time_usec: u64, + pad: TabletPadId, + dial: u32, + value120: i32, + }, TouchDown { time_usec: u64, id: i32, diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 731d3a15..48d2ddf2 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -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, })) } diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index ec81fe7e..4456ede6 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -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, 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, event: LibInputEvent) { let (event, dev) = unpack!(self, event, touch_event); dev.event(InputEvent::TouchDown { diff --git a/src/cli/seat_test.rs b/src/cli/seat_test.rs index 1788bd47..ce8404d0 100644 --- a/src/cli/seat_test.rs +++ b/src/cli/seat_test.rs @@ -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, +} + async fn run(seat_test: Rc) { let tc = &seat_test.tc; let comp = tc.jay_compositor().await; @@ -586,6 +591,30 @@ async fn run(seat_test: Rc) { } 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 { diff --git a/src/ifs/jay_seat_events.rs b/src/ifs/jay_seat_events.rs index 784b55c9..eabdad72 100644 --- a/src/ifs/jay_seat_events.rs +++ b/src/ifs/jay_seat_events.rs @@ -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, diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 726b4415..956dbad8 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -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, diff --git a/src/ifs/wl_seat/tablet.rs b/src/ifs/wl_seat/tablet.rs index 9fa2c277..3148f040 100644 --- a/src/ifs/wl_seat/tablet.rs +++ b/src/ifs/wl_seat/tablet.rs @@ -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, 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, } @@ -87,6 +91,7 @@ pub struct TabletPadGroupInit { pub buttons: Vec, pub rings: Vec, pub strips: Vec, + pub dials: Vec, pub modes: u32, pub mode: u32, } @@ -112,6 +117,7 @@ pub struct Tablet { name: String, pid: u32, vid: u32, + bustype: Option, path: String, bindings: TabletBindings, tools: CopyHashMap>, @@ -186,6 +192,7 @@ pub struct TabletPad { groups: Vec>, strips: Vec>, rings: Vec>, + dials: Vec>, node: CloneCell>, pub(super) pad_owner: PadOwnerHolder, } @@ -196,6 +203,7 @@ pub struct TabletPadGroup { modes: u32, rings: Vec, strips: Vec, + dials: Vec, bindings: TabletBindings, } @@ -207,6 +215,10 @@ pub struct TabletPadRing { bindings: TabletBindings, } +pub struct TabletPadDial { + bindings: TabletBindings, +} + #[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(); + } } } diff --git a/src/ifs/wl_seat/tablet/pad.rs b/src/ifs/wl_seat/tablet/pad.rs index 749146c6..1ea1c28b 100644 --- a/src/ifs/wl_seat/tablet/pad.rs +++ b/src/ifs/wl_seat/tablet/pad.rs @@ -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, + 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, + n: &WlSurface, + dial: &Rc, + 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, n: &WlSurface, diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs index 42c33b8d..101ccf85 100644 --- a/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs +++ b/src/ifs/wl_seat/tablet/zwp_tablet_manager_v2.rs @@ -57,7 +57,7 @@ impl Global for ZwpTabletManagerV2Global { } fn version(&self) -> u32 { - 1 + 2 } } diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_pad_dial_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_pad_dial_v2.rs new file mode 100644 index 00000000..f726bf28 --- /dev/null +++ b/src/ifs/wl_seat/tablet/zwp_tablet_pad_dial_v2.rs @@ -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, + pub tracker: Tracker, + pub version: Version, + pub seat: Rc, + pub dial: Rc, +} + +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) -> Result<(), Self::Error> { + Ok(()) + } + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> 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), +} +efrom!(ZwpTabletPadDialV2Error, ClientError); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs index f992b810..b7b5a72f 100644 --- a/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs +++ b/src/ifs/wl_seat/tablet/zwp_tablet_pad_group_v2.rs @@ -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, diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs index 9be60e55..6055afe3 100644 --- a/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs +++ b/src/ifs/wl_seat/tablet/zwp_tablet_seat_v2.rs @@ -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, @@ -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(); diff --git a/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs b/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs index a5a90158..d0ce3287 100644 --- a/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs +++ b/src/ifs/wl_seat/tablet/zwp_tablet_v2.rs @@ -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 { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 82e71ad1..ea698381 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -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, + dial: &Rc, + value120: i32, + time_usec: u64, + ) { + pad.surface_dial(self, dial, value120, time_usec); + } + fn node_on_tablet_tool_leave(&self, tool: &Rc, time_usec: u64) { tool.surface_leave(self, time_usec); } diff --git a/src/libinput/consts.rs b/src/libinput/consts.rs index 0766b764..fac6b120 100644 --- a/src/libinput/consts.rs +++ b/src/libinput/consts.rs @@ -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, diff --git a/src/libinput/device.rs b/src/libinput/device.rs index 473c2b37..68d89638 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -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 { + 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 { diff --git a/src/libinput/event.rs b/src/libinput/event.rs index abd87765..8af7ff4a 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -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 { + libinput_event_tablet_pad_get_dial_number.map(|f| unsafe { f(self.event) as u32 }) + } + + pub fn dial_delta_v120(&self) -> Option { + 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) } } diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index f1d660d9..f53bb4ab 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -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 $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; +} diff --git a/src/tree.rs b/src/tree.rs index 21c7ba20..136f1260 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -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, + dial: &Rc, + value120: i32, + time_usec: u64, + ) { + let _ = pad; + let _ = time_usec; + let _ = dial; + let _ = value120; + } + fn node_on_tablet_tool_leave(&self, tool: &Rc, time_usec: u64) { let _ = tool; let _ = time_usec; diff --git a/wire/jay_seat_events.txt b/wire/jay_seat_events.txt index 70e01eab..ce08ebb2 100644 --- a/wire/jay_seat_events.txt +++ b/wire/jay_seat_events.txt @@ -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, +} diff --git a/wire/zwp_tablet_pad_dial_v2.txt b/wire/zwp_tablet_pad_dial_v2.txt new file mode 100644 index 00000000..e7d99bc0 --- /dev/null +++ b/wire/zwp_tablet_pad_dial_v2.txt @@ -0,0 +1,15 @@ +request set_feedback { + description: str, + serial: u32, +} + +request destroy { +} + +event delta { + value120: i32, +} + +event frame { + time: u32, +} diff --git a/wire/zwp_tablet_pad_group_v2.txt b/wire/zwp_tablet_pad_group_v2.txt index 320e165a..a7ec7fb1 100644 --- a/wire/zwp_tablet_pad_group_v2.txt +++ b/wire/zwp_tablet_pad_group_v2.txt @@ -25,3 +25,7 @@ event mode_switch { serial: u32, mode: u32, } + +event dial { + dial: id(zwp_tablet_pad_dial_v2), +} diff --git a/wire/zwp_tablet_v2.txt b/wire/zwp_tablet_v2.txt index 5c758d0d..6d377f6a 100644 --- a/wire/zwp_tablet_v2.txt +++ b/wire/zwp_tablet_v2.txt @@ -19,3 +19,7 @@ event done { event removed { } + +event bustype { + bustype: u32, +}