use std::{ sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, }, thread, }; use egui::{Context, ViewportBuilder, ViewportId}; use game::GameWindow; use game_screen::GameScreen; use gilrs::{EventType, Gilrs}; use input::InputWindow; use winit::{event::KeyEvent, event_loop::EventLoopProxy}; use crate::{ application::UserEvent, controller::ControllerManager, emulator::{EmulatorClient, EmulatorCommand, SimId}, input::MappingProvider, }; mod game; mod game_screen; mod input; pub struct WindowManager { p1: GameWindow, p2: Arc>, client: EmulatorClient, input: ChildWindow, controllers: ControllerManager, } impl WindowManager { pub fn new(client: EmulatorClient, proxy: EventLoopProxy) -> Self { let mappings = MappingProvider::new(); let controllers = ControllerManager::new(client.clone(), &mappings); { let mappings = mappings.clone(); thread::spawn(|| process_gamepad_input(mappings, proxy)); } let input = ChildWindow::new(InputWindow::new(mappings)); let p1 = GameWindow::new(client.clone(), SimId::Player1, input.open.clone()); let p2 = GameWindow::new(client.clone(), SimId::Player2, input.open.clone()); Self { p1, p2: Arc::new(Mutex::new(p2)), client, input, controllers, } } pub fn init_renderer(&mut self, render_state: &egui_wgpu::RenderState) { GameScreen::init_pipeline(render_state); self.p2.lock().unwrap().init_renderer(render_state); self.p1.init_renderer(render_state); } pub fn initial_viewport(&self) -> ViewportBuilder { self.p1.initial_viewport() } pub fn show(&mut self, ctx: &Context) { self.p1.show(ctx); self.input.show(ctx); if self.client.has_player_2() { let (viewport_id, viewport) = { let p2 = self.p2.lock().unwrap(); (p2.viewport_id(), p2.initial_viewport()) }; let client = self.client.clone(); let p2 = self.p2.clone(); ctx.show_viewport_deferred(viewport_id, viewport, move |ctx, _| { p2.lock().unwrap().show(ctx); if ctx.input(|i| i.viewport().close_requested()) { client.send_command(EmulatorCommand::StopSecondSim); } }); } } pub fn handle_key_event(&mut self, event: winit::event::KeyEvent) { self.controllers.handle_key_event(&event); self.input.handle_key_event(&event); } pub fn handle_gamepad_event(&mut self, event: gilrs::Event) { self.controllers.handle_gamepad_event(&event); } } fn process_gamepad_input(mappings: MappingProvider, proxy: EventLoopProxy) { let Ok(mut gilrs) = Gilrs::new() else { eprintln!("could not connect gamepad listener"); return; }; while let Some(event) = gilrs.next_event_blocking(None) { if event.event == EventType::Connected { let Some(gamepad) = gilrs.connected_gamepad(event.id) else { continue; }; mappings.map_gamepad(SimId::Player1, &gamepad); } if proxy.send_event(UserEvent::GamepadEvent(event)).is_err() { // main thread has closed! we done return; } } } trait AppWindow { fn viewport_id(&self) -> ViewportId; fn initial_viewport(&self) -> ViewportBuilder; fn show(&mut self, ctx: &Context); fn handle_key_event(&mut self, event: &KeyEvent) { let _ = event; } } struct ChildWindow { pub open: Arc, window: Arc>, } impl ChildWindow { fn new(window: T) -> Self { Self { open: Arc::new(AtomicBool::new(false)), window: Arc::new(Mutex::new(window)), } } fn show(&self, ctx: &Context) { if !self.open.load(Ordering::Relaxed) { return; } let (viewport_id, viewport) = { let window = self.window.lock().unwrap(); (window.viewport_id(), window.initial_viewport()) }; let open = self.open.clone(); let window = self.window.clone(); ctx.show_viewport_deferred(viewport_id, viewport, move |ctx, _| { window.lock().unwrap().show(ctx); if ctx.input(|i| i.viewport().close_requested()) { open.store(false, Ordering::Relaxed); ctx.request_repaint(); } }); } fn handle_key_event(&self, event: &KeyEvent) { if self.open.load(Ordering::Relaxed) { self.window.lock().unwrap().handle_key_event(event); } } }