1use anyhow::Error;
6use carnelian::render::Context;
7use carnelian::{input, Message, Size, ViewAssistant, ViewAssistantContext, ViewAssistantPtr};
8use recovery_util::ota::controller::SendEvent;
9use recovery_util::ota::state_machine::Event as StateMachineEvent;
10use std::collections::VecDeque;
11use zx::Event;
12
13#[cfg(feature = "debug_console")]
14use crate::console::ConsoleMessages;
15#[cfg(test)]
16use mockall::{predicate::*, *};
17
18pub enum ProxyMessages {
19 NewViewAssistant(Option<ViewAssistantPtr>),
20 ReplaceViewAssistant(Option<ViewAssistantPtr>),
21 PopViewAssistant,
22}
23
24pub struct ProxyViewAssistant {
25 event_sender: Option<Box<dyn SendEvent>>,
26 console_view_assistant: Option<ViewAssistantPtr>,
27 view_assistant_stack: VecDeque<ViewAssistantPtr>,
28 first_call_after_switch: bool,
29 #[cfg(feature = "debug_console")]
30 console_active: bool,
31}
32
33impl ProxyViewAssistant {
34 pub fn new(
37 event_sender: Option<Box<dyn SendEvent>>,
38 console_view_assistant: Option<ViewAssistantPtr>,
39 view_assistant_ptr: ViewAssistantPtr,
40 ) -> Result<ProxyViewAssistant, Error> {
41 let mut view_assistant_stack = VecDeque::new();
42 view_assistant_stack.push_front(view_assistant_ptr);
43
44 Ok(ProxyViewAssistant {
45 event_sender,
46 console_view_assistant,
47 view_assistant_stack,
48 first_call_after_switch: true,
49 #[cfg(feature = "debug_console")]
50 console_active: false,
51 })
52 }
53
54 #[cfg(feature = "debug_console")]
56 fn should_console_toggle(&mut self, event: &input::Event) -> bool {
57 if self.console_view_assistant.is_none() {
58 return false;
59 }
60 let mut toggle_requested = false;
61
62 match &event.event_type {
63 input::EventType::Touch(touch_event) => {
64 for contact in &touch_event.contacts {
65 let pointer_event = &input::pointer::Event::new_from_contact(contact);
66 match pointer_event.phase {
67 input::pointer::Phase::Down(location) => {
68 if location.x < 100 && location.y < 100 {
69 toggle_requested = true;
70 break;
71 }
72 }
73 _ => {}
74 };
75 }
76 }
77 _ => {}
78 }
79
80 toggle_requested
81 }
82}
83
84#[cfg_attr(test, automock)]
85impl ViewAssistant for ProxyViewAssistant {
86 fn setup(&mut self, context: &ViewAssistantContext) -> Result<(), Error> {
87 if let Some(console) = self.console_view_assistant.as_mut() {
88 console.setup(context)?;
89 }
90 self.view_assistant_stack.front_mut().unwrap().setup(context)
91 }
92
93 fn resize(&mut self, new_size: &Size) -> Result<(), Error> {
94 self.view_assistant_stack.front_mut().unwrap().resize(new_size)
95 }
96
97 fn render(
98 &mut self,
99 render_context: &mut Context,
100 buffer_ready_event: Event,
101 view_context: &ViewAssistantContext,
102 ) -> Result<(), Error> {
103 #[cfg(feature = "debug_console")]
104 if self.console_active {
105 if let Some(console) = self.console_view_assistant.as_mut() {
106 return console.render(render_context, buffer_ready_event, view_context);
107 } else {
108 eprintln!("Error: Console could not be found to render");
109 }
110 }
111
112 self.view_assistant_stack.front_mut().unwrap().render(
113 render_context,
114 buffer_ready_event,
115 view_context,
116 )
117 }
118
119 fn handle_input_event(
120 &mut self,
121 context: &mut ViewAssistantContext,
122 event: &input::Event,
123 ) -> Result<(), Error> {
124 #[cfg(feature = "debug_console")]
125 if self.should_console_toggle(event) {
126 self.console_active = !self.console_active;
127 return Ok(()); }
129
130 #[cfg(feature = "debug_console")]
131 if self.console_active {
132 return Ok(()); }
134
135 if self.first_call_after_switch {
136 self.first_call_after_switch = false;
137 self.handle_focus_event(context, true)?;
138 }
139 self.view_assistant_stack.front_mut().unwrap().handle_input_event(context, event)
140 }
141
142 fn handle_mouse_event(
143 &mut self,
144 context: &mut ViewAssistantContext,
145 event: &input::Event,
146 mouse_event: &input::mouse::Event,
147 ) -> Result<(), Error> {
148 self.view_assistant_stack.front_mut().unwrap().handle_mouse_event(
149 context,
150 event,
151 mouse_event,
152 )
153 }
154
155 fn handle_touch_event(
156 &mut self,
157 context: &mut ViewAssistantContext,
158 event: &input::Event,
159 touch_event: &input::touch::Event,
160 ) -> Result<(), Error> {
161 self.view_assistant_stack.front_mut().unwrap().handle_touch_event(
162 context,
163 event,
164 touch_event,
165 )
166 }
167
168 fn handle_pointer_event(
169 &mut self,
170 context: &mut ViewAssistantContext,
171 event: &input::Event,
172 pointer_event: &input::pointer::Event,
173 ) -> Result<(), Error> {
174 if self.first_call_after_switch {
175 self.first_call_after_switch = false;
176 self.handle_focus_event(context, true)?;
177 }
178 self.view_assistant_stack.front_mut().unwrap().handle_pointer_event(
179 context,
180 event,
181 pointer_event,
182 )
183 }
184
185 fn handle_keyboard_event(
186 &mut self,
187 context: &mut ViewAssistantContext,
188 event: &input::Event,
189 keyboard_event: &input::keyboard::Event,
190 ) -> Result<(), Error> {
191 self.view_assistant_stack.front_mut().unwrap().handle_keyboard_event(
192 context,
193 event,
194 keyboard_event,
195 )
196 }
197
198 fn handle_consumer_control_event(
199 &mut self,
200 context: &mut ViewAssistantContext,
201 event: &input::Event,
202 consumer_control_event: &input::consumer_control::Event,
203 ) -> Result<(), Error> {
204 self.view_assistant_stack.front_mut().unwrap().handle_consumer_control_event(
205 context,
206 event,
207 consumer_control_event,
208 )
209 }
210
211 fn handle_focus_event(
212 &mut self,
213 context: &mut ViewAssistantContext,
214 focused: bool,
215 ) -> Result<(), Error> {
216 self.view_assistant_stack.front_mut().unwrap().handle_focus_event(context, focused)
217 }
218
219 fn handle_message(&mut self, message: Message) {
220 if message.is::<ProxyMessages>() {
221 let proxy_message = message.downcast::<ProxyMessages>().unwrap();
222 match *proxy_message {
223 ProxyMessages::NewViewAssistant(mut view_assistant_ptr) => {
224 let view_assistant_ptr =
225 std::mem::replace(&mut view_assistant_ptr, None).unwrap();
226 self.first_call_after_switch = true;
227 self.view_assistant_stack.push_front(view_assistant_ptr);
228 }
229 ProxyMessages::ReplaceViewAssistant(mut view_assistant_ptr) => {
230 let view_assistant_ptr =
231 std::mem::replace(&mut view_assistant_ptr, None).unwrap();
232 self.first_call_after_switch = true;
233 self.view_assistant_stack.pop_front();
234 self.view_assistant_stack.push_front(view_assistant_ptr);
235 }
236 ProxyMessages::PopViewAssistant => {
237 if self.view_assistant_stack.len() > 1 {
238 self.first_call_after_switch = true;
239 self.view_assistant_stack.pop_front();
240 }
241 }
242 }
243 return;
244 } else if message.is::<StateMachineEvent>() {
245 if let Some(event) = message.downcast_ref::<StateMachineEvent>() {
246 if let Some(event_sender) = &mut self.event_sender {
247 event_sender.send(event.clone());
248 }
249 }
250 }
251
252 #[cfg(feature = "debug_console")]
253 if message.is::<ConsoleMessages>() {
254 if let Some(console) = self.console_view_assistant.as_mut() {
255 console.handle_message(message);
256 } else {
257 eprintln!("Error: Unable to find console to pass ConsoleMessages");
258 }
259 return;
260 }
261
262 self.view_assistant_stack.front_mut().unwrap().handle_message(message);
263 }
264
265 fn uses_pointer_events(&self) -> bool {
266 self.view_assistant_stack.front().unwrap().uses_pointer_events()
267 }
268
269 fn ownership_changed(&mut self, _owned: bool) -> Result<(), Error> {
270 self.view_assistant_stack.front_mut().unwrap().ownership_changed(_owned)
271 }
272
273 fn get_render_offset(&mut self) -> Option<i64> {
274 self.view_assistant_stack.front_mut().unwrap().get_render_offset()
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use carnelian::make_message;
282
283 enum MockMessageType {
284 MockMessage,
285 }
286
287 fn get_input_event() -> input::Event {
288 let mouse_event = input::mouse::Event {
289 buttons: Default::default(),
290 phase: input::mouse::Phase::Moved,
291 location: Default::default(),
292 };
293 input::Event {
294 event_time: 0,
295 device_id: Default::default(),
296 event_type: input::EventType::Mouse(mouse_event),
297 }
298 }
299
300 #[test]
301 fn test_proxy_view_assistant_switching() -> std::result::Result<(), anyhow::Error> {
302 #[cfg(feature = "debug_console")]
303 let mock0 = MockProxyViewAssistant::new();
304 let mut mock1 = MockProxyViewAssistant::new();
305 mock1.expect_handle_message().times(2).return_const(());
306 let mut mock2 = MockProxyViewAssistant::new();
307 mock2.expect_handle_message().times(1).return_const(());
308 let mut proxy = ProxyViewAssistant::new(
309 None,
310 #[cfg(feature = "debug_console")]
311 Some(Box::new(mock0)),
312 Box::new(mock1),
313 )
314 .unwrap();
315 assert_eq!(proxy.view_assistant_stack.len(), 1);
316 proxy.handle_message(make_message(MockMessageType::MockMessage));
317 proxy.handle_message(make_message(ProxyMessages::NewViewAssistant(Some(Box::new(mock2)))));
318 assert_eq!(proxy.view_assistant_stack.len(), 2);
319 proxy.handle_message(make_message(MockMessageType::MockMessage));
320 proxy.handle_message(make_message(ProxyMessages::PopViewAssistant));
321 assert_eq!(proxy.view_assistant_stack.len(), 1);
322 proxy.handle_message(make_message(MockMessageType::MockMessage));
323 Ok(())
324 }
325
326 #[test]
327 fn test_proxy_view_assistant_pop_first_entry() -> std::result::Result<(), anyhow::Error> {
328 #[cfg(feature = "debug_console")]
329 let mock0 = MockProxyViewAssistant::new();
330 let mut mock1 = MockProxyViewAssistant::new();
331 mock1.expect_handle_message().times(0).return_const(());
332 let mut proxy = ProxyViewAssistant::new(
333 None,
334 #[cfg(feature = "debug_console")]
335 Some(Box::new(mock0)),
336 Box::new(mock1),
337 )
338 .unwrap();
339 assert_eq!(proxy.view_assistant_stack.len(), 1);
340 proxy.handle_message(make_message(ProxyMessages::PopViewAssistant));
341 assert_eq!(proxy.view_assistant_stack.len(), 1);
342 Ok(())
343 }
344
345 #[test]
346 fn test_proxy_view_assistant_replace_view() -> std::result::Result<(), anyhow::Error> {
347 #[cfg(feature = "debug_console")]
348 let mock0 = MockProxyViewAssistant::new();
349 let mut mock1 = MockProxyViewAssistant::new();
350 mock1.expect_handle_message().times(1).return_const(());
351 let mut proxy = ProxyViewAssistant::new(
352 None,
353 #[cfg(feature = "debug_console")]
354 None,
355 Box::new(mock0),
356 )
357 .unwrap();
358 assert_eq!(proxy.view_assistant_stack.len(), 1);
359 proxy.handle_message(make_message(ProxyMessages::ReplaceViewAssistant(Some(Box::new(
360 mock1,
361 )))));
362 assert_eq!(proxy.view_assistant_stack.len(), 1);
363 proxy.handle_message(make_message(MockMessageType::MockMessage));
364 Ok(())
365 }
366
367 #[test]
368 fn test_proxy_view_assistant_first_call_pointer_event() -> std::result::Result<(), anyhow::Error>
369 {
370 let context = &mut ViewAssistantContext::new_for_testing();
371 #[cfg(feature = "debug_console")]
372 let mock0 = MockProxyViewAssistant::new();
373 let mut mock1 = MockProxyViewAssistant::new();
374 mock1.expect_handle_pointer_event().times(2).returning(|_, _, _| Ok(()));
375 mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
376 let mut proxy = ProxyViewAssistant::new(
377 None,
378 #[cfg(feature = "debug_console")]
379 Some(Box::new(mock0)),
380 Box::new(mock1),
381 )
382 .unwrap();
383 let event = get_input_event();
384 let pointer_id =
385 input::pointer::PointerId::Mouse(input::DeviceId("Mouse Event".to_owned()));
386 let pointer_event =
387 input::pointer::Event { phase: input::pointer::Phase::Up, pointer_id: pointer_id };
388 assert_eq!(proxy.first_call_after_switch, true);
389 proxy.handle_pointer_event(context, &event, &pointer_event)?;
390 assert_eq!(proxy.first_call_after_switch, false);
391 proxy.handle_pointer_event(context, &event, &pointer_event)?;
392 assert_eq!(proxy.first_call_after_switch, false);
393 Ok(())
394 }
395
396 #[test]
397 fn test_proxy_view_assistant_first_call_input_event() -> std::result::Result<(), anyhow::Error>
398 {
399 let context = &mut ViewAssistantContext::new_for_testing();
400 #[cfg(feature = "debug_console")]
401 let mock0 = MockProxyViewAssistant::new();
402 let mut mock1 = MockProxyViewAssistant::new();
403 mock1.expect_handle_input_event().times(2).returning(|_, _| Ok(()));
404 mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
405 let mut proxy = ProxyViewAssistant::new(
406 None,
407 #[cfg(feature = "debug_console")]
408 Some(Box::new(mock0)),
409 Box::new(mock1),
410 )
411 .unwrap();
412 let event = get_input_event();
413 assert_eq!(proxy.first_call_after_switch, true);
414 proxy.handle_input_event(context, &event)?;
415 assert_eq!(proxy.first_call_after_switch, false);
416 proxy.handle_input_event(context, &event)?;
417 assert_eq!(proxy.first_call_after_switch, false);
418 Ok(())
419 }
420 #[test]
421
422 fn test_proxy_view_assistant_first_call_input_pointer_event(
423 ) -> std::result::Result<(), anyhow::Error> {
424 let context = &mut ViewAssistantContext::new_for_testing();
425 #[cfg(feature = "debug_console")]
426 let mock0 = MockProxyViewAssistant::new();
427 let mut mock1 = MockProxyViewAssistant::new();
428 mock1.expect_handle_pointer_event().times(1).returning(|_, _, _| Ok(()));
429 mock1.expect_handle_input_event().times(1).returning(|_, _| Ok(()));
430 mock1.expect_handle_focus_event().times(1).returning(|_, _| Ok(()));
431 let mut proxy = ProxyViewAssistant::new(
432 None,
433 #[cfg(feature = "debug_console")]
434 Some(Box::new(mock0)),
435 Box::new(mock1),
436 )
437 .unwrap();
438 let event = get_input_event();
439 let pointer_id =
440 input::pointer::PointerId::Mouse(input::DeviceId("Mouse Event".to_owned()));
441 let pointer_event =
442 input::pointer::Event { phase: input::pointer::Phase::Up, pointer_id: pointer_id };
443 assert_eq!(proxy.first_call_after_switch, true);
444 proxy.handle_pointer_event(context, &event, &pointer_event)?;
445 assert_eq!(proxy.first_call_after_switch, false);
446 proxy.handle_input_event(context, &event)?;
447 assert_eq!(proxy.first_call_after_switch, false);
448 Ok(())
449 }
450}