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.
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.
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}