1use crate::core;
3use crate::core::mouse;
4use crate::core::renderer;
5use crate::core::shell;
6use crate::core::time::Instant;
7use crate::core::widget;
8use crate::core::window;
9use crate::core::{Bytes, Element, Point, Size};
10use crate::instruction;
11use crate::program;
12use crate::program::Program;
13use crate::runtime;
14use crate::runtime::futures::futures::StreamExt;
15use crate::runtime::futures::futures::channel::mpsc;
16use crate::runtime::futures::futures::stream;
17use crate::runtime::futures::subscription;
18use crate::runtime::futures::{Executor, Runtime};
19use crate::runtime::task;
20use crate::runtime::user_interface;
21use crate::runtime::{Task, UserInterface};
22use crate::{Instruction, Selector};
23
24use std::fmt;
25
26pub struct Emulator<P: Program> {
36 state: P::State,
37 runtime: Runtime<P::Executor, mpsc::Sender<Event<P>>, Event<P>>,
38 renderer: P::Renderer,
39 mode: Mode,
40 size: Size,
41 window: core::window::Id,
42 cursor: mouse::Cursor,
43 cache: Option<user_interface::Cache>,
44 pending_tasks: usize,
45}
46
47pub enum Event<P: Program> {
49 Action(Action<P>),
51 Failed(Instruction),
53 Ready,
55}
56
57pub struct Action<P: Program>(Action_<P>);
59
60enum Action_<P: Program> {
61 Runtime(runtime::Action<P::Message>),
62 CountDown,
63}
64
65impl<P: Program + 'static> Emulator<P> {
66 pub fn new(sender: mpsc::Sender<Event<P>>, program: &P, mode: Mode, size: Size) -> Emulator<P> {
72 Self::with_preset(sender, program, mode, size, None)
73 }
74
75 pub fn with_preset(
80 sender: mpsc::Sender<Event<P>>,
81 program: &P,
82 mode: Mode,
83 size: Size,
84 preset: Option<&program::Preset<P::State, P::Message>>,
85 ) -> Emulator<P> {
86 use renderer::Headless;
87
88 let settings = program.settings();
89
90 let executor = P::Executor::new().expect("Create emulator executor");
92
93 let renderer = executor
94 .block_on(P::Renderer::new(renderer::Settings::from(&settings), None))
95 .expect("Create emulator renderer");
96
97 let runtime = Runtime::new(executor, sender);
98
99 let (state, task) = runtime.enter(|| {
100 if let Some(preset) = preset {
101 preset.boot()
102 } else {
103 program.boot()
104 }
105 });
106
107 let mut emulator = Self {
108 state,
109 runtime,
110 renderer,
111 mode,
112 size,
113 cursor: mouse::Cursor::Unavailable,
114 window: core::window::Id::unique(),
115 cache: Some(user_interface::Cache::default()),
116 pending_tasks: 0,
117 };
118
119 emulator.resubscribe(program);
120 emulator.wait_for(task);
121
122 emulator
123 }
124
125 pub fn update(&mut self, program: &P, message: P::Message) {
131 let task = self
132 .runtime
133 .enter(|| program.update(&mut self.state, message));
134
135 self.resubscribe(program);
136
137 match self.mode {
138 Mode::Zen if self.pending_tasks > 0 => self.wait_for(task),
139 _ => {
140 if let Some(stream) = task::into_stream(task) {
141 self.runtime.run(
142 stream
143 .map(Action_::Runtime)
144 .map(Action)
145 .map(Event::Action)
146 .boxed(),
147 );
148 }
149 }
150 }
151 }
152
153 pub fn perform(&mut self, program: &P, action: Action<P>) {
158 match action.0 {
159 Action_::CountDown => {
160 if self.pending_tasks > 0 {
161 self.pending_tasks -= 1;
162
163 if self.pending_tasks == 0 {
164 self.runtime.send(Event::Ready);
165 }
166 }
167 }
168 Action_::Runtime(action) => match action {
169 runtime::Action::Output(message) => {
170 self.update(program, message);
171 }
172 runtime::Action::Widget(operation) => {
173 let mut user_interface = UserInterface::build(
174 program.view(&self.state, self.window),
175 self.size,
176 self.cache.take().unwrap(),
177 &mut self.renderer,
178 );
179
180 let mut operation = Some(operation);
181
182 while let Some(mut current) = operation.take() {
183 user_interface.operate(&self.renderer, &mut current);
184
185 match current.finish() {
186 widget::operation::Outcome::None => {}
187 widget::operation::Outcome::Some(()) => {}
188 widget::operation::Outcome::Chain(next) => {
189 operation = Some(next);
190 }
191 }
192 }
193
194 self.cache = Some(user_interface.into_cache());
195 }
196 runtime::Action::Clipboard(action) => {
197 dbg!(action);
199 }
200 runtime::Action::Window(action) => {
201 use crate::runtime::window;
202
203 match action {
204 window::Action::Open(id, _settings, sender) => {
205 self.window = id;
206
207 let _ = sender.send(self.window);
208 }
209 window::Action::GetOldest(sender) | window::Action::GetLatest(sender) => {
210 let _ = sender.send(Some(self.window));
211 }
212 window::Action::GetSize(id, sender) if id == self.window => {
213 let _ = sender.send(self.size);
214 }
215 window::Action::GetMaximized(id, sender) if id == self.window => {
216 let _ = sender.send(false);
217 }
218 window::Action::GetMinimized(id, sender) if id == self.window => {
219 let _ = sender.send(None);
220 }
221 window::Action::GetPosition(id, sender) if id == self.window => {
222 let _ = sender.send(Some(Point::ORIGIN));
223 }
224 window::Action::GetScaleFactor(id, sender) if id == self.window => {
225 let _ = sender.send(1.0);
226 }
227 window::Action::GetMode(id, sender) if id == self.window => {
228 let _ = sender.send(core::window::Mode::Windowed);
229 }
230 _ => {
231 }
233 }
234 }
235 runtime::Action::System(action) => {
236 dbg!(action);
238 }
239 runtime::Action::Font(action) => {
240 dbg!(action);
242 }
243 runtime::Action::Image(action) => {
244 dbg!(action);
246 }
247 runtime::Action::Backend(action) => {
248 dbg!(action);
250 }
251 runtime::Action::Event { window, event } => {
252 dbg!(window, event);
254 }
255 runtime::Action::Tick => {
256 }
258 runtime::Action::Exit => {
259 }
261 runtime::Action::Reload => {
262 }
264 },
265 }
266 }
267
268 pub fn run(&mut self, program: &P, instruction: &Instruction) {
275 let mut user_interface = UserInterface::build(
276 program.view(&self.state, self.window),
277 self.size,
278 self.cache.take().unwrap(),
279 &mut self.renderer,
280 );
281
282 let mut messages = Vec::new();
283
284 match instruction {
285 Instruction::Interact(interaction) => {
286 let Some(events) = interaction.events(|target| match target {
287 instruction::Target::Id(id) => {
288 use widget::Operation;
289
290 let mut operation = Selector::find(widget::Id::from(id.to_owned()));
291
292 user_interface.operate(
293 &self.renderer,
294 &mut widget::operation::black_box(&mut operation),
295 );
296
297 match operation.finish() {
298 widget::operation::Outcome::Some(widget) => {
299 Some(widget?.visible_bounds()?.center())
300 }
301 _ => None,
302 }
303 }
304 instruction::Target::Text(text) => {
305 use widget::Operation;
306
307 let mut operation = Selector::find(text.as_str());
308
309 user_interface.operate(
310 &self.renderer,
311 &mut widget::operation::black_box(&mut operation),
312 );
313
314 match operation.finish() {
315 widget::operation::Outcome::Some(text) => {
316 Some(text?.visible_bounds()?.center())
317 }
318 _ => None,
319 }
320 }
321 instruction::Target::Point(position) => Some(*position),
322 }) else {
323 self.runtime.send(Event::Failed(instruction.clone()));
324 self.cache = Some(user_interface.into_cache());
325 return;
326 };
327
328 for event in &events {
329 if let core::Event::Mouse(mouse::Event::CursorMoved { position }) = event {
330 self.cursor = mouse::Cursor::Available(*position);
331 }
332 }
333
334 let (_state, _status) = user_interface.update(
335 &window::Headless,
336 &shell::Waker::noop(),
337 &events,
338 self.cursor,
339 &mut self.renderer,
340 &mut messages,
341 );
342
343 self.cache = Some(user_interface.into_cache());
344
345 let task = self.runtime.enter(|| {
346 Task::batch(
347 messages
348 .into_iter()
349 .map(|message| program.update(&mut self.state, message)),
350 )
351 });
352
353 self.resubscribe(program);
354 self.wait_for(task);
355 }
356 Instruction::Expect(expectation) => match expectation {
357 instruction::Expectation::Text(text) => {
358 use widget::Operation;
359
360 let mut operation = Selector::find(text.as_str());
361
362 user_interface.operate(
363 &self.renderer,
364 &mut widget::operation::black_box(&mut operation),
365 );
366
367 match operation.finish() {
368 widget::operation::Outcome::Some(Some(_text)) => {
369 self.runtime.send(Event::Ready);
370 }
371 _ => {
372 self.runtime.send(Event::Failed(instruction.clone()));
373 }
374 }
375
376 self.cache = Some(user_interface.into_cache());
377 }
378 },
379 }
380 }
381
382 fn wait_for(&mut self, task: Task<P::Message>) {
383 if let Some(stream) = task::into_stream(task) {
384 match self.mode {
385 Mode::Zen => {
386 self.pending_tasks += 1;
387
388 self.runtime.run(
389 stream
390 .map(Action_::Runtime)
391 .map(Action)
392 .map(Event::Action)
393 .chain(stream::once(async {
394 Event::Action(Action(Action_::CountDown))
395 }))
396 .boxed(),
397 );
398 }
399 Mode::Patient => {
400 self.runtime.run(
401 stream
402 .map(Action_::Runtime)
403 .map(Action)
404 .map(Event::Action)
405 .chain(stream::once(async { Event::Ready }))
406 .boxed(),
407 );
408 }
409 Mode::Immediate => {
410 self.runtime.run(
411 stream
412 .map(Action_::Runtime)
413 .map(Action)
414 .map(Event::Action)
415 .boxed(),
416 );
417 self.runtime.send(Event::Ready);
418 }
419 }
420 } else if self.pending_tasks == 0 {
421 self.runtime.send(Event::Ready);
422 }
423 }
424
425 fn resubscribe(&mut self, program: &P) {
426 self.runtime
427 .track(subscription::into_recipes(self.runtime.enter(|| {
428 program.subscription(&self.state).map(|message| {
429 Event::Action(Action(Action_::Runtime(runtime::Action::Output(message))))
430 })
431 })));
432 }
433
434 pub fn view(&self, program: &P) -> Element<'_, P::Message, P::Theme, P::Renderer> {
436 program.view(&self.state, self.window)
437 }
438
439 pub fn theme(&self, program: &P) -> Option<P::Theme> {
441 program.theme(&self.state, self.window)
442 }
443
444 pub fn screenshot(
446 &mut self,
447 program: &P,
448 theme: &P::Theme,
449 scale_factor: f32,
450 ) -> window::Screenshot {
451 use core::renderer::Headless;
452
453 let style = program.style(&self.state, theme);
454
455 let mut user_interface = UserInterface::build(
456 program.view(&self.state, self.window),
457 self.size,
458 self.cache.take().unwrap(),
459 &mut self.renderer,
460 );
461
462 let _ = user_interface.update(
464 &window::Headless,
465 &shell::Waker::noop(),
466 &[core::Event::Window(window::Event::RedrawRequested(
467 Instant::now(),
468 ))],
469 mouse::Cursor::Unavailable,
470 &mut self.renderer,
471 &mut Vec::new(),
472 );
473
474 user_interface.draw(
475 &mut self.renderer,
476 theme,
477 &renderer::Style {
478 text_color: style.text_color,
479 },
480 mouse::Cursor::Unavailable,
481 );
482
483 let physical_size = Size::new(
484 (self.size.width * scale_factor).round() as u32,
485 (self.size.height * scale_factor).round() as u32,
486 );
487
488 let rgba = self
489 .renderer
490 .screenshot(physical_size, scale_factor, style.background_color);
491
492 window::Screenshot {
493 rgba: Bytes::from(rgba),
494 size: physical_size,
495 scale_factor,
496 }
497 }
498
499 pub fn into_state(self) -> (P::State, core::window::Id) {
501 (self.state, self.window)
502 }
503}
504
505#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
510pub enum Mode {
511 #[default]
516 Zen,
517 Patient,
519 Immediate,
521}
522
523impl Mode {
524 pub const ALL: &[Self] = &[Self::Zen, Self::Patient, Self::Immediate];
526}
527
528impl fmt::Display for Mode {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 f.write_str(match self {
531 Self::Zen => "Zen",
532 Self::Patient => "Patient",
533 Self::Immediate => "Immediate",
534 })
535 }
536}