1use std::sync::atomic::Ordering;
5use std::sync::{Arc, PoisonError};
6use std::time::{Duration, Instant};
7
8use mirror::Point;
9use mirror::backend::{BackendError, Input, InputAck, InputSink, Release};
10use protocol::code;
11use protocol::message::{Call, KeyParams, Reply, SwipeParams, TapParams, TextMethod, TextParams};
12
13use super::Inner;
14use crate::emulator::{self, TouchPoint};
15
16const SWIPE_STEP: Duration = Duration::from_millis(8);
19
20pub const REST: Duration = Duration::from_millis(60);
29
30fn millis(d: Duration) -> u32 {
31 u32::try_from(d.as_millis()).unwrap_or(u32::MAX)
32}
33
34fn rest(release: Release) -> Option<Duration> {
37 match release {
38 Release::Moving => None,
39 Release::Resting => Some(REST),
40 }
41}
42
43pub fn agent_call(input: &Input, scale: f64) -> Call {
45 match input {
46 Input::Tap { at, hold } => {
47 let (x, y) = at.to_pixels(scale);
48 Call::Tap(TapParams {
49 x,
50 y,
51 duration_ms: hold.map(millis),
52 })
53 }
54 Input::Swipe {
55 from,
56 to,
57 duration,
58 release,
59 } => {
60 let (x1, y1) = from.to_pixels(scale);
61 let (x2, y2) = to.to_pixels(scale);
62 Call::Swipe(SwipeParams {
63 x1,
64 y1,
65 x2,
66 y2,
67 duration_ms: millis(*duration),
68 rest_ms: rest(*release).map(millis),
69 })
70 }
71 Input::Key { code, meta } => Call::Key(KeyParams {
72 code: *code,
73 meta: *meta,
74 }),
75 Input::Text { text, node, method } => Call::TextSet(TextParams {
76 text: text.clone(),
77 node: node.map(|handle| handle.node),
78 mode: (*method != TextMethod::AUTO).then_some(*method),
79 }),
80 }
81}
82
83fn gesture_time(input: &Input) -> Duration {
85 match input {
86 Input::Tap { hold, .. } => hold.unwrap_or(Duration::ZERO),
87 Input::Swipe {
88 duration, release, ..
89 } => *duration + rest(*release).unwrap_or_default(),
90 Input::Key { .. } | Input::Text { .. } => Duration::ZERO,
91 }
92}
93
94pub fn input_ack(reply: Reply) -> Result<InputAck, protocol::Error> {
96 match reply {
97 Reply::Input(ack) => Ok(InputAck {
98 accepted: ack.accepted,
99 method: None,
100 }),
101 Reply::Text(ack) => Ok(InputAck {
102 accepted: true,
103 method: Some(ack.method),
104 }),
105 _ => Err(protocol::Error::new(
106 code::PROTOCOL_ERROR,
107 "the agent answered an input request with another result",
108 )),
109 }
110}
111
112pub fn swipe_touches(
120 from: (i32, i32),
121 to: (i32, i32),
122 duration: Duration,
123 step: Duration,
124 rest: Option<Duration>,
125) -> Vec<(Duration, TouchPoint)> {
126 let steps = if step.is_zero() {
127 1
128 } else {
129 duration.as_nanos().div_ceil(step.as_nanos()).max(1)
130 };
131 let steps = u32::try_from(steps).unwrap_or(u32::MAX);
132 let along = |from: i32, to: i32, i: u32| {
133 from + ((f64::from(to - from) * f64::from(i)) / f64::from(steps)).round() as i32
134 };
135 let mut touches = Vec::with_capacity(steps as usize + 3);
136 touches.push((Duration::ZERO, TouchPoint::down(from.0, from.1)));
137 for i in 1..=steps {
138 touches.push((
139 duration * i / steps,
140 TouchPoint::down(along(from.0, to.0, i), along(from.1, to.1, i)),
141 ));
142 }
143 let (at, end) = match rest {
144 Some(rest) => {
145 let on = (
146 to.0 + (to.0 - from.0).signum(),
147 to.1 + (to.1 - from.1).signum(),
148 );
149 touches.push((duration + rest, TouchPoint::down(on.0, on.1)));
150 (duration + rest, on)
151 }
152 None => (duration, to),
153 };
154 touches.push((at, TouchPoint::up(end.0, end.1)));
155 touches
156}
157
158pub(crate) struct SessionInput {
160 pub(crate) inner: Arc<Inner>,
161}
162
163enum ControllerFailure {
166 BeforeContact(emulator::Error),
167 AfterContact(emulator::Error),
168}
169
170impl SessionInput {
171 fn through_controller(&self, input: &Input) -> Option<Result<InputAck, ControllerFailure>> {
174 let inner = &*self.inner;
175 let link = inner.controller.as_ref()?;
176 if inner
177 .controller_failed
178 .lock()
179 .unwrap_or_else(PoisonError::into_inner)
180 .is_some()
181 {
182 return None;
183 }
184 let pixels = |p: &Point| p.to_pixels(inner.scale);
185 let touches = match input {
186 Input::Tap { at, hold } => {
187 let (x, y) = pixels(at);
188 let hold = hold.unwrap_or(Duration::ZERO);
189 vec![
190 (Duration::ZERO, TouchPoint::down(x, y)),
191 (hold, TouchPoint::up(x, y)),
192 ]
193 }
194 Input::Swipe {
195 from,
196 to,
197 duration,
198 release,
199 } => swipe_touches(
200 pixels(from),
201 pixels(to),
202 *duration,
203 SWIPE_STEP,
204 rest(*release),
205 ),
206 Input::Key { .. } | Input::Text { .. } => return None,
207 };
208 let controller = link.controller().clone();
209 let result = link.block_on(async move {
210 let start = tokio::time::Instant::now();
211 for (i, (at, point)) in touches.iter().enumerate() {
212 tokio::time::sleep_until(start + *at).await;
213 if let Err(error) = controller.touch(std::slice::from_ref(point)).await {
214 return Err(if i == 0 {
215 ControllerFailure::BeforeContact(error)
216 } else {
217 ControllerFailure::AfterContact(error)
218 });
219 }
220 }
221 Ok(())
222 });
223 if !matches!(result, Err(ControllerFailure::BeforeContact(_))) {
224 inner.note_foreign_input(Instant::now());
225 inner.controller_inputs.fetch_add(1, Ordering::SeqCst);
226 }
227 Some(result.map(|()| InputAck {
228 accepted: true,
229 method: None,
230 }))
231 }
232}
233
234impl InputSink for SessionInput {
235 fn perform(&mut self, input: &Input) -> Result<InputAck, BackendError> {
236 match self.through_controller(input) {
237 Some(Ok(ack)) => return Ok(ack),
238 Some(Err(failure)) => {
239 let (error, retry) = match failure {
240 ControllerFailure::BeforeContact(error) => (error, true),
241 ControllerFailure::AfterContact(error) => (error, false),
242 };
243 *self
244 .inner
245 .controller_failed
246 .lock()
247 .unwrap_or_else(PoisonError::into_inner) = Some(error.to_string());
248 if !retry {
249 return Err(BackendError::new(
250 code::INTERNAL,
251 format!(
252 "the emulator controller failed during the gesture ({error}); \
253 later input goes through the agent"
254 ),
255 ));
256 }
257 }
258 None => {}
259 }
260 let inner = &*self.inner;
261 let reply = inner.connection.call(
262 agent_call(input, inner.scale),
263 inner.options.input_deadline + gesture_time(input),
264 )?;
265 inner.agent_inputs.fetch_add(1, Ordering::SeqCst);
266 input_ack(reply)
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273 use mirror::Point;
274 use mirror::backend::NodeHandle;
275 use protocol::code;
276 use protocol::message::{
277 InputAck as AgentAck, KeyParams, SwipeParams, TapParams, TextAck, TextMethod, TextParams,
278 };
279
280 const SCALE: f64 = 2.625;
281
282 #[test]
283 fn taps_and_long_presses_go_out_in_pixels() {
284 let tap = Input::Tap {
285 at: Point::new(100.0, 200.0),
286 hold: None,
287 };
288 assert_eq!(
289 agent_call(&tap, SCALE),
290 Call::Tap(TapParams {
291 x: 263,
292 y: 525,
293 duration_ms: None
294 })
295 );
296 let press = Input::Tap {
297 at: Point::new(10.4, 0.2),
298 hold: Some(Duration::from_millis(800)),
299 };
300 assert_eq!(
301 agent_call(&press, SCALE),
302 Call::Tap(TapParams {
303 x: 27,
304 y: 1,
305 duration_ms: Some(800)
306 })
307 );
308 }
309
310 #[test]
311 fn swipes_keep_their_duration_and_keys_their_code() {
312 let swipe = Input::Swipe {
313 from: Point::new(200.0, 800.0),
314 to: Point::new(200.0, 200.0),
315 duration: Duration::from_millis(300),
316 release: Release::Moving,
317 };
318 assert_eq!(
319 agent_call(&swipe, SCALE),
320 Call::Swipe(SwipeParams {
321 x1: 525,
322 y1: 2100,
323 x2: 525,
324 y2: 525,
325 duration_ms: 300,
326 rest_ms: None
327 })
328 );
329 let key = Input::Key {
330 code: 66,
331 meta: Some(1),
332 };
333 assert_eq!(
334 agent_call(&key, SCALE),
335 Call::Key(KeyParams {
336 code: 66,
337 meta: Some(1)
338 })
339 );
340 }
341
342 #[test]
343 fn text_names_the_node_of_the_latest_read_and_its_method() {
344 let text = Input::Text {
345 text: "hunter2".into(),
346 node: Some(NodeHandle {
347 window: 3,
348 node: 42,
349 }),
350 method: TextMethod::DIRECT,
351 };
352 assert_eq!(
353 agent_call(&text, SCALE),
354 Call::TextSet(TextParams {
355 text: "hunter2".into(),
356 node: Some(42),
357 mode: Some(TextMethod::DIRECT)
358 })
359 );
360 let focused = Input::Text {
361 text: "a".into(),
362 node: None,
363 method: TextMethod::AUTO,
364 };
365 assert_eq!(
366 agent_call(&focused, SCALE),
367 Call::TextSet(TextParams {
368 text: "a".into(),
369 node: None,
370 mode: None
371 }),
372 "auto leaves the choice to the agent"
373 );
374 }
375
376 #[test]
377 fn acknowledgements_carry_the_platforms_verdict_and_the_text_method() {
378 assert_eq!(
379 input_ack(Reply::Input(AgentAck { accepted: false })).unwrap(),
380 InputAck {
381 accepted: false,
382 method: None
383 }
384 );
385 assert_eq!(
386 input_ack(Reply::Text(TextAck {
387 method: TextMethod::KEYS
388 }))
389 .unwrap(),
390 InputAck {
391 accepted: true,
392 method: Some(TextMethod::KEYS)
393 }
394 );
395 let error = input_ack(Reply::Empty).unwrap_err();
396 assert_eq!(error.code, code::PROTOCOL_ERROR);
397 }
398
399 #[test]
403 fn a_swipe_released_resting_asks_the_agent_to_rest_60_ms() {
404 let swipe = Input::Swipe {
405 from: Point::new(200.0, 800.0),
406 to: Point::new(200.0, 200.0),
407 duration: Duration::from_millis(300),
408 release: Release::Resting,
409 };
410 assert_eq!(
411 agent_call(&swipe, SCALE),
412 Call::Swipe(SwipeParams {
413 x1: 525,
414 y1: 2100,
415 x2: 525,
416 y2: 525,
417 duration_ms: 300,
418 rest_ms: Some(60)
419 })
420 );
421 assert_eq!(gesture_time(&swipe), Duration::from_millis(360));
422 }
423
424 #[test]
432 fn a_swipe_released_resting_on_the_controller_rests_then_moves_a_pixel_on_and_lifts() {
433 let touches = swipe_touches(
434 (100, 1000),
435 (100, 200),
436 Duration::from_millis(40),
437 Duration::from_millis(8),
438 Some(Duration::from_millis(60)),
439 );
440 let n = touches.len();
441 assert_eq!(n, 8, "{touches:?}");
442 assert_eq!(
443 touches[n - 3],
444 (Duration::from_millis(40), TouchPoint::down(100, 200))
445 );
446 assert_eq!(
447 touches[n - 2],
448 (Duration::from_millis(100), TouchPoint::down(100, 199))
449 );
450 assert_eq!(
451 touches[n - 1],
452 (Duration::from_millis(100), TouchPoint::up(100, 199))
453 );
454 let across = swipe_touches(
455 (1000, 500),
456 (200, 500),
457 Duration::from_millis(40),
458 Duration::from_millis(8),
459 Some(Duration::from_millis(60)),
460 );
461 assert_eq!(
462 across.last(),
463 Some(&(Duration::from_millis(100), TouchPoint::up(199, 500)))
464 );
465 }
466
467 #[test]
468 fn a_swipe_on_the_controller_is_down_moves_and_up_along_the_line() {
469 let touches = swipe_touches(
470 (100, 1000),
471 (100, 200),
472 Duration::from_millis(40),
473 Duration::from_millis(8),
474 None,
475 );
476 let first = touches.first().unwrap();
477 assert_eq!(first, &(Duration::ZERO, TouchPoint::down(100, 1000)));
478 let last = touches.last().unwrap();
479 assert_eq!(last, &(Duration::from_millis(40), TouchPoint::up(100, 200)));
480 let moves = &touches[1..touches.len() - 1];
481 assert_eq!(moves.len(), 5, "{touches:?}");
482 assert_eq!(moves.last().unwrap().1, TouchPoint::down(100, 200));
483 let mut previous = (Duration::ZERO, 1000);
484 for (at, point) in moves {
485 assert!(*at > previous.0 && point.y < previous.1, "{touches:?}");
486 assert_eq!(point.pressure, crate::emulator::DOWN_PRESSURE);
487 previous = (*at, point.y);
488 }
489 }
490
491 #[test]
492 fn a_swipe_without_duration_still_moves_once() {
493 let touches = swipe_touches(
494 (0, 0),
495 (50, 0),
496 Duration::ZERO,
497 Duration::from_millis(8),
498 None,
499 );
500 assert_eq!(
501 touches,
502 vec![
503 (Duration::ZERO, TouchPoint::down(0, 0)),
504 (Duration::ZERO, TouchPoint::down(50, 0)),
505 (Duration::ZERO, TouchPoint::up(50, 0)),
506 ]
507 );
508 }
509}