1use std::rc::Rc;
2
3use pfx_core::clock::Tick;
4use pfx_gpu::screens::{Device, Display, ScreenPolicy, ScreenReport};
5use pfx_gpu::window::{Attention, Clock, FramePacer, Host, Pace, Point, PresentPreference, Size};
6use pfx_input::{Backend, BindingMap, InputEvent, KeyLabels, LayoutSource};
7use pfx_play::{Cursor, Exit, Notice, PlaySession, Settings, Ui, UiFrame, Warm, Warmer, Warming};
8
9use crate::cursor::{self, Warp};
10use crate::prompt::Part;
11
12#[derive(Clone, Debug, PartialEq)]
13pub enum Event {
14 Input(InputEvent),
15 Attention(Attention),
16 Resized(Size),
17 Refresh(Option<u32>),
18 Monitor(Option<String>),
19 Moved {
20 monitor: Option<String>,
21 position: Point,
22 },
23}
24
25pub fn present_preference(vrr: bool, host: Host) -> PresentPreference {
26 if vrr {
27 PresentPreference::vrr(host)
28 } else {
29 PresentPreference::game()
30 }
31}
32
33pub struct Driver {
34 session: PlaySession,
35 pacer: FramePacer,
36 applied: Settings,
37 device: Device,
38 display: Display,
39 display_changed: bool,
40 present_changed: bool,
41 policy: ScreenPolicy,
42 window: Size,
43 report: Option<ScreenReport>,
44 last_ns: Option<u64>,
45 refresh: Option<u32>,
46 pads: Option<Box<dyn Backend>>,
47 scratch: Vec<InputEvent>,
48 labels: KeyLabels,
49 layout: Option<Box<dyn LayoutSource>>,
50 ui: Rc<Ui>,
51 parts: Vec<Vec<Part>>,
52 motors: u64,
53 frames: u64,
54 seconds: f32,
55 notices: Vec<Notice>,
56 warp: Option<Warp>,
57}
58
59impl Driver {
60 pub fn new(
61 session: PlaySession,
62 settings: Settings,
63 policy: ScreenPolicy,
64 device: Device,
65 window: Size,
66 pads: Option<Box<dyn Backend>>,
67 ) -> Self {
68 let mut driver = Self {
69 session,
70 pacer: FramePacer::new(settings.pacing.caps()),
71 display: Display::new(device, settings.display.clone()),
72 applied: settings.clone(),
73 device,
74 display_changed: false,
75 present_changed: false,
76 policy,
77 window,
78 report: None,
79 last_ns: None,
80 refresh: None,
81 pads,
82 scratch: Vec::new(),
83 labels: KeyLabels::new(),
84 layout: None,
85 ui: Rc::default(),
86 parts: Vec::new(),
87 motors: 0,
88 frames: 0,
89 seconds: 0.0,
90 notices: Vec::new(),
91 warp: None,
92 };
93 driver.apply(&settings, true);
94 driver.refit();
95 driver.session.set_display(driver.display.settings());
96 driver
97 }
98
99 pub fn feed(&mut self, event: Event) {
100 match event {
101 Event::Input(event) => self.session.feed(event),
102 Event::Attention(attention) => self.pacer.on(attention),
103 Event::Resized(size) => {
104 self.window = size;
105 self.display.resized(size);
106 self.refit();
107 }
108 Event::Refresh(refresh) => {
109 self.refresh = refresh;
110 self.pace();
111 }
112 Event::Monitor(name) => self.display.moved_to(name),
113 Event::Moved { monitor, position } => self.display.placed(monitor, position),
114 }
115 }
116
117 pub fn poll(&mut self, clock: &impl Clock) -> Pace {
118 let pace = self.pacer.poll(clock);
119 if pace == Pace::Paused {
120 self.last_ns = None;
121 }
122 pace
123 }
124
125 pub fn start_clock(&mut self, now_ns: u64) {
126 self.last_ns = Some(now_ns);
127 }
128
129 pub fn update(&mut self, now_ns: u64) -> Tick {
130 if let Some(pads) = &mut self.pads {
131 pads.poll(&mut self.scratch);
132 for event in self.scratch.drain(..) {
133 self.session.feed(event);
134 }
135 }
136 let seconds = self
137 .last_ns
138 .map_or(0.0, |last| now_ns.saturating_sub(last) as f64 / 1e9);
139 self.last_ns = Some(now_ns);
140 self.seconds = seconds as f32;
141 self.session.set_display(self.display.settings());
142 let tick = self.session.advance(self.seconds);
143 self.frames += 1;
144 self.settle(tick.alpha);
145 tick
146 }
147
148 pub(crate) fn warm(
149 &mut self,
150 warmer: &mut dyn Warmer,
151 pass: u32,
152 elapsed: f64,
153 budget: f64,
154 ) -> Result<Warming, String> {
155 let mut warm = Warm::new(warmer, pass, elapsed, budget);
156 self.session
157 .game_mut()
158 .warm(&mut warm)
159 .map_err(|error| error.to_string())
160 }
161
162 pub fn step(&mut self) -> bool {
163 if !self.session.step() {
164 return false;
165 }
166 self.seconds = 0.0;
167 self.settle(0.0);
168 true
169 }
170
171 fn settle(&mut self, alpha: f32) {
172 if self.session.exit().is_some() {
173 return;
174 }
175 for command in self.session.take_motors() {
176 if let Some(pads) = &mut self.pads {
177 pads.set_motors(&command);
178 self.motors += 1;
179 }
180 }
181 if let Some(layout) = &mut self.layout {
182 self.labels.refresh(layout.as_mut());
183 }
184 self.warp_pointer();
185 if let Some(modules) = self.session.game_mut().modules() {
186 self.notices.extend(modules.notices());
187 }
188 let changed = self
189 .session
190 .game()
191 .settings()
192 .filter(|settings| **settings != self.applied)
193 .cloned();
194 if let Some(settings) = changed {
195 self.apply(&settings, false);
196 }
197 let frame = self.ui_frame(alpha);
198 self.ui = Rc::new(self.session.ui(&frame));
199 self.parts = self.resolve();
200 }
201
202 fn resolve(&self) -> Vec<Vec<Part>> {
203 let input = self.session.input();
204 self.ui
205 .prompts
206 .iter()
207 .map(|prompt| crate::prompt::parts(input, &prompt.of))
208 .collect()
209 }
210
211 fn apply(&mut self, settings: &Settings, first: bool) {
212 if first || settings.pacing != self.applied.pacing {
213 self.pacer.set_caps(settings.pacing.caps());
214 }
215 if first || settings.rumble != self.applied.rumble {
216 self.session.set_rumble_intensity(settings.rumble.min(100));
217 }
218 if first || settings.audio != self.applied.audio {
219 settings
220 .audio
221 .apply(self.session.world_mut().sounds.mixer());
222 }
223 if settings.binds != self.applied.binds || (first && settings.binds.is_some()) {
224 let map = settings
225 .binds
226 .clone()
227 .unwrap_or_else(|| BindingMap::defaults(self.session.input().actions()));
228 self.session.set_bindings(map);
229 }
230 if !first
231 && settings.display != self.applied.display
232 && settings.display != *self.display.settings()
233 {
234 self.display = Display::new(self.device, settings.display.clone());
235 self.display_changed = true;
236 }
237 if !first && settings.pacing.vrr != self.applied.pacing.vrr {
238 self.present_changed = true;
239 }
240 self.applied = settings.clone();
241 self.pace();
242 }
243
244 fn pace(&mut self) {
245 let vrr = self.applied.pacing.vrr.then_some(self.refresh).flatten();
246 self.pacer.set_vrr(vrr);
247 }
248
249 fn refit(&mut self) {
250 self.report = self.policy.fit(self.device, self.window);
251 self.session
252 .set_viewport(self.report.as_ref().and_then(ScreenReport::viewport));
253 }
254
255 pub fn ui_frame(&self, alpha: f32) -> UiFrame {
256 let window = [self.window.width, self.window.height];
257 let glyph_style = self.session.input().glyph_style();
258 let deck = self.device.is_deck();
259 match &self.report {
260 Some(report) => {
261 let safe = report.safe_layout;
262 let content = report.content_units();
263 UiFrame {
264 layout: report.layout_units(),
265 safe: [safe.x, safe.y, safe.width, safe.height],
266 ui_scale: report.ui_scale,
267 pixels_per_unit: report.pixels_per_unit(),
268 window,
269 window_units: report.window_units(),
270 content: [content.x, content.y, content.width, content.height],
271 backdrop: report.backdrop,
272 deck,
273 glyph_style,
274 alpha,
275 seconds: self.seconds,
276 }
277 }
278 None => UiFrame {
279 window,
280 deck,
281 glyph_style,
282 alpha,
283 seconds: self.seconds,
284 ..UiFrame::default()
285 },
286 }
287 }
288
289 pub fn take_display(&mut self) -> Option<&Display> {
290 if !self.display_changed {
291 return None;
292 }
293 self.display_changed = false;
294 Some(&self.display)
295 }
296
297 pub fn take_present(&mut self) -> Option<bool> {
298 if !self.present_changed {
299 return None;
300 }
301 self.present_changed = false;
302 Some(self.applied.pacing.vrr)
303 }
304
305 pub fn cursor(&self) -> Cursor {
306 self.session.world().cursor()
307 }
308
309 fn warp_pointer(&mut self) {
310 let Some(point) = self.session.world_mut().take_warp() else {
311 return;
312 };
313 let viewport = self.report.as_ref().and_then(ScreenReport::viewport);
314 let warp = cursor::warp(point, viewport, self.window);
315 for event in warp.events() {
316 self.session.feed(event);
317 }
318 self.warp = Some(warp);
319 }
320
321 pub fn take_warp(&mut self) -> Option<[f32; 2]> {
322 self.warp.take().map(|warp| warp.window)
323 }
324
325 pub fn exit(&self) -> Option<&Exit> {
326 self.session.exit()
327 }
328
329 pub fn finish(self) -> Result<u8, String> {
330 let exit = self.session.exit().cloned();
331 self.session.stop();
332 match exit {
333 None => Ok(0),
334 Some(Exit::Quit(code)) => Ok(code),
335 Some(Exit::Failed(error)) => Err(error),
336 }
337 }
338
339 pub fn seconds(&self) -> f32 {
340 self.seconds
341 }
342
343 pub fn display(&self) -> &Display {
344 &self.display
345 }
346
347 pub fn device(&self) -> Device {
348 self.device
349 }
350
351 pub fn session(&self) -> &PlaySession {
352 &self.session
353 }
354
355 pub fn session_mut(&mut self) -> &mut PlaySession {
356 &mut self.session
357 }
358
359 pub fn into_session(self) -> PlaySession {
360 self.session
361 }
362
363 pub fn settings(&self) -> &Settings {
364 &self.applied
365 }
366
367 pub fn report(&self) -> Option<&ScreenReport> {
368 self.report.as_ref()
369 }
370
371 pub fn window(&self) -> Size {
372 self.window
373 }
374
375 pub fn ui(&self) -> &Ui {
376 &self.ui
377 }
378
379 pub(crate) fn shared_ui(&self) -> Rc<Ui> {
380 Rc::clone(&self.ui)
381 }
382
383 pub fn prompt_parts(&self) -> &[Vec<Part>] {
384 &self.parts
385 }
386
387 pub fn labels(&self) -> &KeyLabels {
388 &self.labels
389 }
390
391 pub fn labels_mut(&mut self) -> &mut KeyLabels {
392 &mut self.labels
393 }
394
395 pub fn set_layout(&mut self, layout: Option<Box<dyn LayoutSource>>) {
396 self.layout = layout;
397 }
398
399 pub fn motors_sent(&self) -> u64 {
400 self.motors
401 }
402
403 pub fn effective_fps(&self) -> Option<u32> {
404 self.pacer.effective_fps()
405 }
406
407 pub fn frames(&self) -> u64 {
408 self.frames
409 }
410
411 pub fn ticks(&self) -> u64 {
412 self.session.world().ticks()
413 }
414
415 pub fn take_notices(&mut self) -> Vec<Notice> {
416 std::mem::take(&mut self.notices)
417 }
418
419 pub fn take_audio(&mut self) -> Vec<f32> {
420 self.session.take_audio()
421 }
422}