From 83377ff5fa3991ca78b70ea1abb87b672f8315ed Mon Sep 17 00:00:00 2001 From: Simon Gellis Date: Thu, 28 Nov 2024 10:27:18 -0500 Subject: [PATCH] Stop using nested viewports --- src/app.rs | 329 ++++++++++++++++++++++++++++++++++++++ src/application.rs | 315 ------------------------------------ src/main.rs | 4 +- src/window.rs | 152 +----------------- src/window/game.rs | 38 +++-- src/window/game_screen.rs | 4 +- 6 files changed, 364 insertions(+), 478 deletions(-) create mode 100644 src/app.rs delete mode 100644 src/application.rs diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..4f21223 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,329 @@ +use std::{collections::HashSet, num::NonZero, sync::Arc, thread}; + +use egui::{ + ahash::{HashMap, HashMapExt}, + Context, TextWrapMode, ViewportBuilder, ViewportCommand, ViewportId, ViewportInfo, +}; +use gilrs::{EventType, Gilrs}; +use winit::{ + application::ApplicationHandler, + event::{KeyEvent, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoopProxy}, + window::Window, +}; + +use crate::{ + controller::ControllerManager, + emulator::{EmulatorClient, SimId}, + input::MappingProvider, + window::{AppWindow, GameWindow, InputWindow}, +}; + +pub struct Application { + client: EmulatorClient, + proxy: EventLoopProxy, + mappings: MappingProvider, + controllers: ControllerManager, + viewports: HashMap, + focused: Option, +} + +impl Application { + pub fn new(client: EmulatorClient, proxy: EventLoopProxy) -> Self { + let mappings = MappingProvider::new(); + let controllers = ControllerManager::new(client.clone(), &mappings); + { + let mappings = mappings.clone(); + let proxy = proxy.clone(); + thread::spawn(|| process_gamepad_input(mappings, proxy)); + } + Self { + client, + proxy, + mappings, + controllers, + viewports: HashMap::new(), + focused: None, + } + } + + fn open(&mut self, event_loop: &ActiveEventLoop, window: Box) { + let viewport_id = window.viewport_id(); + if self.viewports.contains_key(&viewport_id) { + return; + } + self.viewports + .insert(viewport_id, Viewport::new(event_loop, window)); + } +} + +impl ApplicationHandler for Application { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let app = GameWindow::new(self.client.clone(), self.proxy.clone(), SimId::Player1); + let wrapper = Viewport::new(event_loop, Box::new(app)); + self.focused = Some(wrapper.id()); + self.viewports.insert(wrapper.id(), wrapper); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let Some(viewport) = self + .viewports + .values_mut() + .find(|v| v.window.id() == window_id) + else { + return; + }; + let viewport_id = viewport.id(); + match &event { + WindowEvent::KeyboardInput { event, .. } => { + self.controllers.handle_key_event(event); + viewport.handle_key_event(event); + } + WindowEvent::Focused(new_focused) => { + self.focused = new_focused.then_some(viewport_id); + } + _ => {} + } + match viewport.on_window_event(event) { + Some(Action::Redraw) => { + for viewport in self.viewports.values_mut() { + viewport.redraw(event_loop); + } + } + Some(Action::Close) => { + self.viewports.remove(&viewport_id); + if viewport_id == ViewportId::ROOT { + event_loop.exit(); + } + } + None => {} + } + } + + fn device_event( + &mut self, + _event_loop: &ActiveEventLoop, + _device_id: winit::event::DeviceId, + event: winit::event::DeviceEvent, + ) { + if let winit::event::DeviceEvent::MouseMotion { delta } = event { + let Some(viewport) = self + .focused + .as_ref() + .and_then(|id| self.viewports.get_mut(id)) + else { + return; + }; + viewport.state.on_mouse_motion(delta); + } + } + + fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { + match event { + UserEvent::GamepadEvent(event) => self.controllers.handle_gamepad_event(&event), + UserEvent::OpenInput => { + let input = InputWindow::new(self.mappings.clone()); + self.open(event_loop, Box::new(input)); + } + UserEvent::OpenPlayer2 => { + let p2 = GameWindow::new(self.client.clone(), self.proxy.clone(), SimId::Player2); + self.open(event_loop, Box::new(p2)); + } + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + if let Some(viewport) = self.viewports.get(&ViewportId::ROOT) { + viewport.window.request_redraw(); + } + } +} + +struct Viewport { + painter: egui_wgpu::winit::Painter, + ctx: Context, + info: ViewportInfo, + commands: Vec, + builder: ViewportBuilder, + window: Arc, + state: egui_winit::State, + app: Box, +} +impl Viewport { + pub fn new(event_loop: &ActiveEventLoop, mut app: Box) -> Self { + let mut painter = egui_wgpu::winit::Painter::new( + egui_wgpu::WgpuConfiguration::default(), + 1, + None, + false, + true, + ); + + let ctx = Context::default(); + ctx.style_mut(|s| { + s.wrap_mode = Some(TextWrapMode::Extend); + s.visuals.menu_rounding = Default::default(); + }); + + let mut info = ViewportInfo::default(); + let builder = app.initial_viewport(); + let (window, state) = create_window_and_state(&ctx, event_loop, &builder, &mut painter); + egui_winit::update_viewport_info(&mut info, &ctx, &window, true); + + app.on_init(painter.render_state().as_ref().unwrap()); + Self { + painter, + ctx, + info, + commands: vec![], + builder, + window, + state, + app, + } + } + + pub fn id(&self) -> ViewportId { + self.app.viewport_id() + } + + pub fn on_window_event(&mut self, event: WindowEvent) -> Option { + let response = self.state.on_window_event(&self.window, &event); + egui_winit::update_viewport_info( + &mut self.info, + self.state.egui_ctx(), + &self.window, + false, + ); + + match event { + WindowEvent::RedrawRequested => Some(Action::Redraw), + WindowEvent::CloseRequested => Some(Action::Close), + WindowEvent::Resized(size) => { + let (Some(width), Some(height)) = + (NonZero::new(size.width), NonZero::new(size.height)) + else { + return None; + }; + self.painter + .on_window_resized(ViewportId::ROOT, width, height); + None + } + _ if response.repaint => Some(Action::Redraw), + _ => None, + } + } + + pub fn handle_key_event(&mut self, event: &KeyEvent) { + self.app.handle_key_event(event); + } + + fn redraw(&mut self, event_loop: &ActiveEventLoop) -> Option { + let mut input = self.state.take_egui_input(&self.window); + input.viewports = std::iter::once((ViewportId::ROOT, self.info.clone())).collect(); + let mut output = self.ctx.run(input, |ctx| { + self.app.show(ctx); + }); + let clipped_primitives = self.ctx.tessellate(output.shapes, output.pixels_per_point); + self.painter.paint_and_update_textures( + ViewportId::ROOT, + output.pixels_per_point, + [0.0, 0.0, 0.0, 0.0], + &clipped_primitives, + &output.textures_delta, + false, + ); + + self.state + .handle_platform_output(&self.window, output.platform_output); + + let Some(viewport_output) = output.viewport_output.remove(&ViewportId::ROOT) else { + return Some(Action::Close); + }; + + let (mut deferred_commands, recreate) = self.builder.patch(viewport_output.builder); + if recreate { + let (window, state) = + create_window_and_state(&self.ctx, event_loop, &self.builder, &mut self.painter); + egui_winit::update_viewport_info(&mut self.info, &self.ctx, &window, true); + self.window = window; + self.state = state; + } + self.commands.append(&mut deferred_commands); + egui_winit::process_viewport_commands( + &self.ctx, + &mut self.info, + std::mem::take(&mut self.commands), + &self.window, + &mut HashSet::default(), + ); + + if self.info.close_requested() { + Some(Action::Close) + } else { + Some(Action::Redraw) + } + } +} + +impl Drop for Viewport { + fn drop(&mut self) { + self.app.on_destroy(); + } +} + +#[derive(Debug)] +pub enum UserEvent { + GamepadEvent(gilrs::Event), + OpenInput, + OpenPlayer2, +} + +pub enum Action { + Redraw, + Close, +} + +fn create_window_and_state( + ctx: &Context, + event_loop: &ActiveEventLoop, + builder: &ViewportBuilder, + painter: &mut egui_wgpu::winit::Painter, +) -> (Arc, egui_winit::State) { + pollster::block_on(painter.set_window(ViewportId::ROOT, None)).unwrap(); + let window = Arc::new(egui_winit::create_window(ctx, event_loop, builder).unwrap()); + pollster::block_on(painter.set_window(ViewportId::ROOT, Some(window.clone()))).unwrap(); + let state = egui_winit::State::new( + ctx.clone(), + ViewportId::ROOT, + event_loop, + Some(window.scale_factor() as f32), + event_loop.system_theme(), + painter.max_texture_side(), + ); + (window, state) +} + +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; + } + } +} diff --git a/src/application.rs b/src/application.rs deleted file mode 100644 index 024caaa..0000000 --- a/src/application.rs +++ /dev/null @@ -1,315 +0,0 @@ -use std::{ - collections::{hash_map::Entry, HashMap, HashSet}, - num::NonZero, - sync::Arc, -}; - -use winit::event_loop::EventLoopProxy; - -use crate::{emulator::EmulatorClient, window::WindowManager}; - -pub struct Application { - client: EmulatorClient, - proxy: EventLoopProxy, - state: Option, -} - -impl Application { - pub fn new(client: EmulatorClient, proxy: EventLoopProxy) -> Self { - Self { - client, - proxy, - state: None, - } - } -} - -impl winit::application::ApplicationHandler for Application { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - self.state = Some(AppState::new( - self.client.clone(), - self.proxy.clone(), - event_loop, - )); - } - - fn window_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - window_id: winit::window::WindowId, - event: winit::event::WindowEvent, - ) { - self.state - .as_mut() - .unwrap() - .window_event(event_loop, window_id, event); - } - - fn device_event( - &mut self, - _event_loop: &winit::event_loop::ActiveEventLoop, - _device_id: winit::event::DeviceId, - event: winit::event::DeviceEvent, - ) { - self.state.as_mut().unwrap().device_event(event) - } - - fn user_event(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop, event: UserEvent) { - self.state.as_mut().unwrap().user_event(event); - } -} - -struct AppState { - painter: egui_wgpu::winit::Painter, - ctx: egui::Context, - viewports: HashMap, - viewports_by_window: HashMap, - focused: Option, - screen: WindowManager, -} -impl AppState { - fn new( - client: EmulatorClient, - proxy: EventLoopProxy, - event_loop: &winit::event_loop::ActiveEventLoop, - ) -> Self { - let mut painter = egui_wgpu::winit::Painter::new( - egui_wgpu::WgpuConfiguration::default(), - 1, - None, - false, - true, - ); - let ctx = egui::Context::default(); - ctx.style_mut(|s| { - s.wrap_mode = Some(egui::TextWrapMode::Extend); - s.visuals.menu_rounding = Default::default(); - }); - ctx.set_embed_viewports(false); - { - let proxy = proxy.clone(); - ctx.set_request_repaint_callback(move |info| { - proxy.send_event(UserEvent::RepaintRequested(info)).unwrap(); - }); - } - - let mut screen = WindowManager::new(client, proxy); - let root_viewport = ViewportState::new( - egui::ViewportId::ROOT, - &ctx, - event_loop, - screen.initial_viewport(), - &mut painter, - ); - screen.init_renderer(painter.render_state().as_ref().unwrap()); - - let mut viewports_by_window = HashMap::new(); - viewports_by_window.insert(root_viewport.window.id(), egui::ViewportId::ROOT); - let mut viewports = HashMap::new(); - viewports.insert(egui::ViewportId::ROOT, root_viewport); - - Self { - painter, - ctx, - viewports, - viewports_by_window, - focused: Some(egui::ViewportId::ROOT), - screen, - } - } - - fn window_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - window_id: winit::window::WindowId, - event: winit::event::WindowEvent, - ) { - let Some(&viewport_id) = self.viewports_by_window.get(&window_id) else { - return; - }; - let Some(viewport) = self.viewports.get_mut(&viewport_id) else { - panic!("Unrecognized viewport"); - }; - viewport.on_window_event(&event); - - match event { - winit::event::WindowEvent::KeyboardInput { event, .. } => { - self.screen.handle_key_event(event); - } - winit::event::WindowEvent::RedrawRequested => { - pollster::block_on( - self.painter - .set_window(viewport_id, Some(viewport.window.clone())), - ) - .unwrap(); - let cb = viewport.viewport_ui_cb.clone(); - let mut input = viewport.state.take_egui_input(&viewport.window); - input.viewports = self - .viewports - .iter() - .map(|(k, v)| (*k, v.info.clone())) - .collect(); - let output = self.ctx.run(input, |ctx| { - if let Some(cb) = cb.as_deref() { - cb(ctx) - } else { - self.screen.show(ctx) - } - }); - let clipped_primitives = - self.ctx.tessellate(output.shapes, output.pixels_per_point); - self.painter.paint_and_update_textures( - viewport_id, - output.pixels_per_point, - [0.0, 0.0, 0.0, 0.0], - &clipped_primitives, - &output.textures_delta, - false, - ); - - let mut live_viewports = HashSet::default(); - for (id, output) in output.viewport_output { - live_viewports.insert(id); - let viewport = match self.viewports.entry(id) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let mut v = ViewportState::new( - id, - &self.ctx, - event_loop, - output.builder, - &mut self.painter, - ); - v.viewport_ui_cb = output.viewport_ui_cb; - self.viewports_by_window.insert(v.window.id(), id); - e.insert(v) - } - }; - egui_winit::process_viewport_commands( - &self.ctx, - &mut viewport.info, - output.commands, - &viewport.window, - &mut HashSet::default(), - ); - if viewport.info.close_requested() { - live_viewports.remove(&id); - } - } - self.viewports.retain(|k, v| { - if live_viewports.contains(k) { - return true; - } - self.viewports_by_window.remove(&v.window.id()); - false - }); - self.painter.gc_viewports(&live_viewports); - if !self.viewports.contains_key(&egui::ViewportId::ROOT) { - event_loop.exit(); - } - } - winit::event::WindowEvent::Resized(size) => { - let (Some(width), Some(height)) = - (NonZero::new(size.width), NonZero::new(size.height)) - else { - return; - }; - self.painter.on_window_resized(viewport_id, width, height); - } - winit::event::WindowEvent::Focused(new_focused) => { - self.focused = new_focused.then_some(viewport_id); - } - winit::event::WindowEvent::CloseRequested => { - if viewport_id == egui::ViewportId::ROOT { - event_loop.exit(); - } else if let Some(viewport) = self.viewports.get_mut(&viewport_id) { - viewport.info.events.push(egui::ViewportEvent::Close); - self.ctx.request_repaint_of(viewport_id); - self.ctx.request_repaint_of(egui::ViewportId::ROOT); - } - } - _ => {} - } - } - - fn device_event(&mut self, event: winit::event::DeviceEvent) { - if let winit::event::DeviceEvent::MouseMotion { delta } = event { - let Some(viewport) = self - .focused - .as_ref() - .and_then(|id| self.viewports.get_mut(id)) - else { - return; - }; - viewport.state.on_mouse_motion(delta); - } - } - - fn user_event(&mut self, event: UserEvent) { - match event { - UserEvent::GamepadEvent(event) => self.screen.handle_gamepad_event(event), - UserEvent::RepaintRequested(info) => { - let Some(viewport) = self.viewports.get(&info.viewport_id) else { - return; - }; - viewport.window.request_redraw(); - } - } - } -} - -struct ViewportState { - info: egui::ViewportInfo, - state: egui_winit::State, - viewport_ui_cb: Option>, - window: Arc, -} - -impl ViewportState { - fn new( - id: egui::ViewportId, - ctx: &egui::Context, - event_loop: &winit::event_loop::ActiveEventLoop, - viewport: egui::ViewportBuilder, - painter: &mut egui_wgpu::winit::Painter, - ) -> Self { - let mut info = egui::ViewportInfo::default(); - let window = Arc::new(egui_winit::create_window(ctx, event_loop, &viewport).unwrap()); - egui_winit::update_viewport_info(&mut info, ctx, &window, true); - - pollster::block_on(painter.set_window(id, Some(window.clone()))).unwrap(); - let state = egui_winit::State::new( - ctx.clone(), - id, - event_loop, - Some(window.scale_factor() as f32), - event_loop.system_theme(), - painter.max_texture_side(), - ); - Self { - info, - state, - viewport_ui_cb: None, - window, - } - } - - fn on_window_event(&mut self, event: &winit::event::WindowEvent) { - let response = self.state.on_window_event(&self.window, event); - if response.repaint { - self.window.request_redraw(); - } - egui_winit::update_viewport_info( - &mut self.info, - self.state.egui_ctx(), - &self.window, - false, - ); - } -} - -#[derive(Debug)] -pub enum UserEvent { - GamepadEvent(gilrs::Event), - RepaintRequested(egui::RequestRepaintInfo), -} diff --git a/src/main.rs b/src/main.rs index 411c0f5..f747fca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ use std::{path::PathBuf, process}; use anyhow::Result; -use application::Application; +use app::Application; use clap::Parser; use emulator::EmulatorBuilder; use thread_priority::{ThreadBuilder, ThreadPriority}; use winit::event_loop::{ControlFlow, EventLoop}; -mod application; +mod app; mod audio; mod controller; mod emulator; diff --git a/src/window.rs b/src/window.rs index 53dd01c..8f0f179 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,157 +1,21 @@ -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, -}; +pub use game::GameWindow; +pub use input::InputWindow; +use winit::event::KeyEvent; 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 { +pub trait AppWindow { fn viewport_id(&self) -> ViewportId; fn initial_viewport(&self) -> ViewportBuilder; fn show(&mut self, ctx: &Context); + fn on_init(&mut self, render_state: &egui_wgpu::RenderState) { + let _ = render_state; + } + fn on_destroy(&mut self) {} 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); - } - } -} diff --git a/src/window/game.rs b/src/window/game.rs index b3c3b7d..23d4542 100644 --- a/src/window/game.rs +++ b/src/window/game.rs @@ -1,40 +1,32 @@ -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, +use crate::{ + app::UserEvent, + emulator::{EmulatorClient, EmulatorCommand, SimId}, }; - -use crate::emulator::{EmulatorClient, EmulatorCommand, SimId}; use egui::{ menu, Button, CentralPanel, Color32, Context, Response, TopBottomPanel, Ui, ViewportBuilder, ViewportCommand, ViewportId, WidgetText, }; +use winit::event_loop::EventLoopProxy; use super::{game_screen::GameScreen, AppWindow}; pub struct GameWindow { client: EmulatorClient, + proxy: EventLoopProxy, sim_id: SimId, - input_window_open: Arc, screen: Option, } impl GameWindow { - pub fn new(client: EmulatorClient, sim_id: SimId, input_window_open: Arc) -> Self { + pub fn new(client: EmulatorClient, proxy: EventLoopProxy, sim_id: SimId) -> Self { Self { client, + proxy, sim_id, - input_window_open, screen: None, } } - pub fn init_renderer(&mut self, render_state: &egui_wgpu::RenderState) { - let (screen, sink) = GameScreen::init(render_state); - self.client - .send_command(EmulatorCommand::SetRenderer(self.sim_id, sink)); - self.screen = Some(screen) - } - fn show_menu(&mut self, ctx: &Context, ui: &mut Ui) { ui.menu_button("ROM", |ui| { if ui.button("Open ROM").clicked() { @@ -101,7 +93,7 @@ impl GameWindow { }); ui.menu_button("Input", |ui| { if ui.button("Bind Inputs").clicked() { - self.input_window_open.store(true, Ordering::Relaxed); + self.proxy.send_event(UserEvent::OpenInput).unwrap(); ui.close_menu(); } }); @@ -112,6 +104,7 @@ impl GameWindow { { self.client .send_command(EmulatorCommand::StartSecondSim(None)); + self.proxy.send_event(UserEvent::OpenPlayer2).unwrap(); ui.close_menu(); } if self.client.has_player_2() { @@ -157,6 +150,19 @@ impl AppWindow for GameWindow { } }); } + + fn on_init(&mut self, render_state: &egui_wgpu::RenderState) { + let (screen, sink) = GameScreen::init(render_state); + self.client + .send_command(EmulatorCommand::SetRenderer(self.sim_id, sink)); + self.screen = Some(screen) + } + + fn on_destroy(&mut self) { + if self.sim_id == SimId::Player2 { + self.client.send_command(EmulatorCommand::StopSecondSim); + } + } } trait UiExt { diff --git a/src/window/game_screen.rs b/src/window/game_screen.rs index 2a00520..e1ee7dd 100644 --- a/src/window/game_screen.rs +++ b/src/window/game_screen.rs @@ -10,7 +10,7 @@ pub struct GameScreen { } impl GameScreen { - pub fn init_pipeline(render_state: &egui_wgpu::RenderState) { + fn init_pipeline(render_state: &egui_wgpu::RenderState) { let device = &render_state.device; let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -101,6 +101,8 @@ impl GameScreen { } pub fn init(render_state: &egui_wgpu::RenderState) -> (Self, TextureSink) { + Self::init_pipeline(render_state); + let device = &render_state.device; let queue = &render_state.queue;