use std::{ sync::{Arc, RwLock}, time::Instant, }; use winit::{ dpi::LogicalSize, event::{Event, KeyEvent, WindowEvent}, event_loop::{ActiveEventLoop, EventLoopProxy}, }; use crate::{ emulator::{SimId, VBKey}, input::InputMapper, }; use super::{ common::{ImguiState, UiExt, WindowState, WindowStateBuilder}, AppWindow, UserEvent, }; pub struct InputWindow { window: WindowState, imgui: ImguiState, input_mapper: Arc>, proxy: EventLoopProxy, now_binding: Option<(SimId, 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, input_mapper: Arc>, proxy: EventLoopProxy, ) -> Self { let window = WindowStateBuilder::new(event_loop) .with_title("Bind Inputs") .with_inner_size(LogicalSize::new(600, 400)) .build(); let imgui = ImguiState::new(&window); Self { window, imgui, input_mapper, now_binding: None, proxy, } } fn draw(&mut self) { let window = &mut self.window; let imgui = &mut self.imgui; let mut context = imgui.context.lock().unwrap(); let now = Instant::now(); context.io_mut().update_delta_time(now - imgui.last_frame); imgui.last_frame = now; let frame = match window.surface.get_current_texture() { Ok(frame) => frame, Err(e) => { if !self.window.minimized { eprintln!("dropped frame: {e:?}"); } return; } }; imgui .platform .prepare_frame(context.io_mut(), &window.window) .expect("Failed to prepare frame"); let ui = context.new_frame(); let mut render_key_bindings = |sim_id: SimId| { if let Some(table) = ui.begin_table("controls", 2) { let binding_names = { let mapper = self.input_mapper.read().unwrap(); mapper.binding_names(&sim_id) }; ui.table_next_row(); 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((sim_id, 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((sim_id, key)); } }); ui.same_line(); if ui.button(format!("Clear##{name}")) { let mut mapper = self.input_mapper.write().unwrap(); mapper.clear_binding(sim_id, key); } } 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(SimId::Player1); tab.end(); } if let Some(tab) = ui.tab_item("Player 2") { render_key_bindings(SimId::Player2); tab.end(); } tabs.end(); } window.end(); } let mut encoder: wgpu::CommandEncoder = window .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); if imgui.last_cursor != ui.mouse_cursor() { imgui.last_cursor = ui.mouse_cursor(); imgui.platform.prepare_render(ui, &window.window); } let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(imgui.clear_color), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, }); // Draw the game imgui .renderer .render(context.render(), &window.queue, &window.device, &mut rpass) .expect("Rendering failed"); drop(rpass); window.queue.submit(Some(encoder.finish())); frame.present(); } fn try_bind_key(&mut self, event: &KeyEvent) { if !event.state.is_pressed() { return; } let Some((sim_id, vb)) = self.now_binding.take() else { return; }; let mut mapper = self.input_mapper.write().unwrap(); mapper.bind_key(sim_id, vb, event.physical_key); } } impl AppWindow for InputWindow { fn id(&self) -> winit::window::WindowId { self.window.window.id() } fn handle_event(&mut self, _: &ActiveEventLoop, event: &Event) { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::Resized(size) => self.window.handle_resize(size), WindowEvent::CloseRequested => { self.proxy.send_event(UserEvent::Close(self.id())).unwrap() } WindowEvent::KeyboardInput { event, .. } => self.try_bind_key(event), WindowEvent::RedrawRequested => self.draw(), _ => (), }, Event::AboutToWait => { self.window.window.request_redraw(); } _ => (), } let window = &self.window; let imgui = &mut self.imgui; let mut context = imgui.context.lock().unwrap(); imgui .platform .handle_event(context.io_mut(), &window.window, event); } }