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,
};
use gilrs::Gilrs;
use std::sync::mpsc::channel;
use window::WindowRenderer;
use std::{
sync::mpsc::channel,
time::{Duration, Instant},
};
use window::{WindowManager, WindowRenderer};
mod audio;
#[cfg(feature = "camera")]
@ -75,77 +78,99 @@ struct Args {
fn main() {
let args = Args::parse();
let factor = if let Some(factor) = args.scale_factor {
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();
}
EmulatorHandler::run(args);
}
enum EmulatorHandler {
enum EmulatorTypes {
Debug(Debugger),
Normal(Box<dyn EmulatorCoreTrait>),
}
struct EmulatorHandler {
emu: EmulatorTypes,
window_manager: WindowManager,
since: Instant,
}
impl EmulatorHandler {
fn run(&mut self) {
match self {
EmulatorHandler::Debug(ref mut debugger) => debugger.step(),
EmulatorHandler::Normal(ref mut core) => core.run(),
fn run(args: Args) -> ! {
let factor = if let Some(factor) = args.scale_factor {
factor
} 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::{
connect::{JoypadState, Renderer},
util::scale_buffer_in_place,
@ -12,16 +17,86 @@ use winit::{
event::{Event, VirtualKeyCode, WindowEvent},
event_loop::EventLoop,
platform::run_return::EventLoopExtRunReturn,
window::{Window, WindowBuilder},
window::{Window, WindowBuilder, WindowId},
};
use winit_input_helper::WinitInputHelper;
pub struct WindowRenderer {
event_loop: EventLoop<()>,
window: Window,
input: WinitInputHelper,
pixels: Pixels,
pub struct WindowInfo {
id: WindowId,
data: Arc<Mutex<WindowData>>,
}
pub struct WindowData {
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,
height: usize,
factor: usize,
@ -32,11 +107,15 @@ pub struct WindowRenderer {
}
impl WindowRenderer {
pub fn new(factor: usize, gamepad_handler: Option<Gilrs>) -> Self {
let event_loop = EventLoop::new();
pub fn new(
factor: usize,
gamepad_handler: Option<Gilrs>,
input: Arc<Mutex<WinitInputHelper>>,
event_loop: &EventLoop<()>,
) -> (Self, WindowInfo) {
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.with_title("Gameboy")
.build(event_loop)
.unwrap();
let real_factor = (window.scale_factor() * factor as f64) as usize;
@ -54,52 +133,30 @@ impl WindowRenderer {
.unwrap()
};
let input = WinitInputHelper::new();
Self {
event_loop,
window,
input,
let data = Arc::new(Mutex::new(WindowData {
scaled_buf: Vec::new(),
pixels,
scaled_buf: vec![],
width: 0,
height: 0,
factor,
real_factor,
gamepad_handler,
joypad_state: JoypadState::default(),
current_rumble: false,
}
}
}));
let info = WindowInfo {
id: window.id(),
data: data.clone(),
};
fn update_events(&mut self) {
self.event_loop.run_return(|event, _, control_flow| {
control_flow.set_wait();
self.input.update(&event);
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
// quit = true;
}
Event::MainEventsCleared => {
control_flow.set_exit();
}
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();
}
_ => {}
}
});
(
Self {
window,
input,
data,
width: 0,
height: 0,
factor,
real_factor,
gamepad_handler,
joypad_state: JoypadState::default(),
current_rumble: false,
},
info,
)
}
}
@ -113,28 +170,30 @@ impl Renderer<[u8; 4]> for WindowRenderer {
let h = (height * self.real_factor) as u32;
self.window.set_inner_size(PhysicalSize::new(w, h));
self.pixels = {
let window_size = self.window.inner_size();
let surface_texture =
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]);
if let Ok(mut data) = self.data.lock() {
data.pixels = {
let window_size = self.window.inner_size();
let surface_texture =
SurfaceTexture::new(window_size.width, window_size.height, &self.window);
Pixels::new(window_size.width, window_size.height, surface_texture).unwrap()
};
data.scaled_buf.resize((w * h) as usize, [0; 4]);
}
self.window.request_redraw();
}
fn display(&mut self, buffer: &[[u8; 4]]) {
scale_buffer_in_place(
buffer,
&mut self.scaled_buf,
self.width,
self.height,
self.real_factor,
);
if let Ok(mut data) = self.data.lock() {
scale_buffer_in_place(
buffer,
&mut data.scaled_buf,
self.width,
self.height,
self.real_factor,
);
}
self.window.request_redraw();
self.update_events();
}
fn set_title(&mut self, title: String) {
@ -142,7 +201,6 @@ impl Renderer<[u8; 4]> for WindowRenderer {
}
fn latest_joypad_state(&mut self) -> JoypadState {
self.update_events();
self.joypad_state.reset();
if let Some(ref mut gamepad_handler) = self.gamepad_handler {
@ -174,19 +232,20 @@ impl Renderer<[u8; 4]> for WindowRenderer {
}
}
self.joypad_state.down |=
self.input.key_held(VirtualKeyCode::Down) || self.input.key_held(VirtualKeyCode::S);
self.joypad_state.up |=
self.input.key_held(VirtualKeyCode::Up) || self.input.key_held(VirtualKeyCode::W);
self.joypad_state.left |=
self.input.key_held(VirtualKeyCode::Left) || self.input.key_held(VirtualKeyCode::A);
self.joypad_state.right |=
self.input.key_held(VirtualKeyCode::Right) || self.input.key_held(VirtualKeyCode::D);
self.joypad_state.start |= self.input.key_held(VirtualKeyCode::Equals);
self.joypad_state.select |= self.input.key_held(VirtualKeyCode::Minus);
self.joypad_state.a |= self.input.key_held(VirtualKeyCode::Apostrophe);
self.joypad_state.b |= self.input.key_held(VirtualKeyCode::Semicolon);
if let Ok(input) = self.input.lock() {
self.joypad_state.down |=
input.key_held(VirtualKeyCode::Down) || input.key_held(VirtualKeyCode::S);
self.joypad_state.up |=
input.key_held(VirtualKeyCode::Up) || input.key_held(VirtualKeyCode::W);
self.joypad_state.left |=
input.key_held(VirtualKeyCode::Left) || input.key_held(VirtualKeyCode::A);
self.joypad_state.right |=
input.key_held(VirtualKeyCode::Right) || input.key_held(VirtualKeyCode::D);
self.joypad_state.start |= input.key_held(VirtualKeyCode::Equals);
self.joypad_state.select |= input.key_held(VirtualKeyCode::Minus);
self.joypad_state.a |= input.key_held(VirtualKeyCode::Apostrophe);
self.joypad_state.b |= input.key_held(VirtualKeyCode::Semicolon);
}
self.joypad_state
}