1#![feature(arbitrary_self_types)]
8
9use std::cell::RefMut;
10use std::rc::Rc;
11use std::time::Duration;
12
13use inset_embedder::{
14 EmbedderClient, Frame, Instant, KeyData, PlatformRef, PointerDataPacket, TextEditingValue,
15 TextInputAction, ViewFocusEvent, ViewId,
16};
17use inset_foundation::{App, AppCell};
18use inset_gestures::GestureBinding;
19use inset_painting::PaintingBinding;
20use inset_rendering::RendererBinding;
21use inset_scheduler::SchedulerBinding;
22use inset_services::{KeyEventManager, TextInput};
23
24pub struct Shell {
29 app: Rc<AppCell>,
30 platform: PlatformRef,
31 clock: Duration,
33 origin: Option<Instant>,
35}
36
37impl Shell {
38 pub fn new(platform: PlatformRef, setup: impl FnOnce(&mut App)) -> Shell {
43 let shell = Shell {
44 app: AppCell::with_platform(platform.clone()),
45 platform,
46 clock: Duration::ZERO,
47 origin: None,
48 };
49 shell.turn(|app| {
50 PaintingBinding::instance(app).install_platform_fonts(app);
52 setup(app);
53 });
54 shell
55 }
56
57 pub fn app(&self) -> RefMut<'_, App> {
60 self.app.borrow_mut()
61 }
62
63 pub fn cell(&self) -> &Rc<AppCell> {
64 &self.app
65 }
66
67 fn turn<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
69 let result = f(&mut self.app.borrow_mut());
70 self.app.checkpoint();
71 result
72 }
73
74 fn push<R>(&mut self, f: impl FnOnce(&mut App) -> R) -> R {
77 if let Some(origin) = self.origin {
78 let elapsed = self.platform.now().saturating_duration_since(origin);
79 self.advance_clock(elapsed);
80 }
81 self.turn(f)
82 }
83
84 fn advance_clock(&mut self, elapsed: Duration) {
86 if elapsed <= self.clock {
87 return;
88 }
89 self.app.elapse(elapsed - self.clock);
90 self.clock = elapsed;
91 }
92}
93
94impl EmbedderClient for Shell {
95 fn frame(&mut self, frame: Frame) {
96 self.origin = Some(self.platform.now() - frame.elapsed);
97 self.advance_clock(frame.elapsed);
98 self.turn(|app| SchedulerBinding::handle_begin_frame(app, Some(frame.elapsed)));
100 self.turn(SchedulerBinding::handle_draw_frame);
101 }
102
103 fn view_added(&mut self, _id: ViewId) {}
104
105 fn view_metrics_changed(&mut self, _id: ViewId) {
106 self.push(|app| RendererBinding::instance(app).handle_metrics_changed(app));
107 }
108
109 fn view_removed(&mut self, _id: ViewId) {}
110
111 fn view_focus_changed(&mut self, event: ViewFocusEvent) {
112 self.push(|app| {
113 let callback = app.platform_callbacks().on_view_focus_change.clone();
114 if let Some(callback) = callback {
115 callback(app, event);
116 }
117 });
118 }
119
120 fn pointer_data_packet(&mut self, packet: PointerDataPacket) {
121 self.push(|app| GestureBinding::instance(app).handle_pointer_data_packet(app, packet));
122 }
123
124 fn key_data(&mut self, data: KeyData) -> bool {
125 self.push(|app| KeyEventManager::instance(app).handle_key_data(app, data))
126 }
127
128 fn platform_brightness_changed(&mut self) {
129 self.push(|app| {
130 let callback = app
131 .platform_callbacks()
132 .on_platform_brightness_changed
133 .clone();
134 if let Some(callback) = callback {
135 callback.call(app);
136 }
137 });
138 }
139
140 fn locales_changed(&mut self) {
141 self.push(|app| {
142 let callback = app.platform_callbacks().on_locale_changed.clone();
143 if let Some(callback) = callback {
144 callback.call(app);
145 }
146 });
147 }
148
149 fn system_fonts_changed(&mut self) {
150 self.push(|app| {
151 PaintingBinding::instance(app).handle_system_fonts_did_change(app);
152 });
153 }
154
155 fn wake(&mut self, elapsed: Duration) {
156 self.advance_clock(elapsed);
157 self.app.wake();
159 }
160
161 fn text_input_editing_value(&mut self, _view: ViewId, value: TextEditingValue) {
162 self.push(|app| TextInput::instance(app).update_editing_value(app, value));
163 }
164
165 fn text_input_action(&mut self, _view: ViewId, action: TextInputAction) {
166 self.push(|app| TextInput::instance(app).perform_action(app, action));
167 }
168
169 fn text_input_closed(&mut self, _view: ViewId) {
170 self.push(|app| TextInput::instance(app).connection_closed(app));
171 }
172
173 fn system_context_menu_hidden(&mut self) {
174 self.push(inset_services::SystemContextMenuController::dispatch_system_hide);
175 }
176
177 fn custom_context_menu_action(&mut self, callback_id: &str) {
178 let callback_id = callback_id.to_string();
179 self.push(move |app| {
180 inset_services::SystemContextMenuController::dispatch_custom_action(app, &callback_id);
181 });
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use std::cell::Cell;
188 use std::rc::Rc;
189 use std::sync::Arc;
190 use std::sync::atomic::{AtomicUsize, Ordering};
191 use std::time::Duration;
192
193 use inset_embedder::{
194 EmbedderClient, Frame, KeyData, KeyEventType, Picture, Platform, PointerChange,
195 PointerData, PointerDataPacket, PointerDeviceKind, View, ViewId, ViewMetrics, ViewRef,
196 };
197 use inset_foundation::App;
198 use inset_gestures::{GestureBinding, PointerRoute};
199 use inset_scheduler::SchedulerBinding;
200 use inset_services::{HardwareKeyboard, KeyEvent, LogicalKeyboardKey, PhysicalKeyboardKey};
201
202 use super::Shell;
203
204 struct RecordingPlatform {
205 frames: Arc<AtomicUsize>,
206 view: Option<ViewRef>,
207 }
208
209 impl Platform for RecordingPlatform {
210 fn target_platform(&self) -> inset_embedder::TargetPlatform {
211 inset_embedder::TargetPlatform::Android
212 }
213
214 fn request_frame(&self) {
215 self.frames.fetch_add(1, Ordering::SeqCst);
216 }
217
218 fn now(&self) -> std::time::Instant {
219 std::time::Instant::now()
220 }
221
222 fn wake_at(&self, _deadline: std::time::Instant) {}
223
224 fn views(&self) -> Vec<ViewRef> {
225 self.view.iter().cloned().collect()
226 }
227
228 fn view(&self, id: ViewId) -> Option<ViewRef> {
229 self.view.as_ref().filter(|view| view.id() == id).cloned()
230 }
231
232 fn implicit_view(&self) -> Option<ViewRef> {
233 self.view.clone()
234 }
235 }
236
237 struct TestView;
238
239 impl View for TestView {
240 fn id(&self) -> ViewId {
241 ViewId(0)
242 }
243
244 fn metrics(&self) -> ViewMetrics {
245 ViewMetrics::default()
246 }
247
248 fn present(&self, _picture: std::sync::Arc<Picture>) {}
249 }
250
251 #[test]
252 fn new_runs_setup_and_frame_runs_both_scheduler_phases() {
253 let frames = Arc::new(AtomicUsize::new(0));
254 let platform = std::rc::Rc::new(RecordingPlatform {
255 frames: Arc::clone(&frames),
256 view: None,
257 });
258 let mut setup_ran = false;
259 let mut shell = Shell::new(platform, |_app: &mut App| {
260 setup_ran = true;
261 });
262 assert!(setup_ran);
263 assert_eq!(frames.load(Ordering::SeqCst), 0);
264
265 SchedulerBinding::schedule_frame(&mut shell.app());
266 assert_eq!(frames.load(Ordering::SeqCst), 1);
267 shell.frame(Frame {
268 elapsed: Duration::from_millis(16),
269 });
270 }
271
272 #[test]
273 fn pointer_data_packet_reaches_gesture_binding() {
274 let ran = Rc::new(Cell::new(false));
275 let ran_flag = Rc::clone(&ran);
276 let platform = std::rc::Rc::new(RecordingPlatform {
277 frames: Arc::new(AtomicUsize::new(0)),
278 view: Some(Rc::new(TestView)),
279 });
280 let mut shell = Shell::new(platform, |app| {
281 let binding = GestureBinding::instance(app);
282 binding.pointer_router(app).add_route(
283 app,
284 1,
285 PointerRoute::new(move |_app, _event| {
286 ran_flag.set(true);
287 }),
288 None,
289 );
290 });
291
292 shell.pointer_data_packet(PointerDataPacket::new(vec![PointerData {
293 change: PointerChange::Down,
294 kind: PointerDeviceKind::Mouse,
295 buttons: 1,
296 pointer_identifier: 1,
297 ..PointerData::default()
298 }]));
299 assert!(ran.get());
300 }
301
302 #[test]
303 fn key_data_reaches_the_hardware_keyboard() {
304 let seen = Rc::new(Cell::new(0));
305 let seen_flag = Rc::clone(&seen);
306 let platform = std::rc::Rc::new(RecordingPlatform {
307 frames: Arc::new(AtomicUsize::new(0)),
308 view: None,
309 });
310 let mut shell = Shell::new(platform, |app| {
311 HardwareKeyboard::instance(app).add_handler(
312 app,
313 Rc::new(move |_app: &mut App, _event: &KeyEvent| {
314 seen_flag.set(seen_flag.get() + 1);
315 true
316 }),
317 );
318 });
319
320 let key_a = KeyData {
321 event_type: KeyEventType::Down,
322 physical: PhysicalKeyboardKey::KEY_A.usb_hid_usage,
323 logical: LogicalKeyboardKey::KEY_A.key_id,
324 character: Some("a".to_owned()),
325 ..KeyData::default()
326 };
327 assert!(shell.key_data(key_a), "the handler claimed the event");
328 assert_eq!(seen.get(), 1);
329
330 let keyboard = HardwareKeyboard::instance(&mut shell.app());
331 assert!(keyboard.is_logical_key_pressed(&shell.app(), LogicalKeyboardKey::KEY_A));
332 }
333
334 #[test]
335 fn wake_and_frame_advance_the_app_clock_and_fire_due_timers() {
336 let platform: inset_embedder::PlatformRef = Rc::new(RecordingPlatform {
337 frames: Arc::new(AtomicUsize::new(0)),
338 view: None,
339 });
340 let fired = Rc::new(Cell::new(Vec::new()));
341 let mut shell = Shell::new(platform, |app| {
342 for (name, delay) in [("early", 10), ("late", 30)] {
343 let fired = Rc::clone(&fired);
344 inset_foundation::Timer::new(
345 app,
346 Duration::from_millis(delay),
347 inset_foundation::Listener::new(move |_app| {
348 let mut log = fired.take();
349 log.push(name);
350 fired.set(log);
351 }),
352 );
353 }
354 });
355 assert!(fired.take().is_empty());
356
357 shell.wake(Duration::from_millis(15));
358 assert_eq!(fired.take(), vec!["early"]);
359
360 shell.frame(Frame {
361 elapsed: Duration::from_millis(32),
362 });
363 assert_eq!(fired.take(), vec!["late"]);
364 }
365
366 #[test]
367 fn view_focus_is_delivered_through_the_dispatcher_callback() {
368 use inset_embedder::{ViewFocusDirection, ViewFocusEvent, ViewFocusState};
369 let received = Rc::new(Cell::new(None));
370 let sink = received.clone();
371 let platform = Rc::new(RecordingPlatform {
372 frames: Arc::default(),
373 view: Some(Rc::new(TestView)),
374 });
375 let mut shell = Shell::new(platform, move |app| {
376 app.platform_callbacks_mut().on_view_focus_change = Some(Rc::new(move |app, event| {
377 let sink = sink.clone();
378 app.schedule_microtask(inset_foundation::Listener::new(move |_| {
379 sink.set(Some(event))
380 }));
381 }));
382 });
383 shell.view_focus_changed(ViewFocusEvent {
384 view_id: ViewId(0),
385 state: ViewFocusState::Focused,
386 direction: ViewFocusDirection::Backward,
387 });
388 let event = received
389 .get()
390 .expect("the shell checkpoints the callback's microtask");
391 assert_eq!(event.view_id, ViewId(0));
392 assert_eq!(event.state, ViewFocusState::Focused);
393 assert_eq!(event.direction, ViewFocusDirection::Backward);
394 }
395}