diff --git a/src/app.rs b/src/app.rs index f17e5b2..b5decd9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,18 +1,25 @@ -use std::{collections::HashSet, num::NonZero, sync::Arc, thread, time::Duration}; +use std::{ + collections::hash_map::Entry, + num::NonZero, + sync::Arc, + thread, + time::{Duration, Instant}, +}; use egui::{ - Context, FontData, FontDefinitions, FontFamily, IconData, TextWrapMode, ViewportBuilder, - ViewportCommand, ViewportId, ViewportInfo, - ahash::{HashMap, HashMapExt}, - style::ScrollStyle, + Context, FontData, FontDefinitions, FontFamily, IconData, PlatformOutput, RawInput, + TextWrapMode, ViewportBuilder, ViewportCommand, ViewportEvent, ViewportId, ViewportIdMap, + ViewportIdSet, ViewportInfo, style::ScrollStyle, }; +use egui_wgpu::winit::Painter; +use egui_winit::EventResponse; use gilrs::{EventType, Gilrs}; use tracing::{error, warn}; use winit::{ application::ApplicationHandler, event::WindowEvent, - event_loop::{ActiveEventLoop, EventLoopProxy}, - window::Window, + event_loop::{ActiveEventLoop, ControlFlow, EventLoopProxy}, + window::{Window, WindowId}, }; use crate::{ @@ -23,11 +30,7 @@ use crate::{ input::{MappingProvider, ShortcutProvider}, memory::MemoryClient, persistence::Persistence, - window::{ - AboutWindow, AppWindow, BgMapWindow, CharacterDataWindow, FrameBufferWindow, GameWindow, - GdbServerWindow, HotkeysWindow, InitArgs, InputWindow, ObjectWindow, ProfileWindow, - RegisterWindow, TerminalWindow, WorldWindow, - }, + window::{AppWindow, ChildWindow, GameScreen, GameWindow, InitArgs}, }; fn load_icon() -> anyhow::Result { @@ -41,28 +44,23 @@ fn load_icon() -> anyhow::Result { }) } +struct SharedViewportState { + viewport_info: ViewportIdMap, + painter: Painter, + resized_viewport: Option, +} + pub struct Application { - icon: Option>, - wgpu: WgpuState, client: EmulatorClient, - proxy: EventLoopProxy, - mappings: MappingProvider, - shortcuts: ShortcutProvider, + ctx: Context, + shared: SharedViewportState, + icon: Option>, + app: GameWindow, controllers: ControllerManager, - memory: Arc, - images: Arc, - persistence: Persistence, - viewports: HashMap, + viewports: ViewportIdMap, focused: Option, - init_debug_port: Option, - init_profiling: bool, - init_bgmap: bool, - init_chardata: bool, - init_objects: bool, - init_worlds: bool, - init_framebuffers: bool, - init_registers: bool, - init_terminal: bool, + redraw_times: ViewportIdMap, + initial_windows: Vec, } impl Application { @@ -84,132 +82,320 @@ impl Application { let proxy = proxy.clone(); thread::spawn(|| process_gamepad_input(mappings, proxy)); } - let app = Self { - icon, - wgpu, - client, - proxy, - mappings, - shortcuts, - memory, - images, - controllers, - persistence, - viewports: HashMap::new(), - focused: None, - init_debug_port: args.debug_port, - init_profiling: args.profile, - init_bgmap: args.bgmap_data, - init_chardata: args.character_data, - init_objects: args.object_data, - init_worlds: args.worlds, - init_framebuffers: args.frame_buffers, - init_registers: args.registers, - init_terminal: args.terminal, - }; - if args.player2 { - app.client - .send_command(EmulatorCommand::StartSecondSim(args.rom.clone())); + let app = GameWindow::new( + ViewportId::ROOT, + client.clone(), + proxy.clone(), + persistence.clone(), + shortcuts.clone(), + &memory, + &images, + mappings.clone(), + SimId::Player1, + ); - app.proxy - .send_event(UserEvent::OpenPlayer2) - .expect("Failed to open Player 2 window"); + let ctx = Context::default(); + let mut fonts = FontDefinitions::default(); + fonts.font_data.insert( + "Selawik".into(), + Arc::new(FontData::from_static(include_bytes!( + "../assets/selawik.ttf" + ))), + ); + fonts + .families + .get_mut(&FontFamily::Proportional) + .unwrap() + .insert(0, "Selawik".into()); + ctx.set_fonts(fonts); + ctx.global_style_mut(|s| { + s.wrap_mode = Some(TextWrapMode::Extend); + s.visuals.menu_corner_radius = Default::default(); + s.spacing.scroll = ScrollStyle::thin(); + }); + egui_extras::install_image_loaders(&ctx); + ctx.add_texture_loader(images.clone()); + ctx.set_embed_viewports(false); + { + let proxy = proxy.clone(); + ctx.set_request_repaint_callback(move |info| { + let _ = proxy.send_event(UserEvent::RequestRedraw( + info.viewport_id, + Instant::now() + info.delay, + )); + }); + } + + let wgpu_config = egui_wgpu::WgpuConfiguration { + present_mode: wgpu::PresentMode::AutoVsync, + wgpu_setup: egui_wgpu::WgpuSetup::Existing(egui_wgpu::WgpuSetupExisting { + instance: wgpu.instance.clone(), + adapter: wgpu.adapter.clone(), + device: wgpu.device.clone(), + queue: wgpu.queue.clone(), + }), + ..egui_wgpu::WgpuConfiguration::default() + }; + + let options = egui_wgpu::RendererOptions::default(); + let painter = pollster::block_on(Painter::new(ctx.clone(), wgpu_config, false, options)); + + let mut initial_windows = vec![]; + if let Some(port) = args.debug_port { + initial_windows.push(ChildWindow::Debugger { port: Some(port) }); + } + if args.profile { + initial_windows.push(ChildWindow::Profiler { launch: true }); + } + if args.character_data { + initial_windows.push(ChildWindow::CharacterData); + } + if args.bgmap_data { + initial_windows.push(ChildWindow::BgMap); + } + if args.object_data { + initial_windows.push(ChildWindow::Objects); + } + if args.worlds { + initial_windows.push(ChildWindow::Worlds); + } + if args.frame_buffers { + initial_windows.push(ChildWindow::FrameBuffers); + } + if args.registers { + initial_windows.push(ChildWindow::Registers); + } + if args.terminal { + initial_windows.push(ChildWindow::Terminal); + } + if args.player2 { + client.send_command(EmulatorCommand::StartSecondSim(args.rom.clone())); + initial_windows.push(ChildWindow::Player2); + } + + Self { + client, + ctx, + shared: SharedViewportState { + viewport_info: ViewportIdMap::default(), + painter, + resized_viewport: None, + }, + icon, + app, + controllers, + viewports: ViewportIdMap::default(), + focused: None, + redraw_times: ViewportIdMap::default(), + initial_windows, } - app } - fn open(&mut self, event_loop: &ActiveEventLoop, window: Box) { - let viewport_id = window.viewport_id(); - if let Some(viewport) = self.viewports.get(&viewport_id) { - viewport.window.focus_window(); - return; + fn check_repaint(&mut self, event_loop: &ActiveEventLoop) { + let now = Instant::now(); + self.redraw_times.retain(|viewport_id, time| { + if *time > now { + return true; + } + if let Some(viewport) = self.viewports.get(viewport_id) { + viewport.window.request_redraw(); + } + false + }); + if let Some(next_repaint_time) = self.redraw_times.values().min().copied() { + event_loop.set_control_flow(ControlFlow::WaitUntil(next_repaint_time)); } - self.viewports.insert( - viewport_id, - Viewport::new( - &self.images, - event_loop, - &self.wgpu, - self.icon.clone(), - window, - ), - ); + } + + fn repaint_all(&mut self, event_loop: &ActiveEventLoop) { + enum ViewportUpdate { + Keep { + recreate: bool, + builder: ViewportBuilder, + parent: ViewportId, + commands: Vec, + }, + Remove, + } + let mut updates = ViewportIdMap::::default(); + for viewport in self.viewports.values() { + updates.insert( + viewport.id, + ViewportUpdate::Keep { + recreate: false, + builder: viewport.builder.clone(), + parent: self + .shared + .viewport_info + .get(&viewport.id) + .and_then(|vp| vp.parent) + .unwrap_or(ViewportId::ROOT), + commands: vec![], + }, + ); + } + + for viewport in self.viewports.values_mut() { + let mut input = viewport.take_egui_input(); + if !self.shared.viewport_info.contains_key(&viewport.id) { + continue; + } + input.viewports = self.shared.viewport_info.clone(); + let output = self.ctx.run_ui(input, |ui| { + if viewport.id == ViewportId::ROOT { + self.app.show(ui); + } else if let Some(cb) = ui.viewport(|v| v.viewport_ui_cb.clone()) { + cb(ui); + } + }); + viewport.handle_platform_output(output.platform_output); + for (id, update) in updates.iter_mut() { + if !output.viewport_output.contains_key(id) { + self.shared.viewport_info.remove(id); + *update = ViewportUpdate::Remove; + } + } + for (id, mut vp) in output.viewport_output { + match updates.entry(id) { + Entry::Vacant(e) => { + e.insert(ViewportUpdate::Keep { + recreate: true, + builder: vp.builder, + parent: vp.parent, + commands: vp.commands, + }); + } + Entry::Occupied(e) => { + let ViewportUpdate::Keep { + recreate, + builder, + commands, + .. + } = e.into_mut() + else { + continue; + }; + let (_, should_recreate) = builder.patch(vp.builder); + *recreate |= should_recreate; + commands.append(&mut vp.commands); + } + } + } + let clipped_primitives = self.ctx.tessellate(output.shapes, output.pixels_per_point); + self.shared.painter.paint_and_update_textures( + viewport.id, + output.pixels_per_point, + [0.0, 0.0, 0.0, 0.0], + &clipped_primitives, + &output.textures_delta, + vec![], + ); + viewport.window.pre_present_notify(); + } + let mut active_viewports = ViewportIdSet::default(); + for (id, update) in updates { + match update { + ViewportUpdate::Keep { + recreate, + builder, + parent, + commands, + } => { + active_viewports.insert(id); + let manager = self + .viewports + .remove(&id) + .filter(|_| !recreate) + .unwrap_or_else(|| { + let manager = ViewportManager::new( + &self.ctx, + id, + parent, + event_loop, + builder, + &mut self.shared, + ); + self.app.handle_init( + id, + InitArgs { + window: &manager.window, + render_state: &self.shared.painter.render_state().unwrap(), + }, + ); + manager + }); + manager.process_viewport_commands(commands, &mut self.shared); + self.viewports.insert(id, manager); + } + ViewportUpdate::Remove => { + self.viewports.remove(&id); + } + } + } + if !active_viewports.contains(&ViewportId::ROOT) { + event_loop.exit(); + } + self.shared.painter.gc_viewports(&active_viewports); } } impl ApplicationHandler for Application { fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if let Some(port) = self.init_debug_port { - let mut server = - GdbServerWindow::new(SimId::Player1, self.client.clone(), self.proxy.clone()); - server.launch(port); - self.open(event_loop, Box::new(server)); + let mut viewport_builder = self.app.initial_viewport(); + if let Some(icon) = &self.icon { + viewport_builder = viewport_builder.with_icon(icon.clone()); } - let app = GameWindow::new( - self.client.clone(), - self.proxy.clone(), - self.persistence.clone(), - self.shortcuts.clone(), - SimId::Player1, + let manager = ViewportManager::new( + &self.ctx, + ViewportId::ROOT, + ViewportId::ROOT, + event_loop, + viewport_builder, + &mut self.shared, ); - self.open(event_loop, Box::new(app)); - let sim_id = SimId::Player1; - if self.init_profiling { - let mut profiler = ProfileWindow::new(sim_id, self.client.clone()); - profiler.launch(); - self.open(event_loop, Box::new(profiler)); + let render_state = self.shared.painter.render_state().unwrap(); + GameScreen::init_pipeline(&render_state); + self.app.handle_init( + ViewportId::ROOT, + InitArgs { + window: &manager.window, + render_state: &render_state, + }, + ); + self.viewports.insert(ViewportId::ROOT, manager); + for window in std::mem::take(&mut self.initial_windows) { + self.app.open(window); } - if self.init_chardata { - let chardata = CharacterDataWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(chardata)); - } - if self.init_bgmap { - let bgmap = BgMapWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(bgmap)); - } - if self.init_objects { - let objects = ObjectWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(objects)); - } - if self.init_worlds { - let world = WorldWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(world)); - } - if self.init_framebuffers { - let fb = FrameBufferWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(fb)); - } - if self.init_registers { - let registers = RegisterWindow::new(sim_id, &self.memory); - self.open(event_loop, Box::new(registers)); - } - if self.init_terminal { - let terminal = TerminalWindow::new(sim_id, &self.client); - self.open(event_loop, Box::new(terminal)); + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + if let Some(viewport) = self.viewports.get(&ViewportId::ROOT) { + viewport.window.request_redraw(); } } fn window_event( &mut self, event_loop: &ActiveEventLoop, - window_id: winit::window::WindowId, + window_id: WindowId, event: WindowEvent, ) { let Some(viewport) = self .viewports .values_mut() - .find(|v| v.window.id() == window_id) + .find(|v| v.has_window_id(window_id)) else { return; }; - let viewport_id = viewport.id(); - let mut queue_redraw = false; - let mut inactive_viewports = HashSet::new(); - let (consumed, action) = viewport.on_window_event(&event); - if !consumed { + let viewport_id = viewport.id; + let (response, close_requested) = viewport.on_window_event(&event, &mut self.shared); + if close_requested && viewport_id == ViewportId::ROOT { + event_loop.exit(); + } + if !response.consumed { match event { WindowEvent::KeyboardInput { event, .. } => { - if !viewport.app.handle_key_event(&event) { + if !self.app.handle_key_event(viewport_id, &event) { self.controllers.handle_key_event(&event); } } @@ -219,39 +405,10 @@ impl ApplicationHandler for Application { _ => {} } } - match action { - Some(Action::Redraw) => { - for viewport in self.viewports.values_mut() { - match viewport.redraw(event_loop) { - Some(Action::Redraw) => { - queue_redraw = true; - } - Some(Action::Close) => { - inactive_viewports.insert(viewport.id()); - } - None => {} - } - } - } - Some(Action::Close) => { - inactive_viewports.insert(viewport_id); - } - None => {} - } - self.viewports - .retain(|k, _| !inactive_viewports.contains(k)); - match self.viewports.get(&viewport_id) { - Some(viewport) => { - if queue_redraw { - viewport.window.request_redraw(); - } - } - None => { - if viewport_id == ViewportId::ROOT { - event_loop.exit(); - } - } + if response.repaint { + self.repaint_all(event_loop); } + self.check_repaint(event_loop); } fn device_event( @@ -268,96 +425,32 @@ impl ApplicationHandler for Application { else { return; }; - viewport.state.on_mouse_motion(delta); + viewport.on_mouse_motion(delta); } } fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { match event { UserEvent::GamepadEvent(event) => { - if let Some(viewport) = self + if !self .focused - .as_ref() - .and_then(|id| self.viewports.get_mut(id)) - && viewport.app.handle_gamepad_event(&event) + .is_some_and(|id| self.app.handle_gamepad_event(id, &event)) { - return; + self.controllers.handle_gamepad_event(&event); } - self.controllers.handle_gamepad_event(&event); } - UserEvent::OpenAbout => { - let about = AboutWindow; - self.open(event_loop, Box::new(about)); - } - UserEvent::OpenCharacterData(sim_id) => { - let chardata = CharacterDataWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(chardata)); - } - UserEvent::OpenBgMap(sim_id) => { - let bgmap = BgMapWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(bgmap)); - } - UserEvent::OpenObjects(sim_id) => { - let objects = ObjectWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(objects)); - } - UserEvent::OpenWorlds(sim_id) => { - let world = WorldWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(world)); - } - UserEvent::OpenFrameBuffers(sim_id) => { - let fb = FrameBufferWindow::new(sim_id, &self.memory, &self.images); - self.open(event_loop, Box::new(fb)); - } - UserEvent::OpenRegisters(sim_id) => { - let registers = RegisterWindow::new(sim_id, &self.memory); - self.open(event_loop, Box::new(registers)); - } - UserEvent::OpenTerminal(sim_id) => { - let terminal = TerminalWindow::new(sim_id, &self.client); - self.open(event_loop, Box::new(terminal)); - } - UserEvent::OpenProfiler(sim_id) => { - let profile = ProfileWindow::new(sim_id, self.client.clone()); - self.open(event_loop, Box::new(profile)); - } - UserEvent::OpenDebugger(sim_id) => { - let debugger = - GdbServerWindow::new(sim_id, self.client.clone(), self.proxy.clone()); - self.open(event_loop, Box::new(debugger)); - } - UserEvent::OpenInput => { - let input = InputWindow::new(self.mappings.clone()); - self.open(event_loop, Box::new(input)); - } - UserEvent::OpenHotkeys => { - let hotkeys = HotkeysWindow::new(self.shortcuts.clone()); - self.open(event_loop, Box::new(hotkeys)); - } - UserEvent::OpenPlayer2 => { - let p2 = GameWindow::new( - self.client.clone(), - self.proxy.clone(), - self.persistence.clone(), - self.shortcuts.clone(), - SimId::Player2, - ); - self.open(event_loop, Box::new(p2)); - } - UserEvent::Quit(sim_id) => { - self.viewports - .retain(|_, viewport| viewport.app.sim_id() != sim_id); - if !self.viewports.contains_key(&ViewportId::ROOT) { - event_loop.exit(); + UserEvent::Quit(sim_id) => match sim_id { + SimId::Player1 => event_loop.exit(), + SimId::Player2 => self.app.close(ChildWindow::Player2), + }, + UserEvent::RequestRedraw(viewport, when) => { + let scheduled = self.redraw_times.entry(viewport).or_insert(when); + if *scheduled > when { + *scheduled = when; } } } - } - - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - if let Some(viewport) = self.viewports.get(&ViewportId::ROOT) { - viewport.window.request_redraw(); - } + self.check_repaint(event_loop); } fn exiting(&mut self, _event_loop: &ActiveEventLoop) { @@ -420,229 +513,109 @@ impl WgpuState { } } -struct Viewport { - painter: egui_wgpu::winit::Painter, - ctx: Context, - info: ViewportInfo, - commands: Vec, - builder: ViewportBuilder, +struct ViewportManager { + id: ViewportId, window: Arc, state: egui_winit::State, - app: Box, + builder: ViewportBuilder, } -impl Viewport { - pub fn new( - images: &Arc, +impl ViewportManager { + fn new( + ctx: &Context, + viewport_id: ViewportId, + parent_id: ViewportId, event_loop: &ActiveEventLoop, - wgpu: &WgpuState, - icon: Option>, - mut app: Box, + builder: ViewportBuilder, + shared: &mut SharedViewportState, ) -> Self { - let ctx = Context::default(); - let mut fonts = FontDefinitions::default(); - fonts.font_data.insert( - "Selawik".into(), - Arc::new(FontData::from_static(include_bytes!( - "../assets/selawik.ttf" - ))), - ); - fonts - .families - .get_mut(&FontFamily::Proportional) - .unwrap() - .insert(0, "Selawik".into()); - ctx.set_fonts(fonts); - ctx.global_style_mut(|s| { - s.wrap_mode = Some(TextWrapMode::Extend); - s.visuals.menu_corner_radius = Default::default(); - s.spacing.scroll = ScrollStyle::thin(); - }); - egui_extras::install_image_loaders(&ctx); - ctx.add_texture_loader(images.clone()); - - let wgpu_config = egui_wgpu::WgpuConfiguration { - present_mode: wgpu::PresentMode::AutoVsync, - wgpu_setup: egui_wgpu::WgpuSetup::Existing(egui_wgpu::WgpuSetupExisting { - instance: wgpu.instance.clone(), - adapter: wgpu.adapter.clone(), - device: wgpu.device.clone(), - queue: wgpu.queue.clone(), - }), - ..egui_wgpu::WgpuConfiguration::default() - }; - - let options = egui_wgpu::RendererOptions::default(); - let mut painter = pollster::block_on(egui_wgpu::winit::Painter::new( + let window = Arc::new(egui_winit::create_window(ctx, event_loop, &builder).unwrap()); + pollster::block_on(shared.painter.set_window(viewport_id, Some(window.clone()))).unwrap(); + let state = egui_winit::State::new( ctx.clone(), - wgpu_config, - false, - options, - )); + viewport_id, + event_loop, + Some(window.scale_factor() as f32), + event_loop.system_theme(), + shared.painter.max_texture_side(), + ); - let mut info = ViewportInfo::default(); - let mut builder = app.initial_viewport(); - if let Some(icon) = icon { - builder = builder.with_icon(icon); - } - let viewport_id = app.viewport_id(); - let (window, state) = - create_window_and_state(&ctx, viewport_id, event_loop, &builder, &mut painter); - egui_winit::update_viewport_info(&mut info, &ctx, &window, true); - - let render_state = painter.render_state(); - let args = InitArgs { - window: &window, - render_state: render_state.as_ref().unwrap(), + let mut info = ViewportInfo { + parent: Some(parent_id), + ..ViewportInfo::default() }; - app.on_init(args); + egui_winit::update_viewport_info(&mut info, ctx, &window, true); + shared.viewport_info.insert(viewport_id, info); + Self { - painter, - ctx, - info, - commands: vec![], - builder, + id: viewport_id, window, state, - app, + builder, } } - pub fn id(&self) -> ViewportId { - self.app.viewport_id() + fn has_window_id(&self, window_id: WindowId) -> bool { + self.window.id() == window_id } - pub fn on_window_event(&mut self, event: &WindowEvent) -> (bool, 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, - ); + fn on_mouse_motion(&mut self, delta: (f64, f64)) { + self.state.on_mouse_motion(delta); + } - let action = match event { - WindowEvent::RedrawRequested => Some(Action::Redraw), - WindowEvent::CloseRequested => Some(Action::Close), - WindowEvent::Resized(size) => { - if let (Some(width), Some(height)) = - (NonZero::new(size.width), NonZero::new(size.height)) - { - self.painter - .on_window_resized(self.app.viewport_id(), width, height); - } - None + fn on_window_event( + &mut self, + event: &WindowEvent, + shared: &mut SharedViewportState, + ) -> (EventResponse, bool) { + if let WindowEvent::Resized(size) = event + && let (Some(width), Some(height)) = + (NonZero::new(size.width), NonZero::new(size.height)) + { + if shared.resized_viewport != Some(self.id) { + shared.resized_viewport = Some(self.id); + shared.painter.on_window_resize_state_change(self.id, true); } - _ if response.repaint => Some(Action::Redraw), - _ => None, - }; - (response.consumed, action) + shared.painter.on_window_resized(self.id, width, height); + } + let response = self.state.on_window_event(&self.window, event); + let info = shared.viewport_info.get_mut(&self.id).unwrap(); + if let WindowEvent::CloseRequested = event { + info.events.push(ViewportEvent::Close); + } + egui_winit::update_viewport_info(info, self.state.egui_ctx(), &self.window, false); + (response, info.close_requested()) } - fn redraw(&mut self, event_loop: &ActiveEventLoop) -> Option { - let viewport_id = self.app.viewport_id(); - let mut input = self.state.take_egui_input(&self.window); - input.viewports = std::iter::once((viewport_id, self.info.clone())).collect(); - input.viewport_id = viewport_id; - let mut output = self.ctx.run_ui(input, |ui| { - self.app.show(ui); - }); - 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, - vec![], - ); + fn take_egui_input(&mut self) -> RawInput { + self.state.take_egui_input(&self.window) + } + fn handle_platform_output(&mut self, platform_output: PlatformOutput) { self.state - .handle_platform_output(&self.window, output.platform_output); + .handle_platform_output(&self.window, platform_output); + } - let Some(mut viewport_output) = output.viewport_output.remove(&viewport_id) 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, - viewport_id, - 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); - self.commands.append(&mut viewport_output.commands); + fn process_viewport_commands( + &self, + commands: Vec, + shared: &mut SharedViewportState, + ) { + let info = shared.viewport_info.get_mut(&self.id).unwrap(); egui_winit::process_viewport_commands( - &self.ctx, - &mut self.info, - std::mem::take(&mut self.commands), + self.state.egui_ctx(), + info, + commands, &self.window, &mut vec![], ); - - 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), - OpenAbout, - OpenCharacterData(SimId), - OpenBgMap(SimId), - OpenObjects(SimId), - OpenWorlds(SimId), - OpenFrameBuffers(SimId), - OpenRegisters(SimId), - OpenTerminal(SimId), - OpenProfiler(SimId), - OpenDebugger(SimId), - OpenInput, - OpenHotkeys, - OpenPlayer2, Quit(SimId), -} - -pub enum Action { - Redraw, - Close, -} - -fn create_window_and_state( - ctx: &Context, - viewport_id: ViewportId, - event_loop: &ActiveEventLoop, - builder: &ViewportBuilder, - painter: &mut egui_wgpu::winit::Painter, -) -> (Arc, egui_winit::State) { - pollster::block_on(painter.set_window(viewport_id, None)).unwrap(); - let window = Arc::new(egui_winit::create_window(ctx, event_loop, builder).unwrap()); - pollster::block_on(painter.set_window(viewport_id, Some(window.clone()))).unwrap(); - let state = egui_winit::State::new( - ctx.clone(), - viewport_id, - event_loop, - Some(window.scale_factor() as f32), - event_loop.system_theme(), - painter.max_texture_side(), - ); - (window, state) + RequestRedraw(ViewportId, Instant), } fn process_gamepad_input(mappings: MappingProvider, proxy: EventLoopProxy) { diff --git a/src/window.rs b/src/window.rs index 6923d07..55a6ae8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,9 +1,9 @@ use std::sync::Arc; pub use about::AboutWindow; -use egui::{Ui, ViewportBuilder, ViewportId}; -pub use game::GameWindow; -pub use game_screen::DisplayMode; +use egui::{Ui, ViewportBuilder}; +pub use game::{ChildWindow, GameWindow}; +pub use game_screen::{DisplayMode, GameScreen}; pub use gdb::GdbServerWindow; pub use hotkeys::HotkeysWindow; pub use input::InputWindow; @@ -28,7 +28,6 @@ mod utils; mod vip; pub trait AppWindow { - fn viewport_id(&self) -> ViewportId; fn sim_id(&self) -> SimId { SimId::Player1 } @@ -40,7 +39,6 @@ pub trait AppWindow { fn on_init(&mut self, args: InitArgs) { let _ = args; } - fn on_destroy(&mut self) {} fn handle_key_event(&mut self, event: &KeyEvent) -> bool { let _ = event; false diff --git a/src/window/about.rs b/src/window/about.rs index ffc6d69..731b5d9 100644 --- a/src/window/about.rs +++ b/src/window/about.rs @@ -1,14 +1,10 @@ -use egui::{CentralPanel, Image, Ui, ViewportBuilder, ViewportId}; +use egui::{CentralPanel, Image, Ui, ViewportBuilder}; use super::AppWindow; pub struct AboutWindow; impl AppWindow for AboutWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of("About") - } - fn initial_viewport(&self) -> ViewportBuilder { ViewportBuilder::default() .with_title("About Lemur") diff --git a/src/window/game.rs b/src/window/game.rs index f121cac..8cbca38 100644 --- a/src/window/game.rs +++ b/src/window/game.rs @@ -1,5 +1,7 @@ use std::{ - sync::{Arc, mpsc}, + collections::HashMap, + ops::{Deref, DerefMut}, + sync::{Arc, Mutex, atomic::AtomicBool, mpsc}, time::Duration, }; @@ -7,9 +9,15 @@ use crate::{ app::UserEvent, config::{COLOR_PRESETS, SimConfig}, emulator::{EmulatorClient, EmulatorCommand, EmulatorState, SimId, SimState}, - input::{Command, ShortcutProvider}, + images::ImageTextureLoader, + input::{Command, MappingProvider, ShortcutProvider}, + memory::MemoryClient, persistence::Persistence, - window::InitArgs, + window::{ + AboutWindow, BgMapWindow, CharacterDataWindow, FrameBufferWindow, GdbServerWindow, + HotkeysWindow, InitArgs, InputWindow, ObjectWindow, ProfileWindow, RegisterWindow, + TerminalWindow, WorldWindow, + }, }; use anyhow::Context as _; use egui::{ @@ -17,7 +25,7 @@ use egui::{ ViewportBuilder, ViewportCommand, ViewportId, Window, }; use egui_notify::{Anchor, Toast, Toasts}; -use winit::event_loop::EventLoopProxy; +use winit::{event::KeyEvent, event_loop::EventLoopProxy}; use super::{ AppWindow, @@ -26,6 +34,7 @@ use super::{ }; pub struct GameWindow { + viewport_id: ViewportId, client: EmulatorClient, proxy: EventLoopProxy, persistence: Persistence, @@ -34,17 +43,27 @@ pub struct GameWindow { config: SimConfig, toasts: Toasts, screen: Option, - messages: Option>, + messages: mpsc::Receiver, + message_sink: mpsc::Sender, color_picker: Option, window: Option>, + memory: Arc, + images: Arc, + mappings: MappingProvider, + children: HashMap, } impl GameWindow { + #[expect(clippy::too_many_arguments)] pub fn new( + viewport_id: ViewportId, client: EmulatorClient, proxy: EventLoopProxy, persistence: Persistence, shortcuts: ShortcutProvider, + memory: &Arc, + images: &Arc, + mappings: MappingProvider, sim_id: SimId, ) -> Self { let config = SimConfig::load(&persistence, sim_id); @@ -52,7 +71,9 @@ impl GameWindow { .with_anchor(Anchor::BottomLeft) .with_margin((10.0, 10.0).into()) .reverse(true); + let (message_sink, messages) = mpsc::channel(); Self { + viewport_id, client, proxy, persistence, @@ -61,9 +82,135 @@ impl GameWindow { config, toasts, screen: None, - messages: None, + messages, + message_sink, color_picker: None, window: None, + memory: memory.clone(), + images: images.clone(), + mappings: mappings.clone(), + children: HashMap::new(), + } + } + + pub fn open(&mut self, window: ChildWindow) { + let viewport_id = window.viewport_id(self.sim_id); + if self.children.contains_key(&viewport_id) { + return; + } + let child = match window { + ChildWindow::About => AppWrapper::of_dyn(AboutWindow), + ChildWindow::CharacterData => AppWrapper::of_dyn(CharacterDataWindow::new( + self.sim_id, + &self.memory, + &self.images, + )), + ChildWindow::BgMap => { + AppWrapper::of_dyn(BgMapWindow::new(self.sim_id, &self.memory, &self.images)) + } + ChildWindow::Objects => { + AppWrapper::of_dyn(ObjectWindow::new(self.sim_id, &self.memory, &self.images)) + } + ChildWindow::Worlds => { + AppWrapper::of_dyn(WorldWindow::new(self.sim_id, &self.memory, &self.images)) + } + ChildWindow::FrameBuffers => AppWrapper::of_dyn(FrameBufferWindow::new( + self.sim_id, + &self.memory, + &self.images, + )), + ChildWindow::Registers => { + AppWrapper::of_dyn(RegisterWindow::new(self.sim_id, &self.memory)) + } + ChildWindow::Terminal => { + AppWrapper::of_dyn(TerminalWindow::new(self.sim_id, &self.client)) + } + ChildWindow::Profiler { launch } => { + let mut profile = ProfileWindow::new(self.sim_id, self.client.clone()); + if launch { + profile.launch(); + } + AppWrapper::of_dyn(profile) + } + ChildWindow::Debugger { port } => { + let mut debugger = + GdbServerWindow::new(self.sim_id, self.client.clone(), self.proxy.clone()); + if let Some(port) = port { + debugger.launch(port); + } + AppWrapper::of_dyn(debugger) + } + ChildWindow::Input => AppWrapper::of_dyn(InputWindow::new(self.mappings.clone())), + ChildWindow::Hotkeys => AppWrapper::of_dyn(HotkeysWindow::new(self.shortcuts.clone())), + ChildWindow::Player2 => { + if self.sim_id == SimId::Player2 { + return; + } + AppWrapper::of_game(GameWindow::new( + viewport_id, + self.client.clone(), + self.proxy.clone(), + self.persistence.clone(), + self.shortcuts.clone(), + &self.memory, + &self.images, + self.mappings.clone(), + SimId::Player2, + )) + } + }; + + let viewport = child.initial_viewport(); + self.children.insert( + viewport_id, + ChildWindowWrapper { + app: Arc::new(Mutex::new(child)), + updates: Some(viewport), + close_requested: Arc::new(AtomicBool::new(false)), + }, + ); + } + + pub fn close(&mut self, window: ChildWindow) { + let id = window.viewport_id(self.sim_id); + self.children.remove(&id); + } + + pub fn handle_init(&mut self, viewport_id: ViewportId, args: InitArgs) { + self.handle_event(viewport_id, |window| window.on_init(args)) + } + + pub fn handle_key_event(&mut self, viewport_id: ViewportId, event: &KeyEvent) -> bool { + self.handle_event(viewport_id, |window| window.handle_key_event(event)) + } + + pub fn handle_gamepad_event(&mut self, viewport_id: ViewportId, event: &gilrs::Event) -> bool { + self.handle_event(viewport_id, |window| window.handle_gamepad_event(event)) + } + + fn handle_event(&mut self, viewport_id: ViewportId, cb: F) -> R + where + F: FnOnce(&mut dyn AppWindow) -> R, + R: Default, + { + let p2_viewport_id = ChildWindow::Player2.viewport_id(SimId::Player1); + + if self.viewport_id == viewport_id { + cb(self) + } else if let Some(child) = self.children.get_mut(&viewport_id) { + cb(child.app.lock().unwrap().deref_mut().deref_mut()) + } else if let Some(mut p2) = self + .children + .get_mut(&p2_viewport_id) + .map(|w| w.app.lock().unwrap()) + { + if let AppWrapper::Game(g) = p2.deref_mut() { + g.handle_event(viewport_id, cb) + } else { + R::default() + } + } else { + R::default() } } @@ -217,7 +364,7 @@ impl GameWindow { { self.client .send_command(EmulatorCommand::StartSecondSim(None)); - self.proxy.send_event(UserEvent::OpenPlayer2).unwrap(); + self.open(ChildWindow::Player2); } if has_player_2 { let linked = self.client.are_sims_linked(); @@ -231,55 +378,37 @@ impl GameWindow { }); ui.menu_button("Tools", |ui| { if ui.button("Terminal").clicked() { - self.proxy - .send_event(UserEvent::OpenTerminal(self.sim_id)) - .unwrap(); + self.open(ChildWindow::Terminal); } if ui.button("Profiler").clicked() { - self.proxy - .send_event(UserEvent::OpenProfiler(self.sim_id)) - .unwrap(); + self.open(ChildWindow::Profiler { launch: false }); } if ui.button("GDB Server").clicked() { - self.proxy - .send_event(UserEvent::OpenDebugger(self.sim_id)) - .unwrap(); + self.open(ChildWindow::Debugger { port: None }); } ui.separator(); if ui.button("Character Data").clicked() { - self.proxy - .send_event(UserEvent::OpenCharacterData(self.sim_id)) - .unwrap(); + self.open(ChildWindow::CharacterData); } if ui.button("Background Maps").clicked() { - self.proxy - .send_event(UserEvent::OpenBgMap(self.sim_id)) - .unwrap(); + self.open(ChildWindow::BgMap); } if ui.button("Objects").clicked() { - self.proxy - .send_event(UserEvent::OpenObjects(self.sim_id)) - .unwrap(); + self.open(ChildWindow::Objects); } if ui.button("Worlds").clicked() { - self.proxy - .send_event(UserEvent::OpenWorlds(self.sim_id)) - .unwrap(); + self.open(ChildWindow::Worlds); } if ui.button("Frame Buffers").clicked() { - self.proxy - .send_event(UserEvent::OpenFrameBuffers(self.sim_id)) - .unwrap(); + self.open(ChildWindow::FrameBuffers); } if ui.button("Registers").clicked() { - self.proxy - .send_event(UserEvent::OpenRegisters(self.sim_id)) - .unwrap(); + self.open(ChildWindow::Registers); } }); ui.menu_button("Help", |ui| { if ui.button("About").clicked() { - self.proxy.send_event(UserEvent::OpenAbout).unwrap(); + self.open(ChildWindow::About); } }); } @@ -424,11 +553,11 @@ impl GameWindow { }); ui.menu_button("Input", |ui| { if ui.button("Bind Inputs").clicked() { - self.proxy.send_event(UserEvent::OpenInput).unwrap(); + self.open(ChildWindow::Input); } }); if ui.button("Hotkeys").clicked() { - self.proxy.send_event(UserEvent::OpenHotkeys).unwrap(); + self.open(ChildWindow::Hotkeys); } } @@ -480,13 +609,6 @@ impl GameWindow { } impl AppWindow for GameWindow { - fn viewport_id(&self) -> ViewportId { - match self.sim_id { - SimId::Player1 => ViewportId::ROOT, - SimId::Player2 => ViewportId::from_hash_of("Player2"), - } - } - fn sim_id(&self) -> SimId { self.sim_id } @@ -504,10 +626,8 @@ impl AppWindow for GameWindow { }; self.update_config(|c| c.dimensions = dimensions); - if let Some(messages) = self.messages.as_mut() { - while let Ok(toast) = messages.try_recv() { - self.toasts.add(toast); - } + while let Ok(toast) = self.messages.try_recv() { + self.toasts.add(toast); } Panel::top("menubar") .exact_size(22.0) @@ -533,22 +653,43 @@ impl AppWindow for GameWindow { } }); self.toasts.show(ui); + self.children.retain(|id, child| { + if child + .close_requested + .load(std::sync::atomic::Ordering::Relaxed) + { + return false; + } + let app = child.app.clone(); + let viewport_builder = child.updates.take().unwrap_or_default(); + let close_requested = child.close_requested.clone(); + ui.show_viewport_deferred(*id, viewport_builder, move |ui, _| { + app.lock().unwrap().show(ui); + if ui.input(|s| s.viewport().close_requested()) { + close_requested.store(true, std::sync::atomic::Ordering::Relaxed); + } + }); + true + }); + ui.request_repaint_after(Duration::from_millis(10)); } fn on_init(&mut self, args: InitArgs) { - let (screen, sink) = GameScreen::init(args.render_state); - let (message_sink, message_source) = mpsc::channel(); - self.client.send_command(EmulatorCommand::ConnectToSim( - self.sim_id, - sink, - message_sink, - )); - self.screen = Some(screen); - self.messages = Some(message_source); + if self.screen.is_none() { + let (screen, sink) = GameScreen::init(args.render_state); + self.client.send_command(EmulatorCommand::ConnectToSim( + self.sim_id, + sink, + self.message_sink.clone(), + )); + self.screen = Some(screen); + } self.window = Some(args.window.clone()); } +} - fn on_destroy(&mut self) { +impl Drop for GameWindow { + fn drop(&mut self) { if self.sim_id == SimId::Player2 { self.client.send_command(EmulatorCommand::StopSecondSim); } @@ -561,3 +702,77 @@ struct ColorPickerState { just_opened: bool, unpause_on_close: bool, } + +struct ChildWindowWrapper { + app: Arc>, + updates: Option, + close_requested: Arc, +} +enum AppWrapper { + Game(Box), + Dyn(Box), +} +impl AppWrapper { + fn of_game(game: GameWindow) -> Self { + Self::Game(Box::new(game)) + } + fn of_dyn(inner: T) -> Self { + Self::Dyn(Box::new(inner)) + } +} +impl Deref for AppWrapper { + type Target = dyn AppWindow + Send + 'static; + fn deref(&self) -> &Self::Target { + match self { + Self::Dyn(inner) => inner.as_ref(), + Self::Game(inner) => inner.as_ref(), + } + } +} +impl DerefMut for AppWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Self::Dyn(inner) => inner.as_mut(), + Self::Game(inner) => inner.as_mut(), + } + } +} + +#[derive(Debug)] +pub enum ChildWindow { + About, + CharacterData, + BgMap, + Objects, + Worlds, + FrameBuffers, + Registers, + Terminal, + Profiler { launch: bool }, + Debugger { port: Option }, + Input, + Hotkeys, + Player2, +} +impl ChildWindow { + fn viewport_id(&self, sim_id: SimId) -> ViewportId { + ViewportId::from_hash_of(format!("{sim_id:?}{}", self.name())) + } + fn name(&self) -> &'static str { + match self { + Self::About => "About", + Self::CharacterData => "CharacterData", + Self::BgMap => "BgMap", + Self::Objects => "Objects", + Self::Worlds => "Worlds", + Self::FrameBuffers => "FrameBuffers", + Self::Registers => "Registers", + Self::Terminal => "Terminal", + Self::Profiler { .. } => "Profiler", + Self::Debugger { .. } => "Debugger", + Self::Input => "Input", + Self::Hotkeys => "Hotkeys", + Self::Player2 => "Player2", + } + } +} diff --git a/src/window/game_screen.rs b/src/window/game_screen.rs index 6040081..e90d728 100644 --- a/src/window/game_screen.rs +++ b/src/window/game_screen.rs @@ -14,7 +14,7 @@ pub struct GameScreen { } impl GameScreen { - fn init_pipeline(render_state: &egui_wgpu::RenderState) { + pub fn init_pipeline(render_state: &egui_wgpu::RenderState) { let device = &render_state.device; let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -117,8 +117,6 @@ 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; diff --git a/src/window/gdb.rs b/src/window/gdb.rs index f999e01..f22dc40 100644 --- a/src/window/gdb.rs +++ b/src/window/gdb.rs @@ -1,4 +1,4 @@ -use egui::{Button, CentralPanel, TextEdit, ViewportBuilder, ViewportId}; +use egui::{Button, CentralPanel, TextEdit, ViewportBuilder}; use winit::event_loop::EventLoopProxy; use crate::{ @@ -42,10 +42,6 @@ impl GdbServerWindow { } impl AppWindow for GdbServerWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("Debugger-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/hotkeys.rs b/src/window/hotkeys.rs index 9ff95b3..1ce5ace 100644 --- a/src/window/hotkeys.rs +++ b/src/window/hotkeys.rs @@ -1,6 +1,5 @@ use egui::{ Button, CentralPanel, Event, KeyboardShortcut, Label, Layout, Slider, Ui, ViewportBuilder, - ViewportId, }; use egui_extras::{Column, TableBuilder}; @@ -125,10 +124,6 @@ impl HotkeysWindow { } impl AppWindow for HotkeysWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of("shortcuts") - } - fn initial_viewport(&self) -> ViewportBuilder { ViewportBuilder::default() .with_title("Keyboard Shortcuts") diff --git a/src/window/input.rs b/src/window/input.rs index a58c47e..7bcc117 100644 --- a/src/window/input.rs +++ b/src/window/input.rs @@ -1,4 +1,4 @@ -use egui::{Button, CentralPanel, Label, Layout, Panel, Ui, ViewportBuilder, ViewportId}; +use egui::{Button, CentralPanel, Label, Layout, Panel, Ui, ViewportBuilder}; use egui_extras::{Column, TableBuilder}; use gilrs::{EventType, GamepadId}; use std::sync::RwLock; @@ -160,10 +160,6 @@ impl InputWindow { } impl AppWindow for InputWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of("input") - } - fn initial_viewport(&self) -> ViewportBuilder { ViewportBuilder::default() .with_title("Bind Inputs") diff --git a/src/window/profile.rs b/src/window/profile.rs index 044538e..71fb81d 100644 --- a/src/window/profile.rs +++ b/src/window/profile.rs @@ -1,7 +1,7 @@ use std::{fs, sync::Arc, time::Duration}; use anyhow::Result; -use egui::{Button, CentralPanel, Checkbox, Label, ViewportBuilder, ViewportId}; +use egui::{Button, CentralPanel, Checkbox, Label, ViewportBuilder}; use egui_notify::{Anchor, Toast, Toasts}; use winit::window::Window; @@ -82,10 +82,6 @@ impl ProfileWindow { } impl AppWindow for ProfileWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("Profile-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/terminal.rs b/src/window/terminal.rs index 6e2c0eb..c4877e5 100644 --- a/src/window/terminal.rs +++ b/src/window/terminal.rs @@ -1,8 +1,6 @@ use std::{collections::VecDeque, sync::mpsc}; -use egui::{ - Align, CentralPanel, FontFamily, Label, RichText, ScrollArea, Ui, ViewportBuilder, ViewportId, -}; +use egui::{Align, CentralPanel, FontFamily, Label, RichText, ScrollArea, Ui, ViewportBuilder}; use crate::emulator::{EmulatorClient, EmulatorCommand, SimId}; @@ -31,10 +29,6 @@ impl TerminalWindow { } impl AppWindow for TerminalWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("terminal-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/vip/bgmap.rs b/src/window/vip/bgmap.rs index d122ef2..86891b9 100644 --- a/src/window/vip/bgmap.rs +++ b/src/window/vip/bgmap.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use egui::{ Align, CentralPanel, Checkbox, Color32, ComboBox, Image, ScrollArea, Slider, TextEdit, - TextureOptions, Ui, ViewportBuilder, ViewportId, + TextureOptions, Ui, ViewportBuilder, }; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; @@ -169,10 +169,6 @@ impl BgMapWindow { } impl AppWindow for BgMapWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("bgmap-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/vip/chardata.rs b/src/window/vip/chardata.rs index 660a00c..7ed2fc5 100644 --- a/src/window/vip/chardata.rs +++ b/src/window/vip/chardata.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use egui::{ Align, CentralPanel, Color32, ComboBox, Image, ScrollArea, Slider, TextEdit, TextureOptions, - Ui, Vec2, ViewportBuilder, ViewportId, + Ui, Vec2, ViewportBuilder, }; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use serde::{Deserialize, Serialize}; @@ -231,10 +231,6 @@ impl CharacterDataWindow { } impl AppWindow for CharacterDataWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("chardata-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/vip/framebuffer.rs b/src/window/vip/framebuffer.rs index 23cf9d5..86a673c 100644 --- a/src/window/vip/framebuffer.rs +++ b/src/window/vip/framebuffer.rs @@ -1,6 +1,6 @@ use egui::{ Align, CentralPanel, Color32, Image, ScrollArea, Slider, TextEdit, TextureOptions, Ui, - ViewportBuilder, ViewportId, + ViewportBuilder, }; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; @@ -134,10 +134,6 @@ impl FrameBufferWindow { } impl AppWindow for FrameBufferWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("framebuffer-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/vip/object.rs b/src/window/vip/object.rs index 8474610..32bc58d 100644 --- a/src/window/vip/object.rs +++ b/src/window/vip/object.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use egui::{ Align, CentralPanel, Checkbox, Color32, ComboBox, Image, ScrollArea, Slider, TextEdit, - TextureOptions, Ui, ViewportBuilder, ViewportId, + TextureOptions, Ui, ViewportBuilder, }; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; @@ -190,10 +190,6 @@ impl ObjectWindow { } impl AppWindow for ObjectWindow { - fn viewport_id(&self) -> egui::ViewportId { - ViewportId::from_hash_of(format!("object-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/vip/registers.rs b/src/window/vip/registers.rs index a72bfbe..5204dab 100644 --- a/src/window/vip/registers.rs +++ b/src/window/vip/registers.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use egui::{ Align, Button, CentralPanel, Checkbox, Color32, Direction, Label, Layout, ScrollArea, TextEdit, - Ui, ViewportBuilder, ViewportId, + Ui, ViewportBuilder, }; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; @@ -630,10 +630,6 @@ fn read_address(registers: &MemoryRef, address: usize) -> T { } impl AppWindow for RegisterWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("registers-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id } diff --git a/src/window/vip/world.rs b/src/window/vip/world.rs index 511bdbd..1c32071 100644 --- a/src/window/vip/world.rs +++ b/src/window/vip/world.rs @@ -2,7 +2,7 @@ use std::{fmt::Display, sync::Arc}; use egui::{ Align, CentralPanel, Checkbox, Color32, ComboBox, Image, ScrollArea, Slider, TextEdit, - TextureOptions, Ui, ViewportBuilder, ViewportId, + TextureOptions, Ui, ViewportBuilder, }; use egui_extras::{Column, Size, StripBuilder, TableBuilder}; use fixed::{ @@ -506,10 +506,6 @@ impl WorldWindow { } impl AppWindow for WorldWindow { - fn viewport_id(&self) -> ViewportId { - ViewportId::from_hash_of(format!("world-{}", self.sim_id)) - } - fn sim_id(&self) -> SimId { self.sim_id }