1use crate::DebugRenderContext;
2use jgenesis_common::frontend::{
3    AudioOutput, EmulatorTrait, InputPoller, PartialClone, Renderer, SaveWriter, TickEffect,
4};
5use jgenesis_common::sync::{SharedVarReceiver, SharedVarSender};
6use std::error::Error;
7
8pub type RunTillNextResult<Emulator, RErr, AErr, SErr> =
9    Result<(), <Emulator as EmulatorTrait>::Err<RErr, AErr, SErr>>;
10
11pub trait DebuggerRunnerProcess<Emulator, R, A, I, S>: Send + 'static
12where
13    Emulator: EmulatorTrait,
14    R: Renderer,
15    A: AudioOutput,
16    I: InputPoller<Emulator::Inputs>,
17    S: SaveWriter,
18{

Run periodic processing, e.g. copying and sending emulator state to the frontend. This will generally get called once per frame while the emulator is running.

Errors

May propagate any errors encountered during processing.

25    fn run(
26        &mut self,
27        emulator: &mut Emulator,
28    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;

Run the emulator until the next frame render.

This exists as a hook so that, if desired, the debugger can call a different emulator entry point while the debugger is active.

Errors

Should propagate any errors encountered while running the emulator.

38    fn run_emulator_till_next_frame(
39        &mut self,
40        emulator: &mut Emulator,
41        renderer: &mut R,
42        audio_output: &mut A,
43        input_poller: &mut I,
44        save_writer: &mut S,
45    ) -> RunTillNextResult<Emulator, R::Err, A::Err, S::Err> {
46        while emulator.tick(renderer, audio_output, input_poller, save_writer)?
47            != TickEffect::FrameRendered
48        {}
49
50        Ok(())
51    }
52}
54pub trait DebuggerMainProcess {

Render the debugger frontend.

Errors

May return any errors encountered while processing or rendering.

60    fn run(
61        &mut self,
62        ctx: DebugRenderContext<'_>,
63    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;
64}
66pub type DebuggerProcesses<Emulator, R, A, I, S> =
67    (Box<dyn DebuggerRunnerProcess<Emulator, R, A, I, S>>, Box<dyn DebuggerMainProcess>);
68
69pub type DebugFn<Emulator, R, A, I, S> = fn() -> DebuggerProcesses<Emulator, R, A, I, S>;
70
71pub struct NullDebugger;
72
73impl<Emulator, R, A, I, S> DebuggerRunnerProcess<Emulator, R, A, I, S> for NullDebugger
74where
75    Emulator: EmulatorTrait,
76    R: Renderer,
77    A: AudioOutput,
78    I: InputPoller<Emulator::Inputs>,
79    S: SaveWriter,
80{
81    fn run(
82        &mut self,
83        _emulator: &mut Emulator,
84    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
85        Ok(())
86    }
87}
88
89impl DebuggerMainProcess for NullDebugger {
90    fn run(
91        &mut self,
92        _ctx: DebugRenderContext<'_>,
93    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
94        Ok(())
95    }
96}

Create a dummy debugger implementation that implements the required traits but does not actually do anything.

100#[must_use]
101pub fn null_debug_fn<Emulator, R, A, I, S>() -> DebuggerProcesses<Emulator, R, A, I, S>
102where
103    Emulator: EmulatorTrait,
104    R: Renderer,
105    A: AudioOutput,
106    I: InputPoller<Emulator::Inputs>,
107    S: SaveWriter,
108{
109    (Box::new(NullDebugger), Box::new(NullDebugger))
110}
112pub struct PartialCloneRunnerProcess<Emulator> {
113    emulator_sender: SharedVarSender<Emulator>,
114}
115
116impl<Emulator, R, A, I, S> DebuggerRunnerProcess<Emulator, R, A, I, S>
117    for PartialCloneRunnerProcess<Emulator>
118where
119    Emulator: EmulatorTrait + PartialClone + Send + Sync + 'static,
120    R: Renderer,
121    A: AudioOutput,
122    I: InputPoller<Emulator::Inputs>,
123    S: SaveWriter,
124{
125    fn run(
126        &mut self,
127        emulator: &mut Emulator,
128    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
129        self.emulator_sender.update(emulator.partial_clone());
130
131        Ok(())
132    }
133}
134
135pub struct CloneRunnerProcess<Emulator> {
136    emulator_sender: SharedVarSender<Emulator>,
137}
138
139impl<Emulator, R, A, I, S> DebuggerRunnerProcess<Emulator, R, A, I, S>
140    for CloneRunnerProcess<Emulator>
141where
142    Emulator: EmulatorTrait + Clone + Send + Sync + 'static,
143    R: Renderer,
144    A: AudioOutput,
145    I: InputPoller<Emulator::Inputs>,
146    S: SaveWriter,
147{
148    fn run(
149        &mut self,
150        emulator: &mut Emulator,
151    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
152        self.emulator_sender.update(emulator.clone());
153
154        Ok(())
155    }
156}
157
158pub type DebugRenderFn<Emulator> = dyn FnMut(DebugRenderContext<'_>, &mut Emulator);
159
160pub struct CloneMainProcess<Emulator> {
161    emulator_receiver: SharedVarReceiver<Emulator>,
162    render_fn: Box<DebugRenderFn<Emulator>>,
163}
164
165impl<Emulator: Send + Sync + 'static> DebuggerMainProcess for CloneMainProcess<Emulator> {
166    fn run(
167        &mut self,
168        ctx: DebugRenderContext<'_>,
169    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
170        if let Some(emulator) = self.emulator_receiver.get() {
171            (self.render_fn)(ctx, emulator);
172        }
173
174        Ok(())
175    }
176}

Create a debugger implementation that sends the emulator state to the debugger frontend once per frame via [jgenesis_common::frontend::PartialClone::partial_clone].

This implementation does not support mutating emulator state or sending commands to the emulator runner thread. render_fn is passed a copy of the current emulator state, not the emulator itself.

183#[must_use]
184pub fn partial_clone_debug_fn<Emulator, R, A, I, S>(
185    render_fn: Box<DebugRenderFn<Emulator>>,
186) -> DebuggerProcesses<Emulator, R, A, I, S>
187where
188    Emulator: EmulatorTrait + PartialClone + Send + Sync + 'static,
189    R: Renderer,
190    A: AudioOutput,
191    I: InputPoller<Emulator::Inputs>,
192    S: SaveWriter,
193{
194    let (emulator_sender, emulator_receiver) = jgenesis_common::sync::new_shared_var();
195
196    let runner_process = PartialCloneRunnerProcess { emulator_sender };
197    let main_process = CloneMainProcess { emulator_receiver, render_fn };
198
199    (Box::new(runner_process), Box::new(main_process))
200}

Similar to [partial_clone_debug_fn] but invokes [Clone::clone] instead of [jgenesis_common::frontend::PartialClone::partial_clone].

Useful when the debug view needs information that is not included in a partial clone, e.g. cartridge ROM.

207#[must_use]
208pub fn clone_debug_fn<Emulator, R, A, I, S>(
209    render_fn: Box<DebugRenderFn<Emulator>>,
210) -> DebuggerProcesses<Emulator, R, A, I, S>
211where
212    Emulator: EmulatorTrait + Clone + Send + Sync + 'static,
213    R: Renderer,
214    A: AudioOutput,
215    I: InputPoller<Emulator::Inputs>,
216    S: SaveWriter,
217{
218    let (emulator_sender, emulator_receiver) = jgenesis_common::sync::new_shared_var();
219
220    let runner_process = CloneRunnerProcess { emulator_sender };
221    let main_process = CloneMainProcess { emulator_receiver, render_fn };
222
223    (Box::new(runner_process), Box::new(main_process))
224}