[InputPoller] implementation that receives inputs updates from another thread.

3use jgenesis_common::frontend::InputPoller;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::{Arc, Mutex};
7#[derive(Debug)]
8pub struct ThreadedInputPoller<Inputs> {
9    cached: Inputs,
10    locked: Arc<Mutex<Inputs>>,
11    updated: Arc<AtomicBool>,
12}
13
14#[derive(Debug)]
15pub struct ThreadedInputPollerHandle<Inputs> {
16    cached: Inputs,
17    locked: Arc<Mutex<Inputs>>,
18    updated: Arc<AtomicBool>,
19}
20
21impl<Inputs: Clone + Eq> ThreadedInputPoller<Inputs> {
22    pub fn new(initial_inputs: Inputs) -> Self {
23        Self {
24            cached: initial_inputs.clone(),
25            locked: Arc::new(Mutex::new(initial_inputs)),
26            updated: Arc::new(AtomicBool::new(false)),
27        }
28    }
29
30    pub fn handle(&self) -> ThreadedInputPollerHandle<Inputs> {
31        ThreadedInputPollerHandle {
32            cached: self.cached.clone(),
33            locked: Arc::clone(&self.locked),
34            updated: Arc::clone(&self.updated),
35        }
36    }
37}
38
39impl<Inputs: Clone + Eq> ThreadedInputPollerHandle<Inputs> {
40    pub fn update_inputs(&mut self, inputs: &Inputs) {
41        if inputs == &self.cached {
42            return;
43        }
44
45        self.cached = inputs.clone();
46        *self.locked.lock().unwrap() = inputs.clone();
47        self.updated.store(true, Ordering::Release);
48    }
49}
50
51impl<Inputs: Clone + Eq> InputPoller<Inputs> for ThreadedInputPoller<Inputs> {
52    fn poll(&mut self) -> &Inputs {
53        if self.updated.load(Ordering::Relaxed)
54            && self.updated.compare_exchange(true, false, Ordering::AcqRel, Ordering::Relaxed)
55                == Ok(true)
56        {
57            self.cached = self.locked.lock().unwrap().clone();
58        }
59
60        &self.cached
61    }
62}