Support running two sims at once
This commit is contained in:
		
							parent
							
								
									9fcf6b3dc5
								
							
						
					
					
						commit
						544990c58f
					
				| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
Subproject commit ae22c95dbee3d0b338168bfdf98143e6eddc6c70
 | 
					Subproject commit b4b9131f3976388b096343725fce1bdf35f4e3df
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/app.rs
								
								
								
								
							
							
						
						
									
										57
									
								
								src/app.rs
								
								
								
								
							| 
						 | 
					@ -5,6 +5,7 @@ use std::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use game::GameWindow;
 | 
					use game::GameWindow;
 | 
				
			||||||
 | 
					use input::InputWindow;
 | 
				
			||||||
use winit::{
 | 
					use winit::{
 | 
				
			||||||
    application::ApplicationHandler,
 | 
					    application::ApplicationHandler,
 | 
				
			||||||
    event::{Event, WindowEvent},
 | 
					    event::{Event, WindowEvent},
 | 
				
			||||||
| 
						 | 
					@ -14,7 +15,7 @@ use winit::{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    controller::ControllerState,
 | 
					    controller::ControllerState,
 | 
				
			||||||
    emulator::{EmulatorClient, EmulatorCommand},
 | 
					    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
				
			||||||
    input::InputMapper,
 | 
					    input::InputMapper,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +29,7 @@ pub struct App {
 | 
				
			||||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
    controller: ControllerState,
 | 
					    controller: ControllerState,
 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
 | 
					    player_2_window: Option<WindowId>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl App {
 | 
					impl App {
 | 
				
			||||||
| 
						 | 
					@ -40,19 +42,19 @@ impl App {
 | 
				
			||||||
            input_mapper,
 | 
					            input_mapper,
 | 
				
			||||||
            controller,
 | 
					            controller,
 | 
				
			||||||
            proxy,
 | 
					            proxy,
 | 
				
			||||||
 | 
					            player_2_window: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ApplicationHandler<UserEvent> for App {
 | 
					impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
 | 
					    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
 | 
				
			||||||
        let mut window = GameWindow::new(
 | 
					        let window = GameWindow::new(
 | 
				
			||||||
            event_loop,
 | 
					            event_loop,
 | 
				
			||||||
 | 
					            SimId::Player1,
 | 
				
			||||||
            self.client.clone(),
 | 
					            self.client.clone(),
 | 
				
			||||||
            self.input_mapper.clone(),
 | 
					 | 
				
			||||||
            self.proxy.clone(),
 | 
					            self.proxy.clone(),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        window.init();
 | 
					 | 
				
			||||||
        self.windows.insert(window.id(), Box::new(window));
 | 
					        self.windows.insert(window.id(), Box::new(window));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,9 +65,9 @@ impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
        event: WindowEvent,
 | 
					        event: WindowEvent,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        if let WindowEvent::KeyboardInput { event, .. } = &event {
 | 
					        if let WindowEvent::KeyboardInput { event, .. } = &event {
 | 
				
			||||||
            if self.controller.key_event(event) {
 | 
					            if let Some((sim_id, pressed)) = self.controller.key_event(event) {
 | 
				
			||||||
                self.client
 | 
					                self.client
 | 
				
			||||||
                    .send_command(EmulatorCommand::SetKeys(self.controller.pressed()));
 | 
					                    .send_command(EmulatorCommand::SetKeys(sim_id, pressed));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let Some(window) = self.windows.get_mut(&window_id) else {
 | 
					        let Some(window) = self.windows.get_mut(&window_id) else {
 | 
				
			||||||
| 
						 | 
					@ -74,13 +76,30 @@ impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
        window.handle_event(event_loop, &Event::WindowEvent { window_id, event });
 | 
					        window.handle_event(event_loop, &Event::WindowEvent { window_id, event });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
 | 
					    fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
 | 
				
			||||||
        match event {
 | 
					        match event {
 | 
				
			||||||
            UserEvent::OpenWindow(mut window) => {
 | 
					            UserEvent::OpenInputWindow => {
 | 
				
			||||||
                window.init();
 | 
					                let window =
 | 
				
			||||||
                self.windows.insert(window.id(), window);
 | 
					                    InputWindow::new(event_loop, self.input_mapper.clone(), self.proxy.clone());
 | 
				
			||||||
 | 
					                self.windows.insert(window.id(), Box::new(window));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            UserEvent::OpenPlayer2Window => {
 | 
				
			||||||
 | 
					                if self.player_2_window.is_some() {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                let window = GameWindow::new(
 | 
				
			||||||
 | 
					                    event_loop,
 | 
				
			||||||
 | 
					                    SimId::Player2,
 | 
				
			||||||
 | 
					                    self.client.clone(),
 | 
				
			||||||
 | 
					                    self.proxy.clone(),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                self.player_2_window = Some(window.id());
 | 
				
			||||||
 | 
					                self.windows.insert(window.id(), Box::new(window));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            UserEvent::Close(window_id) => {
 | 
				
			||||||
 | 
					                if self.player_2_window == Some(window_id) {
 | 
				
			||||||
 | 
					                    self.player_2_window.take();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            UserEvent::CloseWindow(window_id) => {
 | 
					 | 
				
			||||||
                self.windows.remove(&window_id);
 | 
					                self.windows.remove(&window_id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -112,20 +131,12 @@ impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait AppWindow {
 | 
					pub trait AppWindow {
 | 
				
			||||||
    fn id(&self) -> WindowId;
 | 
					    fn id(&self) -> WindowId;
 | 
				
			||||||
    fn init(&mut self);
 | 
					 | 
				
			||||||
    fn handle_event(&mut self, event_loop: &ActiveEventLoop, event: &Event<UserEvent>);
 | 
					    fn handle_event(&mut self, event_loop: &ActiveEventLoop, event: &Event<UserEvent>);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum UserEvent {
 | 
					pub enum UserEvent {
 | 
				
			||||||
    OpenWindow(Box<dyn AppWindow>),
 | 
					    OpenInputWindow,
 | 
				
			||||||
    CloseWindow(WindowId),
 | 
					    OpenPlayer2Window,
 | 
				
			||||||
}
 | 
					    Close(WindowId),
 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Debug for UserEvent {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Self::OpenWindow(window) => f.debug_tuple("OpenWindow").field(&window.id()).finish(),
 | 
					 | 
				
			||||||
            Self::CloseWindow(window_id) => f.debug_tuple("CloseWindow").field(window_id).finish(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,4 @@
 | 
				
			||||||
use std::{
 | 
					use std::{sync::Arc, time::Instant};
 | 
				
			||||||
    sync::{Arc, RwLock},
 | 
					 | 
				
			||||||
    time::Instant,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use wgpu::util::DeviceExt as _;
 | 
					use wgpu::util::DeviceExt as _;
 | 
				
			||||||
use winit::{
 | 
					use winit::{
 | 
				
			||||||
    dpi::LogicalSize,
 | 
					    dpi::LogicalSize,
 | 
				
			||||||
| 
						 | 
					@ -11,24 +8,22 @@ use winit::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    emulator::{EmulatorClient, EmulatorCommand},
 | 
					    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
				
			||||||
    input::InputMapper,
 | 
					 | 
				
			||||||
    renderer::GameRenderer,
 | 
					    renderer::GameRenderer,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    common::{ImguiState, WindowState, WindowStateBuilder},
 | 
					    common::{ImguiState, WindowState, WindowStateBuilder},
 | 
				
			||||||
    input::InputWindow,
 | 
					 | 
				
			||||||
    AppWindow, UserEvent,
 | 
					    AppWindow, UserEvent,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct GameWindow {
 | 
					pub struct GameWindow {
 | 
				
			||||||
    window: WindowState,
 | 
					    window: WindowState,
 | 
				
			||||||
    imgui: Option<ImguiState>,
 | 
					    imgui: ImguiState,
 | 
				
			||||||
    pipeline: wgpu::RenderPipeline,
 | 
					    pipeline: wgpu::RenderPipeline,
 | 
				
			||||||
    bind_group: wgpu::BindGroup,
 | 
					    bind_group: wgpu::BindGroup,
 | 
				
			||||||
 | 
					    sim_id: SimId,
 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
					 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
    paused_due_to_minimize: bool,
 | 
					    paused_due_to_minimize: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -36,21 +31,29 @@ pub struct GameWindow {
 | 
				
			||||||
impl GameWindow {
 | 
					impl GameWindow {
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        event_loop: &ActiveEventLoop,
 | 
					        event_loop: &ActiveEventLoop,
 | 
				
			||||||
 | 
					        sim_id: SimId,
 | 
				
			||||||
        client: EmulatorClient,
 | 
					        client: EmulatorClient,
 | 
				
			||||||
        input_mapper: Arc<RwLock<InputMapper>>,
 | 
					 | 
				
			||||||
        proxy: EventLoopProxy<UserEvent>,
 | 
					        proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let title = if sim_id == SimId::Player2 {
 | 
				
			||||||
 | 
					            "Shrooms VB (Player 2)"
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            "Shrooms VB"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
        let window = WindowStateBuilder::new(event_loop)
 | 
					        let window = WindowStateBuilder::new(event_loop)
 | 
				
			||||||
            .with_title("Shrooms VB")
 | 
					            .with_title(title)
 | 
				
			||||||
            .with_inner_size(LogicalSize::new(384, 244))
 | 
					            .with_inner_size(LogicalSize::new(384, 244))
 | 
				
			||||||
            .build();
 | 
					            .build();
 | 
				
			||||||
        let device = &window.device;
 | 
					        let device = &window.device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let eyes = Arc::new(GameRenderer::create_texture(device, "eye"));
 | 
					        let eyes = Arc::new(GameRenderer::create_texture(device, "eye"));
 | 
				
			||||||
        client.send_command(EmulatorCommand::SetRenderer(GameRenderer {
 | 
					        client.send_command(EmulatorCommand::SetRenderer(
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
 | 
					            GameRenderer {
 | 
				
			||||||
                queue: window.queue.clone(),
 | 
					                queue: window.queue.clone(),
 | 
				
			||||||
                eyes: eyes.clone(),
 | 
					                eyes: eyes.clone(),
 | 
				
			||||||
        }));
 | 
					            },
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
        let eyes = eyes.create_view(&wgpu::TextureViewDescriptor::default());
 | 
					        let eyes = eyes.create_view(&wgpu::TextureViewDescriptor::default());
 | 
				
			||||||
        let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
 | 
					        let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
 | 
				
			||||||
        let colors = Colors {
 | 
					        let colors = Colors {
 | 
				
			||||||
| 
						 | 
					@ -158,13 +161,15 @@ impl GameWindow {
 | 
				
			||||||
            cache: None,
 | 
					            cache: None,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let imgui = ImguiState::new(&window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            window,
 | 
					            window,
 | 
				
			||||||
            imgui: None,
 | 
					            imgui,
 | 
				
			||||||
            pipeline: render_pipeline,
 | 
					            pipeline: render_pipeline,
 | 
				
			||||||
            bind_group,
 | 
					            bind_group,
 | 
				
			||||||
 | 
					            sim_id,
 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
            input_mapper,
 | 
					 | 
				
			||||||
            proxy,
 | 
					            proxy,
 | 
				
			||||||
            paused_due_to_minimize: false,
 | 
					            paused_due_to_minimize: false,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -172,7 +177,7 @@ impl GameWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw(&mut self, event_loop: &ActiveEventLoop) {
 | 
					    fn draw(&mut self, event_loop: &ActiveEventLoop) {
 | 
				
			||||||
        let window = &mut self.window;
 | 
					        let window = &mut self.window;
 | 
				
			||||||
        let imgui = self.imgui.as_mut().unwrap();
 | 
					        let imgui = &mut self.imgui;
 | 
				
			||||||
        let mut context = imgui.context.lock().unwrap();
 | 
					        let mut context = imgui.context.lock().unwrap();
 | 
				
			||||||
        let mut new_size = None;
 | 
					        let mut new_size = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -204,7 +209,8 @@ impl GameWindow {
 | 
				
			||||||
                        .show_open_single_file()
 | 
					                        .show_open_single_file()
 | 
				
			||||||
                        .unwrap();
 | 
					                        .unwrap();
 | 
				
			||||||
                    if let Some(path) = rom {
 | 
					                    if let Some(path) = rom {
 | 
				
			||||||
                        self.client.send_command(EmulatorCommand::LoadGame(path));
 | 
					                        self.client
 | 
				
			||||||
 | 
					                            .send_command(EmulatorCommand::LoadGame(self.sim_id, path));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if ui.menu_item("Quit") {
 | 
					                if ui.menu_item("Quit") {
 | 
				
			||||||
| 
						 | 
					@ -240,14 +246,14 @@ impl GameWindow {
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            ui.menu("Input", || {
 | 
					            ui.menu("Input", || {
 | 
				
			||||||
                if ui.menu_item("Bind Inputs") {
 | 
					                if ui.menu_item("Bind Inputs") {
 | 
				
			||||||
                    let input_window = Box::new(InputWindow::new(
 | 
					                    self.proxy.send_event(UserEvent::OpenInputWindow).unwrap();
 | 
				
			||||||
                        event_loop,
 | 
					                }
 | 
				
			||||||
                        self.input_mapper.clone(),
 | 
					            });
 | 
				
			||||||
                        self.proxy.clone(),
 | 
					            ui.menu("Multiplayer", || {
 | 
				
			||||||
                    ));
 | 
					                if self.sim_id == SimId::Player1 && ui.menu_item("Open Player 2") {
 | 
				
			||||||
                    self.proxy
 | 
					                    self.client
 | 
				
			||||||
                        .send_event(UserEvent::OpenWindow(input_window))
 | 
					                        .send_command(EmulatorCommand::StartSecondSim(None));
 | 
				
			||||||
                        .unwrap();
 | 
					                    self.proxy.send_event(UserEvent::OpenPlayer2Window).unwrap();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					@ -321,11 +327,6 @@ impl AppWindow for GameWindow {
 | 
				
			||||||
        self.window.window.id()
 | 
					        self.window.window.id()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn init(&mut self) {
 | 
					 | 
				
			||||||
        self.imgui = Some(ImguiState::new(&self.window));
 | 
					 | 
				
			||||||
        self.window.window.request_redraw();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn handle_event(&mut self, event_loop: &ActiveEventLoop, event: &Event<UserEvent>) {
 | 
					    fn handle_event(&mut self, event_loop: &ActiveEventLoop, event: &Event<UserEvent>) {
 | 
				
			||||||
        match event {
 | 
					        match event {
 | 
				
			||||||
            Event::WindowEvent { event, .. } => match event {
 | 
					            Event::WindowEvent { event, .. } => match event {
 | 
				
			||||||
| 
						 | 
					@ -341,7 +342,14 @@ impl AppWindow for GameWindow {
 | 
				
			||||||
                        self.paused_due_to_minimize = false;
 | 
					                        self.paused_due_to_minimize = false;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                WindowEvent::CloseRequested => event_loop.exit(),
 | 
					                WindowEvent::CloseRequested => {
 | 
				
			||||||
 | 
					                    if self.sim_id == SimId::Player2 {
 | 
				
			||||||
 | 
					                        self.client.send_command(EmulatorCommand::StopSecondSim);
 | 
				
			||||||
 | 
					                        self.proxy.send_event(UserEvent::Close(self.id())).unwrap();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        event_loop.exit();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                WindowEvent::RedrawRequested => self.draw(event_loop),
 | 
					                WindowEvent::RedrawRequested => self.draw(event_loop),
 | 
				
			||||||
                _ => (),
 | 
					                _ => (),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
| 
						 | 
					@ -351,9 +359,7 @@ impl AppWindow for GameWindow {
 | 
				
			||||||
            _ => (),
 | 
					            _ => (),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let window = &self.window;
 | 
					        let window = &self.window;
 | 
				
			||||||
        let Some(imgui) = self.imgui.as_mut() else {
 | 
					        let imgui = &mut self.imgui;
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        let mut context = imgui.context.lock().unwrap();
 | 
					        let mut context = imgui.context.lock().unwrap();
 | 
				
			||||||
        imgui
 | 
					        imgui
 | 
				
			||||||
            .platform
 | 
					            .platform
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,12 @@ use winit::{
 | 
				
			||||||
    dpi::LogicalSize,
 | 
					    dpi::LogicalSize,
 | 
				
			||||||
    event::{Event, KeyEvent, WindowEvent},
 | 
					    event::{Event, KeyEvent, WindowEvent},
 | 
				
			||||||
    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
					    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
				
			||||||
    platform::modifier_supplement::KeyEventExtModifierSupplement,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{input::InputMapper, shrooms_vb_core::VBKey};
 | 
					use crate::{
 | 
				
			||||||
 | 
					    emulator::{SimId, VBKey},
 | 
				
			||||||
 | 
					    input::InputMapper,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    common::{ImguiState, UiExt, WindowState, WindowStateBuilder},
 | 
					    common::{ImguiState, UiExt, WindowState, WindowStateBuilder},
 | 
				
			||||||
| 
						 | 
					@ -19,10 +21,10 @@ use super::{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct InputWindow {
 | 
					pub struct InputWindow {
 | 
				
			||||||
    window: WindowState,
 | 
					    window: WindowState,
 | 
				
			||||||
    imgui: Option<ImguiState>,
 | 
					    imgui: ImguiState,
 | 
				
			||||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
    now_binding: Option<VBKey>,
 | 
					    now_binding: Option<(SimId, VBKey)>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const KEY_NAMES: [(VBKey, &str); 14] = [
 | 
					const KEY_NAMES: [(VBKey, &str); 14] = [
 | 
				
			||||||
| 
						 | 
					@ -52,9 +54,10 @@ impl InputWindow {
 | 
				
			||||||
            .with_title("Bind Inputs")
 | 
					            .with_title("Bind Inputs")
 | 
				
			||||||
            .with_inner_size(LogicalSize::new(600, 400))
 | 
					            .with_inner_size(LogicalSize::new(600, 400))
 | 
				
			||||||
            .build();
 | 
					            .build();
 | 
				
			||||||
 | 
					        let imgui = ImguiState::new(&window);
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            window,
 | 
					            window,
 | 
				
			||||||
            imgui: None,
 | 
					            imgui,
 | 
				
			||||||
            input_mapper,
 | 
					            input_mapper,
 | 
				
			||||||
            now_binding: None,
 | 
					            now_binding: None,
 | 
				
			||||||
            proxy,
 | 
					            proxy,
 | 
				
			||||||
| 
						 | 
					@ -63,7 +66,7 @@ impl InputWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw(&mut self) {
 | 
					    fn draw(&mut self) {
 | 
				
			||||||
        let window = &mut self.window;
 | 
					        let window = &mut self.window;
 | 
				
			||||||
        let imgui = self.imgui.as_mut().unwrap();
 | 
					        let imgui = &mut self.imgui;
 | 
				
			||||||
        let mut context = imgui.context.lock().unwrap();
 | 
					        let mut context = imgui.context.lock().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let now = Instant::now();
 | 
					        let now = Instant::now();
 | 
				
			||||||
| 
						 | 
					@ -85,11 +88,11 @@ impl InputWindow {
 | 
				
			||||||
            .expect("Failed to prepare frame");
 | 
					            .expect("Failed to prepare frame");
 | 
				
			||||||
        let ui = context.new_frame();
 | 
					        let ui = context.new_frame();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut render_key_bindings = || {
 | 
					        let mut render_key_bindings = |sim_id: SimId| {
 | 
				
			||||||
            if let Some(table) = ui.begin_table("controls", 2) {
 | 
					            if let Some(table) = ui.begin_table("controls", 2) {
 | 
				
			||||||
                let binding_names = {
 | 
					                let binding_names = {
 | 
				
			||||||
                    let mapper = self.input_mapper.read().unwrap();
 | 
					                    let mapper = self.input_mapper.read().unwrap();
 | 
				
			||||||
                    mapper.binding_names()
 | 
					                    mapper.binding_names(&sim_id)
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                ui.table_next_row();
 | 
					                ui.table_next_row();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,20 +103,20 @@ impl InputWindow {
 | 
				
			||||||
                    ui.group(|| {
 | 
					                    ui.group(|| {
 | 
				
			||||||
                        ui.right_align_text(name, space * 0.20);
 | 
					                        ui.right_align_text(name, space * 0.20);
 | 
				
			||||||
                        ui.same_line();
 | 
					                        ui.same_line();
 | 
				
			||||||
                        let label_text = if self.now_binding == Some(key) {
 | 
					                        let label_text = if self.now_binding == Some((sim_id, key)) {
 | 
				
			||||||
                            "Press any input"
 | 
					                            "Press any input"
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            binding.unwrap_or("")
 | 
					                            binding.unwrap_or("")
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        let label = format!("{}##{}", label_text, name);
 | 
					                        let label = format!("{}##{}", label_text, name);
 | 
				
			||||||
                        if ui.button_with_size(label, [space * 0.60, 0.0]) {
 | 
					                        if ui.button_with_size(label, [space * 0.60, 0.0]) {
 | 
				
			||||||
                            self.now_binding = Some(key);
 | 
					                            self.now_binding = Some((sim_id, key));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    ui.same_line();
 | 
					                    ui.same_line();
 | 
				
			||||||
                    if ui.button(format!("Clear##{name}")) {
 | 
					                    if ui.button(format!("Clear##{name}")) {
 | 
				
			||||||
                        let mut mapper = self.input_mapper.write().unwrap();
 | 
					                        let mut mapper = self.input_mapper.write().unwrap();
 | 
				
			||||||
                        mapper.clear_binding(key);
 | 
					                        mapper.clear_binding(sim_id, key);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,7 +127,11 @@ impl InputWindow {
 | 
				
			||||||
        if let Some(window) = ui.fullscreen_window() {
 | 
					        if let Some(window) = ui.fullscreen_window() {
 | 
				
			||||||
            if let Some(tabs) = ui.tab_bar("tabs") {
 | 
					            if let Some(tabs) = ui.tab_bar("tabs") {
 | 
				
			||||||
                if let Some(tab) = ui.tab_item("Player 1") {
 | 
					                if let Some(tab) = ui.tab_item("Player 1") {
 | 
				
			||||||
                    render_key_bindings();
 | 
					                    render_key_bindings(SimId::Player1);
 | 
				
			||||||
 | 
					                    tab.end();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if let Some(tab) = ui.tab_item("Player 2") {
 | 
				
			||||||
 | 
					                    render_key_bindings(SimId::Player2);
 | 
				
			||||||
                    tab.end();
 | 
					                    tab.end();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                tabs.end();
 | 
					                tabs.end();
 | 
				
			||||||
| 
						 | 
					@ -175,11 +182,11 @@ impl InputWindow {
 | 
				
			||||||
        if !event.state.is_pressed() {
 | 
					        if !event.state.is_pressed() {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let Some(vb) = self.now_binding.take() else {
 | 
					        let Some((sim_id, vb)) = self.now_binding.take() else {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let mut mapper = self.input_mapper.write().unwrap();
 | 
					        let mut mapper = self.input_mapper.write().unwrap();
 | 
				
			||||||
        mapper.bind_key(vb, event.key_without_modifiers());
 | 
					        mapper.bind_key(sim_id, vb, event.physical_key);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,19 +195,13 @@ impl AppWindow for InputWindow {
 | 
				
			||||||
        self.window.window.id()
 | 
					        self.window.window.id()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn init(&mut self) {
 | 
					 | 
				
			||||||
        self.imgui = Some(ImguiState::new(&self.window));
 | 
					 | 
				
			||||||
        self.window.window.request_redraw();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn handle_event(&mut self, _: &ActiveEventLoop, event: &Event<UserEvent>) {
 | 
					    fn handle_event(&mut self, _: &ActiveEventLoop, event: &Event<UserEvent>) {
 | 
				
			||||||
        match event {
 | 
					        match event {
 | 
				
			||||||
            Event::WindowEvent { event, .. } => match event {
 | 
					            Event::WindowEvent { event, .. } => match event {
 | 
				
			||||||
                WindowEvent::Resized(size) => self.window.handle_resize(size),
 | 
					                WindowEvent::Resized(size) => self.window.handle_resize(size),
 | 
				
			||||||
                WindowEvent::CloseRequested => self
 | 
					                WindowEvent::CloseRequested => {
 | 
				
			||||||
                    .proxy
 | 
					                    self.proxy.send_event(UserEvent::Close(self.id())).unwrap()
 | 
				
			||||||
                    .send_event(UserEvent::CloseWindow(self.id()))
 | 
					                }
 | 
				
			||||||
                    .unwrap(),
 | 
					 | 
				
			||||||
                WindowEvent::KeyboardInput { event, .. } => self.try_bind_key(event),
 | 
					                WindowEvent::KeyboardInput { event, .. } => self.try_bind_key(event),
 | 
				
			||||||
                WindowEvent::RedrawRequested => self.draw(),
 | 
					                WindowEvent::RedrawRequested => self.draw(),
 | 
				
			||||||
                _ => (),
 | 
					                _ => (),
 | 
				
			||||||
| 
						 | 
					@ -212,9 +213,7 @@ impl AppWindow for InputWindow {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let window = &self.window;
 | 
					        let window = &self.window;
 | 
				
			||||||
        let Some(imgui) = self.imgui.as_mut() else {
 | 
					        let imgui = &mut self.imgui;
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        let mut context = imgui.context.lock().unwrap();
 | 
					        let mut context = imgui.context.lock().unwrap();
 | 
				
			||||||
        imgui
 | 
					        imgui
 | 
				
			||||||
            .platform
 | 
					            .platform
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,48 +2,46 @@ use std::sync::{Arc, RwLock};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use winit::event::{ElementState, KeyEvent};
 | 
					use winit::event::{ElementState, KeyEvent};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{input::InputMapper, shrooms_vb_core::VBKey};
 | 
					use crate::{
 | 
				
			||||||
 | 
					    emulator::{SimId, VBKey},
 | 
				
			||||||
 | 
					    input::InputMapper,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct ControllerState {
 | 
					pub struct ControllerState {
 | 
				
			||||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
    pressed: VBKey,
 | 
					    pressed: Vec<VBKey>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ControllerState {
 | 
					impl ControllerState {
 | 
				
			||||||
    pub fn new(input_mapper: Arc<RwLock<InputMapper>>) -> Self {
 | 
					    pub fn new(input_mapper: Arc<RwLock<InputMapper>>) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            input_mapper,
 | 
					            input_mapper,
 | 
				
			||||||
            pressed: VBKey::SGN,
 | 
					            pressed: vec![VBKey::SGN; 2],
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn pressed(&self) -> VBKey {
 | 
					    pub fn key_event(&mut self, event: &KeyEvent) -> Option<(SimId, VBKey)> {
 | 
				
			||||||
        self.pressed
 | 
					        let (sim_id, input) = self.key_event_to_input(event)?;
 | 
				
			||||||
    }
 | 
					        let pressed = &mut self.pressed[sim_id.to_index()];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn key_event(&mut self, event: &KeyEvent) -> bool {
 | 
					 | 
				
			||||||
        let Some(input) = self.key_event_to_input(event) else {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        match event.state {
 | 
					        match event.state {
 | 
				
			||||||
            ElementState::Pressed => {
 | 
					            ElementState::Pressed => {
 | 
				
			||||||
                if self.pressed.contains(input) {
 | 
					                if pressed.contains(input) {
 | 
				
			||||||
                    return false;
 | 
					                    return None;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                self.pressed.insert(input);
 | 
					                pressed.insert(input);
 | 
				
			||||||
                true
 | 
					                Some((sim_id, *pressed))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ElementState::Released => {
 | 
					            ElementState::Released => {
 | 
				
			||||||
                if !self.pressed.contains(input) {
 | 
					                if !pressed.contains(input) {
 | 
				
			||||||
                    return false;
 | 
					                    return None;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                self.pressed.remove(input);
 | 
					                pressed.remove(input);
 | 
				
			||||||
                true
 | 
					                Some((sim_id, *pressed))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn key_event_to_input(&self, event: &KeyEvent) -> Option<VBKey> {
 | 
					    fn key_event_to_input(&self, event: &KeyEvent) -> Option<(SimId, VBKey)> {
 | 
				
			||||||
        let mapper = self.input_mapper.read().unwrap();
 | 
					        let mapper = self.input_mapper.read().unwrap();
 | 
				
			||||||
        mapper.key_event(event)
 | 
					        mapper.key_event(event)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										116
									
								
								src/emulator.rs
								
								
								
								
							
							
						
						
									
										116
									
								
								src/emulator.rs
								
								
								
								
							| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
 | 
					    collections::HashMap,
 | 
				
			||||||
    fs,
 | 
					    fs,
 | 
				
			||||||
    path::{Path, PathBuf},
 | 
					    path::{Path, PathBuf},
 | 
				
			||||||
    sync::{
 | 
					    sync::{
 | 
				
			||||||
| 
						 | 
					@ -10,11 +11,11 @@ use std::{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::Result;
 | 
					use anyhow::Result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{audio::Audio, renderer::GameRenderer};
 | 
				
			||||||
    audio::Audio,
 | 
					use shrooms_vb_core::Sim;
 | 
				
			||||||
    renderer::GameRenderer,
 | 
					pub use shrooms_vb_core::VBKey;
 | 
				
			||||||
    shrooms_vb_core::{CoreVB, VBKey},
 | 
					
 | 
				
			||||||
};
 | 
					mod shrooms_vb_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct EmulatorBuilder {
 | 
					pub struct EmulatorBuilder {
 | 
				
			||||||
    rom: Option<PathBuf>,
 | 
					    rom: Option<PathBuf>,
 | 
				
			||||||
| 
						 | 
					@ -23,6 +24,20 @@ pub struct EmulatorBuilder {
 | 
				
			||||||
    has_game: Arc<AtomicBool>,
 | 
					    has_game: Arc<AtomicBool>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
				
			||||||
 | 
					pub enum SimId {
 | 
				
			||||||
 | 
					    Player1,
 | 
				
			||||||
 | 
					    Player2,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl SimId {
 | 
				
			||||||
 | 
					    pub fn to_index(self) -> usize {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::Player1 => 0,
 | 
				
			||||||
 | 
					            Self::Player2 => 1,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl EmulatorBuilder {
 | 
					impl EmulatorBuilder {
 | 
				
			||||||
    pub fn new() -> (Self, EmulatorClient) {
 | 
					    pub fn new() -> (Self, EmulatorClient) {
 | 
				
			||||||
        let (queue, commands) = mpsc::channel();
 | 
					        let (queue, commands) = mpsc::channel();
 | 
				
			||||||
| 
						 | 
					@ -50,17 +65,17 @@ impl EmulatorBuilder {
 | 
				
			||||||
    pub fn build(self) -> Result<Emulator> {
 | 
					    pub fn build(self) -> Result<Emulator> {
 | 
				
			||||||
        let mut emulator = Emulator::new(self.commands, self.running, self.has_game)?;
 | 
					        let mut emulator = Emulator::new(self.commands, self.running, self.has_game)?;
 | 
				
			||||||
        if let Some(path) = self.rom {
 | 
					        if let Some(path) = self.rom {
 | 
				
			||||||
            emulator.load_rom(&path)?;
 | 
					            emulator.load_rom_from_file(SimId::Player1, &path)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(emulator)
 | 
					        Ok(emulator)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Emulator {
 | 
					pub struct Emulator {
 | 
				
			||||||
    sim: CoreVB,
 | 
					    sims: Vec<Sim>,
 | 
				
			||||||
    audio: Audio,
 | 
					    audio: Audio,
 | 
				
			||||||
    commands: mpsc::Receiver<EmulatorCommand>,
 | 
					    commands: mpsc::Receiver<EmulatorCommand>,
 | 
				
			||||||
    renderer: Option<GameRenderer>,
 | 
					    renderers: HashMap<SimId, GameRenderer>,
 | 
				
			||||||
    running: Arc<AtomicBool>,
 | 
					    running: Arc<AtomicBool>,
 | 
				
			||||||
    has_game: Arc<AtomicBool>,
 | 
					    has_game: Arc<AtomicBool>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -72,24 +87,50 @@ impl Emulator {
 | 
				
			||||||
        has_game: Arc<AtomicBool>,
 | 
					        has_game: Arc<AtomicBool>,
 | 
				
			||||||
    ) -> Result<Self> {
 | 
					    ) -> Result<Self> {
 | 
				
			||||||
        Ok(Self {
 | 
					        Ok(Self {
 | 
				
			||||||
            sim: CoreVB::new(),
 | 
					            sims: vec![],
 | 
				
			||||||
            audio: Audio::init()?,
 | 
					            audio: Audio::init()?,
 | 
				
			||||||
            commands,
 | 
					            commands,
 | 
				
			||||||
            renderer: None,
 | 
					            renderers: HashMap::new(),
 | 
				
			||||||
            running,
 | 
					            running,
 | 
				
			||||||
            has_game,
 | 
					            has_game,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn load_rom(&mut self, path: &Path) -> Result<()> {
 | 
					    pub fn load_rom_from_file(&mut self, sim_id: SimId, path: &Path) -> Result<()> {
 | 
				
			||||||
        let bytes = fs::read(path)?;
 | 
					        let bytes = fs::read(path)?;
 | 
				
			||||||
        self.sim.reset();
 | 
					        self.load_rom(sim_id, bytes)?;
 | 
				
			||||||
        self.sim.load_rom(bytes)?;
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start_second_sim(&mut self, rom: Option<PathBuf>) -> Result<()> {
 | 
				
			||||||
 | 
					        let bytes = if let Some(path) = rom {
 | 
				
			||||||
 | 
					            fs::read(path)?
 | 
				
			||||||
 | 
					        } else if let Some(rom) = self.sims.first().and_then(|s| s.clone_rom()) {
 | 
				
			||||||
 | 
					            rom
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return Ok(());
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        self.load_rom(SimId::Player2, bytes)?;
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn load_rom(&mut self, sim_id: SimId, bytes: Vec<u8>) -> Result<()> {
 | 
				
			||||||
 | 
					        while self.sims.len() <= sim_id.to_index() {
 | 
				
			||||||
 | 
					            self.sims.push(Sim::new());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let sim = &mut self.sims[sim_id.to_index()];
 | 
				
			||||||
 | 
					        sim.reset();
 | 
				
			||||||
 | 
					        sim.load_rom(bytes)?;
 | 
				
			||||||
        self.has_game.store(true, Ordering::Release);
 | 
					        self.has_game.store(true, Ordering::Release);
 | 
				
			||||||
        self.running.store(true, Ordering::Release);
 | 
					        self.running.store(true, Ordering::Release);
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn stop_second_sim(&mut self) {
 | 
				
			||||||
 | 
					        self.renderers.remove(&SimId::Player2);
 | 
				
			||||||
 | 
					        self.sims.truncate(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn run(&mut self) {
 | 
					    pub fn run(&mut self) {
 | 
				
			||||||
        let mut eye_contents = vec![0u8; 384 * 224 * 2];
 | 
					        let mut eye_contents = vec![0u8; 384 * 224 * 2];
 | 
				
			||||||
        let mut audio_samples = vec![];
 | 
					        let mut audio_samples = vec![];
 | 
				
			||||||
| 
						 | 
					@ -97,15 +138,20 @@ impl Emulator {
 | 
				
			||||||
            let mut idle = true;
 | 
					            let mut idle = true;
 | 
				
			||||||
            if self.running.load(Ordering::Acquire) {
 | 
					            if self.running.load(Ordering::Acquire) {
 | 
				
			||||||
                idle = false;
 | 
					                idle = false;
 | 
				
			||||||
                self.sim.emulate_frame();
 | 
					                Sim::emulate_many(&mut self.sims);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if let Some(renderer) = &mut self.renderer {
 | 
					            for (sim_id, renderer) in self.renderers.iter_mut() {
 | 
				
			||||||
                if self.sim.read_pixels(&mut eye_contents) {
 | 
					                if let Some(sim) = self.sims.get_mut(sim_id.to_index()) {
 | 
				
			||||||
 | 
					                    if sim.read_pixels(&mut eye_contents) {
 | 
				
			||||||
                        idle = false;
 | 
					                        idle = false;
 | 
				
			||||||
                        renderer.render(&eye_contents);
 | 
					                        renderer.render(&eye_contents);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            self.sim.read_samples(&mut audio_samples);
 | 
					            }
 | 
				
			||||||
 | 
					            let weight = 1.0 / self.sims.len() as f32;
 | 
				
			||||||
 | 
					            for sim in self.sims.iter_mut() {
 | 
				
			||||||
 | 
					                sim.read_samples(&mut audio_samples, weight);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if !audio_samples.is_empty() {
 | 
					            if !audio_samples.is_empty() {
 | 
				
			||||||
                idle = false;
 | 
					                idle = false;
 | 
				
			||||||
                self.audio.update(&audio_samples);
 | 
					                self.audio.update(&audio_samples);
 | 
				
			||||||
| 
						 | 
					@ -137,14 +183,22 @@ impl Emulator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn handle_command(&mut self, command: EmulatorCommand) {
 | 
					    fn handle_command(&mut self, command: EmulatorCommand) {
 | 
				
			||||||
        match command {
 | 
					        match command {
 | 
				
			||||||
            EmulatorCommand::SetRenderer(renderer) => {
 | 
					            EmulatorCommand::SetRenderer(sim_id, renderer) => {
 | 
				
			||||||
                self.renderer = Some(renderer);
 | 
					                self.renderers.insert(sim_id, renderer);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            EmulatorCommand::LoadGame(path) => {
 | 
					            EmulatorCommand::LoadGame(sim_id, path) => {
 | 
				
			||||||
                if let Err(error) = self.load_rom(&path) {
 | 
					                if let Err(error) = self.load_rom_from_file(sim_id, &path) {
 | 
				
			||||||
                    eprintln!("error loading rom: {}", error);
 | 
					                    eprintln!("error loading rom: {}", error);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::StartSecondSim(path) => {
 | 
				
			||||||
 | 
					                if let Err(error) = self.start_second_sim(path) {
 | 
				
			||||||
 | 
					                    eprintln!("error starting second sim: {}", error);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::StopSecondSim => {
 | 
				
			||||||
 | 
					                self.stop_second_sim();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            EmulatorCommand::Pause => {
 | 
					            EmulatorCommand::Pause => {
 | 
				
			||||||
                self.running.store(false, Ordering::Release);
 | 
					                self.running.store(false, Ordering::Release);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -154,11 +208,17 @@ impl Emulator {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            EmulatorCommand::Reset => {
 | 
					            EmulatorCommand::Reset => {
 | 
				
			||||||
                self.sim.reset();
 | 
					                for sim in self.sims.iter_mut() {
 | 
				
			||||||
 | 
					                    sim.reset();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if !self.sims.is_empty() {
 | 
				
			||||||
                    self.running.store(true, Ordering::Release);
 | 
					                    self.running.store(true, Ordering::Release);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            EmulatorCommand::SetKeys(keys) => {
 | 
					            }
 | 
				
			||||||
                self.sim.set_keys(keys);
 | 
					            EmulatorCommand::SetKeys(sim_id, keys) => {
 | 
				
			||||||
 | 
					                if let Some(sim) = self.sims.get_mut(sim_id.to_index()) {
 | 
				
			||||||
 | 
					                    sim.set_keys(keys);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -166,12 +226,14 @@ impl Emulator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum EmulatorCommand {
 | 
					pub enum EmulatorCommand {
 | 
				
			||||||
    SetRenderer(GameRenderer),
 | 
					    SetRenderer(SimId, GameRenderer),
 | 
				
			||||||
    LoadGame(PathBuf),
 | 
					    LoadGame(SimId, PathBuf),
 | 
				
			||||||
 | 
					    StartSecondSim(Option<PathBuf>),
 | 
				
			||||||
 | 
					    StopSecondSim,
 | 
				
			||||||
    Pause,
 | 
					    Pause,
 | 
				
			||||||
    Resume,
 | 
					    Resume,
 | 
				
			||||||
    Reset,
 | 
					    Reset,
 | 
				
			||||||
    SetKeys(VBKey),
 | 
					    SetKeys(SimId, VBKey),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,12 @@ enum VBDataType {
 | 
				
			||||||
    F32 = 5,
 | 
					    F32 = 5,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[repr(i32)]
 | 
				
			||||||
 | 
					#[derive(FromPrimitive, ToPrimitive)]
 | 
				
			||||||
 | 
					enum VBOption {
 | 
				
			||||||
 | 
					    PseudoHalt = 0,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bitflags! {
 | 
					bitflags! {
 | 
				
			||||||
    #[repr(transparent)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
					    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
				
			||||||
| 
						 | 
					@ -52,8 +58,8 @@ type OnFrame = extern "C" fn(sim: *mut VB) -> c_int;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[link(name = "vb")]
 | 
					#[link(name = "vb")]
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
    #[link_name = "vbEmulate"]
 | 
					    #[link_name = "vbEmulateEx"]
 | 
				
			||||||
    fn vb_emulate(sim: *mut VB, cycles: *mut u32) -> c_int;
 | 
					    fn vb_emulate_ex(sims: *mut *mut VB, count: c_uint, cycles: *mut u32) -> c_int;
 | 
				
			||||||
    #[link_name = "vbGetCartROM"]
 | 
					    #[link_name = "vbGetCartROM"]
 | 
				
			||||||
    fn vb_get_cart_rom(sim: *mut VB, size: *mut u32) -> *mut c_void;
 | 
					    fn vb_get_cart_rom(sim: *mut VB, size: *mut u32) -> *mut c_void;
 | 
				
			||||||
    #[link_name = "vbGetPixels"]
 | 
					    #[link_name = "vbGetPixels"]
 | 
				
			||||||
| 
						 | 
					@ -81,10 +87,12 @@ extern "C" {
 | 
				
			||||||
    fn vb_reset(sim: *mut VB);
 | 
					    fn vb_reset(sim: *mut VB);
 | 
				
			||||||
    #[link_name = "vbSetCartROM"]
 | 
					    #[link_name = "vbSetCartROM"]
 | 
				
			||||||
    fn vb_set_cart_rom(sim: *mut VB, rom: *mut c_void, size: u32) -> c_int;
 | 
					    fn vb_set_cart_rom(sim: *mut VB, rom: *mut c_void, size: u32) -> c_int;
 | 
				
			||||||
    #[link_name = "vbSetKeys"]
 | 
					 | 
				
			||||||
    fn vb_set_keys(sim: *mut VB, keys: u16) -> u16;
 | 
					 | 
				
			||||||
    #[link_name = "vbSetFrameCallback"]
 | 
					    #[link_name = "vbSetFrameCallback"]
 | 
				
			||||||
    fn vb_set_frame_callback(sim: *mut VB, on_frame: OnFrame);
 | 
					    fn vb_set_frame_callback(sim: *mut VB, on_frame: OnFrame);
 | 
				
			||||||
 | 
					    #[link_name = "vbSetKeys"]
 | 
				
			||||||
 | 
					    fn vb_set_keys(sim: *mut VB, keys: u16) -> u16;
 | 
				
			||||||
 | 
					    #[link_name = "vbSetOption"]
 | 
				
			||||||
 | 
					    fn vb_set_option(sim: *mut VB, key: VBOption, value: c_int);
 | 
				
			||||||
    #[link_name = "vbSetSamples"]
 | 
					    #[link_name = "vbSetSamples"]
 | 
				
			||||||
    fn vb_set_samples(
 | 
					    fn vb_set_samples(
 | 
				
			||||||
        sim: *mut VB,
 | 
					        sim: *mut VB,
 | 
				
			||||||
| 
						 | 
					@ -113,14 +121,15 @@ struct VBState {
 | 
				
			||||||
    frame_seen: bool,
 | 
					    frame_seen: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct CoreVB {
 | 
					#[repr(transparent)]
 | 
				
			||||||
 | 
					pub struct Sim {
 | 
				
			||||||
    sim: *mut VB,
 | 
					    sim: *mut VB,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SAFETY: the memory pointed to by sim is valid
 | 
					// SAFETY: the memory pointed to by sim is valid
 | 
				
			||||||
unsafe impl Send for CoreVB {}
 | 
					unsafe impl Send for Sim {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CoreVB {
 | 
					impl Sim {
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
        // init the VB instance itself
 | 
					        // init the VB instance itself
 | 
				
			||||||
        let size = unsafe { vb_size_of() };
 | 
					        let size = unsafe { vb_size_of() };
 | 
				
			||||||
| 
						 | 
					@ -128,6 +137,7 @@ impl CoreVB {
 | 
				
			||||||
        let memory = vec![0u64; size.div_ceil(4)];
 | 
					        let memory = vec![0u64; size.div_ceil(4)];
 | 
				
			||||||
        let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
 | 
					        let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
 | 
				
			||||||
        unsafe { vb_init(sim) };
 | 
					        unsafe { vb_init(sim) };
 | 
				
			||||||
 | 
					        unsafe { vb_set_option(sim, VBOption::PseudoHalt, 1) };
 | 
				
			||||||
        unsafe { vb_reset(sim) };
 | 
					        unsafe { vb_reset(sim) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // set up userdata
 | 
					        // set up userdata
 | 
				
			||||||
| 
						 | 
					@ -140,7 +150,7 @@ impl CoreVB {
 | 
				
			||||||
        let samples: *mut c_void = Box::into_raw(audio_buffer.into_boxed_slice()).cast();
 | 
					        let samples: *mut c_void = Box::into_raw(audio_buffer.into_boxed_slice()).cast();
 | 
				
			||||||
        unsafe { vb_set_samples(sim, samples, VBDataType::F32, AUDIO_CAPACITY_SAMPLES as u32) };
 | 
					        unsafe { vb_set_samples(sim, samples, VBDataType::F32, AUDIO_CAPACITY_SAMPLES as u32) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        CoreVB { sim }
 | 
					        Sim { sim }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn reset(&mut self) {
 | 
					    pub fn reset(&mut self) {
 | 
				
			||||||
| 
						 | 
					@ -162,6 +172,17 @@ impl CoreVB {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn clone_rom(&self) -> Option<Vec<u8>> {
 | 
				
			||||||
 | 
					        let mut size = 0;
 | 
				
			||||||
 | 
					        let rom = unsafe { vb_get_cart_rom(self.sim, &mut size) };
 | 
				
			||||||
 | 
					        if rom.is_null() {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // SAFETY: rom definitely points to a valid array of `size` bytes
 | 
				
			||||||
 | 
					        let slice: &[u8] = unsafe { slice::from_raw_parts(rom.cast(), size as usize) };
 | 
				
			||||||
 | 
					        Some(slice.to_vec())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn unload_rom(&mut self) -> Option<Vec<u8>> {
 | 
					    fn unload_rom(&mut self) -> Option<Vec<u8>> {
 | 
				
			||||||
        let mut size = 0;
 | 
					        let mut size = 0;
 | 
				
			||||||
        let rom = unsafe { vb_get_cart_rom(self.sim, &mut size) };
 | 
					        let rom = unsafe { vb_get_cart_rom(self.sim, &mut size) };
 | 
				
			||||||
| 
						 | 
					@ -173,9 +194,11 @@ impl CoreVB {
 | 
				
			||||||
        Some(vec)
 | 
					        Some(vec)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn emulate_frame(&mut self) {
 | 
					    pub fn emulate_many(sims: &mut [Sim]) {
 | 
				
			||||||
        let mut cycles = 20_000_000;
 | 
					        let mut cycles = 20_000_000;
 | 
				
			||||||
        unsafe { vb_emulate(self.sim, &mut cycles) };
 | 
					        let count = sims.len() as c_uint;
 | 
				
			||||||
 | 
					        let sims = sims.as_mut_ptr().cast();
 | 
				
			||||||
 | 
					        unsafe { vb_emulate_ex(sims, count, &mut cycles) };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn read_pixels(&mut self, buffers: &mut [u8]) -> bool {
 | 
					    pub fn read_pixels(&mut self, buffers: &mut [u8]) -> bool {
 | 
				
			||||||
| 
						 | 
					@ -203,14 +226,17 @@ impl CoreVB {
 | 
				
			||||||
        true
 | 
					        true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn read_samples(&mut self, samples: &mut Vec<f32>) {
 | 
					    pub fn read_samples(&mut self, samples: &mut Vec<f32>, weight: f32) {
 | 
				
			||||||
        let mut position = 0;
 | 
					        let mut position = 0;
 | 
				
			||||||
        let ptr =
 | 
					        let ptr =
 | 
				
			||||||
            unsafe { vb_get_samples(self.sim, ptr::null_mut(), ptr::null_mut(), &mut position) };
 | 
					            unsafe { vb_get_samples(self.sim, ptr::null_mut(), ptr::null_mut(), &mut position) };
 | 
				
			||||||
        // SAFETY: position is an offset in a buffer of (f32, f32). so, position * 2 is an offset in a buffer of f32.
 | 
					        // SAFETY: position is an offset in a buffer of (f32, f32). so, position * 2 is an offset in a buffer of f32.
 | 
				
			||||||
        let read_samples: &mut [f32] =
 | 
					        let read_samples: &[f32] =
 | 
				
			||||||
            unsafe { slice::from_raw_parts_mut(ptr.cast(), position as usize * 2) };
 | 
					            unsafe { slice::from_raw_parts(ptr.cast(), position as usize * 2) };
 | 
				
			||||||
        samples.extend_from_slice(read_samples);
 | 
					        samples.resize(read_samples.len(), 0.0);
 | 
				
			||||||
 | 
					        for (index, sample) in read_samples.iter().enumerate() {
 | 
				
			||||||
 | 
					            samples[index] += sample * weight;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            vb_set_samples(
 | 
					            vb_set_samples(
 | 
				
			||||||
| 
						 | 
					@ -227,7 +253,7 @@ impl CoreVB {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Drop for CoreVB {
 | 
					impl Drop for Sim {
 | 
				
			||||||
    fn drop(&mut self) {
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
        let ptr =
 | 
					        let ptr =
 | 
				
			||||||
            unsafe { vb_get_samples(self.sim, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()) };
 | 
					            unsafe { vb_get_samples(self.sim, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()) };
 | 
				
			||||||
							
								
								
									
										81
									
								
								src/input.rs
								
								
								
								
							
							
						
						
									
										81
									
								
								src/input.rs
								
								
								
								
							| 
						 | 
					@ -2,15 +2,14 @@ use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use winit::{
 | 
					use winit::{
 | 
				
			||||||
    event::KeyEvent,
 | 
					    event::KeyEvent,
 | 
				
			||||||
    keyboard::{Key, NamedKey},
 | 
					    keyboard::{KeyCode, PhysicalKey},
 | 
				
			||||||
    platform::modifier_supplement::KeyEventExtModifierSupplement,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::shrooms_vb_core::VBKey;
 | 
					use crate::emulator::{SimId, VBKey};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct InputMapper {
 | 
					pub struct InputMapper {
 | 
				
			||||||
    vb_bindings: HashMap<VBKey, Key>,
 | 
					    vb_bindings: HashMap<SimId, HashMap<VBKey, PhysicalKey>>,
 | 
				
			||||||
    key_bindings: HashMap<Key, VBKey>,
 | 
					    key_bindings: HashMap<PhysicalKey, (SimId, VBKey)>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl InputMapper {
 | 
					impl InputMapper {
 | 
				
			||||||
| 
						 | 
					@ -19,30 +18,48 @@ impl InputMapper {
 | 
				
			||||||
            vb_bindings: HashMap::new(),
 | 
					            vb_bindings: HashMap::new(),
 | 
				
			||||||
            key_bindings: HashMap::new(),
 | 
					            key_bindings: HashMap::new(),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        mapper.bind_key(VBKey::SEL, Key::Character("a".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::SEL, PhysicalKey::Code(KeyCode::KeyA));
 | 
				
			||||||
        mapper.bind_key(VBKey::STA, Key::Character("s".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::STA, PhysicalKey::Code(KeyCode::KeyS));
 | 
				
			||||||
        mapper.bind_key(VBKey::B, Key::Character("d".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::B, PhysicalKey::Code(KeyCode::KeyD));
 | 
				
			||||||
        mapper.bind_key(VBKey::A, Key::Character("f".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::A, PhysicalKey::Code(KeyCode::KeyF));
 | 
				
			||||||
        mapper.bind_key(VBKey::LT, Key::Character("e".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::LT, PhysicalKey::Code(KeyCode::KeyE));
 | 
				
			||||||
        mapper.bind_key(VBKey::RT, Key::Character("r".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::RT, PhysicalKey::Code(KeyCode::KeyR));
 | 
				
			||||||
        mapper.bind_key(VBKey::RU, Key::Character("i".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::RU, PhysicalKey::Code(KeyCode::KeyI));
 | 
				
			||||||
        mapper.bind_key(VBKey::RL, Key::Character("j".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::RL, PhysicalKey::Code(KeyCode::KeyJ));
 | 
				
			||||||
        mapper.bind_key(VBKey::RD, Key::Character("k".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::RD, PhysicalKey::Code(KeyCode::KeyK));
 | 
				
			||||||
        mapper.bind_key(VBKey::RR, Key::Character("l".into()));
 | 
					        mapper.bind_key(SimId::Player1, VBKey::RR, PhysicalKey::Code(KeyCode::KeyL));
 | 
				
			||||||
        mapper.bind_key(VBKey::LU, Key::Named(NamedKey::ArrowUp));
 | 
					        mapper.bind_key(
 | 
				
			||||||
        mapper.bind_key(VBKey::LL, Key::Named(NamedKey::ArrowLeft));
 | 
					            SimId::Player1,
 | 
				
			||||||
        mapper.bind_key(VBKey::LD, Key::Named(NamedKey::ArrowDown));
 | 
					            VBKey::LU,
 | 
				
			||||||
        mapper.bind_key(VBKey::LR, Key::Named(NamedKey::ArrowRight));
 | 
					            PhysicalKey::Code(KeyCode::ArrowUp),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        mapper.bind_key(
 | 
				
			||||||
 | 
					            SimId::Player1,
 | 
				
			||||||
 | 
					            VBKey::LL,
 | 
				
			||||||
 | 
					            PhysicalKey::Code(KeyCode::ArrowLeft),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        mapper.bind_key(
 | 
				
			||||||
 | 
					            SimId::Player1,
 | 
				
			||||||
 | 
					            VBKey::LD,
 | 
				
			||||||
 | 
					            PhysicalKey::Code(KeyCode::ArrowDown),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        mapper.bind_key(
 | 
				
			||||||
 | 
					            SimId::Player1,
 | 
				
			||||||
 | 
					            VBKey::LR,
 | 
				
			||||||
 | 
					            PhysicalKey::Code(KeyCode::ArrowRight),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        mapper
 | 
					        mapper
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn binding_names(&self) -> HashMap<VBKey, String> {
 | 
					    pub fn binding_names(&self, sim_id: &SimId) -> HashMap<VBKey, String> {
 | 
				
			||||||
        self.vb_bindings
 | 
					        let Some(bindings) = self.vb_bindings.get(sim_id) else {
 | 
				
			||||||
 | 
					            return HashMap::new();
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        bindings
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .map(|(k, v)| {
 | 
					            .map(|(k, v)| {
 | 
				
			||||||
                let name = match v {
 | 
					                let name = match v {
 | 
				
			||||||
                    Key::Character(char) => char.to_string(),
 | 
					                    PhysicalKey::Code(code) => format!("{code:?}"),
 | 
				
			||||||
                    Key::Named(key) => format!("{:?}", key),
 | 
					 | 
				
			||||||
                    k => format!("{:?}", k),
 | 
					                    k => format!("{:?}", k),
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                (*k, name)
 | 
					                (*k, name)
 | 
				
			||||||
| 
						 | 
					@ -50,22 +67,22 @@ impl InputMapper {
 | 
				
			||||||
            .collect()
 | 
					            .collect()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn bind_key(&mut self, vb: VBKey, key: Key) {
 | 
					    pub fn bind_key(&mut self, sim_id: SimId, vb: VBKey, key: PhysicalKey) {
 | 
				
			||||||
        if let Some(old) = self.vb_bindings.insert(vb, key.clone()) {
 | 
					        let vb_bindings = self.vb_bindings.entry(sim_id).or_default();
 | 
				
			||||||
 | 
					        if let Some(old) = vb_bindings.insert(vb, key) {
 | 
				
			||||||
            self.key_bindings.remove(&old);
 | 
					            self.key_bindings.remove(&old);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.key_bindings.insert(key, vb);
 | 
					        self.key_bindings.insert(key, (sim_id, vb));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn clear_binding(&mut self, vb: VBKey) {
 | 
					    pub fn clear_binding(&mut self, sim_id: SimId, vb: VBKey) {
 | 
				
			||||||
        if let Some(old) = self.vb_bindings.remove(&vb) {
 | 
					        let vb_bindings = self.vb_bindings.entry(sim_id).or_default();
 | 
				
			||||||
 | 
					        if let Some(old) = vb_bindings.remove(&vb) {
 | 
				
			||||||
            self.key_bindings.remove(&old);
 | 
					            self.key_bindings.remove(&old);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn key_event(&self, event: &KeyEvent) -> Option<VBKey> {
 | 
					    pub fn key_event(&self, event: &KeyEvent) -> Option<(SimId, VBKey)> {
 | 
				
			||||||
        self.key_bindings
 | 
					        self.key_bindings.get(&event.physical_key).copied()
 | 
				
			||||||
            .get(&event.key_without_modifiers())
 | 
					 | 
				
			||||||
            .cloned()
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,6 @@ mod controller;
 | 
				
			||||||
mod emulator;
 | 
					mod emulator;
 | 
				
			||||||
mod input;
 | 
					mod input;
 | 
				
			||||||
mod renderer;
 | 
					mod renderer;
 | 
				
			||||||
mod shrooms_vb_core;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Parser)]
 | 
					#[derive(Parser)]
 | 
				
			||||||
struct Args {
 | 
					struct Args {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue