Implement multiplayer #2
44
src/app.rs
44
src/app.rs
|
@ -2,7 +2,8 @@ use std::{collections::HashSet, num::NonZero, sync::Arc, thread};
|
||||||
|
|
||||||
use egui::{
|
use egui::{
|
||||||
ahash::{HashMap, HashMapExt},
|
ahash::{HashMap, HashMapExt},
|
||||||
Context, TextWrapMode, ViewportBuilder, ViewportCommand, ViewportId, ViewportInfo,
|
Context, FontData, FontDefinitions, FontFamily, TextWrapMode, ViewportBuilder, ViewportCommand,
|
||||||
|
ViewportId, ViewportInfo,
|
||||||
};
|
};
|
||||||
use gilrs::{EventType, Gilrs};
|
use gilrs::{EventType, Gilrs};
|
||||||
use winit::{
|
use winit::{
|
||||||
|
@ -89,20 +90,37 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
let mut queue_redraw = false;
|
||||||
|
let mut inactive_viewports = HashSet::new();
|
||||||
match viewport.on_window_event(event) {
|
match viewport.on_window_event(event) {
|
||||||
Some(Action::Redraw) => {
|
Some(Action::Redraw) => {
|
||||||
for viewport in self.viewports.values_mut() {
|
for viewport in self.viewports.values_mut() {
|
||||||
viewport.redraw(event_loop);
|
match viewport.redraw(event_loop) {
|
||||||
|
Some(Action::Redraw) => {
|
||||||
|
queue_redraw = true;
|
||||||
|
}
|
||||||
|
Some(Action::Close) => {
|
||||||
|
inactive_viewports.insert(viewport.id());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Action::Close) => {
|
Some(Action::Close) => {
|
||||||
self.viewports.remove(&viewport_id);
|
inactive_viewports.insert(viewport_id);
|
||||||
if viewport_id == ViewportId::ROOT {
|
|
||||||
event_loop.exit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
self.viewports
|
||||||
|
.retain(|k, _| !inactive_viewports.contains(k));
|
||||||
|
match self.viewports.get(&ViewportId::ROOT) {
|
||||||
|
Some(viewport) => {
|
||||||
|
if queue_redraw {
|
||||||
|
viewport.window.request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => event_loop.exit(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_event(
|
fn device_event(
|
||||||
|
@ -165,6 +183,17 @@ impl Viewport {
|
||||||
);
|
);
|
||||||
|
|
||||||
let ctx = Context::default();
|
let ctx = Context::default();
|
||||||
|
let mut fonts = FontDefinitions::empty();
|
||||||
|
fonts.font_data.insert(
|
||||||
|
"Selawik".into(),
|
||||||
|
FontData::from_static(include_bytes!("../assets/selawik.ttf")),
|
||||||
|
);
|
||||||
|
fonts
|
||||||
|
.families
|
||||||
|
.get_mut(&FontFamily::Proportional)
|
||||||
|
.unwrap()
|
||||||
|
.insert(0, "Selawik".into());
|
||||||
|
ctx.set_fonts(fonts);
|
||||||
ctx.style_mut(|s| {
|
ctx.style_mut(|s| {
|
||||||
s.wrap_mode = Some(TextWrapMode::Extend);
|
s.wrap_mode = Some(TextWrapMode::Extend);
|
||||||
s.visuals.menu_rounding = Default::default();
|
s.visuals.menu_rounding = Default::default();
|
||||||
|
@ -242,7 +271,7 @@ impl Viewport {
|
||||||
self.state
|
self.state
|
||||||
.handle_platform_output(&self.window, output.platform_output);
|
.handle_platform_output(&self.window, output.platform_output);
|
||||||
|
|
||||||
let Some(viewport_output) = output.viewport_output.remove(&ViewportId::ROOT) else {
|
let Some(mut viewport_output) = output.viewport_output.remove(&ViewportId::ROOT) else {
|
||||||
return Some(Action::Close);
|
return Some(Action::Close);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -255,6 +284,7 @@ impl Viewport {
|
||||||
self.state = state;
|
self.state = state;
|
||||||
}
|
}
|
||||||
self.commands.append(&mut deferred_commands);
|
self.commands.append(&mut deferred_commands);
|
||||||
|
self.commands.append(&mut viewport_output.commands);
|
||||||
egui_winit::process_viewport_commands(
|
egui_winit::process_viewport_commands(
|
||||||
&self.ctx,
|
&self.ctx,
|
||||||
&mut self.info,
|
&mut self.info,
|
||||||
|
|
|
@ -3,8 +3,8 @@ use crate::{
|
||||||
emulator::{EmulatorClient, EmulatorCommand, SimId},
|
emulator::{EmulatorClient, EmulatorCommand, SimId},
|
||||||
};
|
};
|
||||||
use egui::{
|
use egui::{
|
||||||
menu, Button, CentralPanel, Color32, Context, Response, TopBottomPanel, Ui, ViewportBuilder,
|
menu, Button, CentralPanel, Color32, Context, Frame, Response, TopBottomPanel, Ui,
|
||||||
ViewportCommand, ViewportId, WidgetText,
|
ViewportBuilder, ViewportCommand, ViewportId, WidgetText,
|
||||||
};
|
};
|
||||||
use winit::event_loop::EventLoopProxy;
|
use winit::event_loop::EventLoopProxy;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ impl GameWindow {
|
||||||
for scale in 1..=4 {
|
for scale in 1..=4 {
|
||||||
let label = format!("x{scale}");
|
let label = format!("x{scale}");
|
||||||
let scale = scale as f32;
|
let scale = scale as f32;
|
||||||
let dims = (384.0 * scale, 224.0 * scale + 20.0).into();
|
let dims = (384.0 * scale, 224.0 * scale + 22.0).into();
|
||||||
if ui
|
if ui
|
||||||
.selectable_button((current_dims - dims).length() < 1.0, label)
|
.selectable_button((current_dims - dims).length() < 1.0, label)
|
||||||
.clicked()
|
.clicked()
|
||||||
|
@ -133,18 +133,19 @@ impl AppWindow for GameWindow {
|
||||||
fn initial_viewport(&self) -> ViewportBuilder {
|
fn initial_viewport(&self) -> ViewportBuilder {
|
||||||
ViewportBuilder::default()
|
ViewportBuilder::default()
|
||||||
.with_title("Shrooms VB")
|
.with_title("Shrooms VB")
|
||||||
.with_inner_size((384.0, 244.0))
|
.with_inner_size((384.0, 246.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context) {
|
fn show(&mut self, ctx: &Context) {
|
||||||
TopBottomPanel::top("menubar")
|
TopBottomPanel::top("menubar")
|
||||||
.exact_height(20.0)
|
.exact_height(22.0)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
menu::bar(ui, |ui| {
|
menu::bar(ui, |ui| {
|
||||||
self.show_menu(ctx, ui);
|
self.show_menu(ctx, ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
let frame = Frame::central_panel(&ctx.style()).fill(Color32::BLACK);
|
||||||
|
CentralPanel::default().frame(frame).show(ctx, |ui| {
|
||||||
if let Some(screen) = self.screen.as_ref() {
|
if let Some(screen) = self.screen.as_ref() {
|
||||||
ui.add(screen);
|
ui.add(screen);
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,14 +171,24 @@ struct GameScreenCallback {
|
||||||
impl egui_wgpu::CallbackTrait for GameScreenCallback {
|
impl egui_wgpu::CallbackTrait for GameScreenCallback {
|
||||||
fn paint(
|
fn paint(
|
||||||
&self,
|
&self,
|
||||||
_info: egui::PaintCallbackInfo,
|
info: egui::PaintCallbackInfo,
|
||||||
render_pass: &mut wgpu::RenderPass<'static>,
|
render_pass: &mut wgpu::RenderPass<'static>,
|
||||||
callback_resources: &egui_wgpu::CallbackResources,
|
callback_resources: &egui_wgpu::CallbackResources,
|
||||||
) {
|
) {
|
||||||
let resources: &SharedGameScreenResources = callback_resources.get().unwrap();
|
let resources: &SharedGameScreenResources = callback_resources.get().unwrap();
|
||||||
// TODO: maintain aspect ratio
|
let viewport = info.viewport_in_pixels();
|
||||||
|
let left = viewport.left_px as f32;
|
||||||
|
let top = viewport.top_px as f32;
|
||||||
|
let width = viewport.width_px as f32;
|
||||||
|
let height = viewport.height_px as f32;
|
||||||
|
let aspect_ratio = 384.0 / 224.0;
|
||||||
|
let w = width.min(height * aspect_ratio);
|
||||||
|
let h = height.min(width / aspect_ratio);
|
||||||
|
let x = left + (width - w) / 2.0;
|
||||||
|
let y = top + (height - h) / 2.0;
|
||||||
render_pass.set_pipeline(&resources.pipeline);
|
render_pass.set_pipeline(&resources.pipeline);
|
||||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
render_pass.set_viewport(x, y, w, h, 0.0, 1.0);
|
||||||
render_pass.draw(0..6, 0..1);
|
render_pass.draw(0..6, 0..1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue