1 use {Event, Input, Motion};
2 
3 /// Stores the touch state.
4 #[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
5 pub enum Touch {
6     /// The start of touch, for example
7     /// a finger pressed down on a touch screen.
8     Start,
9     /// The move of touch, for example
10     /// a finger moving while touching a touch screen.
11     Move,
12     /// The end of touch, for example
13     /// taking a finger away from a touch screen.
14     End,
15     /// The cancel of touch, for example
16     /// the window loses focus.
17     Cancel,
18 }
19 
20 /// Touch arguments
21 ///
22 /// The `id` might be reused for different touches that do not overlap in time.
23 ///
24 /// - Coordinates are normalized to support both touch screens and trackpads
25 /// - Supports both 2D and 3D touch
26 /// - The pressure direction vector should have maximum length 1
27 ///
28 /// For 2D touch the pressure is pointed in the z direction.
29 /// Use `.pressure()` to get the pressure magnitude.
30 #[derive(Copy, Clone, Deserialize, Serialize, PartialEq, PartialOrd, Debug)]
31 pub struct TouchArgs {
32     /// A unique identifier for touch device.
33     pub device: i64,
34     /// A unique identifier for touch event.
35     pub id: i64,
36     /// The touch position, normalized 0..1.
37     pub position_3d: [f64; 3],
38     /// The touch pressure vector, normalized 0..1.
39     pub pressure_3d: [f64; 3],
40     /// Whether the touch is in 3D.
41     pub is_3d: bool,
42     /// The touch state.
43     pub touch: Touch,
44 }
45 
46 impl TouchArgs {
47     /// Creates arguments for 2D touch.
new(device: i64, id: i64, position: [f64; 2], pressure: f64, touch: Touch) -> TouchArgs48     pub fn new(device: i64, id: i64, position: [f64; 2], pressure: f64, touch: Touch) -> TouchArgs {
49         TouchArgs {
50             device: device,
51             id: id,
52             position_3d: [position[0], position[1], 0.0],
53             pressure_3d: [0.0, 0.0, pressure],
54             is_3d: false,
55             touch: touch,
56         }
57     }
58 
59     /// Creates arguments for 3D touch.
60     ///
61     /// The pressure direction vector should have maximum length 1.
new_3d(device: i64, id: i64, position_3d: [f64; 3], pressure_3d: [f64; 3], touch: Touch) -> TouchArgs62     pub fn new_3d(device: i64,
63                   id: i64,
64                   position_3d: [f64; 3],
65                   pressure_3d: [f64; 3],
66                   touch: Touch)
67                   -> TouchArgs {
68         TouchArgs {
69             device: device,
70             id: id,
71             position_3d,
72             pressure_3d,
73             is_3d: true,
74             touch: touch,
75         }
76     }
77 
78     /// The position of the touch in 2D.
position(&self) -> [f64; 2]79     pub fn position(&self) -> [f64; 2] {
80         [self.position_3d[0], self.position_3d[1]]
81     }
82 
83     /// The position of the touch in 3D.
position_3d(&self) -> [f64; 3]84     pub fn position_3d(&self) -> [f64; 3] {self.position_3d}
85 
86     /// The pressure magnitude, normalized 0..1.
pressure(&self) -> f6487     pub fn pressure(&self) -> f64 {
88         let px = self.pressure_3d[0];
89         let py = self.pressure_3d[1];
90         let pz = self.pressure_3d[2];
91         (px * px + py * py + pz * pz).sqrt()
92     }
93 
94     /// The pressure vector in 3D.
pressure_3d(&self) -> [f64; 3]95     pub fn pressure_3d(&self) -> [f64; 3] {self.pressure_3d}
96 }
97 
98 /// When a touch is started, moved, ended or cancelled.
99 pub trait TouchEvent: Sized {
100     /// Creates a touch event.
101     ///
102     /// Preserves time stamp from original input event, if any.
from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>103     fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>;
104     /// Calls closure if this is a touch event.
touch<U, F>(&self, f: F) -> Option<U> where F: FnMut(&TouchArgs) -> U105     fn touch<U, F>(&self, f: F) -> Option<U> where F: FnMut(&TouchArgs) -> U;
106     /// Returns touch arguments.
touch_args(&self) -> Option<TouchArgs>107     fn touch_args(&self) -> Option<TouchArgs> {
108         self.touch(|args| args.clone())
109     }
110 }
111 
112 impl TouchEvent for Event {
from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>113     fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self> {
114         let timestamp = if let Event::Input(_, x) = old_event {*x} else {None};
115         Some(Event::Input(Input::Move(Motion::Touch(*args)), timestamp))
116     }
117 
touch<U, F>(&self, mut f: F) -> Option<U> where F: FnMut(&TouchArgs) -> U118     fn touch<U, F>(&self, mut f: F) -> Option<U>
119         where F: FnMut(&TouchArgs) -> U
120     {
121         match *self {
122             Event::Input(Input::Move(Motion::Touch(ref args)), _) => Some(f(args)),
123             _ => None,
124         }
125     }
126 }
127 
128 #[cfg(test)]
129 mod tests {
130     use super::*;
131 
132     #[test]
test_input_touch()133     fn test_input_touch() {
134         let pos = [0.0; 2];
135         let e: Event = TouchArgs::new(0, 0, pos, 1.0, Touch::Start).into();
136         let a: Option<Event> =
137             TouchEvent::from_touch_args(&TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
138         let b: Option<Event> = a.clone()
139             .unwrap()
140             .touch(|t| {
141                 TouchEvent::from_touch_args(&TouchArgs::new(t.device,
142                                                             t.id,
143                                                             t.position(),
144                                                             t.pressure(),
145                                                             Touch::Start),
146                                             a.as_ref().unwrap())
147             })
148             .unwrap();
149         assert_eq!(a, b);
150     }
151 
152     #[test]
test_input_touch_3d()153     fn test_input_touch_3d() {
154         use super::super::Event;
155 
156         let pos = [0.0; 3];
157         let pressure = [0.0, 0.0, 1.0];
158         let e: Event = TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start).into();
159         let a: Option<Event> =
160             TouchEvent::from_touch_args(&TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
161         let b: Option<Event> = a.clone()
162             .unwrap()
163             .touch(|t| {
164                 TouchEvent::from_touch_args(&TouchArgs::new_3d(t.device,
165                                                                t.id,
166                                                                t.position_3d(),
167                                                                t.pressure_3d(),
168                                                                Touch::Start),
169                                             a.as_ref().unwrap())
170             })
171             .unwrap();
172         assert_eq!(a, b);
173     }
174 }
175