fix windows
This commit is contained in:
parent
9e836927c4
commit
429e3b4085
2 changed files with 234 additions and 150 deletions
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue