ft6x36/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4use embedded_hal::i2c::{ErrorType, I2c, SevenBitAddress};
5use num_enum::{FromPrimitive, IntoPrimitive};
6
7#[cfg(feature = "event_process")]
8use core::time::Duration;
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13const DEFAULT_FT6X36_ADDRESS: u8 = 0x38;
14const REPORT_SIZE: usize = 0x0f;
15
16#[cfg(feature = "event_process")]
17const MAX_DELTA_TOUCH_EVENT: Duration = Duration::from_millis(200);
18
19/// Represents the dimensions of the device
20#[derive(Copy, Clone, Debug)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub struct Dimension(pub u16, pub u16);
23
24/// Driver representation holding:
25///
26/// - The I2C Slave address of the device
27/// - The I2C Bus used to communicate with the device
28/// - Some information on the device when it's initialized
29pub struct Ft6x36<I2C> {
30    /// Address of the I2C Slave device
31    address: u8,
32    /// I2C bus used to communicate with the device
33    i2c: I2C,
34    /// Information of the device when it's initialized
35    info: Option<Ft6x36Info>,
36    #[cfg(feature = "event_process")]
37    /// Raw events
38    events: (Option<TimedRawTouchEvent>, Option<TimedRawTouchEvent>),
39    #[cfg(feature = "event_process")]
40    /// Event process config
41    config: ProcessEventConfig,
42    /// Orientation of the screen
43    orientation: Orientation,
44    /// Dimensions of the device
45    size: Dimension,
46}
47
48/// Represents the orientation of the device
49#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
50#[derive(Copy, Clone, Debug)]
51#[cfg_attr(feature = "defmt", derive(defmt::Format))]
52pub enum Orientation {
53    Portrait,
54    Landscape,
55    InvertedPortrait,
56    InvertedLandscape,
57}
58
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60#[cfg(feature = "event_process")]
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62pub struct ProcessEventConfig {
63    gesture_timing: Duration,
64    max_swipe_delta: u16,
65    min_swipe_delta: u16,
66}
67
68#[cfg(feature = "event_process")]
69impl Default for ProcessEventConfig {
70    fn default() -> Self {
71        ProcessEventConfig {
72            gesture_timing: Duration::from_millis(800),
73            max_swipe_delta: 30,
74            min_swipe_delta: 30,
75        }
76    }
77}
78
79#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
80#[cfg(feature = "event_process")]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82#[derive(Debug, PartialEq, Eq)]
83pub struct TimedRawTouchEvent {
84    time: Duration,
85    event: RawTouchEvent,
86}
87
88#[allow(dead_code)]
89#[derive(Copy, Clone, Debug)]
90#[cfg_attr(feature = "defmt", derive(defmt::Format))]
91pub struct Diagnostics {
92    power_mode: u8,
93    g_mode: u8,
94    lib_version: u16,
95    state: u8,
96    control_mode: u8,
97}
98
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100#[derive(Copy, Clone, Debug, PartialEq, Eq)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102pub struct TouchPoint {
103    pub touch_type: TouchType,
104    pub x: u16,
105    pub y: u16,
106}
107
108impl TouchPoint {
109    fn translate_coordinates(self, size: Dimension, orientation: Orientation) -> Self {
110        fn difference_or_zero(o1: u16, o2: u16) -> u16 {
111            if o2 > o1 {
112                0
113            } else {
114                o1 - o2
115            }
116        }
117        let TouchPoint { touch_type, x, y } = self;
118        let (x, y) = match orientation {
119            Orientation::Portrait => (x, y),
120            Orientation::InvertedPortrait => {
121                (difference_or_zero(size.0, x), difference_or_zero(size.1, y))
122            }
123            Orientation::Landscape => (y, difference_or_zero(size.0, x)),
124            Orientation::InvertedLandscape => (difference_or_zero(size.1, y), x),
125        };
126        TouchPoint { x, y, touch_type }
127    }
128}
129
130impl From<&[u8]> for TouchPoint {
131    fn from(data: &[u8]) -> Self {
132        let x: u16 = ((data[0] as u16 & 0x0f) << 8) | (data[1] as u16);
133        let y: u16 = ((data[2] as u16 & 0x0f) << 8) | (data[3] as u16);
134        let touch_type: TouchType = data[0].into();
135
136        Self { touch_type, x, y }
137    }
138}
139
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142#[cfg_attr(feature = "defmt", derive(defmt::Format))]
143pub enum Direction {
144    Up,
145    Down,
146    Left,
147    Right,
148}
149
150#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152#[cfg_attr(feature = "defmt", derive(defmt::Format))]
153pub enum Zoom {
154    ZoomIn(TouchPoint),
155    ZoomOut(TouchPoint),
156}
157
158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
159#[derive(Clone, Copy, Debug, PartialEq, Eq)]
160#[cfg_attr(feature = "defmt", derive(defmt::Format))]
161pub struct SwipeInfo {
162    pub velocity: u16,
163    pub point: TouchPoint,
164}
165
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
167#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168#[cfg_attr(feature = "defmt", derive(defmt::Format))]
169pub enum TouchEvent {
170    TouchOnePoint(TouchPoint),
171    TouchTwoPoint(TouchPoint, TouchPoint),
172    Swipe(Direction, SwipeInfo),
173    Zoom(Zoom),
174}
175
176/// Device mode
177#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
178#[repr(u8)]
179#[derive(Clone, Copy, Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq)]
180#[cfg_attr(feature = "defmt", derive(defmt::Format))]
181pub enum DeviceMode {
182    /// Working mode
183    #[default]
184    Working = 0b000,
185    /// Factory mode
186    Factory = 0b100,
187}
188
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
190#[repr(u8)]
191#[derive(Clone, Copy, Debug, IntoPrimitive, PartialEq, Eq)]
192#[cfg_attr(feature = "defmt", derive(defmt::Format))]
193pub enum TouchType {
194    Press = 0b00,
195    Release = 0b01,
196    Contact = 0b10,
197    Invalid = 0b11,
198}
199
200impl From<u8> for TouchType {
201    fn from(data: u8) -> Self {
202        match (data >> 6) & 0b0000_0011 {
203            0b00 => TouchType::Press,
204            0b01 => TouchType::Release,
205            0b10 => TouchType::Contact,
206            _ => TouchType::Invalid,
207        }
208    }
209}
210
211/// Touch event full raw report
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213#[allow(dead_code)]
214#[derive(Copy, Clone, Debug, Eq, PartialEq)]
215#[cfg_attr(feature = "defmt", derive(defmt::Format))]
216pub struct RawTouchEvent {
217    /// Device mode
218    pub device_mode: DeviceMode,
219    pub gesture_id: GestureId,
220    pub p1: Option<TouchPoint>,
221    pub p2: Option<TouchPoint>,
222}
223
224impl From<[u8; REPORT_SIZE]> for RawTouchEvent {
225    fn from(report: [u8; REPORT_SIZE]) -> Self {
226        let (p1, p2) = match report[2] {
227            1 => (Some(TouchPoint::from(&report[3..7])), None),
228            2 => (
229                Some(TouchPoint::from(&report[3..7])),
230                Some(TouchPoint::from(&report[9..13])),
231            ),
232            _ => (None, None),
233        };
234
235        RawTouchEvent {
236            device_mode: report[0].into(),
237            gesture_id: report[1].into(),
238            p1,
239            p2,
240        }
241    }
242}
243
244impl RawTouchEvent {
245    fn translate_orientation(self, size: Dimension, orientation: Orientation) -> Self {
246        let RawTouchEvent {
247            device_mode,
248            gesture_id,
249            p1,
250            p2,
251        } = self;
252        let p1 = p1.map(|p| p.translate_coordinates(size, orientation));
253        let p2 = p2.map(|p| p.translate_coordinates(size, orientation));
254        RawTouchEvent {
255            device_mode,
256            gesture_id,
257            p1,
258            p2,
259        }
260    }
261}
262
263/// Settings for gesture detection
264/// (Currently not working)
265#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
266#[allow(dead_code)]
267#[derive(Debug, Clone, Copy)]
268#[cfg_attr(feature = "defmt", derive(defmt::Format))]
269pub struct GestureParams {
270    minimum_angle: u8,
271    offset_left_right: u8,
272    offset_up_down: u8,
273    dist_left_right: u8,
274    dist_up_down: u8,
275    dist_zoom: u8,
276}
277
278macro_rules! get_offset {
279    ($first:expr, $relative_offset:expr) => {
280        ($relative_offset as u8 - $first as u8) as usize
281    };
282}
283
284/// Documented registers of the device
285#[allow(dead_code)]
286#[repr(u8)]
287#[derive(Debug, Clone, Copy, IntoPrimitive)]
288enum Reg {
289    DeviceMode = 0x00,
290
291    GestId = 0x01,
292    TouchDeviceStatus = 0x02,
293
294    P1XH = 0x03,
295    P1XL = 0x04,
296    P1YH = 0x05,
297    P1YL = 0x06,
298    P1Weight = 0x07,
299    P1Misc = 0x08,
300
301    P2XH = 0x09,
302    P2XL = 0x0A,
303    P2YH = 0x0B,
304    P2YL = 0x0C,
305    P2Weight = 0x0D,
306    P2Misc = 0x0E,
307
308    TouchDetectionThreshold = 0x80,
309    TouchFilterCoeff = 0x85,
310    ControlMode = 0x86,
311    TimeActiveMonitor = 0x87,
312    PeriodActive = 0x88,
313    PeriodMonitor = 0x89,
314
315    GestRadianValue = 0x91,
316    GestOffsetLeftRight = 0x92,
317    GestOffsetUpDown = 0x93,
318    GestDistLeftRight = 0x94,
319    GestDistUpDown = 0x95,
320    GestDistZoom = 0x96,
321
322    LibVersionH = 0xA1,
323    LibVersionL = 0xA2,
324    ChipId = 0xA3,
325
326    GMode = 0xA4,
327    PowerMode = 0xA5,
328    FirmwareId = 0xA6,
329    PanelId = 0xA8,
330    ReleaseCode = 0xAF,
331
332    OperatingMode = 0xBC,
333}
334
335/// Known and detected gestures (currently not working though)
336#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
337#[repr(u8)]
338#[derive(Clone, Copy, Debug, IntoPrimitive, FromPrimitive, PartialEq, Eq)]
339#[cfg_attr(feature = "defmt", derive(defmt::Format))]
340pub enum GestureId {
341    #[default]
342    NoGesture = 0x00,
343    MoveUp = 0x10,
344    MoveRight = 0x14,
345    MoveDown = 0x18,
346    MoveLeft = 0x1C,
347    ZoomIn = 0x48,
348    ZoomOut = 0x49,
349}
350
351/// Enum describing known chips
352#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
353#[repr(u8)]
354#[derive(Clone, Copy, Debug, IntoPrimitive, FromPrimitive)]
355#[cfg_attr(feature = "defmt", derive(defmt::Format))]
356pub enum ChipId {
357    #[default]
358    Unknown,
359    Ft6206 = 0x06,
360    Ft6236 = 0x36,
361    Ft6236u = 0x64,
362}
363
364/// A structure giving information on the current device
365#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
366#[allow(dead_code)]
367#[derive(Clone, Copy, Debug)]
368pub struct Ft6x36Info {
369    /// ChipId, known chips are: Ft6206, Ft6236 and Ft6236u
370    chip_id: ChipId,
371    /// Firmware Id
372    firmware_id: u8,
373    /// Panel id
374    panel_id: u8,
375    /// Version of the release code
376    release_code: u8,
377}
378
379impl<I2C> Ft6x36<I2C>
380where
381    I2C: I2c<SevenBitAddress>,
382{
383    /// Create a new Ft6x36 device with the default slave address
384    ///
385    /// # Arguments
386    ///
387    /// - `i2c` I2C bus used to communicate with the device
388    /// - `size` [`Dimension`] of the device
389    ///
390    /// # Returns
391    ///
392    /// - [Ft6x36 driver](Ft6x36) created
393    ///
394    pub fn new(i2c: I2C, size: Dimension) -> Self {
395        Self {
396            address: DEFAULT_FT6X36_ADDRESS,
397            i2c,
398            info: None,
399            #[cfg(feature = "event_process")]
400            events: (None, None),
401            #[cfg(feature = "event_process")]
402            config: ProcessEventConfig::default(),
403            orientation: Orientation::Portrait,
404            size,
405        }
406    }
407
408    /// Create a new Ft6x36 device with the default slave address
409    ///
410    /// # Arguments
411    ///
412    /// - `i2c` I2C bus used to communicate with the device
413    /// - `size` [`Dimension`] of the device
414    /// - `config`- [`ProcessEventConfig`] for the event processor
415    ///
416    /// # Returns
417    ///
418    /// - [Ft6x36 driver](Ft6x36) created
419    ///
420    #[cfg(feature = "event_process")]
421    pub fn new_with_config(i2c: I2C, size: Dimension, config: ProcessEventConfig) -> Self {
422        Self {
423            address: DEFAULT_FT6X36_ADDRESS,
424            i2c,
425            info: None,
426            events: (None, None),
427            config,
428            size,
429            orientation: Orientation::Portrait,
430        }
431    }
432
433    /// Initialize the device
434    ///
435    /// Currently it only gathers information on the device and initializes the
436    /// [info structure of the driver](Ft6x36Info)
437    ///
438    pub fn init(&mut self) -> Result<(), <I2C as ErrorType>::Error> {
439        let mut buf: [u8; 13] = [0; 13];
440        self.i2c
441            .write_read(self.address, &[Reg::ChipId.into()], &mut buf)?;
442        let chip_id: ChipId = buf[get_offset!(Reg::ChipId, Reg::ChipId)].into();
443        let firmware_id: u8 = buf[get_offset!(Reg::ChipId, Reg::FirmwareId)];
444        let panel_id: u8 = buf[get_offset!(Reg::ChipId, Reg::PanelId)];
445        let release_code: u8 = buf[get_offset!(Reg::ChipId, Reg::ReleaseCode)];
446        self.info = Some(Ft6x36Info {
447            chip_id,
448            firmware_id,
449            panel_id,
450            release_code,
451        });
452        self.set_control_mode(0)?;
453        Ok(())
454    }
455
456    /// Change orientation for the device
457    ///
458    /// # Arguments
459    ///
460    /// - `orientation` - set the new [`Orientation`]
461    pub fn set_orientation(&mut self, orientation: Orientation) {
462        self.orientation = orientation;
463    }
464
465    /// Get the full raw report of touch events
466    ///
467    /// # Returns
468    ///
469    /// - [`TouchEvent`] the full TouchEvent report
470    pub fn get_touch_event(&mut self) -> Result<RawTouchEvent, <I2C as ErrorType>::Error> {
471        let mut report: [u8; REPORT_SIZE] = [0; REPORT_SIZE];
472        self.i2c
473            .write_read(self.address, &[Reg::DeviceMode.into()], &mut report)?;
474
475        let event: RawTouchEvent = report.into();
476        Ok(event.translate_orientation(self.size, self.orientation))
477    }
478
479    /// Get the current gesture detection parameters
480    /// (Currently not working)
481    pub fn get_gesture_params(&mut self) -> Result<GestureParams, <I2C as ErrorType>::Error> {
482        let mut buf: [u8; 6] = [0; 6];
483
484        self.i2c
485            .write_read(self.address, &[Reg::GestRadianValue.into()], &mut buf)?;
486        Ok(GestureParams {
487            minimum_angle: buf[0],
488            offset_left_right: buf[1],
489            offset_up_down: buf[2],
490            dist_left_right: buf[3],
491            dist_up_down: buf[4],
492            dist_zoom: buf[5],
493        })
494    }
495
496    /// Set the touch detection threshold
497    ///
498    /// # Arguments
499    ///
500    /// - `value` the threshold value
501    pub fn set_touch_threshold(&mut self, value: u8) -> Result<(), <I2C as ErrorType>::Error> {
502        self.i2c
503            .write(self.address, &[Reg::TouchDetectionThreshold.into(), value])
504    }
505
506    /// Set the touch filter coefficient
507    ///
508    /// # Arguments
509    ///
510    /// - `value` the touch filter coefficient
511    pub fn set_touch_filter_coefficient(
512        &mut self,
513        value: u8,
514    ) -> Result<(), <I2C as ErrorType>::Error> {
515        self.i2c
516            .write(self.address, &[Reg::TouchFilterCoeff.into(), value])
517    }
518
519    /// Set the control mode
520    ///
521    /// - 0: Will keep the Active mode when there is no touching
522    /// - 1: Switching from Active mode to Monitor mode automatically when there
523    ///   is no touching and the TimeActiveMonitor period is elapsed
524    ///
525    /// # Arguments
526    ///
527    /// - `value` the control mode
528    pub fn set_control_mode(&mut self, value: u8) -> Result<(), <I2C as ErrorType>::Error> {
529        self.i2c
530            .write(self.address, &[Reg::ControlMode.into(), value])
531    }
532
533    /// Set the period used to switch from Active to Monitor mode
534    ///
535    /// # Arguments
536    ///
537    /// - `value` the switching period
538    pub fn set_time_active_monitor(&mut self, value: u8) -> Result<(), <I2C as ErrorType>::Error> {
539        self.i2c
540            .write(self.address, &[Reg::TimeActiveMonitor.into(), value])
541    }
542
543    /// Set the report rate in Active mode
544    ///
545    /// # Arguments
546    ///
547    /// - `value` the report rate in Active mode
548    pub fn set_period_active(&mut self, value: u8) -> Result<(), <I2C as ErrorType>::Error> {
549        self.i2c
550            .write(self.address, &[Reg::PeriodActive.into(), value])
551    }
552
553    /// Set the report rate in Monitor mode
554    ///
555    /// # Arguments
556    ///
557    /// - `value` the report rate in Monitor mode
558    pub fn set_period_monitor(&mut self, value: u8) -> Result<(), <I2C as ErrorType>::Error> {
559        self.i2c
560            .write(self.address, &[Reg::PeriodMonitor.into(), value])
561    }
562
563    /// Set the minimum angle for gesture detection
564    ///
565    /// # Arguments
566    ///
567    /// - `value` The value of the minimum allowed angle while rotating gesture
568    ///   mode
569    pub fn set_gesture_minimum_angle(
570        &mut self,
571        value: u8,
572    ) -> Result<(), <I2C as ErrorType>::Error> {
573        self.i2c
574            .write(self.address, &[Reg::GestRadianValue.into(), value])
575    }
576
577    /// Set the maximum offset for detecting Moving left and Moving right gestures
578    ///
579    /// # Arguments
580    ///
581    /// - `value` The value of the maximum offset for detecting Moving left and Moving right gestures
582    ///   mode
583    pub fn set_gesture_offset_left_right(
584        &mut self,
585        value: u8,
586    ) -> Result<(), <I2C as ErrorType>::Error> {
587        self.i2c
588            .write(self.address, &[Reg::GestOffsetLeftRight.into(), value])
589    }
590
591    /// Set the maximum offset for detecting Moving up and Moving down gestures
592    ///
593    /// # Arguments
594    ///
595    /// - `value` The value of the maximum offset for detecting Moving up and Moving down gestures
596    ///   mode
597    pub fn set_gesture_offset_up_down(
598        &mut self,
599        value: u8,
600    ) -> Result<(), <I2C as ErrorType>::Error> {
601        self.i2c
602            .write(self.address, &[Reg::GestOffsetUpDown.into(), value])
603    }
604
605    /// Set the minimum distance for detecting Moving up and Moving down gestures
606    ///
607    /// # Arguments
608    ///
609    /// - `value` The value of the minimum distance for detecting Moving up and Moving down gestures
610    ///   mode
611    pub fn set_gesture_distance_up_down(
612        &mut self,
613        value: u8,
614    ) -> Result<(), <I2C as ErrorType>::Error> {
615        self.i2c
616            .write(self.address, &[Reg::GestDistUpDown.into(), value])
617    }
618
619    /// Set the minimum distance for detecting Moving left and Moving right gestures
620    ///
621    /// # Arguments
622    ///
623    /// - `value` The value of the minimum distance for detecting Moving left and Moving right
624    ///   gestures mode
625    pub fn set_gesture_distance_left_right(
626        &mut self,
627        value: u8,
628    ) -> Result<(), <I2C as ErrorType>::Error> {
629        self.i2c
630            .write(self.address, &[Reg::GestDistLeftRight.into(), value])
631    }
632
633    /// Set the minimum distance for detecting zoom gestures
634    ///
635    /// # Arguments
636    ///
637    /// - `value` The value of the minimum distance for detecting zoom gestures mode
638    pub fn set_gesture_distance_zoom(
639        &mut self,
640        value: u8,
641    ) -> Result<(), <I2C as ErrorType>::Error> {
642        self.i2c
643            .write(self.address, &[Reg::GestDistZoom.into(), value])
644    }
645
646    /// Get device information
647    ///
648    /// # Returns
649    ///
650    /// - `None` if the device is not initialized
651    /// - [`Some(Ft6x36Info)`](Ft6x36Info) otherwise
652    pub fn get_info(&self) -> Option<Ft6x36Info> {
653        self.info
654    }
655
656    /// Get device Diagnostics
657    ///
658    /// # Returns
659    ///
660    /// -
661    pub fn get_diagnostics(&mut self) -> Result<Diagnostics, <I2C as ErrorType>::Error> {
662        let mut buf: [u8; 1] = [0];
663        self.i2c
664            .write_read(self.address, &[Reg::PowerMode.into()], &mut buf)?;
665        let power_mode = buf[0];
666        self.i2c
667            .write_read(self.address, &[Reg::GMode.into()], &mut buf)?;
668        let g_mode = buf[0];
669        self.i2c
670            .write_read(self.address, &[Reg::OperatingMode.into()], &mut buf)?;
671        let state = buf[0];
672        self.i2c
673            .write_read(self.address, &[Reg::LibVersionH.into()], &mut buf)?;
674        let lib_version_h = buf[0];
675        self.i2c
676            .write_read(self.address, &[Reg::LibVersionL.into()], &mut buf)?;
677        let lib_version_l = buf[0];
678        let lib_version: u16 = (lib_version_h as u16) << 8 | lib_version_l as u16;
679        self.i2c
680            .write_read(self.address, &[Reg::ControlMode.into()], &mut buf)?;
681        let control_mode = buf[0];
682        Ok(Diagnostics {
683            power_mode,
684            g_mode,
685            state,
686            lib_version,
687            control_mode,
688        })
689    }
690
691    #[cfg(feature = "event_process")]
692    pub fn process_event(
693        &mut self,
694        time: core::time::Duration,
695        event: RawTouchEvent,
696    ) -> Option<TouchEvent> {
697        // If there are no touch points, it means the end of gesture
698        // We need to analyze it
699        if event.p1.is_none() {
700            match (self.events.0.take(), self.events.1.take()) {
701                (Some(e1), Some(mut e2)) => {
702                    if (e2.time - e1.time).le(&self.config.gesture_timing) {
703                        (match (e1.event.p1, e2.event.p1) {
704                            (Some(e1p1), Some(e2p1)) => process_swipe(e1p1, e2p1, &self.config),
705                            _ => None,
706                        })
707                        .or_else(|| process_touch_points(e2.event.p1.take(), e2.event.p2.take()))
708                    } else {
709                        process_touch_points(e2.event.p1.take(), e2.event.p2.take())
710                    }
711                }
712                (Some(mut e1), None) => {
713                    process_touch_points(e1.event.p1.take(), e1.event.p2.take())
714                }
715                (_, _) => None,
716            }
717        } else {
718            // The gesture is not terminated, we should keep track of the last event
719            if self.events.0.is_some() {
720                // Though if a previous event was TouchTwoPoint we should not
721                // ditch it in favor of a TouchOnePoint if there are happening
722                // close to each other
723                match &self.events.1 {
724                    Some(old_evt1) => {
725                        let time_evt1 = old_evt1.time;
726                        let old_evt1 = old_evt1.event;
727                        if old_evt1.p2.is_none()
728                            || event.p2.is_some()
729                            || (time - time_evt1) > MAX_DELTA_TOUCH_EVENT
730                        {
731                            self.events.1 = Some(TimedRawTouchEvent { time, event })
732                        }
733                    }
734                    None => self.events.1 = Some(TimedRawTouchEvent { time, event }),
735                }
736            } else {
737                self.events.0 = Some(TimedRawTouchEvent { time, event });
738            }
739            None
740        }
741    }
742}
743
744#[cfg(feature = "event_process")]
745fn process_touch_points(
746    mut p1: Option<TouchPoint>,
747    mut p2: Option<TouchPoint>,
748) -> Option<TouchEvent> {
749    match (p1.take(), p2.take()) {
750        (Some(p1), Some(p2)) => Some(TouchEvent::TouchTwoPoint(p1, p2)),
751        (Some(p1), None) => Some(TouchEvent::TouchOnePoint(p1)),
752        _ => None,
753    }
754}
755
756#[cfg(feature = "event_process")]
757fn process_swipe(
758    e1p1: TouchPoint,
759    e2p1: TouchPoint,
760    config: &ProcessEventConfig,
761) -> Option<TouchEvent> {
762    let delta_x = (e1p1.x as i16 - e2p1.x as i16).unsigned_abs();
763    let delta_y = (e1p1.y as i16 - e2p1.y as i16).unsigned_abs();
764    if delta_x < config.max_swipe_delta && delta_y > config.min_swipe_delta {
765        if e1p1.y > e2p1.y {
766            Some(TouchEvent::Swipe(
767                Direction::Down,
768                SwipeInfo {
769                    velocity: e1p1.y - e2p1.y,
770                    point: e1p1,
771                },
772            ))
773        } else {
774            Some(TouchEvent::Swipe(
775                Direction::Up,
776                SwipeInfo {
777                    velocity: e2p1.y - e1p1.y,
778                    point: e1p1,
779                },
780            ))
781        }
782    } else if delta_x > config.max_swipe_delta && delta_y < config.min_swipe_delta {
783        if e1p1.x > e2p1.x {
784            Some(TouchEvent::Swipe(
785                Direction::Right,
786                SwipeInfo {
787                    velocity: e1p1.x - e2p1.x,
788                    point: e1p1,
789                },
790            ))
791        } else {
792            Some(TouchEvent::Swipe(
793                Direction::Left,
794                SwipeInfo {
795                    velocity: e2p1.x - e1p1.x,
796                    point: e1p1,
797                },
798            ))
799        }
800    } else {
801        None
802    }
803}
804
805#[cfg(test)]
806mod test {
807    use super::*;
808
809    #[test]
810    fn test_raw_event_from_report_ok() {
811        #[allow(clippy::unusual_byte_groupings)]
812        let report: [u8; REPORT_SIZE] = [
813            0x00,         // Device Mode
814            0x00,         // Gesture id
815            0x02,         // Touch status
816            0b10_00_0000, // Touch type _ Reserved _ XH
817            0x13,         // XL
818            0b0000_0000,  // Touch id _ YH
819            0x14,         // YL
820            0x00,         // Weight
821            0x00,         // Misc
822            0b10_00_0000, // Touch type _ Reserved _ XH
823            0x25,         // XL
824            0b0000_0000,  // Touch id _ YH
825            0x26,         // YL
826            0x00,         // Weight
827            0x00,         // Misc
828        ];
829        let actual: RawTouchEvent = report.into();
830
831        let expected = RawTouchEvent {
832            device_mode: DeviceMode::Working,
833            gesture_id: GestureId::NoGesture,
834            p1: Some(TouchPoint {
835                touch_type: TouchType::Contact,
836                x: 0x13,
837                y: 0x14,
838            }),
839            p2: Some(TouchPoint {
840                touch_type: TouchType::Contact,
841                x: 0x25,
842                y: 0x26,
843            }),
844        };
845
846        assert_eq!(actual, expected);
847    }
848
849    #[test]
850    fn test_raw_event_from_report_ok_high_value() {
851        #[allow(clippy::unusual_byte_groupings)]
852        let report: [u8; REPORT_SIZE] = [
853            0x00,         // Device Mode
854            0x00,         // Gesture id
855            0x02,         // Touch status
856            0b10_00_0001, // Touch type _ Reserved _ XH
857            0x13,         // XL
858            0b0000_0010,  // Touch id _ YH
859            0x14,         // YL
860            0x00,         // Weight
861            0x00,         // Misc
862            0b10_00_0100, // Touch type _ Reserved _ XH
863            0x25,         // XL
864            0b0000_1000,  // Touch id _ YH
865            0x26,         // YL
866            0x00,         // Weight
867            0x00,         // Misc
868        ];
869        let actual: RawTouchEvent = report.into();
870
871        let expected = RawTouchEvent {
872            device_mode: DeviceMode::Working,
873            gesture_id: GestureId::NoGesture,
874            p1: Some(TouchPoint {
875                touch_type: TouchType::Contact,
876                x: 0x0113,
877                y: 0x0214,
878            }),
879            p2: Some(TouchPoint {
880                touch_type: TouchType::Contact,
881                x: 0x0425,
882                y: 0x0826,
883            }),
884        };
885
886        assert_eq!(actual, expected);
887    }
888
889    #[test]
890    fn test_process_swipe_right_ok() {
891        let p1 = TouchPoint {
892            x: 340,
893            y: 200,
894            touch_type: TouchType::Contact,
895        };
896        let p2 = TouchPoint {
897            x: 25,
898            y: 203,
899            touch_type: TouchType::Contact,
900        };
901        let actual = process_swipe(p1, p2, &ProcessEventConfig::default());
902        assert!(actual.is_some());
903        let actual = actual.unwrap();
904
905        let expected = TouchEvent::Swipe(
906            Direction::Right,
907            SwipeInfo {
908                velocity: 315,
909                point: TouchPoint {
910                    x: 340,
911                    y: 200,
912                    touch_type: TouchType::Contact,
913                },
914            },
915        );
916
917        assert_eq!(actual, expected);
918    }
919
920    #[test]
921    fn test_process_swipe_left_ok() {
922        let p1 = TouchPoint {
923            x: 231,
924            y: 200,
925            touch_type: TouchType::Contact,
926        };
927        let p2 = TouchPoint {
928            x: 334,
929            y: 197,
930            touch_type: TouchType::Contact,
931        };
932        let actual = process_swipe(p1, p2, &ProcessEventConfig::default());
933        assert!(actual.is_some());
934        let actual = actual.unwrap();
935
936        let expected = TouchEvent::Swipe(
937            Direction::Left,
938            SwipeInfo {
939                velocity: 103,
940                point: TouchPoint {
941                    x: 231,
942                    y: 200,
943                    touch_type: TouchType::Contact,
944                },
945            },
946        );
947
948        assert_eq!(actual, expected);
949    }
950
951    #[test]
952    fn test_process_swipe_up_ok() {
953        let p1 = TouchPoint {
954            x: 231,
955            y: 200,
956            touch_type: TouchType::Contact,
957        };
958        let p2 = TouchPoint {
959            x: 234,
960            y: 302,
961            touch_type: TouchType::Contact,
962        };
963        let actual = process_swipe(p1, p2, &ProcessEventConfig::default());
964        assert!(actual.is_some());
965        let actual = actual.unwrap();
966
967        let expected = TouchEvent::Swipe(
968            Direction::Up,
969            SwipeInfo {
970                velocity: 102,
971                point: TouchPoint {
972                    x: 231,
973                    y: 200,
974                    touch_type: TouchType::Contact,
975                },
976            },
977        );
978
979        assert_eq!(actual, expected);
980    }
981
982    #[test]
983    fn test_process_swipe_down_ok() {
984        let p1 = TouchPoint {
985            x: 231,
986            y: 200,
987            touch_type: TouchType::Contact,
988        };
989        let p2 = TouchPoint {
990            x: 225,
991            y: 25,
992            touch_type: TouchType::Contact,
993        };
994        let actual = process_swipe(p1, p2, &ProcessEventConfig::default());
995        assert!(actual.is_some());
996        let actual = actual.unwrap();
997
998        let expected = TouchEvent::Swipe(
999            Direction::Down,
1000            SwipeInfo {
1001                velocity: 175,
1002                point: TouchPoint {
1003                    x: 231,
1004                    y: 200,
1005                    touch_type: TouchType::Contact,
1006                },
1007            },
1008        );
1009
1010        assert_eq!(actual, expected);
1011    }
1012}