Implement multiplayer #2
			
				
			
		
		
		
	| 
						 | 
					@ -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<UserEvent>,
 | 
				
			||||||
 | 
					    mappings: MappingProvider,
 | 
				
			||||||
 | 
					    controllers: ControllerManager,
 | 
				
			||||||
 | 
					    viewports: HashMap<ViewportId, Viewport>,
 | 
				
			||||||
 | 
					    focused: Option<ViewportId>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Application {
 | 
				
			||||||
 | 
					    pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>) -> 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<dyn AppWindow>) {
 | 
				
			||||||
 | 
					        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<UserEvent> 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<ViewportCommand>,
 | 
				
			||||||
 | 
					    builder: ViewportBuilder,
 | 
				
			||||||
 | 
					    window: Arc<Window>,
 | 
				
			||||||
 | 
					    state: egui_winit::State,
 | 
				
			||||||
 | 
					    app: Box<dyn AppWindow>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl Viewport {
 | 
				
			||||||
 | 
					    pub fn new(event_loop: &ActiveEventLoop, mut app: Box<dyn AppWindow>) -> 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<Action> {
 | 
				
			||||||
 | 
					        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<Action> {
 | 
				
			||||||
 | 
					        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<Window>, 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<UserEvent>) {
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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<UserEvent>,
 | 
					 | 
				
			||||||
    state: Option<AppState>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Application {
 | 
					 | 
				
			||||||
    pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            client,
 | 
					 | 
				
			||||||
            proxy,
 | 
					 | 
				
			||||||
            state: None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl winit::application::ApplicationHandler<UserEvent> 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<egui::ViewportId, ViewportState>,
 | 
					 | 
				
			||||||
    viewports_by_window: HashMap<winit::window::WindowId, egui::ViewportId>,
 | 
					 | 
				
			||||||
    focused: Option<egui::ViewportId>,
 | 
					 | 
				
			||||||
    screen: WindowManager,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
impl AppState {
 | 
					 | 
				
			||||||
    fn new(
 | 
					 | 
				
			||||||
        client: EmulatorClient,
 | 
					 | 
				
			||||||
        proxy: EventLoopProxy<UserEvent>,
 | 
					 | 
				
			||||||
        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<Arc<egui::DeferredViewportUiCallback>>,
 | 
					 | 
				
			||||||
    window: Arc<winit::window::Window>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,13 @@
 | 
				
			||||||
use std::{path::PathBuf, process};
 | 
					use std::{path::PathBuf, process};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::Result;
 | 
					use anyhow::Result;
 | 
				
			||||||
use application::Application;
 | 
					use app::Application;
 | 
				
			||||||
use clap::Parser;
 | 
					use clap::Parser;
 | 
				
			||||||
use emulator::EmulatorBuilder;
 | 
					use emulator::EmulatorBuilder;
 | 
				
			||||||
use thread_priority::{ThreadBuilder, ThreadPriority};
 | 
					use thread_priority::{ThreadBuilder, ThreadPriority};
 | 
				
			||||||
use winit::event_loop::{ControlFlow, EventLoop};
 | 
					use winit::event_loop::{ControlFlow, EventLoop};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod application;
 | 
					mod app;
 | 
				
			||||||
mod audio;
 | 
					mod audio;
 | 
				
			||||||
mod controller;
 | 
					mod controller;
 | 
				
			||||||
mod emulator;
 | 
					mod emulator;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										152
									
								
								src/window.rs
								
								
								
								
							
							
						
						
									
										152
									
								
								src/window.rs
								
								
								
								
							| 
						 | 
					@ -1,157 +1,21 @@
 | 
				
			||||||
use std::{
 | 
					 | 
				
			||||||
    sync::{
 | 
					 | 
				
			||||||
        atomic::{AtomicBool, Ordering},
 | 
					 | 
				
			||||||
        Arc, Mutex,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    thread,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use egui::{Context, ViewportBuilder, ViewportId};
 | 
					use egui::{Context, ViewportBuilder, ViewportId};
 | 
				
			||||||
use game::GameWindow;
 | 
					pub use game::GameWindow;
 | 
				
			||||||
use game_screen::GameScreen;
 | 
					pub use input::InputWindow;
 | 
				
			||||||
use gilrs::{EventType, Gilrs};
 | 
					use winit::event::KeyEvent;
 | 
				
			||||||
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;
 | 
				
			||||||
mod game_screen;
 | 
					mod game_screen;
 | 
				
			||||||
mod input;
 | 
					mod input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct WindowManager {
 | 
					pub trait AppWindow {
 | 
				
			||||||
    p1: GameWindow,
 | 
					 | 
				
			||||||
    p2: Arc<Mutex<GameWindow>>,
 | 
					 | 
				
			||||||
    client: EmulatorClient,
 | 
					 | 
				
			||||||
    input: ChildWindow<InputWindow>,
 | 
					 | 
				
			||||||
    controllers: ControllerManager,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
impl WindowManager {
 | 
					 | 
				
			||||||
    pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>) -> 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<UserEvent>) {
 | 
					 | 
				
			||||||
    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 viewport_id(&self) -> ViewportId;
 | 
				
			||||||
    fn initial_viewport(&self) -> ViewportBuilder;
 | 
					    fn initial_viewport(&self) -> ViewportBuilder;
 | 
				
			||||||
    fn show(&mut self, ctx: &Context);
 | 
					    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) {
 | 
					    fn handle_key_event(&mut self, event: &KeyEvent) {
 | 
				
			||||||
        let _ = event;
 | 
					        let _ = event;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ChildWindow<T: AppWindow> {
 | 
					 | 
				
			||||||
    pub open: Arc<AtomicBool>,
 | 
					 | 
				
			||||||
    window: Arc<Mutex<T>>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
impl<T: AppWindow + Send + 'static> ChildWindow<T> {
 | 
					 | 
				
			||||||
    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);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,40 +1,32 @@
 | 
				
			||||||
use std::sync::{
 | 
					use crate::{
 | 
				
			||||||
    atomic::{AtomicBool, Ordering},
 | 
					    app::UserEvent,
 | 
				
			||||||
    Arc,
 | 
					    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
					 | 
				
			||||||
use egui::{
 | 
					use egui::{
 | 
				
			||||||
    menu, Button, CentralPanel, Color32, Context, Response, TopBottomPanel, Ui, ViewportBuilder,
 | 
					    menu, Button, CentralPanel, Color32, Context, Response, TopBottomPanel, Ui, ViewportBuilder,
 | 
				
			||||||
    ViewportCommand, ViewportId, WidgetText,
 | 
					    ViewportCommand, ViewportId, WidgetText,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					use winit::event_loop::EventLoopProxy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{game_screen::GameScreen, AppWindow};
 | 
					use super::{game_screen::GameScreen, AppWindow};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct GameWindow {
 | 
					pub struct GameWindow {
 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
    input_window_open: Arc<AtomicBool>,
 | 
					 | 
				
			||||||
    screen: Option<GameScreen>,
 | 
					    screen: Option<GameScreen>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl GameWindow {
 | 
					impl GameWindow {
 | 
				
			||||||
    pub fn new(client: EmulatorClient, sim_id: SimId, input_window_open: Arc<AtomicBool>) -> Self {
 | 
					    pub fn new(client: EmulatorClient, proxy: EventLoopProxy<UserEvent>, sim_id: SimId) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
 | 
					            proxy,
 | 
				
			||||||
            sim_id,
 | 
					            sim_id,
 | 
				
			||||||
            input_window_open,
 | 
					 | 
				
			||||||
            screen: None,
 | 
					            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) {
 | 
					    fn show_menu(&mut self, ctx: &Context, ui: &mut Ui) {
 | 
				
			||||||
        ui.menu_button("ROM", |ui| {
 | 
					        ui.menu_button("ROM", |ui| {
 | 
				
			||||||
            if ui.button("Open ROM").clicked() {
 | 
					            if ui.button("Open ROM").clicked() {
 | 
				
			||||||
| 
						 | 
					@ -101,7 +93,7 @@ impl GameWindow {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        ui.menu_button("Input", |ui| {
 | 
					        ui.menu_button("Input", |ui| {
 | 
				
			||||||
            if ui.button("Bind Inputs").clicked() {
 | 
					            if ui.button("Bind Inputs").clicked() {
 | 
				
			||||||
                self.input_window_open.store(true, Ordering::Relaxed);
 | 
					                self.proxy.send_event(UserEvent::OpenInput).unwrap();
 | 
				
			||||||
                ui.close_menu();
 | 
					                ui.close_menu();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					@ -112,6 +104,7 @@ impl GameWindow {
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                self.client
 | 
					                self.client
 | 
				
			||||||
                    .send_command(EmulatorCommand::StartSecondSim(None));
 | 
					                    .send_command(EmulatorCommand::StartSecondSim(None));
 | 
				
			||||||
 | 
					                self.proxy.send_event(UserEvent::OpenPlayer2).unwrap();
 | 
				
			||||||
                ui.close_menu();
 | 
					                ui.close_menu();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if self.client.has_player_2() {
 | 
					            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 {
 | 
					trait UiExt {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ pub struct GameScreen {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl 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 device = &render_state.device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
 | 
					        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) {
 | 
					    pub fn init(render_state: &egui_wgpu::RenderState) -> (Self, TextureSink) {
 | 
				
			||||||
 | 
					        Self::init_pipeline(render_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let device = &render_state.device;
 | 
					        let device = &render_state.device;
 | 
				
			||||||
        let queue = &render_state.queue;
 | 
					        let queue = &render_state.queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue