Skip to main content

pfx_game/
driver.rs

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}