1use std::collections::VecDeque;
10use std::sync::{Arc, Mutex, PoisonError};
11use std::time::Duration;
12
13use ashlar_control::{Reply, Request};
14use egui::{Event, Rect, ViewportCommand, ViewportId, pos2, vec2};
15use serde_json::Value;
16
17use crate::runtime::Runtime;
18use crate::session::Document;
19
20#[derive(Default)]
22struct Wakes {
23 pending: Vec<(Duration, u64)>,
25}
26
27pub struct Driver<D: Document> {
29 pub runtime: Runtime<D>,
30 ctx: egui::Context,
31 time: f64,
32 wakes: Arc<Mutex<Wakes>>,
33 next: Option<f64>,
35 input: VecDeque<Event>,
36 screen: Rect,
37 frames: u64,
38 closed: bool,
39 close_requested: bool,
40 screenshots: bool,
42 hidden: bool,
44}
45
46impl<D: Document> Driver<D> {
47 pub fn new(runtime: Runtime<D>) -> Self {
49 let ctx = egui::Context::default();
50 let wakes: Arc<Mutex<Wakes>> = Arc::default();
51 let w = Arc::clone(&wakes);
52 ctx.set_request_repaint_callback(move |info| {
53 if info.viewport_id == ViewportId::ROOT {
54 w.lock().unwrap_or_else(PoisonError::into_inner).pending.push((info.delay, info.current_cumulative_pass_nr));
55 }
56 });
57 Driver {
58 runtime,
59 ctx,
60 time: 0.0,
61 wakes,
62 next: Some(0.0),
63 input: VecDeque::new(),
64 screen: Rect::from_min_size(pos2(0.0, 0.0), vec2(1280.0, 800.0)),
65 frames: 0,
66 closed: false,
67 close_requested: false,
68 screenshots: true,
69 hidden: false,
70 }
71 }
72
73 pub fn into_runtime(self) -> Runtime<D> {
75 self.runtime
76 }
77
78 pub fn ctx(&self) -> &egui::Context {
79 &self.ctx
80 }
81
82 pub fn set_screen(&mut self, width: f32, height: f32) {
83 self.screen = Rect::from_min_size(pos2(0.0, 0.0), vec2(width, height));
84 self.next = Some(self.time);
85 }
86
87 pub fn time(&self) -> f64 {
89 self.time
90 }
91
92 pub fn frames(&self) -> u64 {
94 self.frames
95 }
96
97 pub fn is_closed(&self) -> bool {
99 self.closed
100 }
101
102 pub fn push_event(&mut self, e: Event) {
104 self.input.push_back(e);
105 }
106
107 pub fn set_screenshots(&mut self, deliver: bool) {
110 self.screenshots = deliver;
111 }
112
113 pub fn set_hidden(&mut self, hidden: bool) {
118 self.hidden = hidden;
119 self.next = Some(self.time);
120 }
121
122 pub fn is_hidden(&self) -> bool {
124 self.hidden
125 }
126
127 pub fn request_close(&mut self) {
129 self.close_requested = true;
130 }
131
132 pub fn frame(&mut self) -> egui::FullOutput {
134 if self.hidden {
135 self.logic_only();
136 return egui::FullOutput::default();
137 }
138 let mut raw = egui::RawInput { screen_rect: Some(self.screen), time: Some(self.time), focused: true, ..Default::default() };
139 raw.events.extend(self.input.drain(..));
140 if std::mem::take(&mut self.close_requested) {
141 raw.viewports.entry(ViewportId::ROOT).or_default().events.push(egui::ViewportEvent::Close);
142 }
143 let ctx = self.ctx.clone();
144 self.runtime.raw_input_hook(&ctx, &mut raw);
145 let rt = &mut self.runtime;
146 let mut out = ctx.run_ui(raw, |ui| {
147 rt.logic(ui.ctx());
148 rt.ui(ui);
149 });
150 out.textures_delta.clear();
151 self.frames = self.frames.saturating_add(1);
152 let done = self.ctx.cumulative_pass_nr();
153 self.next = None;
154 if let Some(vp) = out.viewport_output.get(&ViewportId::ROOT) {
155 if vp.repaint_delay < Duration::MAX {
156 self.next = Some(self.time + vp.repaint_delay.as_secs_f64());
157 }
158 for c in &vp.commands {
159 match c {
160 ViewportCommand::Screenshot(_) if !self.screenshots => {}
161 ViewportCommand::Screenshot(user_data) => {
162 let [w, h] = [self.screen.width() as usize, self.screen.height() as usize];
163 let image = Arc::new(egui::ColorImage::new([w, h], vec![egui::Color32::from_gray(30); w * h]));
164 self.input.push_back(Event::Screenshot { viewport_id: ViewportId::ROOT, user_data: user_data.clone(), image });
165 }
166 ViewportCommand::Close => self.closed = true,
167 _ => {}
168 }
169 }
170 }
171 self.wakes.lock().unwrap_or_else(PoisonError::into_inner).pending.retain(|(_, nr)| *nr >= done);
173 out
174 }
175
176 fn logic_only(&mut self) {
178 let mut raw = egui::RawInput { screen_rect: Some(self.screen), time: Some(self.time), focused: false, ..Default::default() };
179 raw.viewports.entry(ViewportId::ROOT).or_default().minimized = Some(true);
180 let (shots, rest): (Vec<Event>, Vec<Event>) = self.input.drain(..).partition(|e| matches!(e, Event::Screenshot { .. }));
183 self.input.extend(rest);
184 let done = self.ctx.cumulative_pass_nr();
187 let (rt, time) = (&mut self.runtime, self.time);
188 let out = self.ctx.run_logic(&raw, |ctx| {
189 rt.logic(ctx);
190 if let Some(mut out) = rt.hidden_pass(ctx, Some(time), shots) {
192 out.textures_delta.clear();
193 }
194 });
195 self.frames = self.frames.saturating_add(1);
196 self.next = None;
197 for c in out.viewport_commands.get(&ViewportId::ROOT).into_iter().flatten() {
198 match c {
199 ViewportCommand::Screenshot(user_data) if self.screenshots => {
200 let [w, h] = [self.screen.width() as usize, self.screen.height() as usize];
201 let image = Arc::new(egui::ColorImage::new([w, h], vec![egui::Color32::from_gray(30); w * h]));
202 self.input.push_back(Event::Screenshot { viewport_id: ViewportId::ROOT, user_data: user_data.clone(), image });
203 self.next = Some(self.time);
204 }
205 ViewportCommand::Focus | ViewportCommand::Minimized(false) | ViewportCommand::Visible(true) => {
206 self.hidden = false;
207 self.next = Some(self.time);
208 }
209 ViewportCommand::Close => self.closed = true,
210 _ => {}
211 }
212 }
213 self.wakes.lock().unwrap_or_else(PoisonError::into_inner).pending.retain(|(_, nr)| *nr >= done);
214 }
215
216 fn due(&self) -> Option<f64> {
218 let wake =
219 self.wakes.lock().unwrap_or_else(PoisonError::into_inner).pending.iter().map(|(d, _)| self.time + d.as_secs_f64()).reduce(f64::min);
220 let input = ((!self.input.is_empty() && !self.hidden) || self.close_requested).then_some(self.time);
222 [self.next, wake, input].into_iter().flatten().reduce(f64::min)
223 }
224
225 pub fn advance(&mut self, secs: f64) -> u64 {
228 let end = self.time + secs.max(0.0);
229 let start = self.frames;
230 let min_dt = 1.0 / 120.0;
231 let mut last_frame = f64::NEG_INFINITY;
232 while !self.closed {
233 let Some(due) = self.due() else { break };
234 let at = due.max(last_frame + min_dt).max(self.time);
235 if at > end {
236 break;
237 }
238 self.time = at;
239 self.frame();
240 last_frame = at;
241 }
242 self.time = self.time.max(end);
243 self.frames - start
244 }
245
246 pub fn settle(&mut self, max: u64) -> u64 {
249 let start = self.frames;
250 let horizon = 0.25;
251 while self.frames - start < max && !self.closed {
252 match self.due() {
253 Some(t) if t <= self.time + horizon => {
254 self.time = t.max(self.time) + 1.0 / 120.0;
255 self.frame();
256 }
257 _ => break,
258 }
259 }
260 self.frames - start
261 }
262
263 pub fn request(&mut self, method: &str, params: Value) -> Reply {
267 let (req, rx) = Request::new(method, params);
268 self.runtime.inject_request(req);
269 self.time += 1.0 / 120.0;
270 self.frame();
271 let limit = self.time + 600.0;
272 for _ in 0..10_000 {
273 if let Ok(r) = rx.try_recv() {
274 return r;
275 }
276 if self.closed {
277 break;
278 }
279 let Some(due) = self.due().filter(|d| *d <= limit) else { break };
280 self.time = due.max(self.time + 1.0 / 120.0);
281 self.frame();
282 }
283 rx.try_recv().unwrap_or_else(|_| Reply::err(format!("`{method}` was not answered, and no frame is pending")))
284 }
285
286 pub fn call(&mut self, method: &str, params: Value) -> Result<Value, String> {
288 self.request(method, params).into_result().map_err(|(e, _)| e)
289 }
290}