Skip to content

Commit

Permalink
fix: fixed wasm resizing to be restricted by browser viewport (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukexor authored May 22, 2024
1 parent 3ca03ac commit b59d4c9
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 39 deletions.
13 changes: 10 additions & 3 deletions tetanes/src/nes/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,17 @@ impl Config {
}

#[must_use]
pub fn window_size(&self) -> egui::Vec2 {
let scale = self.renderer.scale;
pub fn window_size(&self, aspect_ratio: f32) -> egui::Vec2 {
self.window_size_for_scale(aspect_ratio, self.renderer.scale)
}

#[must_use]
pub fn window_size_for_scale(&self, aspect_ratio: f32, scale: f32) -> egui::Vec2 {
let texture_size = self.texture_size();
egui::Vec2::new(scale * texture_size.x, scale * texture_size.y)
egui::Vec2::new(
(scale * aspect_ratio * texture_size.x).ceil(),
(scale * texture_size.y).ceil(),
)
}

#[must_use]
Expand Down
2 changes: 1 addition & 1 deletion tetanes/src/nes/emulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ impl State {
ConfigEvent::ZapperConnected(connected) => {
self.control_deck.connect_zapper(*connected);
}
ConfigEvent::HideOverscan(_) | ConfigEvent::InputBindings | ConfigEvent::Scale(_) => (),
ConfigEvent::HideOverscan(_) | ConfigEvent::InputBindings => (),
}
}

Expand Down
22 changes: 18 additions & 4 deletions tetanes/src/nes/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ pub enum ConfigEvent {
RewindInterval(u32),
RunAhead(usize),
SaveSlot(u8),
Scale(f32),
Speed(f32),
VideoFilter(VideoFilter),
ZapperConnected(bool),
Expand Down Expand Up @@ -136,6 +135,8 @@ pub enum EmulationEvent {
#[derive(Debug, Clone)]
#[must_use]
pub enum RendererEvent {
#[cfg(target_arch = "wasm32")]
BrowserResized((f32, f32)),
FrameStats(FrameStats),
ShowMenubar(bool),
ScaleChanged,
Expand Down Expand Up @@ -333,7 +334,12 @@ impl Running {
Event::WindowEvent {
window_id, event, ..
} => {
let res = self.renderer.on_window_event(window_id, &event);
let res = self.renderer.on_window_event(
window_id,
&event,
#[cfg(target_arch = "wasm32")]
&self.cfg,
);
if res.repaint {
self.repaint_times.insert(window_id, Instant::now());
}
Expand Down Expand Up @@ -397,7 +403,11 @@ impl Running {
if matches!(event, NesEvent::Emulation(_) | NesEvent::Config(_)) {
self.emulation.on_event(&event);
}
self.renderer.on_event(&event);
self.renderer.on_event(
&event,
#[cfg(target_arch = "wasm32")]
&self.cfg,
);

match event {
NesEvent::Config(ConfigEvent::InputBindings) => {
Expand Down Expand Up @@ -497,7 +507,11 @@ impl Running {
trace!("Nes event: {event:?}");

self.emulation.on_event(&event);
self.renderer.on_event(&event);
self.renderer.on_event(
&event,
#[cfg(target_arch = "wasm32")]
&self.cfg,
);
match event {
NesEvent::Ui(event) => self.on_ui_event(event),
NesEvent::Emulation(EmulationEvent::LoadRomPath(path)) => {
Expand Down
90 changes: 77 additions & 13 deletions tetanes/src/nes/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ impl Renderer {
.and_then(|viewport| viewport.window.clone())
}

pub fn window_size(&self, cfg: &Config) -> Vec2 {
self.window_size_for_scale(cfg, cfg.renderer.scale)
}

pub fn window_size_for_scale(&self, cfg: &Config, scale: f32) -> Vec2 {
let aspect_ratio = self.gui.aspect_ratio(cfg);
let mut window_size = cfg.window_size_for_scale(aspect_ratio, scale);
window_size.x *= aspect_ratio;
window_size.y += self.gui.menu_height;
window_size
}

pub fn root_viewport<R>(&self, reader: impl FnOnce(&Viewport) -> R) -> Option<R> {
self.state
.borrow()
Expand Down Expand Up @@ -344,7 +356,7 @@ impl Renderer {
}

/// Handle event.
pub fn on_event(&mut self, event: &NesEvent) {
pub fn on_event(&mut self, event: &NesEvent, #[cfg(target_arch = "wasm32")] cfg: &Config) {
match event {
NesEvent::Emulation(event) => match event {
EmulationEvent::ReplayRecord(recording) => {
Expand All @@ -359,6 +371,22 @@ impl Renderer {
_ => (),
},
NesEvent::Renderer(event) => match event {
#[cfg(target_arch = "wasm32")]
RendererEvent::BrowserResized((browser_width, _)) => {
if let Some(canvas) = crate::sys::platform::get_canvas() {
let canvas_width = canvas.width() as f32;
let desired_window_size = self.window_size(cfg);

if canvas_width < desired_window_size.x
&& canvas_width < 0.8 * browser_width
{
self.ctx.send_viewport_cmd_to(
ViewportId::ROOT,
ViewportCommand::InnerSize(desired_window_size),
);
}
}
}
RendererEvent::FrameStats(stats) => {
self.gui.frame_stats = *stats;
}
Expand Down Expand Up @@ -435,11 +463,16 @@ impl Renderer {
}

/// Handle window event.
pub fn on_window_event(&mut self, window_id: WindowId, event: &WindowEvent) -> EventResponse {
pub fn on_window_event(
&mut self,
window_id: WindowId,
event: &WindowEvent,
#[cfg(target_arch = "wasm32")] cfg: &Config,
) -> EventResponse {
let viewport_id = self.viewport_id_for_window(window_id);
let mut state = self.state.borrow_mut();
match event {
WindowEvent::Focused(focused) => {
let mut state = self.state.borrow_mut();
state.focused = focused.then(|| viewport_id).flatten();
if let Some(viewport) = viewport_id
.as_ref()
Expand All @@ -452,6 +485,7 @@ impl Renderer {
}
}
WindowEvent::Occluded(occluded) => {
let mut state = self.state.borrow_mut();
// Note: Does not trigger on all platforms
if let Some(viewport) = viewport_id
.as_ref()
Expand All @@ -466,6 +500,7 @@ impl Renderer {
}
WindowEvent::CloseRequested | WindowEvent::Destroyed => {
if let Some(viewport_id) = viewport_id {
let mut state = self.state.borrow_mut();
if viewport_id == ViewportId::ROOT {
self.tx.nes_event(UiEvent::Terminate);
} else if let Some(viewport) = state.viewports.get_mut(&viewport_id) {
Expand All @@ -485,10 +520,42 @@ impl Renderer {
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
state
.painter
.borrow_mut()
.on_window_resized(viewport_id, width, height);
{
self.state
.borrow_mut()
.painter
.borrow_mut()
.on_window_resized(viewport_id, width, height);
}

#[cfg(target_arch = "wasm32")]
if let Some(canvas) = crate::sys::platform::get_canvas() {
// On wasm, width is constrained by the browser
if !self.fullscreen() {
let aspect_ratio = self.gui.aspect_ratio(cfg);
let canvas_width = canvas.width() as f32;

let desired_window_size = self.window_size(cfg);
if canvas_width < desired_window_size.x {
let current_scale = cfg.renderer.scale;
let actual_scale =
canvas_width as f32 / (aspect_ratio * Ppu::WIDTH as f32);
if current_scale > actual_scale {
let mut window_size =
self.window_size_for_scale(cfg, actual_scale);
window_size.x = canvas_width;
self.ctx.send_viewport_cmd_to(
ViewportId::ROOT,
ViewportCommand::InnerSize(window_size),
);
self.add_message(
MessageType::Warn,
"Configured window scale exceeds browser width.",
);
}
}
}
}
}
}
}
Expand All @@ -503,6 +570,7 @@ impl Renderer {
_ => (),
}

let mut state = self.state.borrow_mut();
let mut res = viewport_id
.and_then(|viewport_id| {
state.viewports.get_mut(&viewport_id).and_then(|viewport| {
Expand Down Expand Up @@ -557,7 +625,7 @@ impl Renderer {
ctx: &egui::Context,
cfg: &Config,
) -> anyhow::Result<(Window, ViewportBuilder)> {
let window_size = cfg.window_size();
let window_size = cfg.window_size(cfg.deck.region.aspect_ratio());
let viewport_builder = ViewportBuilder::default()
.with_app_id(Config::WINDOW_TITLE)
.with_title(Config::WINDOW_TITLE)
Expand Down Expand Up @@ -922,13 +990,9 @@ impl Renderer {

if self.gui.resize_window {
if !self.fullscreen() {
let aspect_ratio = self.gui.aspect_ratio(cfg);
let mut window_size = cfg.window_size();
window_size.x *= aspect_ratio;
window_size.y += self.gui.menu_height;
self.ctx.send_viewport_cmd_to(
ViewportId::ROOT,
ViewportCommand::InnerSize(window_size),
ViewportCommand::InnerSize(self.window_size(cfg)),
);
}
self.gui.resize_window = false;
Expand Down
14 changes: 4 additions & 10 deletions tetanes/src/nes/renderer/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
action::{Action, Debug, DebugStep, Debugger, Feature, Setting, Ui as UiAction},
config::Config,
emulation::FrameStats,
event::{ConfigEvent, EmulationEvent, NesEvent, SendNesEvent, UiEvent},
event::{ConfigEvent, EmulationEvent, NesEvent, RendererEvent, SendNesEvent, UiEvent},
input::{ActionBindings, Gamepads, Input},
rom::{RomAsset, HOMEBREW_ROMS},
version::Version,
Expand Down Expand Up @@ -1194,9 +1194,7 @@ impl Gui {
if ui.add(button).clicked() {
let new_scale = cfg.increment_scale();
if scale != new_scale {
self.resize_window = true;
self.resize_texture = true;
self.tx.nes_event(ConfigEvent::Scale(cfg.renderer.scale));
self.tx.nes_event(RendererEvent::ScaleChanged);
}
}

Expand All @@ -1205,9 +1203,7 @@ impl Gui {
if ui.add(button).clicked() {
let new_scale = cfg.decrement_scale();
if scale != new_scale {
self.resize_window = true;
self.resize_texture = true;
self.tx.nes_event(ConfigEvent::Scale(cfg.renderer.scale));
self.tx.nes_event(RendererEvent::ScaleChanged);
}
}

Expand Down Expand Up @@ -2575,9 +2571,7 @@ impl Gui {
ui.radio_value(&mut cfg.renderer.scale, 5.0, "5x");
});
if scale != cfg.renderer.scale {
self.resize_window = true;
self.resize_texture = true;
self.tx.nes_event(ConfigEvent::Scale(cfg.renderer.scale));
self.tx.nes_event(RendererEvent::ScaleChanged);
}
}

Expand Down
42 changes: 34 additions & 8 deletions tetanes/src/sys/platform/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
nes::{
event::{EmulationEvent, NesEvent, ReplayData, SendNesEvent, UiEvent},
event::{EmulationEvent, NesEvent, RendererEvent, ReplayData, SendNesEvent, UiEvent},
rom::RomData,
Running,
},
Expand Down Expand Up @@ -59,7 +59,7 @@ impl Initialize for Running {
for input_id in [html_ids::ROM_INPUT, html_ids::REPLAY_INPUT] {
let on_change = Closure::<dyn FnMut(_)>::new({
let tx = self.tx.clone();
move |evt: web_sys::MouseEvent| {
move |evt: web_sys::Event| {
match FileReader::new() {
Ok(reader) => {
let Some(file) = evt
Expand All @@ -73,7 +73,7 @@ impl Initialize for Running {
};
match reader.read_as_array_buffer(&file) {
Ok(_) => {
let onload = Closure::<dyn FnMut()>::new({
let on_load = Closure::<dyn FnMut()>::new({
let reader = reader.clone();
let tx = tx.clone();
move || match reader.result() {
Expand All @@ -97,8 +97,8 @@ impl Initialize for Running {
Err(err) => on_error(&tx, err),
}
});
reader.set_onload(Some(onload.as_ref().unchecked_ref()));
onload.forget();
reader.set_onload(Some(on_load.as_ref().unchecked_ref()));
on_load.forget();
}
Err(err) => on_error(&tx, err),
}
Expand All @@ -110,7 +110,7 @@ impl Initialize for Running {

let on_cancel = Closure::<dyn FnMut(_)>::new({
let tx = self.tx.clone();
move |_: web_sys::MouseEvent| tx.nes_event(UiEvent::FileDialogCancelled)
move |_: web_sys::Event| tx.nes_event(UiEvent::FileDialogCancelled)
});

let input = document
Expand All @@ -130,6 +130,32 @@ impl Initialize for Running {
on_cancel.forget();
}

let on_resize = Closure::<dyn FnMut(_)>::new({
let tx = self.tx.clone();
move |_: web_sys::Event| {
if let Some(window) = web_sys::window() {
tx.nes_event(RendererEvent::BrowserResized((
window
.inner_width()
.ok()
.and_then(|w| w.as_f64())
.map_or(0.0, |w| w as f32),
window
.inner_height()
.ok()
.and_then(|h| h.as_f64())
.map_or(0.0, |h| h as f32),
)));
}
}
});
if let Err(err) =
window.add_event_listener_with_callback("resize", on_resize.as_ref().unchecked_ref())
{
on_error(&self.tx, err);
}
on_resize.forget();

if let Some(status) = document.get_element_by_id(html_ids::LOADING_STATUS) {
tracing::info!(
"removing hidden class from loading status: {}",
Expand Down Expand Up @@ -170,14 +196,14 @@ mod html_ids {
pub(super) const REPLAY_INPUT: &str = "load-replay";
}

fn get_canvas() -> Option<web_sys::HtmlCanvasElement> {
pub fn get_canvas() -> Option<web_sys::HtmlCanvasElement> {
window()
.and_then(|win| win.document())
.and_then(|doc| doc.get_element_by_id(html_ids::CANVAS))
.and_then(|canvas| canvas.dyn_into::<HtmlCanvasElement>().ok())
}

fn focus_canvas() {
pub fn focus_canvas() {
if let Some(canvas) = get_canvas() {
let _ = canvas.focus();
}
Expand Down

0 comments on commit b59d4c9

Please sign in to comment.