rewrite it in rust #1
			
				
			
		
		
		
	
							
								
								
									
										62
									
								
								src/app.rs
								
								
								
								
							
							
						
						
									
										62
									
								
								src/app.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,4 +1,8 @@
 | 
			
		|||
use std::{collections::HashMap, fmt::Debug};
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::HashMap,
 | 
			
		||||
    fmt::Debug,
 | 
			
		||||
    sync::{Arc, RwLock},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use game::GameWindow;
 | 
			
		||||
use winit::{
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +12,11 @@ use winit::{
 | 
			
		|||
    window::WindowId,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::emulator::EmulatorClient;
 | 
			
		||||
use crate::{
 | 
			
		||||
    controller::ControllerState,
 | 
			
		||||
    emulator::{EmulatorClient, EmulatorCommand},
 | 
			
		||||
    input::InputMapper,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod common;
 | 
			
		||||
mod game;
 | 
			
		||||
| 
						 | 
				
			
			@ -16,32 +24,35 @@ mod input;
 | 
			
		|||
 | 
			
		||||
pub struct App {
 | 
			
		||||
    windows: HashMap<WindowId, Box<dyn AppWindow>>,
 | 
			
		||||
    focused_window: Option<WindowId>,
 | 
			
		||||
    client: EmulatorClient,
 | 
			
		||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
			
		||||
    controller: ControllerState,
 | 
			
		||||
    proxy: EventLoopProxy<UserEvent>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl App {
 | 
			
		||||
    pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>) -> Self {
 | 
			
		||||
        let input_mapper = Arc::new(RwLock::new(InputMapper::new()));
 | 
			
		||||
        let controller = ControllerState::new(input_mapper.clone());
 | 
			
		||||
        Self {
 | 
			
		||||
            windows: HashMap::new(),
 | 
			
		||||
            focused_window: None,
 | 
			
		||||
            client,
 | 
			
		||||
            input_mapper,
 | 
			
		||||
            controller,
 | 
			
		||||
            proxy,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn active_window(&mut self) -> Option<&mut Box<dyn AppWindow>> {
 | 
			
		||||
        let active_window = self.focused_window?;
 | 
			
		||||
        self.windows.get_mut(&active_window)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ApplicationHandler<UserEvent> for App {
 | 
			
		||||
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
 | 
			
		||||
        let mut window = GameWindow::new(event_loop, self.client.clone(), self.proxy.clone());
 | 
			
		||||
        let mut window = GameWindow::new(
 | 
			
		||||
            event_loop,
 | 
			
		||||
            self.client.clone(),
 | 
			
		||||
            self.input_mapper.clone(),
 | 
			
		||||
            self.proxy.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        window.init();
 | 
			
		||||
        self.focused_window = Some(window.id());
 | 
			
		||||
        self.windows.insert(window.id(), Box::new(window));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,11 +62,10 @@ impl ApplicationHandler<UserEvent> for App {
 | 
			
		|||
        window_id: WindowId,
 | 
			
		||||
        event: WindowEvent,
 | 
			
		||||
    ) {
 | 
			
		||||
        if let WindowEvent::Focused(focused) = event {
 | 
			
		||||
            if focused {
 | 
			
		||||
                self.focused_window = Some(window_id);
 | 
			
		||||
            } else {
 | 
			
		||||
                self.focused_window = None;
 | 
			
		||||
        if let WindowEvent::KeyboardInput { event, .. } = &event {
 | 
			
		||||
            if self.controller.key_event(event) {
 | 
			
		||||
                self.client
 | 
			
		||||
                    .send_command(EmulatorCommand::SetKeys(self.controller.pressed()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let Some(window) = self.windows.get_mut(&window_id) else {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,17 +92,21 @@ impl ApplicationHandler<UserEvent> for App {
 | 
			
		|||
        device_id: winit::event::DeviceId,
 | 
			
		||||
        event: winit::event::DeviceEvent,
 | 
			
		||||
    ) {
 | 
			
		||||
        let Some(window) = self.active_window() else {
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
        window.handle_event(event_loop, &Event::DeviceEvent { device_id, event });
 | 
			
		||||
        for window in self.windows.values_mut() {
 | 
			
		||||
            window.handle_event(
 | 
			
		||||
                event_loop,
 | 
			
		||||
                &Event::DeviceEvent {
 | 
			
		||||
                    device_id,
 | 
			
		||||
                    event: event.clone(),
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
 | 
			
		||||
        let Some(window) = self.active_window() else {
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
        window.handle_event(event_loop, &Event::AboutToWait);
 | 
			
		||||
        for window in self.windows.values_mut() {
 | 
			
		||||
            window.handle_event(event_loop, &Event::AboutToWait);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -235,14 +235,23 @@ impl<'a> Drop for ContextLock<'a> {
 | 
			
		|||
 | 
			
		||||
pub trait UiExt {
 | 
			
		||||
    fn fullscreen_window(&self) -> Option<WindowToken<'_>>;
 | 
			
		||||
    fn right_align_text<T: AsRef<str>>(&self, text: T, space: f32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UiExt for imgui::Ui {
 | 
			
		||||
    fn fullscreen_window(&self) -> Option<WindowToken<'_>> {
 | 
			
		||||
        self.window("fullscreen")
 | 
			
		||||
            .position([0.0, 0.0], imgui::Condition::Always)
 | 
			
		||||
            .size(self.window_size(), imgui::Condition::Always)
 | 
			
		||||
            .size(self.io().display_size, imgui::Condition::Always)
 | 
			
		||||
            .flags(imgui::WindowFlags::NO_DECORATION)
 | 
			
		||||
            .begin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn right_align_text<T: AsRef<str>>(&self, text: T, space: f32) {
 | 
			
		||||
        let width = self.calc_text_size(text.as_ref())[0];
 | 
			
		||||
        let [left, y] = self.cursor_pos();
 | 
			
		||||
        let right = left + space;
 | 
			
		||||
        self.set_cursor_pos([right - width, y]);
 | 
			
		||||
        self.text(text);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,18 @@
 | 
			
		|||
use std::{sync::Arc, time::Instant};
 | 
			
		||||
use std::{
 | 
			
		||||
    sync::{Arc, RwLock},
 | 
			
		||||
    time::Instant,
 | 
			
		||||
};
 | 
			
		||||
use wgpu::util::DeviceExt as _;
 | 
			
		||||
use winit::{
 | 
			
		||||
    dpi::LogicalSize,
 | 
			
		||||
    event::{Event, KeyEvent, WindowEvent},
 | 
			
		||||
    event::{Event, WindowEvent},
 | 
			
		||||
    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
			
		||||
    window::WindowId,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    controller::ControllerState,
 | 
			
		||||
    emulator::{EmulatorClient, EmulatorCommand},
 | 
			
		||||
    input::InputMapper,
 | 
			
		||||
    renderer::GameRenderer,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +28,7 @@ pub struct GameWindow {
 | 
			
		|||
    pipeline: wgpu::RenderPipeline,
 | 
			
		||||
    bind_group: wgpu::BindGroup,
 | 
			
		||||
    client: EmulatorClient,
 | 
			
		||||
    controller: ControllerState,
 | 
			
		||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
			
		||||
    proxy: EventLoopProxy<UserEvent>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +36,7 @@ impl GameWindow {
 | 
			
		|||
    pub fn new(
 | 
			
		||||
        event_loop: &ActiveEventLoop,
 | 
			
		||||
        client: EmulatorClient,
 | 
			
		||||
        input_mapper: Arc<RwLock<InputMapper>>,
 | 
			
		||||
        proxy: EventLoopProxy<UserEvent>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let window = WindowStateBuilder::new(event_loop)
 | 
			
		||||
| 
						 | 
				
			
			@ -153,15 +157,13 @@ impl GameWindow {
 | 
			
		|||
            cache: None,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let controller = ControllerState::new();
 | 
			
		||||
        client.send_command(EmulatorCommand::SetKeys(controller.pressed()));
 | 
			
		||||
        Self {
 | 
			
		||||
            window,
 | 
			
		||||
            imgui: None,
 | 
			
		||||
            pipeline: render_pipeline,
 | 
			
		||||
            bind_group,
 | 
			
		||||
            client,
 | 
			
		||||
            controller,
 | 
			
		||||
            input_mapper,
 | 
			
		||||
            proxy,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +172,7 @@ impl GameWindow {
 | 
			
		|||
        let window = &mut self.window;
 | 
			
		||||
        let imgui = self.imgui.as_mut().unwrap();
 | 
			
		||||
        let mut context = imgui.context.lock().unwrap();
 | 
			
		||||
        let mut new_size = None;
 | 
			
		||||
 | 
			
		||||
        let now = Instant::now();
 | 
			
		||||
        context.io_mut().update_delta_time(now - imgui.last_frame);
 | 
			
		||||
| 
						 | 
				
			
			@ -226,13 +229,18 @@ impl GameWindow {
 | 
			
		|||
                    if ui.menu_item_config(label).selected(selected).build() {
 | 
			
		||||
                        if let Some(size) = window.window.request_inner_size(dims) {
 | 
			
		||||
                            window.handle_resize(&size);
 | 
			
		||||
                            new_size = Some(size);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            ui.menu("Input", || {
 | 
			
		||||
                if ui.menu_item("Map Input") {
 | 
			
		||||
                    let input_window = Box::new(InputWindow::new(event_loop, self.proxy.clone()));
 | 
			
		||||
                if ui.menu_item("Bind Inputs") {
 | 
			
		||||
                    let input_window = Box::new(InputWindow::new(
 | 
			
		||||
                        event_loop,
 | 
			
		||||
                        self.input_mapper.clone(),
 | 
			
		||||
                        self.proxy.clone(),
 | 
			
		||||
                    ));
 | 
			
		||||
                    self.proxy
 | 
			
		||||
                        .send_event(UserEvent::OpenWindow(input_window))
 | 
			
		||||
                        .unwrap();
 | 
			
		||||
| 
						 | 
				
			
			@ -287,17 +295,21 @@ impl GameWindow {
 | 
			
		|||
 | 
			
		||||
        drop(rpass);
 | 
			
		||||
 | 
			
		||||
        if let Some(size) = new_size {
 | 
			
		||||
            imgui.platform.handle_event::<UserEvent>(
 | 
			
		||||
                context.io_mut(),
 | 
			
		||||
                &window.window,
 | 
			
		||||
                &Event::WindowEvent {
 | 
			
		||||
                    window_id: window.window.id(),
 | 
			
		||||
                    event: WindowEvent::Resized(size),
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        window.queue.submit(Some(encoder.finish()));
 | 
			
		||||
 | 
			
		||||
        frame.present();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_key_event(&mut self, event: &KeyEvent) {
 | 
			
		||||
        if self.controller.key_event(event) {
 | 
			
		||||
            self.client
 | 
			
		||||
                .send_command(EmulatorCommand::SetKeys(self.controller.pressed()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AppWindow for GameWindow {
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +319,7 @@ impl AppWindow for GameWindow {
 | 
			
		|||
 | 
			
		||||
    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>) {
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +327,6 @@ impl AppWindow for GameWindow {
 | 
			
		|||
            Event::WindowEvent { event, .. } => match event {
 | 
			
		||||
                WindowEvent::Resized(size) => self.window.handle_resize(size),
 | 
			
		||||
                WindowEvent::CloseRequested => event_loop.exit(),
 | 
			
		||||
                WindowEvent::KeyboardInput { event, .. } => self.handle_key_event(event),
 | 
			
		||||
                WindowEvent::RedrawRequested => self.draw(event_loop),
 | 
			
		||||
                _ => (),
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										105
									
								
								src/app/input.rs
								
								
								
								
							
							
						
						
									
										105
									
								
								src/app/input.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,29 +1,62 @@
 | 
			
		|||
use std::time::Instant;
 | 
			
		||||
 | 
			
		||||
use winit::{
 | 
			
		||||
    event::{Event, WindowEvent},
 | 
			
		||||
    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
			
		||||
use std::{
 | 
			
		||||
    sync::{Arc, RwLock},
 | 
			
		||||
    time::Instant,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use winit::{
 | 
			
		||||
    dpi::LogicalSize,
 | 
			
		||||
    event::{Event, KeyEvent, WindowEvent},
 | 
			
		||||
    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
			
		||||
    platform::modifier_supplement::KeyEventExtModifierSupplement,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{input::InputMapper, shrooms_vb_core::VBKey};
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    common::{ImguiState, UiExt as _, WindowState, WindowStateBuilder},
 | 
			
		||||
    common::{ImguiState, UiExt, WindowState, WindowStateBuilder},
 | 
			
		||||
    AppWindow, UserEvent,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub struct InputWindow {
 | 
			
		||||
    window: WindowState,
 | 
			
		||||
    imgui: Option<ImguiState>,
 | 
			
		||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
			
		||||
    proxy: EventLoopProxy<UserEvent>,
 | 
			
		||||
    now_binding: Option<VBKey>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const KEY_NAMES: [(VBKey, &str); 14] = [
 | 
			
		||||
    (VBKey::LU, "Up"),
 | 
			
		||||
    (VBKey::LD, "Down"),
 | 
			
		||||
    (VBKey::LL, "Left"),
 | 
			
		||||
    (VBKey::LR, "Right"),
 | 
			
		||||
    (VBKey::SEL, "Select"),
 | 
			
		||||
    (VBKey::STA, "Start"),
 | 
			
		||||
    (VBKey::B, "B"),
 | 
			
		||||
    (VBKey::A, "A"),
 | 
			
		||||
    (VBKey::LT, "L-Trigger"),
 | 
			
		||||
    (VBKey::RT, "R-Trigger"),
 | 
			
		||||
    (VBKey::RU, "R-Up"),
 | 
			
		||||
    (VBKey::RD, "R-Down"),
 | 
			
		||||
    (VBKey::RL, "R-Left"),
 | 
			
		||||
    (VBKey::RR, "R-Right"),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
impl InputWindow {
 | 
			
		||||
    pub fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<UserEvent>) -> Self {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        event_loop: &ActiveEventLoop,
 | 
			
		||||
        input_mapper: Arc<RwLock<InputMapper>>,
 | 
			
		||||
        proxy: EventLoopProxy<UserEvent>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let window = WindowStateBuilder::new(event_loop)
 | 
			
		||||
            .with_title("Map Inputs")
 | 
			
		||||
            .with_title("Bind Inputs")
 | 
			
		||||
            .with_inner_size(LogicalSize::new(600, 400))
 | 
			
		||||
            .build();
 | 
			
		||||
        Self {
 | 
			
		||||
            window,
 | 
			
		||||
            imgui: None,
 | 
			
		||||
            input_mapper,
 | 
			
		||||
            now_binding: None,
 | 
			
		||||
            proxy,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -50,17 +83,50 @@ impl InputWindow {
 | 
			
		|||
            .expect("Failed to prepare frame");
 | 
			
		||||
        let ui = context.new_frame();
 | 
			
		||||
 | 
			
		||||
        if let Some(window) = ui.fullscreen_window() {
 | 
			
		||||
        let mut render_key_bindings = || {
 | 
			
		||||
            if let Some(table) = ui.begin_table("controls", 2) {
 | 
			
		||||
                let binding_names = {
 | 
			
		||||
                    let mapper = self.input_mapper.read().unwrap();
 | 
			
		||||
                    mapper.binding_names()
 | 
			
		||||
                };
 | 
			
		||||
                ui.table_next_row();
 | 
			
		||||
 | 
			
		||||
                ui.table_next_column();
 | 
			
		||||
                ui.text("Key");
 | 
			
		||||
                for (key, name) in KEY_NAMES {
 | 
			
		||||
                    let binding = binding_names.get(&key).map(|s| s.as_str());
 | 
			
		||||
                    ui.table_next_column();
 | 
			
		||||
                    let [space, _] = ui.content_region_avail();
 | 
			
		||||
                    ui.group(|| {
 | 
			
		||||
                        ui.right_align_text(name, space * 0.20);
 | 
			
		||||
                        ui.same_line();
 | 
			
		||||
                        let label_text = if self.now_binding == Some(key) {
 | 
			
		||||
                            "Press any input"
 | 
			
		||||
                        } else {
 | 
			
		||||
                            binding.unwrap_or("")
 | 
			
		||||
                        };
 | 
			
		||||
                        let label = format!("{}##{}", label_text, name);
 | 
			
		||||
                        if ui.button_with_size(label, [space * 0.60, 0.0]) {
 | 
			
		||||
                            self.now_binding = Some(key);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    ui.same_line();
 | 
			
		||||
                    if ui.button(format!("Clear##{name}")) {
 | 
			
		||||
                        let mut mapper = self.input_mapper.write().unwrap();
 | 
			
		||||
                        mapper.clear_binding(key);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ui.table_next_column();
 | 
			
		||||
                ui.text("Value");
 | 
			
		||||
                table.end();
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if let Some(window) = ui.fullscreen_window() {
 | 
			
		||||
            if let Some(tabs) = ui.tab_bar("tabs") {
 | 
			
		||||
                if let Some(tab) = ui.tab_item("Player 1") {
 | 
			
		||||
                    render_key_bindings();
 | 
			
		||||
                    tab.end();
 | 
			
		||||
                }
 | 
			
		||||
                tabs.end();
 | 
			
		||||
            }
 | 
			
		||||
            window.end();
 | 
			
		||||
        }
 | 
			
		||||
        let mut encoder: wgpu::CommandEncoder = window
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +168,17 @@ impl InputWindow {
 | 
			
		|||
 | 
			
		||||
        frame.present();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn try_bind_key(&mut self, event: &KeyEvent) {
 | 
			
		||||
        if !event.state.is_pressed() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let Some(vb) = self.now_binding.take() else {
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
        let mut mapper = self.input_mapper.write().unwrap();
 | 
			
		||||
        mapper.bind_key(vb, event.key_without_modifiers());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AppWindow for InputWindow {
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +188,7 @@ impl AppWindow for InputWindow {
 | 
			
		|||
 | 
			
		||||
    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>) {
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +199,7 @@ impl AppWindow for InputWindow {
 | 
			
		|||
                    .proxy
 | 
			
		||||
                    .send_event(UserEvent::CloseWindow(self.id()))
 | 
			
		||||
                    .unwrap(),
 | 
			
		||||
                WindowEvent::KeyboardInput { event, .. } => self.try_bind_key(event),
 | 
			
		||||
                WindowEvent::RedrawRequested => self.draw(),
 | 
			
		||||
                _ => (),
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,18 @@
 | 
			
		|||
use winit::{
 | 
			
		||||
    event::{ElementState, KeyEvent},
 | 
			
		||||
    keyboard::{Key, NamedKey},
 | 
			
		||||
};
 | 
			
		||||
use std::sync::{Arc, RwLock};
 | 
			
		||||
 | 
			
		||||
use crate::shrooms_vb_core::VBKey;
 | 
			
		||||
use winit::event::{ElementState, KeyEvent};
 | 
			
		||||
 | 
			
		||||
use crate::{input::InputMapper, shrooms_vb_core::VBKey};
 | 
			
		||||
 | 
			
		||||
pub struct ControllerState {
 | 
			
		||||
    input_mapper: Arc<RwLock<InputMapper>>,
 | 
			
		||||
    pressed: VBKey,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ControllerState {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
    pub fn new(input_mapper: Arc<RwLock<InputMapper>>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            input_mapper,
 | 
			
		||||
            pressed: VBKey::SGN,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +22,7 @@ impl ControllerState {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn key_event(&mut self, event: &KeyEvent) -> bool {
 | 
			
		||||
        let Some(input) = self.key_to_input(&event.logical_key) else {
 | 
			
		||||
        let Some(input) = self.key_event_to_input(event) else {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
        match event.state {
 | 
			
		||||
| 
						 | 
				
			
			@ -42,23 +43,8 @@ impl ControllerState {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn key_to_input(&self, key: &Key) -> Option<VBKey> {
 | 
			
		||||
        match key.as_ref() {
 | 
			
		||||
            Key::Character("a") => Some(VBKey::SEL),
 | 
			
		||||
            Key::Character("s") => Some(VBKey::STA),
 | 
			
		||||
            Key::Character("d") => Some(VBKey::B),
 | 
			
		||||
            Key::Character("f") => Some(VBKey::A),
 | 
			
		||||
            Key::Character("e") => Some(VBKey::LT),
 | 
			
		||||
            Key::Character("r") => Some(VBKey::RT),
 | 
			
		||||
            Key::Character("i") => Some(VBKey::RU),
 | 
			
		||||
            Key::Character("j") => Some(VBKey::RL),
 | 
			
		||||
            Key::Character("k") => Some(VBKey::RD),
 | 
			
		||||
            Key::Character("l") => Some(VBKey::RR),
 | 
			
		||||
            Key::Named(NamedKey::ArrowUp) => Some(VBKey::LU),
 | 
			
		||||
            Key::Named(NamedKey::ArrowLeft) => Some(VBKey::LL),
 | 
			
		||||
            Key::Named(NamedKey::ArrowDown) => Some(VBKey::LD),
 | 
			
		||||
            Key::Named(NamedKey::ArrowRight) => Some(VBKey::LR),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    fn key_event_to_input(&self, event: &KeyEvent) -> Option<VBKey> {
 | 
			
		||||
        let mapper = self.input_mapper.read().unwrap();
 | 
			
		||||
        mapper.key_event(event)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use winit::{
 | 
			
		||||
    event::KeyEvent,
 | 
			
		||||
    keyboard::{Key, NamedKey},
 | 
			
		||||
    platform::modifier_supplement::KeyEventExtModifierSupplement,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::shrooms_vb_core::VBKey;
 | 
			
		||||
 | 
			
		||||
pub struct InputMapper {
 | 
			
		||||
    vb_bindings: HashMap<VBKey, Key>,
 | 
			
		||||
    key_bindings: HashMap<Key, VBKey>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InputMapper {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        let mut mapper = Self {
 | 
			
		||||
            vb_bindings: HashMap::new(),
 | 
			
		||||
            key_bindings: HashMap::new(),
 | 
			
		||||
        };
 | 
			
		||||
        mapper.bind_key(VBKey::SEL, Key::Character("a".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::STA, Key::Character("s".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::B, Key::Character("d".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::A, Key::Character("f".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::LT, Key::Character("e".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::RT, Key::Character("r".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::RU, Key::Character("i".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::RL, Key::Character("j".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::RD, Key::Character("k".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::RR, Key::Character("l".into()));
 | 
			
		||||
        mapper.bind_key(VBKey::LU, Key::Named(NamedKey::ArrowUp));
 | 
			
		||||
        mapper.bind_key(VBKey::LL, Key::Named(NamedKey::ArrowLeft));
 | 
			
		||||
        mapper.bind_key(VBKey::LD, Key::Named(NamedKey::ArrowDown));
 | 
			
		||||
        mapper.bind_key(VBKey::LR, Key::Named(NamedKey::ArrowRight));
 | 
			
		||||
        mapper
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn binding_names(&self) -> HashMap<VBKey, String> {
 | 
			
		||||
        self.vb_bindings
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|(k, v)| {
 | 
			
		||||
                let name = match v {
 | 
			
		||||
                    Key::Character(char) => char.to_string(),
 | 
			
		||||
                    Key::Named(key) => format!("{:?}", key),
 | 
			
		||||
                    k => format!("{:?}", k),
 | 
			
		||||
                };
 | 
			
		||||
                (*k, name)
 | 
			
		||||
            })
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_key(&mut self, vb: VBKey, key: Key) {
 | 
			
		||||
        if let Some(old) = self.vb_bindings.insert(vb, key.clone()) {
 | 
			
		||||
            self.key_bindings.remove(&old);
 | 
			
		||||
        }
 | 
			
		||||
        self.key_bindings.insert(key, vb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear_binding(&mut self, vb: VBKey) {
 | 
			
		||||
        if let Some(old) = self.vb_bindings.remove(&vb) {
 | 
			
		||||
            self.key_bindings.remove(&old);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn key_event(&self, event: &KeyEvent) -> Option<VBKey> {
 | 
			
		||||
        self.key_bindings
 | 
			
		||||
            .get(&event.key_without_modifiers())
 | 
			
		||||
            .cloned()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ mod app;
 | 
			
		|||
mod audio;
 | 
			
		||||
mod controller;
 | 
			
		||||
mod emulator;
 | 
			
		||||
mod input;
 | 
			
		||||
mod renderer;
 | 
			
		||||
mod shrooms_vb_core;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,8 @@ enum VBDataType {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bitflags! {
 | 
			
		||||
    #[derive(Clone, Copy, Debug)]
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
    pub struct VBKey: u16 {
 | 
			
		||||
        const PWR = 0x0001;
 | 
			
		||||
        const SGN = 0x0002;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue