fix windows

This commit is contained in:
Alex Janka 2023-05-03 14:15:59 +10:00
parent 9e836927c4
commit 429e3b4085
2 changed files with 234 additions and 150 deletions

View file

@ -11,8 +11,11 @@ use gb_emu_lib::{
EmulatorCore, EmulatorCore,
}; };
use gilrs::Gilrs; use gilrs::Gilrs;
use std::sync::mpsc::channel; use std::{
use window::WindowRenderer; sync::mpsc::channel,
time::{Duration, Instant},
};
use window::{WindowManager, WindowRenderer};
mod audio; mod audio;
#[cfg(feature = "camera")] #[cfg(feature = "camera")]
@ -75,77 +78,99 @@ struct Args {
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
let factor = if let Some(factor) = args.scale_factor { EmulatorHandler::run(args);
factor
} else {
3
};
let tile_window: Option<WindowRenderer> = if args.tile_window {
Some(WindowRenderer::new(factor, None))
} else {
None
};
let (sender, receiver) = channel::<EmulatorMessage>();
let (debug_sender, debug_receiver) = channel::<EmulatorMessage>();
ctrlc::set_handler(move || {
sender.send(EmulatorMessage::Stop).unwrap();
debug_sender.send(EmulatorMessage::Stop).unwrap()
})
.unwrap();
let (output, _stream) = audio::create_output(args.mute);
let window = WindowRenderer::new(factor, Some(Gilrs::new().unwrap()));
let rom = RomFile::Path(args.rom);
let options = EmulatorOptions::new(window, rom, output)
.with_save_path(args.save)
.with_serial_target(if args.connect_serial {
SerialTarget::Stdout
} else {
SerialTarget::None
})
.with_bootrom(args.bootrom.map(RomFile::Path), args.show_bootrom)
.with_no_save(args.no_save)
.with_tile_window(tile_window)
.with_cgb_mode(!args.dmg);
// let core: Box<dyn EmulatorCoreTrait> = if args.camera {
// Box::new(EmulatorCore::init(receiver, options, Webcam::new()))
// } else {
// Box::new(EmulatorCore::init(receiver, options, NoCamera::default()))
// };
#[cfg(not(feature = "camera"))]
let core: Box<dyn EmulatorCoreTrait> =
Box::new(EmulatorCore::init(receiver, options, NoCamera::default()));
#[cfg(feature = "camera")]
let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new()));
let mut handler = if args.debug {
EmulatorHandler::Debug(Debugger::new(core, debug_receiver))
} else {
EmulatorHandler::Normal(core)
};
loop {
handler.run();
}
} }
enum EmulatorHandler { enum EmulatorTypes {
Debug(Debugger), Debug(Debugger),
Normal(Box<dyn EmulatorCoreTrait>), Normal(Box<dyn EmulatorCoreTrait>),
} }
struct EmulatorHandler {
emu: EmulatorTypes,
window_manager: WindowManager,
since: Instant,
}
impl EmulatorHandler { impl EmulatorHandler {
fn run(&mut self) { fn run(args: Args) -> ! {
match self { let factor = if let Some(factor) = args.scale_factor {
EmulatorHandler::Debug(ref mut debugger) => debugger.step(), factor
EmulatorHandler::Normal(ref mut core) => core.run(), } else {
3
};
let (sender, receiver) = channel::<EmulatorMessage>();
let (debug_sender, debug_receiver) = channel::<EmulatorMessage>();
ctrlc::set_handler(move || {
sender.send(EmulatorMessage::Stop).unwrap();
debug_sender.send(EmulatorMessage::Stop).unwrap()
})
.unwrap();
let (output, _stream) = audio::create_output(args.mute);
let rom = RomFile::Path(args.rom);
let mut window_manager = WindowManager::new();
let window = window_manager.add(factor, Some(Gilrs::new().unwrap()));
let tile_window: Option<WindowRenderer> = if args.tile_window {
Some(window_manager.add(factor, None))
} else {
None
};
let options = EmulatorOptions::new(window, rom, output)
.with_save_path(args.save)
.with_serial_target(if args.connect_serial {
SerialTarget::Stdout
} else {
SerialTarget::None
})
.with_bootrom(args.bootrom.map(RomFile::Path), args.show_bootrom)
.with_no_save(args.no_save)
.with_tile_window(tile_window)
.with_cgb_mode(!args.dmg);
// let core: Box<dyn EmulatorCoreTrait> = if args.camera {
// Box::new(EmulatorCore::init(receiver, options, Webcam::new()))
// } else {
// Box::new(EmulatorCore::init(receiver, options, NoCamera::default()))
// };
#[cfg(not(feature = "camera"))]
let core: Box<dyn EmulatorCoreTrait> =
Box::new(EmulatorCore::init(receiver, options, NoCamera::default()));
#[cfg(feature = "camera")]
let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new()));
let emu = if args.debug {
EmulatorTypes::Debug(Debugger::new(core, debug_receiver))
} else {
EmulatorTypes::Normal(core)
};
let since = Instant::now();
let mut h = Self {
emu,
window_manager,
since,
};
loop {
h.cycle();
}
}
fn cycle(&mut self) {
if self.since.elapsed() >= Duration::from_millis(10) {
self.window_manager.update_events();
self.since = Instant::now();
}
match self.emu {
EmulatorTypes::Debug(ref mut debugger) => debugger.step(),
EmulatorTypes::Normal(ref mut core) => core.run(),
} }
} }
} }

View file

@ -1,3 +1,8 @@
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use gb_emu_lib::{ use gb_emu_lib::{
connect::{JoypadState, Renderer}, connect::{JoypadState, Renderer},
util::scale_buffer_in_place, util::scale_buffer_in_place,
@ -12,16 +17,86 @@ use winit::{
event::{Event, VirtualKeyCode, WindowEvent}, event::{Event, VirtualKeyCode, WindowEvent},
event_loop::EventLoop, event_loop::EventLoop,
platform::run_return::EventLoopExtRunReturn, platform::run_return::EventLoopExtRunReturn,
window::{Window, WindowBuilder}, window::{Window, WindowBuilder, WindowId},
}; };
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
pub struct WindowRenderer { pub struct WindowInfo {
event_loop: EventLoop<()>, id: WindowId,
window: Window, data: Arc<Mutex<WindowData>>,
input: WinitInputHelper, }
pixels: Pixels,
pub struct WindowData {
scaled_buf: Vec<[u8; 4]>, scaled_buf: Vec<[u8; 4]>,
pixels: Pixels,
}
pub struct WindowManager {
event_loop: EventLoop<()>,
windows: HashMap<WindowId, Arc<Mutex<WindowData>>>,
input: Arc<Mutex<WinitInputHelper>>,
}
impl WindowManager {
pub(crate) fn new() -> Self {
Self {
event_loop: EventLoop::new(),
windows: HashMap::new(),
input: Arc::new(Mutex::new(WinitInputHelper::new())),
}
}
pub(crate) fn add(&mut self, factor: usize, gamepad_handler: Option<Gilrs>) -> WindowRenderer {
let (r, info) = WindowRenderer::new(
factor,
gamepad_handler,
self.input.clone(),
&self.event_loop,
);
self.windows.insert(info.id, info.data);
r
}
pub fn update_events(&mut self) {
self.event_loop.run_return(|event, _, control_flow| {
control_flow.set_wait();
if let Ok(mut i) = self.input.lock() {
i.update(&event);
}
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id: _,
} => {
// quit = true;
}
Event::MainEventsCleared => {
control_flow.set_exit();
}
Event::RedrawRequested(window_id) => {
if let Some(w) = self.windows.get(&window_id) {
if let Ok(mut w) = w.lock() {
let scaled = w.scaled_buf.clone();
for (pixel, source) in
w.pixels.frame_mut().chunks_exact_mut(4).zip(&scaled)
{
pixel.copy_from_slice(source);
}
w.pixels.render().unwrap();
}
}
}
_ => {}
}
});
}
}
pub struct WindowRenderer {
window: Window,
input: Arc<Mutex<WinitInputHelper>>,
data: Arc<Mutex<WindowData>>,
width: usize, width: usize,
height: usize, height: usize,
factor: usize, factor: usize,
@ -32,11 +107,15 @@ pub struct WindowRenderer {
} }
impl WindowRenderer { impl WindowRenderer {
pub fn new(factor: usize, gamepad_handler: Option<Gilrs>) -> Self { pub fn new(
let event_loop = EventLoop::new(); factor: usize,
gamepad_handler: Option<Gilrs>,
input: Arc<Mutex<WinitInputHelper>>,
event_loop: &EventLoop<()>,
) -> (Self, WindowInfo) {
let window = WindowBuilder::new() let window = WindowBuilder::new()
.with_title("A fantastic window!") .with_title("Gameboy")
.build(&event_loop) .build(event_loop)
.unwrap(); .unwrap();
let real_factor = (window.scale_factor() * factor as f64) as usize; let real_factor = (window.scale_factor() * factor as f64) as usize;
@ -54,52 +133,30 @@ impl WindowRenderer {
.unwrap() .unwrap()
}; };
let input = WinitInputHelper::new(); let data = Arc::new(Mutex::new(WindowData {
Self { scaled_buf: Vec::new(),
event_loop,
window,
input,
pixels, pixels,
scaled_buf: vec![], }));
width: 0, let info = WindowInfo {
height: 0, id: window.id(),
factor, data: data.clone(),
real_factor, };
gamepad_handler,
joypad_state: JoypadState::default(),
current_rumble: false,
}
}
fn update_events(&mut self) { (
self.event_loop.run_return(|event, _, control_flow| { Self {
control_flow.set_wait(); window,
self.input.update(&event); input,
data,
match event { width: 0,
Event::WindowEvent { height: 0,
event: WindowEvent::CloseRequested, factor,
.. real_factor,
} => { gamepad_handler,
// quit = true; joypad_state: JoypadState::default(),
} current_rumble: false,
Event::MainEventsCleared => { },
control_flow.set_exit(); info,
} )
Event::RedrawRequested(_) => {
for (pixel, source) in self
.pixels
.frame_mut()
.chunks_exact_mut(4)
.zip(&self.scaled_buf)
{
pixel.copy_from_slice(source);
}
self.pixels.render().unwrap();
}
_ => {}
}
});
} }
} }
@ -113,28 +170,30 @@ impl Renderer<[u8; 4]> for WindowRenderer {
let h = (height * self.real_factor) as u32; let h = (height * self.real_factor) as u32;
self.window.set_inner_size(PhysicalSize::new(w, h)); self.window.set_inner_size(PhysicalSize::new(w, h));
self.pixels = { if let Ok(mut data) = self.data.lock() {
let window_size = self.window.inner_size(); data.pixels = {
let surface_texture = let window_size = self.window.inner_size();
SurfaceTexture::new(window_size.width, window_size.height, &self.window); let surface_texture =
Pixels::new(window_size.width, window_size.height, surface_texture).unwrap() SurfaceTexture::new(window_size.width, window_size.height, &self.window);
}; Pixels::new(window_size.width, window_size.height, surface_texture).unwrap()
};
self.scaled_buf.resize((w * h) as usize, [0; 4]);
data.scaled_buf.resize((w * h) as usize, [0; 4]);
}
self.window.request_redraw(); self.window.request_redraw();
} }
fn display(&mut self, buffer: &[[u8; 4]]) { fn display(&mut self, buffer: &[[u8; 4]]) {
scale_buffer_in_place( if let Ok(mut data) = self.data.lock() {
buffer, scale_buffer_in_place(
&mut self.scaled_buf, buffer,
self.width, &mut data.scaled_buf,
self.height, self.width,
self.real_factor, self.height,
); self.real_factor,
);
}
self.window.request_redraw(); self.window.request_redraw();
self.update_events();
} }
fn set_title(&mut self, title: String) { fn set_title(&mut self, title: String) {
@ -142,7 +201,6 @@ impl Renderer<[u8; 4]> for WindowRenderer {
} }
fn latest_joypad_state(&mut self) -> JoypadState { fn latest_joypad_state(&mut self) -> JoypadState {
self.update_events();
self.joypad_state.reset(); self.joypad_state.reset();
if let Some(ref mut gamepad_handler) = self.gamepad_handler { if let Some(ref mut gamepad_handler) = self.gamepad_handler {
@ -174,19 +232,20 @@ impl Renderer<[u8; 4]> for WindowRenderer {
} }
} }
self.joypad_state.down |= if let Ok(input) = self.input.lock() {
self.input.key_held(VirtualKeyCode::Down) || self.input.key_held(VirtualKeyCode::S); self.joypad_state.down |=
self.joypad_state.up |= input.key_held(VirtualKeyCode::Down) || input.key_held(VirtualKeyCode::S);
self.input.key_held(VirtualKeyCode::Up) || self.input.key_held(VirtualKeyCode::W); self.joypad_state.up |=
self.joypad_state.left |= input.key_held(VirtualKeyCode::Up) || input.key_held(VirtualKeyCode::W);
self.input.key_held(VirtualKeyCode::Left) || self.input.key_held(VirtualKeyCode::A); self.joypad_state.left |=
self.joypad_state.right |= input.key_held(VirtualKeyCode::Left) || input.key_held(VirtualKeyCode::A);
self.input.key_held(VirtualKeyCode::Right) || self.input.key_held(VirtualKeyCode::D); self.joypad_state.right |=
self.joypad_state.start |= self.input.key_held(VirtualKeyCode::Equals); input.key_held(VirtualKeyCode::Right) || input.key_held(VirtualKeyCode::D);
self.joypad_state.select |= self.input.key_held(VirtualKeyCode::Minus); self.joypad_state.start |= input.key_held(VirtualKeyCode::Equals);
self.joypad_state.a |= self.input.key_held(VirtualKeyCode::Apostrophe); self.joypad_state.select |= input.key_held(VirtualKeyCode::Minus);
self.joypad_state.b |= self.input.key_held(VirtualKeyCode::Semicolon); self.joypad_state.a |= input.key_held(VirtualKeyCode::Apostrophe);
self.joypad_state.b |= input.key_held(VirtualKeyCode::Semicolon);
}
self.joypad_state self.joypad_state
} }