From 107a7b2df6aa802ec349ef974ff7c5ff8eaadac2 Mon Sep 17 00:00:00 2001 From: Fedor Logachev Date: Wed, 22 May 2024 16:07:00 -0600 Subject: [PATCH] rebaseme --- js/gl.js | 17 +++++++++++++++-- src/conf.rs | 6 +++++- src/event.rs | 4 ++-- src/lib.rs | 23 ++++++++++++++++++++++- src/native.rs | 3 +++ src/native/linux_x11.rs | 38 +++++++++++++++++++++++++++++++------- src/native/wasm.rs | 7 ++++--- src/native/windows.rs | 26 ++++++++++++++++++++++---- 8 files changed, 104 insertions(+), 20 deletions(-) diff --git a/js/gl.js b/js/gl.js index fc9cdea0..a6955fa7 100644 --- a/js/gl.js +++ b/js/gl.js @@ -20,8 +20,12 @@ var clipboard = null; var plugins = []; var wasm_memory; +var animation_frame_timeout; var high_dpi = false; +// if true, requestAnimationFrame will only be called from "schedule_update" +// if false, requestAnimationFrame will be called at the end of each frame +var blocking_event_loop = false; canvas.focus(); @@ -424,7 +428,12 @@ function resize(canvas, on_resize) { function animation() { wasm_exports.frame(); - window.requestAnimationFrame(animation); + if (!window.blocking_event_loop) { + if (animation_frame_timeout) { + window.cancelAnimationFrame(animation_frame_timeout); + } + animation_frame_timeout = window.requestAnimationFrame(animation); + } } const SAPP_EVENTTYPE_TOUCHES_BEGAN = 10; @@ -1114,7 +1123,7 @@ var importObject = { window.high_dpi = high_dpi; resize(canvas); }, - run_animation_loop: function (ptr) { + run_animation_loop: function (blocking) { canvas.onmousemove = function (event) { var relative_position = mouse_relative_position(event.clientX, event.clientY); var x = relative_position.x; @@ -1314,6 +1323,7 @@ var importObject = { window.addEventListener("focus", checkFocus); window.addEventListener("blur", checkFocus); + window.blocking_event_loop = blocking; window.requestAnimationFrame(animation); }, @@ -1387,6 +1397,9 @@ var importObject = { canvas.width = new_width; canvas.height = new_height; resize(canvas, wasm_exports.resize); + }, + sapp_schedule_update: function () { + window.requestAnimationFrame(animation); } } }; diff --git a/src/conf.rs b/src/conf.rs index 2700c921..023f07a0 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -98,6 +98,9 @@ pub struct Platform { /// the way to limit FPS in the game! pub swap_interval: Option, + /// update()/draw() will only be called after `window::request_update()`. + pub blocking_event_loop: bool, + /// Whether the framebuffer should have an alpha channel. /// Currently supported only on Android /// TODO: Make it works on web, on web it should make a transparent HTML5 canvas @@ -113,9 +116,10 @@ impl Default for Platform { fn default() -> Platform { Platform { linux_x11_gl: LinuxX11Gl::GLXWithEGLFallback, - swap_interval: None, linux_backend: LinuxBackend::X11Only, apple_gfx_api: AppleGfxApi::OpenGl, + blocking_event_loop: false, + swap_interval: None, framebuffer_alpha: false, wayland_use_fallback_decorations: true, } diff --git a/src/event.rs b/src/event.rs index 69aeb47f..8773ff1c 100644 --- a/src/event.rs +++ b/src/event.rs @@ -163,8 +163,8 @@ pub trait EventHandler { /// When the app is in background, Android destroys the rendering surface, /// while app is still alive and can do some usefull calculations. /// Note that in this case drawing from update may lead to crashes. - fn update(&mut self); - fn draw(&mut self); + fn update(&mut self) {} + fn draw(&mut self) {} fn resize_event(&mut self, _width: f32, _height: f32) {} fn mouse_motion_event(&mut self, _x: f32, _y: f32) {} fn mouse_wheel_event(&mut self, _x: f32, _y: f32) {} diff --git a/src/lib.rs b/src/lib.rs index 6a158d8d..5872b8e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,6 +143,11 @@ pub mod window { d.high_dpi } + pub fn blocking_event_loop() -> bool { + let d = native_display().lock().unwrap(); + d.blocking_event_loop + } + /// This function simply quits the application without /// giving the user a chance to intervene. Usually this might /// be called when the user clicks the 'Ok' button in a 'Really Quit?' @@ -190,6 +195,21 @@ pub mod window { d.native_requests.send(native::Request::SetCursorGrab(grab)); } + /// With `conf.platform.blocking_event_loop`, `schedule_update` called from an + /// event handler makes draw()/update() functions to be called without waiting + /// for a next event. + /// + /// Does nothing without `conf.platform.blocking_event_loop`. + pub fn schedule_update() { + let mut d = native_display().lock().unwrap(); + d.native_requests.send(native::Request::Wat {}); + + #[cfg(target_arch = "wasm32")] + unsafe { + native::wasm::sapp_schedule_update(); + } + } + /// Show or hide the mouse cursor pub fn show_mouse(shown: bool) { let mut d = native_display().lock().unwrap(); @@ -214,7 +234,8 @@ pub mod window { pub fn set_window_position(new_x: u32, new_y: u32) { let mut d = native_display().lock().unwrap(); - d.native_requests.send(native::Request::SetWindowPosition { new_x, new_y }); + d.native_requests + .send(native::Request::SetWindowPosition { new_x, new_y }); } /// Get the position of the window. diff --git a/src/native.rs b/src/native.rs index e1a05103..b72fc47b 100644 --- a/src/native.rs +++ b/src/native.rs @@ -16,6 +16,7 @@ pub(crate) struct NativeDisplayData { pub native_requests: mpsc::Sender, pub clipboard: Box, pub dropped_files: DroppedFiles, + pub blocking_event_loop: bool, #[cfg(target_vendor = "apple")] pub view: crate::native::apple::frameworks::ObjcId, @@ -47,6 +48,7 @@ impl NativeDisplayData { native_requests, clipboard, dropped_files: Default::default(), + blocking_event_loop: false, #[cfg(target_vendor = "apple")] gfx_api: crate::conf::AppleGfxApi::OpenGl, #[cfg(target_vendor = "apple")] @@ -59,6 +61,7 @@ impl NativeDisplayData { #[derive(Debug)] pub(crate) enum Request { + Wat {}, SetCursorGrab(bool), ShowMouse(bool), SetMouseCursor(crate::CursorIcon), diff --git a/src/native/linux_x11.rs b/src/native/linux_x11.rs index 9d18c0eb..682d90c7 100644 --- a/src/native/linux_x11.rs +++ b/src/native/linux_x11.rs @@ -27,6 +27,7 @@ pub struct X11Display { repeated_keycodes: [bool; 256], empty_cursor: libx11::Cursor, cursor_cache: HashMap, + update_requested: bool, } impl X11Display { @@ -317,6 +318,9 @@ impl X11Display { use Request::*; unsafe { match request { + Wat {} => { + self.update_requested = true; + } SetCursorGrab(grab) => self.set_cursor_grab(self.window, grab), ShowMouse(show) => self.show_mouse(show), SetMouseCursor(icon) => self.set_cursor(self.window, Some(icon)), @@ -382,6 +386,7 @@ where crate::set_display(NativeDisplayData { high_dpi: conf.high_dpi, dpi_scale: display.libx11.update_system_dpi(display.display), + blocking_event_loop: conf.platform.blocking_event_loop, ..NativeDisplayData::new(w, h, tx, clipboard) }); if conf.fullscreen { @@ -395,7 +400,16 @@ where display.process_request(request); } glx.make_current(display.display, glx_window, glx_context); - let count = (display.libx11.XPending)(display.display); + + let mut count = (display.libx11.XPending)(display.display); + let block_on_wait = conf.platform.blocking_event_loop && !display.update_requested; + if block_on_wait { + // if there are multiple events pending, it is still desired to process + // them all in one frame. + // However, when there are no events in the queue, +1 hack + // will block main thread and release the cpu until the new event. + count = count + 1; + } for _ in 0..count { let mut xevent = _XEvent { type_0: 0 }; @@ -403,11 +417,14 @@ where display.process_event(&mut xevent, &mut *event_handler); } - event_handler.update(); - event_handler.draw(); + if !conf.platform.blocking_event_loop || display.update_requested { + event_handler.update(); + event_handler.draw(); - glx.swap_buffers(display.display, glx_window); - (display.libx11.XFlush)(display.display); + glx.swap_buffers(display.display, glx_window); + (display.libx11.XFlush)(display.display); + display.update_requested = false; + } } glx.destroy_context(display.display, glx_window, glx_context); @@ -479,6 +496,7 @@ where crate::set_display(NativeDisplayData { high_dpi: conf.high_dpi, dpi_scale: display.libx11.update_system_dpi(display.display), + blocking_event_loop: conf.platform.blocking_event_loop, ..NativeDisplayData::new(w, h, tx, clipboard) }); if conf.fullscreen { @@ -494,8 +512,13 @@ where display.process_request(request); } - let count = (display.libx11.XPending)(display.display); - + let mut count = (display.libx11.XPending)(display.display); + let block_on_wait = conf.platform.blocking_event_loop && !display.update_requested; + display.update_requested = false; + if block_on_wait { + // same thing as in glx loop, explained there + count = count + 1; + } for _ in 0..count { let mut xevent = _XEvent { type_0: 0 }; (display.libx11.XNextEvent)(display.display, &mut xevent); @@ -559,6 +582,7 @@ where libxi, repeated_keycodes: [false; 256], cursor_cache: HashMap::new(), + update_requested: true, }; display diff --git a/src/native/wasm.rs b/src/native/wasm.rs index 693cb300..c1c7c2b1 100644 --- a/src/native/wasm.rs +++ b/src/native/wasm.rs @@ -78,6 +78,7 @@ where let h = unsafe { canvas_height() as _ }; let clipboard = Box::new(Clipboard); crate::set_display(NativeDisplayData { + blocking_event_queue: conf.platform.blocking_event_queue, ..NativeDisplayData::new(w, h, tx, clipboard) }); EVENT_HANDLER.with(|g| { @@ -86,7 +87,7 @@ where // start requestAnimationFrame loop unsafe { - run_animation_loop(); + run_animation_loop(conf.platform.blocking_event_loop); } } @@ -100,7 +101,7 @@ pub unsafe fn sapp_height() -> ::std::os::raw::c_int { extern "C" { pub fn setup_canvas_size(high_dpi: bool); - pub fn run_animation_loop(); + pub fn run_animation_loop(blocking: bool); pub fn canvas_width() -> i32; pub fn canvas_height() -> i32; pub fn dpi_scale() -> f32; @@ -126,7 +127,7 @@ extern "C" { pub fn sapp_set_fullscreen(fullscreen: bool); pub fn sapp_is_fullscreen() -> bool; pub fn sapp_set_window_size(new_width: u32, new_height: u32); - + pub fn sapp_schedule_update(); pub fn now() -> f64; } diff --git a/src/native/windows.rs b/src/native/windows.rs index a2b3754f..36b1c65b 100644 --- a/src/native/windows.rs +++ b/src/native/windows.rs @@ -137,7 +137,17 @@ impl WindowsDisplay { let mut new_rect = rect; new_rect.right = new_rect.right - new_rect.left + new_x as i32; new_rect.bottom = new_rect.bottom - new_rect.top + new_y as i32; - unsafe { SetWindowPos(self.wnd, HWND_TOP, new_x as i32, new_y as i32, 0, 0, SWP_NOSIZE) }; + unsafe { + SetWindowPos( + self.wnd, + HWND_TOP, + new_x as i32, + new_y as i32, + 0, + 0, + SWP_NOSIZE, + ) + }; } } @@ -752,8 +762,10 @@ impl WindowsDisplay { let mut client_rect: RECT = std::mem::zeroed(); if GetClientRect(hwnd, &mut client_rect as *mut _ as _) != 0 { // Calculate window width and height based on the client area - let window_width = ((client_rect.right - client_rect.left) as f32 / self.window_scale) as i32; - let window_height = ((client_rect.bottom - client_rect.top) as f32 / self.window_scale) as i32; + let window_width = + ((client_rect.right - client_rect.left) as f32 / self.window_scale) as i32; + let window_height = + ((client_rect.bottom - client_rect.top) as f32 / self.window_scale) as i32; // Prevent a framebuffer size of 0 when the window is minimized let fb_width = ((window_width as f32 * self.content_scale) as i32).max(1); @@ -883,7 +895,13 @@ where crate::set_display(NativeDisplayData { high_dpi: conf.high_dpi, dpi_scale: display.window_scale, - ..NativeDisplayData::new(conf.window_width, conf.window_height, tx, clipboard) + ..NativeDisplayData::new( + conf.window_width, + conf.window_height, + tx, + clipboard, + conf.blocking_event_queue, + ) }); display.update_dimensions(wnd);