Functional input binding
This commit is contained in:
		
							parent
							
								
									a69247dd33
								
							
						
					
					
						commit
						5cb36d0bcc
					
				
							
								
								
									
										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 game::GameWindow;
 | 
				
			||||||
use winit::{
 | 
					use winit::{
 | 
				
			||||||
| 
						 | 
					@ -8,7 +12,11 @@ use winit::{
 | 
				
			||||||
    window::WindowId,
 | 
					    window::WindowId,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::EmulatorClient;
 | 
					use crate::{
 | 
				
			||||||
 | 
					    controller::ControllerState,
 | 
				
			||||||
 | 
					    emulator::{EmulatorClient, EmulatorCommand},
 | 
				
			||||||
 | 
					    input::InputMapper,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod common;
 | 
					mod common;
 | 
				
			||||||
mod game;
 | 
					mod game;
 | 
				
			||||||
| 
						 | 
					@ -16,32 +24,35 @@ mod input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct App {
 | 
					pub struct App {
 | 
				
			||||||
    windows: HashMap<WindowId, Box<dyn AppWindow>>,
 | 
					    windows: HashMap<WindowId, Box<dyn AppWindow>>,
 | 
				
			||||||
    focused_window: Option<WindowId>,
 | 
					 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
 | 
					    controller: ControllerState,
 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl App {
 | 
					impl App {
 | 
				
			||||||
    pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>) -> Self {
 | 
					    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 {
 | 
					        Self {
 | 
				
			||||||
            windows: HashMap::new(),
 | 
					            windows: HashMap::new(),
 | 
				
			||||||
            focused_window: None,
 | 
					 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
 | 
					            input_mapper,
 | 
				
			||||||
 | 
					            controller,
 | 
				
			||||||
            proxy,
 | 
					            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 {
 | 
					impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
 | 
					    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();
 | 
					        window.init();
 | 
				
			||||||
        self.focused_window = Some(window.id());
 | 
					 | 
				
			||||||
        self.windows.insert(window.id(), Box::new(window));
 | 
					        self.windows.insert(window.id(), Box::new(window));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,11 +62,10 @@ impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
        window_id: WindowId,
 | 
					        window_id: WindowId,
 | 
				
			||||||
        event: WindowEvent,
 | 
					        event: WindowEvent,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        if let WindowEvent::Focused(focused) = event {
 | 
					        if let WindowEvent::KeyboardInput { event, .. } = &event {
 | 
				
			||||||
            if focused {
 | 
					            if self.controller.key_event(event) {
 | 
				
			||||||
                self.focused_window = Some(window_id);
 | 
					                self.client
 | 
				
			||||||
            } else {
 | 
					                    .send_command(EmulatorCommand::SetKeys(self.controller.pressed()));
 | 
				
			||||||
                self.focused_window = None;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let Some(window) = self.windows.get_mut(&window_id) else {
 | 
					        let Some(window) = self.windows.get_mut(&window_id) else {
 | 
				
			||||||
| 
						 | 
					@ -82,17 +92,21 @@ impl ApplicationHandler<UserEvent> for App {
 | 
				
			||||||
        device_id: winit::event::DeviceId,
 | 
					        device_id: winit::event::DeviceId,
 | 
				
			||||||
        event: winit::event::DeviceEvent,
 | 
					        event: winit::event::DeviceEvent,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        let Some(window) = self.active_window() else {
 | 
					        for window in self.windows.values_mut() {
 | 
				
			||||||
            return;
 | 
					            window.handle_event(
 | 
				
			||||||
        };
 | 
					                event_loop,
 | 
				
			||||||
        window.handle_event(event_loop, &Event::DeviceEvent { device_id, event });
 | 
					                &Event::DeviceEvent {
 | 
				
			||||||
 | 
					                    device_id,
 | 
				
			||||||
 | 
					                    event: event.clone(),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
 | 
					    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
 | 
				
			||||||
        let Some(window) = self.active_window() else {
 | 
					        for window in self.windows.values_mut() {
 | 
				
			||||||
            return;
 | 
					            window.handle_event(event_loop, &Event::AboutToWait);
 | 
				
			||||||
        };
 | 
					        }
 | 
				
			||||||
        window.handle_event(event_loop, &Event::AboutToWait);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -235,14 +235,23 @@ impl<'a> Drop for ContextLock<'a> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait UiExt {
 | 
					pub trait UiExt {
 | 
				
			||||||
    fn fullscreen_window(&self) -> Option<WindowToken<'_>>;
 | 
					    fn fullscreen_window(&self) -> Option<WindowToken<'_>>;
 | 
				
			||||||
 | 
					    fn right_align_text<T: AsRef<str>>(&self, text: T, space: f32);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl UiExt for imgui::Ui {
 | 
					impl UiExt for imgui::Ui {
 | 
				
			||||||
    fn fullscreen_window(&self) -> Option<WindowToken<'_>> {
 | 
					    fn fullscreen_window(&self) -> Option<WindowToken<'_>> {
 | 
				
			||||||
        self.window("fullscreen")
 | 
					        self.window("fullscreen")
 | 
				
			||||||
            .position([0.0, 0.0], imgui::Condition::Always)
 | 
					            .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)
 | 
					            .flags(imgui::WindowFlags::NO_DECORATION)
 | 
				
			||||||
            .begin()
 | 
					            .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 wgpu::util::DeviceExt as _;
 | 
				
			||||||
use winit::{
 | 
					use winit::{
 | 
				
			||||||
    dpi::LogicalSize,
 | 
					    dpi::LogicalSize,
 | 
				
			||||||
    event::{Event, KeyEvent, WindowEvent},
 | 
					    event::{Event, WindowEvent},
 | 
				
			||||||
    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
					    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
				
			||||||
    window::WindowId,
 | 
					    window::WindowId,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    controller::ControllerState,
 | 
					 | 
				
			||||||
    emulator::{EmulatorClient, EmulatorCommand},
 | 
					    emulator::{EmulatorClient, EmulatorCommand},
 | 
				
			||||||
 | 
					    input::InputMapper,
 | 
				
			||||||
    renderer::GameRenderer,
 | 
					    renderer::GameRenderer,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +28,7 @@ pub struct GameWindow {
 | 
				
			||||||
    pipeline: wgpu::RenderPipeline,
 | 
					    pipeline: wgpu::RenderPipeline,
 | 
				
			||||||
    bind_group: wgpu::BindGroup,
 | 
					    bind_group: wgpu::BindGroup,
 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
    controller: ControllerState,
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +36,7 @@ impl GameWindow {
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        event_loop: &ActiveEventLoop,
 | 
					        event_loop: &ActiveEventLoop,
 | 
				
			||||||
        client: EmulatorClient,
 | 
					        client: EmulatorClient,
 | 
				
			||||||
 | 
					        input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
        proxy: EventLoopProxy<UserEvent>,
 | 
					        proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        let window = WindowStateBuilder::new(event_loop)
 | 
					        let window = WindowStateBuilder::new(event_loop)
 | 
				
			||||||
| 
						 | 
					@ -153,15 +157,13 @@ impl GameWindow {
 | 
				
			||||||
            cache: None,
 | 
					            cache: None,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let controller = ControllerState::new();
 | 
					 | 
				
			||||||
        client.send_command(EmulatorCommand::SetKeys(controller.pressed()));
 | 
					 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            window,
 | 
					            window,
 | 
				
			||||||
            imgui: None,
 | 
					            imgui: None,
 | 
				
			||||||
            pipeline: render_pipeline,
 | 
					            pipeline: render_pipeline,
 | 
				
			||||||
            bind_group,
 | 
					            bind_group,
 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
            controller,
 | 
					            input_mapper,
 | 
				
			||||||
            proxy,
 | 
					            proxy,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -170,6 +172,7 @@ impl GameWindow {
 | 
				
			||||||
        let window = &mut self.window;
 | 
					        let window = &mut self.window;
 | 
				
			||||||
        let imgui = self.imgui.as_mut().unwrap();
 | 
					        let imgui = self.imgui.as_mut().unwrap();
 | 
				
			||||||
        let mut context = imgui.context.lock().unwrap();
 | 
					        let mut context = imgui.context.lock().unwrap();
 | 
				
			||||||
 | 
					        let mut new_size = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let now = Instant::now();
 | 
					        let now = Instant::now();
 | 
				
			||||||
        context.io_mut().update_delta_time(now - imgui.last_frame);
 | 
					        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 ui.menu_item_config(label).selected(selected).build() {
 | 
				
			||||||
                        if let Some(size) = window.window.request_inner_size(dims) {
 | 
					                        if let Some(size) = window.window.request_inner_size(dims) {
 | 
				
			||||||
                            window.handle_resize(&size);
 | 
					                            window.handle_resize(&size);
 | 
				
			||||||
 | 
					                            new_size = Some(size);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            ui.menu("Input", || {
 | 
					            ui.menu("Input", || {
 | 
				
			||||||
                if ui.menu_item("Map Input") {
 | 
					                if ui.menu_item("Bind Inputs") {
 | 
				
			||||||
                    let input_window = Box::new(InputWindow::new(event_loop, self.proxy.clone()));
 | 
					                    let input_window = Box::new(InputWindow::new(
 | 
				
			||||||
 | 
					                        event_loop,
 | 
				
			||||||
 | 
					                        self.input_mapper.clone(),
 | 
				
			||||||
 | 
					                        self.proxy.clone(),
 | 
				
			||||||
 | 
					                    ));
 | 
				
			||||||
                    self.proxy
 | 
					                    self.proxy
 | 
				
			||||||
                        .send_event(UserEvent::OpenWindow(input_window))
 | 
					                        .send_event(UserEvent::OpenWindow(input_window))
 | 
				
			||||||
                        .unwrap();
 | 
					                        .unwrap();
 | 
				
			||||||
| 
						 | 
					@ -287,17 +295,21 @@ impl GameWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        drop(rpass);
 | 
					        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()));
 | 
					        window.queue.submit(Some(encoder.finish()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        frame.present();
 | 
					        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 {
 | 
					impl AppWindow for GameWindow {
 | 
				
			||||||
| 
						 | 
					@ -307,6 +319,7 @@ impl AppWindow for GameWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn init(&mut self) {
 | 
					    fn init(&mut self) {
 | 
				
			||||||
        self.imgui = Some(ImguiState::new(&self.window));
 | 
					        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>) {
 | 
				
			||||||
| 
						 | 
					@ -314,7 +327,6 @@ impl AppWindow for GameWindow {
 | 
				
			||||||
            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 => event_loop.exit(),
 | 
					                WindowEvent::CloseRequested => event_loop.exit(),
 | 
				
			||||||
                WindowEvent::KeyboardInput { event, .. } => self.handle_key_event(event),
 | 
					 | 
				
			||||||
                WindowEvent::RedrawRequested => self.draw(event_loop),
 | 
					                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 std::{
 | 
				
			||||||
 | 
					    sync::{Arc, RwLock},
 | 
				
			||||||
use winit::{
 | 
					    time::Instant,
 | 
				
			||||||
    event::{Event, WindowEvent},
 | 
					 | 
				
			||||||
    event_loop::{ActiveEventLoop, EventLoopProxy},
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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::{
 | 
					use super::{
 | 
				
			||||||
    common::{ImguiState, UiExt as _, WindowState, WindowStateBuilder},
 | 
					    common::{ImguiState, UiExt, WindowState, WindowStateBuilder},
 | 
				
			||||||
    AppWindow, UserEvent,
 | 
					    AppWindow, UserEvent,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct InputWindow {
 | 
					pub struct InputWindow {
 | 
				
			||||||
    window: WindowState,
 | 
					    window: WindowState,
 | 
				
			||||||
    imgui: Option<ImguiState>,
 | 
					    imgui: Option<ImguiState>,
 | 
				
			||||||
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    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 {
 | 
					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)
 | 
					        let window = WindowStateBuilder::new(event_loop)
 | 
				
			||||||
            .with_title("Map Inputs")
 | 
					            .with_title("Bind Inputs")
 | 
				
			||||||
 | 
					            .with_inner_size(LogicalSize::new(600, 400))
 | 
				
			||||||
            .build();
 | 
					            .build();
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            window,
 | 
					            window,
 | 
				
			||||||
            imgui: None,
 | 
					            imgui: None,
 | 
				
			||||||
 | 
					            input_mapper,
 | 
				
			||||||
 | 
					            now_binding: None,
 | 
				
			||||||
            proxy,
 | 
					            proxy,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -50,17 +83,50 @@ impl InputWindow {
 | 
				
			||||||
            .expect("Failed to prepare frame");
 | 
					            .expect("Failed to prepare frame");
 | 
				
			||||||
        let ui = context.new_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) {
 | 
					            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_row();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ui.table_next_column();
 | 
					                for (key, name) in KEY_NAMES {
 | 
				
			||||||
                ui.text("Key");
 | 
					                    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();
 | 
					                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();
 | 
					            window.end();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let mut encoder: wgpu::CommandEncoder = window
 | 
					        let mut encoder: wgpu::CommandEncoder = window
 | 
				
			||||||
| 
						 | 
					@ -102,6 +168,17 @@ impl InputWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        frame.present();
 | 
					        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 {
 | 
					impl AppWindow for InputWindow {
 | 
				
			||||||
| 
						 | 
					@ -111,6 +188,7 @@ impl AppWindow for InputWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn init(&mut self) {
 | 
					    fn init(&mut self) {
 | 
				
			||||||
        self.imgui = Some(ImguiState::new(&self.window));
 | 
					        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>) {
 | 
				
			||||||
| 
						 | 
					@ -121,6 +199,7 @@ impl AppWindow for InputWindow {
 | 
				
			||||||
                    .proxy
 | 
					                    .proxy
 | 
				
			||||||
                    .send_event(UserEvent::CloseWindow(self.id()))
 | 
					                    .send_event(UserEvent::CloseWindow(self.id()))
 | 
				
			||||||
                    .unwrap(),
 | 
					                    .unwrap(),
 | 
				
			||||||
 | 
					                WindowEvent::KeyboardInput { event, .. } => self.try_bind_key(event),
 | 
				
			||||||
                WindowEvent::RedrawRequested => self.draw(),
 | 
					                WindowEvent::RedrawRequested => self.draw(),
 | 
				
			||||||
                _ => (),
 | 
					                _ => (),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,18 @@
 | 
				
			||||||
use winit::{
 | 
					use std::sync::{Arc, RwLock};
 | 
				
			||||||
    event::{ElementState, KeyEvent},
 | 
					 | 
				
			||||||
    keyboard::{Key, NamedKey},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::shrooms_vb_core::VBKey;
 | 
					use winit::event::{ElementState, KeyEvent};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{input::InputMapper, shrooms_vb_core::VBKey};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct ControllerState {
 | 
					pub struct ControllerState {
 | 
				
			||||||
 | 
					    input_mapper: Arc<RwLock<InputMapper>>,
 | 
				
			||||||
    pressed: VBKey,
 | 
					    pressed: VBKey,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ControllerState {
 | 
					impl ControllerState {
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub fn new(input_mapper: Arc<RwLock<InputMapper>>) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
 | 
					            input_mapper,
 | 
				
			||||||
            pressed: VBKey::SGN,
 | 
					            pressed: VBKey::SGN,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -21,7 +22,7 @@ impl ControllerState {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn key_event(&mut self, event: &KeyEvent) -> bool {
 | 
					    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;
 | 
					            return false;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        match event.state {
 | 
					        match event.state {
 | 
				
			||||||
| 
						 | 
					@ -42,23 +43,8 @@ impl ControllerState {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn key_to_input(&self, key: &Key) -> Option<VBKey> {
 | 
					    fn key_event_to_input(&self, event: &KeyEvent) -> Option<VBKey> {
 | 
				
			||||||
        match key.as_ref() {
 | 
					        let mapper = self.input_mapper.read().unwrap();
 | 
				
			||||||
            Key::Character("a") => Some(VBKey::SEL),
 | 
					        mapper.key_event(event)
 | 
				
			||||||
            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,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 audio;
 | 
				
			||||||
mod controller;
 | 
					mod controller;
 | 
				
			||||||
mod emulator;
 | 
					mod emulator;
 | 
				
			||||||
 | 
					mod input;
 | 
				
			||||||
mod renderer;
 | 
					mod renderer;
 | 
				
			||||||
mod shrooms_vb_core;
 | 
					mod shrooms_vb_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,8 @@ enum VBDataType {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bitflags! {
 | 
					bitflags! {
 | 
				
			||||||
    #[derive(Clone, Copy, Debug)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
 | 
					    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
				
			||||||
    pub struct VBKey: u16 {
 | 
					    pub struct VBKey: u16 {
 | 
				
			||||||
        const PWR = 0x0001;
 | 
					        const PWR = 0x0001;
 | 
				
			||||||
        const SGN = 0x0002;
 | 
					        const SGN = 0x0002;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue