diff --git a/docs/features.md b/docs/features.md index 12605549..4bca24ce 100644 --- a/docs/features.md +++ b/docs/features.md @@ -141,7 +141,7 @@ Jay supports the following wayland protocols: | ext_session_lock_manager_v1 | 1 | Yes | | ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes | | org_kde_kwin_server_decoration_manager | 1 | | -| wl_compositor | 6[^no_touch] | | +| wl_compositor | 6 | | | wl_data_device_manager | 3 | | | wl_drm | 2 | | | wl_output | 4 | | @@ -179,12 +179,5 @@ Jay supports the following wayland protocols: | zxdg_decoration_manager_v1 | 1 | | | zxdg_output_manager_v1 | 3 | | -[^no_touch]: Touch input is not supported. [^lsaccess]: Sandboxes can restrict access to this protocol. [^ts_rejected]: Seat creation is always rejected. - -## Missing Features - -The following features are currently not supported but might get implemented in the future: - -- Touch support. diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 8b7f9115..cc2bf7f3 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -897,6 +897,10 @@ impl Client { self.send(&ClientMessage::SetTransformMatrix { device, matrix }) } + pub fn set_calibration_matrix(&self, device: InputDevice, matrix: [[f32; 3]; 2]) { + self.send(&ClientMessage::SetCalibrationMatrix { device, matrix }) + } + pub fn set_px_per_wheel_scroll(&self, device: InputDevice, px: f64) { self.send(&ClientMessage::SetPxPerWheelScroll { device, px }) } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 573c83d6..e53d2350 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -502,6 +502,10 @@ pub enum ClientMessage<'a> { connector: Option, mode: TearingMode, }, + SetCalibrationMatrix { + device: InputDevice, + matrix: [[f32; 3]; 2], + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index e472640c..349a868a 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -80,6 +80,13 @@ impl InputDevice { get!().set_transform_matrix(self, matrix); } + /// Sets the calibration matrix of the device. + /// + /// This corresponds to the libinput setting of the same name. + pub fn set_calibration_matrix(self, matrix: [[f32; 3]; 2]) { + get!().set_calibration_matrix(self, matrix); + } + /// Returns the name of the device. pub fn name(self) -> String { get!(String::new()).device_name(self) diff --git a/release-notes.md b/release-notes.md index 0ec87cae..e7e1abd0 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,6 +3,7 @@ - Add fine-grained damage tracking. - Add support for adaptive sync. - Add support for tearing. +- Add support for touch input. # 1.4.0 (2024-07-07) diff --git a/src/backend.rs b/src/backend.rs index f3e40ff9..1ede68ed 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -167,6 +167,12 @@ pub trait InputDevice { None } fn set_transform_matrix(&self, matrix: TransformMatrix); + fn calibration_matrix(&self) -> Option<[[f32; 3]; 2]> { + None + } + fn set_calibration_matrix(&self, m: [[f32; 3]; 2]) { + let _ = m; + } fn name(&self) -> Rc; fn dev_t(&self) -> Option { None @@ -392,6 +398,27 @@ pub enum InputEvent { source: Option, position: Option, }, + TouchDown { + time_usec: u64, + id: i32, + x_normed: Fixed, + y_normed: Fixed, + }, + TouchUp { + time_usec: u64, + id: i32, + }, + TouchMotion { + time_usec: u64, + id: i32, + x_normed: Fixed, + y_normed: Fixed, + }, + TouchCancel { + time_usec: u64, + id: i32, + }, + TouchFrame, } pub enum DrmEvent { diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 880b18da..c40cd371 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -376,6 +376,7 @@ struct InputDeviceProperties { drag_enabled: Cell>, drag_lock_enabled: Cell>, natural_scrolling_enabled: Cell>, + calibration_matrix: Cell>, } #[derive(Clone)] @@ -436,6 +437,9 @@ impl MetalInputDevice { if let Some(enabled) = self.desired.natural_scrolling_enabled.get() { self.set_natural_scrolling_enabled(enabled); } + if let Some(lh) = self.desired.calibration_matrix.get() { + self.set_calibration_matrix(lh); + } self.fetch_effective(); } @@ -465,6 +469,11 @@ impl MetalInputDevice { .natural_scrolling_enabled .set(Some(device.natural_scrolling_enabled())); } + if device.has_calibration_matrix() { + self.effective + .calibration_matrix + .set(Some(device.get_calibration_matrix())); + } } fn pre_pause(&self) { @@ -721,6 +730,22 @@ impl InputDevice for MetalInputDevice { groups, })) } + + fn calibration_matrix(&self) -> Option<[[f32; 3]; 2]> { + self.effective.calibration_matrix.get() + } + + fn set_calibration_matrix(&self, m: [[f32; 3]; 2]) { + self.desired.calibration_matrix.set(Some(m)); + if let Some(dev) = self.inputdev.get() { + if dev.device().has_calibration_matrix() { + dev.device().set_calibration_matrix(m); + self.effective + .calibration_matrix + .set(Some(dev.device().get_calibration_matrix())); + } + } + } } impl MetalInputDevice { diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index 18911d2e..8f638a79 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -121,6 +121,11 @@ 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_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), + c::LIBINPUT_EVENT_TOUCH_CANCEL => self.handle_touch_cancel(event), + c::LIBINPUT_EVENT_TOUCH_FRAME => self.handle_touch_frame(event), _ => {} } } @@ -539,4 +544,45 @@ impl MetalBackend { }, }); } + + fn handle_touch_down(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::TouchDown { + time_usec: event.time_usec(), + id: event.seat_slot(), + x_normed: Fixed::from_f64(event.x_transformed(1)), + y_normed: Fixed::from_f64(event.y_transformed(1)), + }) + } + + fn handle_touch_up(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::TouchUp { + time_usec: event.time_usec(), + id: event.seat_slot(), + }) + } + + fn handle_touch_motion(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::TouchMotion { + time_usec: event.time_usec(), + id: event.seat_slot(), + x_normed: Fixed::from_f64(event.x_transformed(1)), + y_normed: Fixed::from_f64(event.y_transformed(1)), + }) + } + + fn handle_touch_cancel(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::TouchCancel { + time_usec: event.time_usec(), + id: event.seat_slot(), + }) + } + + fn handle_touch_frame(self: &Rc, event: LibInputEvent) { + let (_, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::TouchFrame) + } } diff --git a/src/cli/input.rs b/src/cli/input.rs index 280ee485..b41c2c4b 100644 --- a/src/cli/input.rs +++ b/src/cli/input.rs @@ -131,6 +131,8 @@ pub enum DeviceCommand { MapToOutput(MapToOutputArgs), /// Removes the mapping from this device to an output. RemoveMapping, + /// Set the calibration matrix. + SetCalibrationMatrix(SetCalibrationMatrixArgs), } #[derive(ValueEnum, Debug, Clone)] @@ -200,6 +202,16 @@ pub struct SetTransformMatrixArgs { pub m22: f64, } +#[derive(Args, Debug, Clone)] +pub struct SetCalibrationMatrixArgs { + pub m00: f32, + pub m01: f32, + pub m02: f32, + pub m10: f32, + pub m11: f32, + pub m12: f32, +} + #[derive(Args, Debug, Clone)] pub struct MapToOutputArgs { /// The output to map to. @@ -272,6 +284,7 @@ struct InputDevice { pub px_per_wheel_scroll: Option, pub transform_matrix: Option<[[f64; 2]; 2]>, pub output: Option, + pub calibration_matrix: Option<[[f32; 3]; 2]>, } #[derive(Clone, Debug, Default)] @@ -595,6 +608,21 @@ impl Input { output: None, }); } + DeviceCommand::SetCalibrationMatrix(a) => { + self.handle_error(input, |e| { + eprintln!("Could not modify the calibration matrix: {}", e); + }); + tc.send(jay_input::SetCalibrationMatrix { + self_id: input, + id: args.device, + m00: a.m00, + m01: a.m01, + m02: a.m02, + m10: a.m10, + m11: a.m11, + m12: a.m12, + }); + } } tc.round_trip().await; } @@ -728,6 +756,9 @@ impl Input { if let Some(v) = &device.output { println!("{prefix} mapped to output: {}", v); } + if let Some(v) = &device.calibration_matrix { + println!("{prefix} calibration matrix: {:?}", v); + } } async fn get(self: &Rc, input: JayInputId) -> Data { @@ -792,6 +823,7 @@ impl Input { px_per_wheel_scroll: is_pointer.then_some(msg.px_per_wheel_scroll), transform_matrix: uapi::pod_read(msg.transform_matrix).ok(), output: None, + calibration_matrix: None, }); }); jay_input::InputDeviceOutput::handle(tc, input, data.clone(), |data, msg| { @@ -800,6 +832,13 @@ impl Input { last.output = Some(msg.output.to_string()); } }); + jay_input::CalibrationMatrix::handle(tc, input, data.clone(), |data, msg| { + let mut data = data.borrow_mut(); + if let Some(last) = data.input_device.last_mut() { + last.calibration_matrix = + Some([[msg.m00, msg.m01, msg.m02], [msg.m10, msg.m11, msg.m12]]); + } + }); tc.round_trip().await; let x = data.borrow_mut().clone(); x diff --git a/src/cli/seat_test.rs b/src/cli/seat_test.rs index cdc8422e..8a523971 100644 --- a/src/cli/seat_test.rs +++ b/src/cli/seat_test.rs @@ -15,7 +15,8 @@ use { TabletPadStripSource, TabletPadStripStop, TabletToolButton, TabletToolDistance, TabletToolDown, TabletToolFrame, TabletToolMotion, TabletToolPressure, TabletToolProximityIn, TabletToolProximityOut, TabletToolRotation, - TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel, + TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel, TouchCancel, + TouchDown, TouchMotion, TouchUp, }, }, }, @@ -583,6 +584,54 @@ async fn run(seat_test: Rc) { } println!(); }); + let st = seat_test.clone(); + TouchDown::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!( + "Time: {:.4}, Touch: {}, Down: {}x{}", + time(ev.time_usec), + ev.id, + ev.x, + ev.y + ); + } + }); + let st = seat_test.clone(); + TouchUp::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!("Time: {:.4}, Touch: {}, Up", time(ev.time_usec), ev.id); + } + }); + let st = seat_test.clone(); + TouchMotion::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!( + "Time: {:.4}, Touch: {} Motion: {}x{}", + time(ev.time_usec), + ev.id, + ev.x, + ev.y + ); + } + }); + let st = seat_test.clone(); + TouchCancel::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!("Time: {:.4}, Touch: {}, Cancel", time(ev.time_usec), ev.id); + } + }); pending::<()>().await; } diff --git a/src/config/handler.rs b/src/config/handler.rs index 2e471910..5b9e5396 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -689,6 +689,16 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_set_calibration_matrix( + &self, + device: InputDevice, + matrix: [[f32; 3]; 2], + ) -> Result<(), CphError> { + let dev = self.get_device_handler_data(device)?; + dev.device.set_calibration_matrix(matrix); + Ok(()) + } + fn handle_get_workspace(&self, name: &str) { let name = Rc::new(name.to_owned()); let ws = match self.workspaces_by_name.get(&name) { @@ -1897,6 +1907,9 @@ impl ConfigProxyHandler { ClientMessage::SetTearingMode { connector, mode } => self .handle_set_tearing_mode(connector, mode) .wrn("set_tearing_mode")?, + ClientMessage::SetCalibrationMatrix { device, matrix } => self + .handle_set_calibration_matrix(device, matrix) + .wrn("set_calibration_matrix")?, } Ok(()) } diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index bebbf4a4..e7faef22 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -66,7 +66,7 @@ impl Global for JayCompositorGlobal { } fn version(&self) -> u32 { - 3 + 4 } fn required_caps(&self) -> ClientCaps { @@ -336,7 +336,7 @@ impl JayCompositorRequestHandler for JayCompositor { } fn get_input(&self, req: GetInput, _slf: &Rc) -> Result<(), Self::Error> { - let sc = Rc::new(JayInput::new(req.id, &self.client)); + let sc = Rc::new(JayInput::new(req.id, &self.client, self.version)); track!(self.client, sc); self.client.add_client_obj(&sc)?; Ok(()) diff --git a/src/ifs/jay_input.rs b/src/ifs/jay_input.rs index 605e26a8..f0362763 100644 --- a/src/ifs/jay_input.rs +++ b/src/ifs/jay_input.rs @@ -24,14 +24,18 @@ pub struct JayInput { pub id: JayInputId, pub client: Rc, pub tracker: Tracker, + pub version: Version, } +const CALIBRATION_MATRIX_SINCE: Version = Version(4); + impl JayInput { - pub fn new(id: JayInputId, client: &Rc) -> Self { + pub fn new(id: JayInputId, client: &Rc, version: Version) -> Self { Self { id, client: client.clone(), tracker: Default::default(), + version, } } @@ -138,6 +142,19 @@ impl JayInput { }); } } + if self.version >= CALIBRATION_MATRIX_SINCE { + if let Some(m) = dev.calibration_matrix() { + self.client.event(CalibrationMatrix { + self_id: self.id, + m00: m[0][0], + m01: m[0][1], + m02: m[0][2], + m10: m[1][0], + m11: m[1][1], + m12: m[1][2], + }); + } + } } fn device(&self, id: u32) -> Result, JayInputError> { @@ -424,11 +441,24 @@ impl JayInputRequestHandler for JayInput { Ok(()) }) } + + fn set_calibration_matrix( + &self, + req: SetCalibrationMatrix, + _slf: &Rc, + ) -> Result<(), Self::Error> { + self.or_error(|| { + let dev = self.device(req.id)?; + dev.device + .set_calibration_matrix([[req.m00, req.m01, req.m02], [req.m10, req.m11, req.m12]]); + Ok(()) + }) + } } object_base! { self = JayInput; - version = Version(1); + version = self.version; } impl Object for JayInput {} diff --git a/src/ifs/jay_seat_events.rs b/src/ifs/jay_seat_events.rs index 9df7d121..4df6980c 100644 --- a/src/ifs/jay_seat_events.rs +++ b/src/ifs/jay_seat_events.rs @@ -468,6 +468,46 @@ impl JaySeatEvents { ring, }); } + + 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, + seat: seat.raw(), + time_usec, + id, + x, + y, + }); + } + + pub fn send_touch_up(&self, seat: SeatId, time_usec: u64, id: i32) { + self.client.event(TouchUp { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + }); + } + + pub fn send_touch_motion(&self, seat: SeatId, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + self.client.event(TouchMotion { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + x, + y, + }); + } + + pub fn send_touch_cancel(&self, seat: SeatId, time_usec: u64, id: i32) { + self.client.event(TouchCancel { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + }); + } } impl JaySeatEventsRequestHandler for JaySeatEvents { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 7fa17c21..339a7a63 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -6,6 +6,7 @@ mod kb_owner; mod pointer_owner; pub mod tablet; pub mod text_input; +mod touch_owner; pub mod wl_keyboard; pub mod wl_pointer; pub mod wl_touch; @@ -52,6 +53,7 @@ use { zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2, zwp_input_method_v2::ZwpInputMethodV2, zwp_text_input_v3::ZwpTextInputV3, }, + touch_owner::TouchOwnerHolder, wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE}, wl_pointer::WlPointer, wl_touch::WlTouch, @@ -79,7 +81,7 @@ use { }, wire::{ wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, - WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, + WlSeatId, WlTouchId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, ZwpTextInputV3Id, }, xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState}, @@ -103,7 +105,6 @@ pub use { pub const POINTER: u32 = 1; const KEYBOARD: u32 = 2; -#[allow(dead_code)] const TOUCH: u32 = 4; #[allow(dead_code)] @@ -142,6 +143,8 @@ pub struct WlSeatGlobal { name: GlobalName, state: Rc, seat_name: String, + capabilities: Cell, + num_touch_devices: NumCell, pos_time_usec: Cell, pointer_stack: RefCell>>, pointer_stack_modified: Cell, @@ -173,6 +176,7 @@ pub struct WlSeatGlobal { pointer_owner: PointerOwnerHolder, kb_owner: KbOwnerHolder, gesture_owner: GestureOwnerHolder, + touch_owner: TouchOwnerHolder, dropped_dnd: RefCell>, shortcuts: RefCell>>, queue_link: RefCell>>>, @@ -213,6 +217,8 @@ impl WlSeatGlobal { name, state: state.clone(), seat_name: seat_name.to_string(), + capabilities: Cell::new(0), + num_touch_devices: Default::default(), pos_time_usec: Cell::new(0), pointer_stack: RefCell::new(vec![]), pointer_stack_modified: Cell::new(false), @@ -237,6 +243,7 @@ impl WlSeatGlobal { pointer_owner: Default::default(), kb_owner: Default::default(), gesture_owner: Default::default(), + touch_owner: Default::default(), dropped_dnd: RefCell::new(None), shortcuts: Default::default(), queue_link: Default::default(), @@ -269,9 +276,24 @@ impl WlSeatGlobal { } }); slf.tree_changed_handler.set(Some(future)); + slf.update_capabilities(); slf } + fn update_capabilities(&self) { + let mut caps = POINTER | KEYBOARD; + if self.num_touch_devices.get() > 0 { + caps |= TOUCH; + } + if self.capabilities.replace(caps) != caps { + for client in self.bindings.borrow().values() { + for seat in client.values() { + seat.send_capabilities(); + } + } + } + } + pub fn keymap(&self) -> Rc { self.seat_kb_map.get() } @@ -852,6 +874,7 @@ impl WlSeatGlobal { self.primary_selection.set(None); self.pointer_owner.clear(); self.kb_owner.clear(); + self.touch_owner.clear(); *self.dropped_dnd.borrow_mut() = None; self.queue_link.take(); self.tree_changed_handler.set(None); @@ -888,6 +911,7 @@ impl WlSeatGlobal { pointers: Default::default(), relative_pointers: Default::default(), keyboards: Default::default(), + touches: Default::default(), version, tracker: Default::default(), }); @@ -994,6 +1018,7 @@ pub struct WlSeat { pointers: CopyHashMap>, relative_pointers: CopyHashMap>, keyboards: CopyHashMap>, + touches: CopyHashMap>, version: Version, tracker: Tracker, } @@ -1004,7 +1029,7 @@ impl WlSeat { fn send_capabilities(self: &Rc) { self.client.event(Capabilities { self_id: self.id, - capabilities: POINTER | KEYBOARD, + capabilities: self.global.capabilities.get(), }) } @@ -1059,6 +1084,7 @@ impl WlSeatRequestHandler for WlSeat { let p = Rc::new(WlTouch::new(req.id, slf)); track!(self.client, p); self.client.add_client_obj(&p)?; + self.touches.set(req.id, p); Ok(()) } @@ -1096,6 +1122,7 @@ impl Object for WlSeat { self.pointers.clear(); self.relative_pointers.clear(); self.keyboards.clear(); + self.touches.clear(); } } @@ -1146,6 +1173,10 @@ impl DeviceHandlerData { if let Some(info) = &self.tablet_pad_init { old.tablet_remove_tablet_pad(info.id); } + if self.is_touch { + old.num_touch_devices.fetch_sub(1); + old.update_capabilities(); + } } self.update_xkb_state(); if let Some(seat) = &seat { @@ -1155,6 +1186,10 @@ impl DeviceHandlerData { if let Some(info) = &self.tablet_pad_init { seat.tablet_add_tablet_pad(self.device.id(), info); } + if self.is_touch { + seat.num_touch_devices.fetch_add(1); + seat.update_capabilities(); + } } } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 5cbbf231..ee6c55c7 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -24,6 +24,7 @@ use { AXIS_STOP_SINCE_VERSION, AXIS_VALUE120_SINCE_VERSION, IDENTICAL, INVERTED, POINTER_FRAME_SINCE_VERSION, WHEEL_TILT, WHEEL_TILT_SINCE_VERSION, }, + wl_touch::WlTouch, zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus}, zwp_relative_pointer_v1::ZwpRelativePointerV1, Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, CHANGE_TREE, @@ -31,6 +32,7 @@ use { wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface}, }, object::Version, + rect::Rect, state::DeviceHandlerData, tree::{Direction, Node, ToplevelNode}, utils::{bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap}, @@ -54,6 +56,7 @@ pub struct NodeSeatState { pointer_foci: SmallMap, 1>, kb_foci: SmallMap, 1>, gesture_foci: SmallMap, 1>, + touch_foci: SmallMap, 1>, pointer_grabs: SmallMap, 1>, dnd_targets: SmallMap, 1>, tablet_pad_foci: SmallMap, 1>, @@ -111,6 +114,14 @@ impl NodeSeatState { self.tablet_tool_foci.remove(&tool.id); } + pub(super) fn touch_begin(&self, seat: &Rc) { + self.touch_foci.insert(seat.id, seat.clone()); + } + + pub(super) fn touch_end(&self, seat: &WlSeatGlobal) { + self.touch_foci.remove(&seat.id); + } + pub(super) fn add_dnd_target(&self, seat: &Rc) { self.dnd_targets.insert(seat.id, seat.clone()); } @@ -184,6 +195,9 @@ impl NodeSeatState { while let Some((_, pad)) = self.tablet_pad_foci.pop() { pad.pad_owner.focus_root(&pad); } + while let Some((_, seat)) = self.touch_foci.pop() { + seat.touch_owner.cancel(&seat); + } self.release_kb_focus2(focus_last); } @@ -230,7 +244,11 @@ impl WlSeatGlobal { | InputEvent::TabletPadButton { time_usec, .. } | InputEvent::TabletPadModeSwitch { time_usec, .. } | InputEvent::TabletPadRing { time_usec, .. } - | InputEvent::TabletPadStrip { time_usec, .. } => { + | InputEvent::TabletPadStrip { time_usec, .. } + | InputEvent::TouchDown { time_usec, .. } + | InputEvent::TouchUp { time_usec, .. } + | InputEvent::TouchMotion { time_usec, .. } + | InputEvent::TouchCancel { time_usec, .. } => { self.last_input_usec.set(time_usec); if self.idle_notifications.is_not_empty() { for notification in self.idle_notifications.lock().drain_values() { @@ -243,7 +261,8 @@ impl WlSeatGlobal { | InputEvent::AxisStop { .. } | InputEvent::Axis120 { .. } | InputEvent::TabletToolAdded { .. } - | InputEvent::TabletToolRemoved { .. } => {} + | InputEvent::TabletToolRemoved { .. } + | InputEvent::TouchFrame => {} } match event { InputEvent::ConnectorPosition { .. } @@ -274,6 +293,11 @@ impl WlSeatGlobal { InputEvent::TabletPadModeSwitch { .. } => {} InputEvent::TabletPadRing { .. } => {} InputEvent::TabletPadStrip { .. } => {} + InputEvent::TouchDown { .. } => {} + InputEvent::TouchUp { .. } => {} + InputEvent::TouchMotion { .. } => {} + InputEvent::TouchCancel { .. } => {} + InputEvent::TouchFrame => {} } match event { InputEvent::Key { @@ -407,6 +431,21 @@ impl WlSeatGlobal { source, position, } => self.tablet_event_pad_strip(pad, strip, source, position, time_usec), + InputEvent::TouchDown { + time_usec, + id, + x_normed, + y_normed, + } => self.touch_down(time_usec, id, dev.get_rect(&self.state), x_normed, y_normed), + InputEvent::TouchUp { time_usec, id } => self.touch_up(time_usec, id), + InputEvent::TouchMotion { + time_usec, + id, + x_normed, + y_normed, + } => self.touch_motion(time_usec, id, dev.get_rect(&self.state), x_normed, y_normed), + InputEvent::TouchCancel { time_usec, id } => self.touch_cancel(time_usec, id), + InputEvent::TouchFrame => self.touch_frame(), } } @@ -613,6 +652,58 @@ impl WlSeatGlobal { } } + fn touch_down( + self: &Rc, + time_usec: u64, + id: i32, + rect: Rect, + x_normed: Fixed, + y_normed: Fixed, + ) { + self.cursor_group().deactivate(); + let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64()); + let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64()); + self.state.for_each_seat_tester(|t| { + t.send_touch_down(self.id, time_usec, id, x, y); + }); + self.touch_owner.down(self, time_usec, id, x, y); + } + + fn touch_up(self: &Rc, time_usec: u64, id: i32) { + self.state.for_each_seat_tester(|t| { + t.send_touch_up(self.id, time_usec, id); + }); + self.touch_owner.up(self, time_usec, id); + } + + fn touch_motion( + self: &Rc, + time_usec: u64, + id: i32, + rect: Rect, + x_normed: Fixed, + y_normed: Fixed, + ) { + self.cursor_group().deactivate(); + let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64()); + let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64()); + self.state.for_each_seat_tester(|t| { + t.send_touch_motion(self.id, time_usec, id, x, y); + }); + self.touch_owner.motion(self, time_usec, id, x, y); + } + + fn touch_cancel(self: &Rc, time_usec: u64, id: i32) { + self.state.for_each_seat_tester(|t| { + t.send_touch_cancel(self.id, time_usec, id); + }); + self.touch_owner.cancel(self); + } + + fn touch_frame(self: &Rc) { + self.touch_owner.frame(self); + } + pub(super) fn key_event( self: &Rc, time_usec: u64, @@ -744,7 +835,7 @@ impl WlSeatGlobal { self.kb_owner.set_kb_node(self, node); } - fn for_each_seat(&self, ver: Version, client: ClientId, mut f: C) + pub(super) fn for_each_seat(&self, ver: Version, client: ClientId, mut f: C) where C: FnMut(&Rc), { @@ -794,6 +885,18 @@ impl WlSeatGlobal { }) } + fn for_each_touch(&self, ver: Version, client: ClientId, mut f: C) + where + C: FnMut(&Rc), + { + self.for_each_seat(ver, client, |seat| { + let touches = seat.touches.lock(); + for touch in touches.values() { + f(touch); + } + }) + } + pub fn for_each_data_device(&self, ver: Version, client: ClientId, mut f: C) where C: FnMut(&Rc), @@ -869,6 +972,16 @@ impl WlSeatGlobal { // client.flush(); } + pub fn surface_touch_event(&self, ver: Version, surface: &WlSurface, mut f: F) + where + F: FnMut(&Rc), + { + let client = &surface.client; + self.for_each_touch(ver, client.id, |p| { + f(p); + }); + } + fn cursor_moved(self: &Rc, time_usec: u64) { self.pos_time_usec.set(time_usec); self.changes.or_assign(CHANGE_CURSOR_MOVED); @@ -1133,6 +1246,53 @@ impl WlSeatGlobal { } } +// Touch callbacks +impl WlSeatGlobal { + pub fn touch_down_surface( + self: &Rc, + surface: &WlSurface, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let serial = surface.client.next_serial(); + let time = (time_usec / 1000) as _; + self.surface_touch_event(Version::ALL, surface, |t| { + t.send_down(serial, time, surface.id, id, x, y) + }); + if let Some(node) = surface.get_focus_node(self.id) { + self.focus_node(node); + } + } + + pub fn touch_up_surface(&self, surface: &WlSurface, time_usec: u64, id: i32) { + let serial = surface.client.next_serial(); + let time = (time_usec / 1000) as _; + self.surface_touch_event(Version::ALL, surface, |t| t.send_up(serial, time, id)) + } + + pub fn touch_motion_surface( + &self, + surface: &WlSurface, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let time = (time_usec / 1000) as _; + self.surface_touch_event(Version::ALL, surface, |t| t.send_motion(time, id, x, y)); + } + + pub fn touch_frame_surface(&self, surface: &WlSurface) { + self.surface_touch_event(Version::ALL, surface, |t| t.send_frame()) + } + + pub fn touch_cancel_surface(&self, surface: &WlSurface) { + self.surface_touch_event(Version::ALL, surface, |t| t.send_cancel()) + } +} + // Dnd callbacks impl WlSeatGlobal { pub fn dnd_surface_leave(&self, surface: &WlSurface, dnd: &Dnd) { diff --git a/src/ifs/wl_seat/touch_owner.rs b/src/ifs/wl_seat/touch_owner.rs new file mode 100644 index 00000000..c3d61205 --- /dev/null +++ b/src/ifs/wl_seat/touch_owner.rs @@ -0,0 +1,167 @@ +use { + crate::{ + fixed::Fixed, + ifs::wl_seat::WlSeatGlobal, + tree::{FindTreeUsecase, FoundNode, Node}, + utils::{clonecell::CloneCell, smallmap::SmallMap}, + }, + std::rc::Rc, +}; + +pub struct TouchOwnerHolder { + default: Rc, + owner: CloneCell>, +} + +impl Default for TouchOwnerHolder { + fn default() -> Self { + Self { + default: Rc::new(DefaultTouchOwner), + owner: CloneCell::new(Rc::new(DefaultTouchOwner)), + } + } +} + +impl TouchOwnerHolder { + pub fn down(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + self.owner.get().down(seat, time_usec, id, x, y) + } + + pub fn up(&self, seat: &Rc, time_usec: u64, id: i32) { + self.owner.get().up(seat, time_usec, id) + } + + pub fn motion(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + self.owner.get().motion(seat, time_usec, id, x, y) + } + + pub fn frame(&self, seat: &Rc) { + self.owner.get().frame(seat) + } + + pub fn cancel(&self, seat: &Rc) { + self.owner.get().cancel(seat) + } + + pub fn clear(&self) { + self.set_default_owner(); + } + + fn set_default_owner(&self) { + self.owner.set(self.default.clone()); + } +} + +struct DefaultTouchOwner; + +struct GrabTouchOwner { + node: Rc, + down_ids: SmallMap, +} + +trait TouchOwner { + fn down(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed); + fn up(&self, seat: &Rc, time_usec: u64, id: i32); + fn motion(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed); + fn frame(&self, seat: &Rc); + fn cancel(&self, seat: &Rc); +} + +impl TouchOwner for DefaultTouchOwner { + fn down(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + let mut found_tree = seat.found_tree.borrow_mut(); + let x_int = x.round_down(); + let y_int = y.round_down(); + found_tree.push(FoundNode { + node: seat.state.root.clone(), + x: x_int, + y: y_int, + }); + seat.state + .root + .node_find_tree_at(x_int, y_int, &mut found_tree, FindTreeUsecase::None); + let node = found_tree.pop(); + found_tree.clear(); + drop(found_tree); + if let Some(node) = node { + node.node.node_seat_state().touch_begin(seat); + let owner = Rc::new(GrabTouchOwner { + node: node.node, + down_ids: Default::default(), + }); + seat.touch_owner.owner.set(owner.clone()); + owner.down(seat, time_usec, id, x, y); + } + } + + fn up(&self, _seat: &Rc, _time_usec: u64, _id: i32) { + // nothing + } + + fn motion(&self, _seat: &Rc, _time_usec: u64, _id: i32, _x: Fixed, _y: Fixed) { + // nothing + } + + fn frame(&self, _seat: &Rc) { + // nothing + } + + fn cancel(&self, _seat: &Rc) { + // nothing + } +} + +impl GrabTouchOwner { + fn translate(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) { + let x_int = x.round_down(); + let y_int = y.round_down(); + let (x_int, y_int) = self.node.node_absolute_position().translate(x_int, y_int); + (x.apply_fract(x_int), y.apply_fract(y_int)) + } + + fn revert_to_default(&self, seat: &Rc) { + self.node.node_seat_state().touch_end(seat); + seat.touch_owner.set_default_owner(); + } +} + +impl TouchOwner for GrabTouchOwner { + fn down(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + if self.down_ids.insert(id, ()).is_some() { + return; + } + let (x, y) = self.translate(x, y); + self.node + .clone() + .node_on_touch_down(seat, time_usec, id, x, y); + } + + fn up(&self, seat: &Rc, time_usec: u64, id: i32) { + if self.down_ids.remove(&id).is_none() { + return; + } + self.node.clone().node_on_touch_up(seat, time_usec, id); + } + + fn motion(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + if !self.down_ids.contains(&id) { + return; + } + let (x, y) = self.translate(x, y); + self.node + .clone() + .node_on_touch_motion(seat, time_usec, id, x, y); + } + + fn frame(&self, seat: &Rc) { + self.node.node_on_touch_frame(seat); + if self.down_ids.is_empty() { + self.revert_to_default(seat); + } + } + + fn cancel(&self, seat: &Rc) { + self.node.node_on_touch_cancel(seat); + self.revert_to_default(seat); + } +} diff --git a/src/ifs/wl_seat/wl_touch.rs b/src/ifs/wl_seat/wl_touch.rs index 6a7a5a8d..384f4543 100644 --- a/src/ifs/wl_seat/wl_touch.rs +++ b/src/ifs/wl_seat/wl_touch.rs @@ -1,29 +1,20 @@ use { crate::{ client::ClientError, + fixed::Fixed, ifs::wl_seat::WlSeat, leaks::Tracker, - object::Object, - wire::{wl_touch::*, WlTouchId}, + object::{Object, Version}, + wire::{wl_touch::*, WlSurfaceId, WlTouchId}, }, std::rc::Rc, thiserror::Error, }; #[allow(dead_code)] -const DOWN: u32 = 0; +pub const SHAPE_SINCE_VERSION: Version = Version(6); #[allow(dead_code)] -const UP: u32 = 1; -#[allow(dead_code)] -const MOTION: u32 = 2; -#[allow(dead_code)] -const FRAME: u32 = 3; -#[allow(dead_code)] -const CANCEL: u32 = 4; -#[allow(dead_code)] -const SHAPE: u32 = 5; -#[allow(dead_code)] -const ORIENTATION: u32 = 6; +pub const ORIENTATION_DIRECTION_SINCE_VERSION: Version = Version(6); pub struct WlTouch { id: WlTouchId, @@ -39,12 +30,79 @@ impl WlTouch { tracker: Default::default(), } } + + pub fn send_down( + &self, + serial: u32, + time: u32, + surface: WlSurfaceId, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.seat.client.event(Down { + self_id: self.id, + serial, + time, + surface, + id, + x, + y, + }) + } + + pub fn send_up(&self, serial: u32, time: u32, id: i32) { + self.seat.client.event(Up { + self_id: self.id, + serial, + time, + id, + }) + } + + pub fn send_motion(&self, time: u32, id: i32, x: Fixed, y: Fixed) { + self.seat.client.event(Motion { + self_id: self.id, + time, + id, + x, + y, + }) + } + + pub fn send_frame(&self) { + self.seat.client.event(Frame { self_id: self.id }) + } + + pub fn send_cancel(&self) { + self.seat.client.event(Cancel { self_id: self.id }) + } + + #[allow(dead_code)] + pub fn send_shape(&self, id: i32, major: Fixed, minor: Fixed) { + self.seat.client.event(Shape { + self_id: self.id, + id, + major, + minor, + }) + } + + #[allow(dead_code)] + pub fn send_orientation(&self, id: i32, orientation: Fixed) { + self.seat.client.event(Orientation { + self_id: self.id, + id, + orientation, + }) + } } impl WlTouchRequestHandler for WlTouch { type Error = WlTouchError; fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { + self.seat.touches.remove(&self.id); self.seat.client.remove_obj(self)?; Ok(()) } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 77dd8165..049c5034 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -1643,6 +1643,40 @@ impl Node for WlSurface { seat.mods_surface(self, kb_state); } + fn node_on_touch_down( + self: Rc, + seat: &Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + seat.touch_down_surface(&self, time_usec, id, x, y) + } + + fn node_on_touch_up(self: Rc, seat: &Rc, time_usec: u64, id: i32) { + seat.touch_up_surface(&self, time_usec, id) + } + + fn node_on_touch_motion( + self: Rc, + seat: &WlSeatGlobal, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + seat.touch_motion_surface(&self, time_usec, id, x, y) + } + + fn node_on_touch_frame(&self, seat: &WlSeatGlobal) { + seat.touch_frame_surface(&self) + } + + fn node_on_touch_cancel(&self, seat: &WlSeatGlobal) { + seat.touch_cancel_surface(&self) + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/src/libinput/device.rs b/src/libinput/device.rs index 76f813f9..bf217781 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -10,7 +10,9 @@ use { libinput_device, libinput_device_config_accel_get_profile, libinput_device_config_accel_get_speed, libinput_device_config_accel_is_available, libinput_device_config_accel_set_profile, libinput_device_config_accel_set_speed, - libinput_device_config_left_handed_get, + libinput_device_config_calibration_get_matrix, + libinput_device_config_calibration_has_matrix, + libinput_device_config_calibration_set_matrix, libinput_device_config_left_handed_get, libinput_device_config_left_handed_is_available, libinput_device_config_left_handed_set, libinput_device_config_scroll_get_natural_scroll_enabled, @@ -265,6 +267,25 @@ impl<'a> LibInputDevice<'a> { _phantom: Default::default(), }) } + + pub fn has_calibration_matrix(&self) -> bool { + unsafe { libinput_device_config_calibration_has_matrix(self.dev) != 0 } + } + + pub fn set_calibration_matrix(&self, m: [[f32; 3]; 2]) { + let m = [m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]]; + unsafe { + libinput_device_config_calibration_set_matrix(self.dev, &m); + } + } + + pub fn get_calibration_matrix(&self) -> [[f32; 3]; 2] { + let mut m = [0.0; 6]; + unsafe { + libinput_device_config_calibration_get_matrix(self.dev, &mut m); + } + [[m[0], m[1], m[2]], [m[3], m[4], m[5]]] + } } impl<'a> LibInputDeviceGroup<'a> { diff --git a/src/libinput/event.rs b/src/libinput/event.rs index 45885c48..3048a0fb 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -16,17 +16,17 @@ use { libinput_event_get_gesture_event, libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_switch_event, libinput_event_get_tablet_pad_event, libinput_event_get_tablet_tool_event, - libinput_event_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key, - libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, - libinput_event_pointer, libinput_event_pointer_get_button, - libinput_event_pointer_get_button_state, libinput_event_pointer_get_dx, - libinput_event_pointer_get_dx_unaccelerated, libinput_event_pointer_get_dy, - libinput_event_pointer_get_dy_unaccelerated, libinput_event_pointer_get_scroll_value, - libinput_event_pointer_get_scroll_value_v120, libinput_event_pointer_get_time_usec, - libinput_event_pointer_has_axis, 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_get_touch_event, libinput_event_get_type, libinput_event_keyboard, + libinput_event_keyboard_get_key, libinput_event_keyboard_get_key_state, + libinput_event_keyboard_get_time_usec, libinput_event_pointer, + libinput_event_pointer_get_button, libinput_event_pointer_get_button_state, + libinput_event_pointer_get_dx, libinput_event_pointer_get_dx_unaccelerated, + libinput_event_pointer_get_dy, libinput_event_pointer_get_dy_unaccelerated, + libinput_event_pointer_get_scroll_value, libinput_event_pointer_get_scroll_value_v120, + libinput_event_pointer_get_time_usec, libinput_event_pointer_has_axis, + 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_mode_group, libinput_event_tablet_pad_get_ring_number, libinput_event_tablet_pad_get_ring_position, libinput_event_tablet_pad_get_ring_source, @@ -40,10 +40,12 @@ use { libinput_event_tablet_tool_get_tool, libinput_event_tablet_tool_get_wheel_delta_discrete, libinput_event_tablet_tool_get_x_transformed, - libinput_event_tablet_tool_get_y_transformed, libinput_tablet_tool, - libinput_tablet_tool_get_serial, libinput_tablet_tool_get_tool_id, - libinput_tablet_tool_get_type, libinput_tablet_tool_get_user_data, - libinput_tablet_tool_set_user_data, + libinput_event_tablet_tool_get_y_transformed, libinput_event_touch, + libinput_event_touch_get_seat_slot, libinput_event_touch_get_time_usec, + libinput_event_touch_get_x_transformed, libinput_event_touch_get_y_transformed, + libinput_tablet_tool, libinput_tablet_tool_get_serial, + libinput_tablet_tool_get_tool_id, libinput_tablet_tool_get_type, + libinput_tablet_tool_get_user_data, libinput_tablet_tool_set_user_data, }, }, std::marker::PhantomData, @@ -89,6 +91,11 @@ pub struct LibInputTabletTool<'a> { pub(super) _phantom: PhantomData<&'a ()>, } +pub struct LibInputEventTouch<'a> { + pub(super) event: *mut libinput_event_touch, + pub(super) _phantom: PhantomData<&'a ()>, +} + impl<'a> Drop for LibInputEvent<'a> { fn drop(&mut self) { unsafe { @@ -155,6 +162,11 @@ impl<'a> LibInputEvent<'a> { LibInputEventTabletPad, libinput_event_get_tablet_pad_event ); + converter!( + touch_event, + LibInputEventTouch, + libinput_event_get_touch_event + ); } impl<'a> LibInputEventKeyboard<'a> { @@ -467,3 +479,29 @@ impl<'a> LibInputEventTabletPad<'a> { } } } + +impl<'a> LibInputEventTouch<'a> { + pub fn seat_slot(&self) -> i32 { + unsafe { libinput_event_touch_get_seat_slot(self.event) } + } + + // pub fn x(&self) -> f64 { + // unsafe { libinput_event_touch_get_x(self.event) } + // } + // + // pub fn y(&self) -> f64 { + // unsafe { libinput_event_touch_get_y(self.event) } + // } + + pub fn x_transformed(&self, width: u32) -> f64 { + unsafe { libinput_event_touch_get_x_transformed(self.event, width) } + } + + pub fn y_transformed(&self, height: u32) -> f64 { + unsafe { libinput_event_touch_get_y_transformed(self.event, height) } + } + + pub fn time_usec(&self) -> u64 { + unsafe { libinput_event_touch_get_time_usec(self.event) } + } +} diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index c937abb3..56f7a7c8 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -30,6 +30,8 @@ pub struct libinput_tablet_pad_mode_group(u8); pub struct libinput_tablet_tool(u8); // #[repr(transparent)] // pub struct libinput_tablet_pad(u8); +#[repr(transparent)] +pub struct libinput_event_touch(u8); #[link(name = "input")] extern "C" { @@ -357,6 +359,29 @@ extern "C" { // group: *mut libinput_tablet_pad_mode_group, // button: c::c_uint, // ) -> c::c_int; + + pub fn libinput_event_get_touch_event(event: *mut libinput_event) -> *mut libinput_event_touch; + pub fn libinput_event_touch_get_seat_slot(event: *mut libinput_event_touch) -> i32; + pub fn libinput_event_touch_get_time_usec(event: *mut libinput_event_touch) -> u64; + // pub fn libinput_event_touch_get_x(event: *mut libinput_event_touch) -> f64; + pub fn libinput_event_touch_get_x_transformed( + event: *mut libinput_event_touch, + width: u32, + ) -> f64; + // pub fn libinput_event_touch_get_y(event: *mut libinput_event_touch) -> f64; + pub fn libinput_event_touch_get_y_transformed( + event: *mut libinput_event_touch, + height: u32, + ) -> f64; + pub fn libinput_device_config_calibration_has_matrix(device: *mut libinput_device) -> c::c_int; + pub fn libinput_device_config_calibration_set_matrix( + device: *mut libinput_device, + matrix: *const [f32; 6], + ) -> libinput_config_status; + pub fn libinput_device_config_calibration_get_matrix( + device: *mut libinput_device, + matrix: *mut [f32; 6], + ) -> c::c_int; } #[repr(C)] diff --git a/src/state.rs b/src/state.rs index 7af22022..5318ab8d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -278,6 +278,7 @@ pub struct DeviceHandlerData { pub output: CloneCell>>, pub tablet_init: Option>, pub tablet_pad_init: Option>, + pub is_touch: bool, } pub struct ConnectorData { diff --git a/src/tasks/input_device.rs b/src/tasks/input_device.rs index 3404d763..d255a5c1 100644 --- a/src/tasks/input_device.rs +++ b/src/tasks/input_device.rs @@ -1,6 +1,6 @@ use { crate::{ - backend::InputDevice, + backend::{InputDevice, InputDeviceCapability}, ifs::wl_seat::PX_PER_SCROLL, state::{DeviceHandlerData, InputDeviceData, State}, tasks::udev_utils::{udev_props, UdevProps}, @@ -26,6 +26,7 @@ pub fn handle(state: &Rc, dev: Rc) { output: Default::default(), tablet_init: dev.tablet_info(), tablet_pad_init: dev.tablet_pad_info(), + is_touch: dev.has_capability(InputDeviceCapability::Touch), }); let ae = Rc::new(AsyncEvent::default()); let oh = DeviceHandler { diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index a9ed6216..5468132a 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -330,7 +330,7 @@ impl ToolClient { self_id: s.registry, name: s.jay_compositor.0, interface: JayCompositor.name(), - version: s.jay_compositor.1.min(3), + version: s.jay_compositor.1.min(4), id: id.into(), }); self.jay_compositor.set(Some(id)); diff --git a/src/tree.rs b/src/tree.rs index 58f679f0..797820c1 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -200,6 +200,50 @@ pub trait Node: 'static { let _ = kb_state; } + fn node_on_touch_down( + self: Rc, + seat: &Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let _ = seat; + let _ = time_usec; + let _ = id; + let _ = x; + let _ = y; + } + + fn node_on_touch_up(self: Rc, seat: &Rc, time_usec: u64, id: i32) { + let _ = seat; + let _ = time_usec; + let _ = id; + } + + fn node_on_touch_motion( + self: Rc, + seat: &WlSeatGlobal, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let _ = seat; + let _ = time_usec; + let _ = id; + let _ = x; + let _ = y; + } + + fn node_on_touch_frame(&self, seat: &WlSeatGlobal) { + let _ = seat; + } + + fn node_on_touch_cancel(&self, seat: &WlSeatGlobal) { + let _ = seat; + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index 625787b2..e2fc7532 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -250,6 +250,7 @@ pub struct Input { pub keymap: Option, pub switch_actions: AHashMap, pub output: Option>, + pub calibration_matrix: Option<[[f32; 3]; 2]>, } #[derive(Debug, Clone)] diff --git a/toml-config/src/config/parsers/input.rs b/toml-config/src/config/parsers/input.rs index 3b5ff6b8..dbe78335 100644 --- a/toml-config/src/config/parsers/input.rs +++ b/toml-config/src/config/parsers/input.rs @@ -40,6 +40,12 @@ pub enum InputParserError { TwoColumns, #[error("Transform matrix entries must be floats")] Float, + #[error("Calibration matrix must have exactly two rows")] + CaliTwoRows, + #[error("Calibration matrix must have exactly three columns")] + CaliThreeColumns, + #[error("Calibration matrix entries must be floats")] + CaliFloat, } pub struct InputParser<'a> { @@ -80,6 +86,7 @@ impl<'a> Parser for InputParser<'a> { on_converted_to_tablet_val, output_val, remove_mapping, + calibration_matrix, ), ) = ext.extract(( ( @@ -103,6 +110,7 @@ impl<'a> Parser for InputParser<'a> { opt(val("on-converted-to-tablet")), opt(val("output")), recover(opt(bol("remove-mapping"))), + recover(opt(val("calibration-matrix"))), ), ))?; let accel_profile = match accel_profile { @@ -214,6 +222,16 @@ impl<'a> Parser for InputParser<'a> { output = Some(None); } } + let calibration_matrix = match calibration_matrix { + None => None, + Some(matrix) => match matrix.parse(&mut CalibrationMatrixParser) { + Ok(v) => Some(v), + Err(e) => { + log::warn!("Could not parse calibration matrix: {}", self.cx.error(e)); + None + } + }, + }; Ok(Input { tag: tag.despan_into(), match_: match_val.parse_map(&mut InputMatchParser(self.cx))?, @@ -229,6 +247,7 @@ impl<'a> Parser for InputParser<'a> { keymap, switch_actions, output, + calibration_matrix, }) } } @@ -311,3 +330,45 @@ impl Parser for TransformMatrixRowParser { Ok([extract(&array[0])?, extract(&array[1])?]) } } + +struct CalibrationMatrixParser; + +impl Parser for CalibrationMatrixParser { + type Value = [[f32; 3]; 2]; + type Error = InputParserError; + const EXPECTED: &'static [DataType] = &[DataType::Array]; + + fn parse_array(&mut self, span: Span, array: &[Spanned]) -> ParseResult { + if array.len() != 2 { + return Err(InputParserError::CaliTwoRows.spanned(span)); + } + Ok([ + array[0].parse(&mut CalibrationMatrixRowParser)?, + array[1].parse(&mut CalibrationMatrixRowParser)?, + ]) + } +} + +struct CalibrationMatrixRowParser; + +impl Parser for CalibrationMatrixRowParser { + type Value = [f32; 3]; + type Error = InputParserError; + const EXPECTED: &'static [DataType] = &[DataType::Array]; + + fn parse_array(&mut self, span: Span, array: &[Spanned]) -> ParseResult { + if array.len() != 3 { + return Err(InputParserError::CaliThreeColumns.spanned(span)); + } + let extract = |v: &Spanned| match v.value { + Value::Float(f) => Ok(f as f32), + Value::Integer(f) => Ok(f as _), + _ => Err(InputParserError::CaliFloat.spanned(v.span)), + }; + Ok([ + extract(&array[0])?, + extract(&array[1])?, + extract(&array[2])?, + ]) + } +} diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 4651bf7e..da6b25a3 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -437,6 +437,9 @@ impl Input { c.remove_mapping(); } } + if let Some(v) = self.calibration_matrix { + c.set_calibration_matrix(v); + } } } diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 4b335ee2..04cf12e7 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -861,6 +861,18 @@ "remove-mapping": { "type": "boolean", "description": "Removes the mapping of from this device to an output.\n\nThis should only be used within `configure-input` actions.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-x = { type = \"configure-input\", input = { match.tag = \"wacom\", remove-mapping = true } }\n\n [[inputs]]\n tag = \"wacom\"\n match.name = \"Wacom Bamboo Comic 2FG Pen\"\n output.connector = \"DP-1\"\n ```\n" + }, + "calibration-matrix": { + "type": "array", + "description": "The calibration matrix of the device. This matrix should be 2x3.\n\nSee the libinput documentation for more details.\n\n- Example: To flip the device 90 degrees:\n\n ```toml\n [[inputs]]\n calibration-matrix = [[0, 1, 0], [-1, 0, 1]]\n ```\n", + "items": { + "type": "array", + "description": "", + "items": { + "type": "number", + "description": "" + } + } } }, "required": [ diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index 7e615924..95d5adf4 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -1768,6 +1768,21 @@ The table has the following fields: The value of this field should be a boolean. +- `calibration-matrix` (optional): + + The calibration matrix of the device. This matrix should be 2x3. + + See the libinput documentation for more details. + + - Example: To flip the device 90 degrees: + + ```toml + [[inputs]] + calibration-matrix = [[0, 1, 0], [-1, 0, 1]] + ``` + + The value of this field should be an array of arrays of numbers. + ### `InputMatch` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 7b16e0e8..190de9c5 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -1328,6 +1328,24 @@ Input: match.name = "Wacom Bamboo Comic 2FG Pen" output.connector = "DP-1" ``` + calibration-matrix: + kind: array + items: + kind: array + items: + kind: number + required: false + description: | + The calibration matrix of the device. This matrix should be 2x3. + + See the libinput documentation for more details. + + - Example: To flip the device 90 degrees: + + ```toml + [[inputs]] + calibration-matrix = [[0, 1, 0], [-1, 0, 1]] + ``` AccelProfile: diff --git a/wire/jay_input.txt b/wire/jay_input.txt index 91a7875d..c5469936 100644 --- a/wire/jay_input.txt +++ b/wire/jay_input.txt @@ -114,6 +114,16 @@ request map_to_output { output: optstr, } +request set_calibration_matrix (since = 4) { + id: u32, + m00: pod(f32), + m01: pod(f32), + m02: pod(f32), + m10: pod(f32), + m11: pod(f32), + m12: pod(f32), +} + # events event seat { @@ -158,3 +168,12 @@ event input_device_output { id: u32, output: str, } + +event calibration_matrix (since = 4) { + m00: pod(f32), + m01: pod(f32), + m02: pod(f32), + m10: pod(f32), + m11: pod(f32), + m12: pod(f32), +} diff --git a/wire/jay_seat_events.txt b/wire/jay_seat_events.txt index f7cb0163..70e01eab 100644 --- a/wire/jay_seat_events.txt +++ b/wire/jay_seat_events.txt @@ -239,3 +239,31 @@ event tablet_pad_ring_frame { input_device: u32, ring: u32, } + +event touch_down { + seat: u32, + time_usec: pod(u64), + id: i32, + x: fixed, + y: fixed, +} + +event touch_up { + seat: u32, + time_usec: pod(u64), + id: i32, +} + +event touch_motion { + seat: u32, + time_usec: pod(u64), + id: i32, + x: fixed, + y: fixed, +} + +event touch_cancel { + seat: u32, + time_usec: pod(u64), + id: i32, +} diff --git a/wire/wl_touch.txt b/wire/wl_touch.txt index 11ec77dd..a5a21c3f 100644 --- a/wire/wl_touch.txt +++ b/wire/wl_touch.txt @@ -23,7 +23,7 @@ event up { event motion { time: u32, - id: u32, + id: i32, x: fixed, y: fixed, }