From 99c0f84a9fc771c9c96099232de3716ddf27ca80 Mon Sep 17 00:00:00 2001 From: Osspial Date: Wed, 22 Aug 2018 17:55:27 -0400 Subject: [PATCH] Add request_redraw --- examples/request_redraw.rs | 28 ++++++ src/events.rs | 4 +- src/platform/windows/events_loop.rs | 129 ++++++++++++++++++++-------- src/platform/windows/window.rs | 19 +++- src/window.rs | 15 ++++ 5 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 examples/request_redraw.rs diff --git a/examples/request_redraw.rs b/examples/request_redraw.rs new file mode 100644 index 0000000000..a870fb8df0 --- /dev/null +++ b/examples/request_redraw.rs @@ -0,0 +1,28 @@ +extern crate winit; +use winit::{Event, WindowEvent}; +use std::time::{Instant, Duration}; + +fn main() { + let events_loop = winit::EventLoop::new(); + + let window = winit::WindowBuilder::new() + .with_title("A fantastic window!") + .build(&events_loop) + .unwrap(); + + events_loop.run(move |event, _, control_flow| { + println!("{:?}", event); + + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = winit::ControlFlow::Exit, + Event::EventsCleared => { + window.request_redraw(); + *control_flow = winit::ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0)) + }, + _ => () + } + }); +} diff --git a/src/events.rs b/src/events.rs index 407afdfe45..dab2d7d06e 100644 --- a/src/events.rs +++ b/src/events.rs @@ -140,8 +140,8 @@ pub enum WindowEvent { /// Motion on some analog axis. May report data redundant to other, more specific events. AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 }, - /// The window needs to be redrawn. - Redraw, + /// The OS or application has requested that the window be redrawn. + RedrawRequested, /// Touch event has been received Touch(Touch), diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index c8a1c6b3ab..ee74dc35a4 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -16,6 +16,7 @@ use winapi::shared::basetsd::DWORD_PTR; use winapi::shared::basetsd::UINT_PTR; use std::{mem, ptr}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, Instant}; use std::rc::Rc; use std::cell::RefCell; @@ -129,11 +130,10 @@ pub(crate) struct SubclassInput { impl SubclassInput { unsafe fn send_event(&self, event: Event) { - let runner = self.event_loop_runner.borrow_mut(); - if let Some(runner) = *runner { - (*runner).process_event(event); - } else { - panic!("Attempted to send event without active runner"); + let mut runner = self.event_loop_runner.borrow_mut(); + match *runner { + ELRSharedOption::Runner(runner) => (*runner).process_event(event), + ELRSharedOption::Buffer(ref mut buffer) => buffer.push(event) } } } @@ -145,11 +145,10 @@ struct ThreadMsgTargetSubclassInput { impl ThreadMsgTargetSubclassInput { unsafe fn send_event(&self, event: Event) { - let runner = self.event_loop_runner.borrow_mut(); - if let Some(runner) = *runner { - (*runner).process_event(event); - } else { - panic!("Attempted to send event without active runner"); + let mut runner = self.event_loop_runner.borrow_mut(); + match *runner { + ELRSharedOption::Runner(runner) => (*runner).process_event(event), + ELRSharedOption::Buffer(ref mut buffer) => buffer.push(event) } } } @@ -159,6 +158,7 @@ pub struct EventLoop { thread_id: DWORD, thread_msg_target: HWND, thread_msg_sender: Sender, + trigger_newevents_on_redraw: Arc, pub(crate) runner_shared: EventLoopRunnerShared, } @@ -171,12 +171,13 @@ impl EventLoop { become_dpi_aware(dpi_aware); let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; - let runner_shared = Rc::new(RefCell::new(None)); + let runner_shared = Rc::new(RefCell::new(ELRSharedOption::Buffer(vec![]))); let (thread_msg_target, thread_msg_sender) = thread_event_target_window(runner_shared.clone()); EventLoop { thread_id, thread_msg_target, thread_msg_sender, + trigger_newevents_on_redraw: Arc::new(AtomicBool::new(true)), runner_shared } } @@ -197,7 +198,19 @@ impl EventLoop { modal_loop_data: None, event_handler: &mut event_handler }; - *runner.event_loop.events_loop.runner_shared.borrow_mut() = Some(&mut runner); + { + let runner_shared = runner.event_loop.events_loop.runner_shared.clone(); + let mut runner_shared = runner_shared.borrow_mut(); + let mut event_buffer = vec![]; + if let ELRSharedOption::Buffer(ref mut buffer) = *runner_shared { + mem::swap(buffer, &mut event_buffer); + } + for event in event_buffer.drain(..) { + runner.process_event(event); + } + *runner_shared = ELRSharedOption::Runner(&mut runner); + } + let timer_handle = winuser::SetTimer(ptr::null_mut(), 0, 0x7FFFFFFF, None); let mut msg = mem::uninitialized(); @@ -242,7 +255,7 @@ impl EventLoop { } runner.call_event_handler(Event::LoopDestroyed); - *runner.event_loop.events_loop.runner_shared.borrow_mut() = None; + *runner.event_loop.events_loop.runner_shared.borrow_mut() = ELRSharedOption::Buffer(vec![]); } drop(event_handler); @@ -259,12 +272,17 @@ impl EventLoop { #[inline(always)] pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor { EventLoopThreadExecutor { - thread_id: self.thread_id + thread_id: self.thread_id, + trigger_newevents_on_redraw: self.trigger_newevents_on_redraw.clone() } } } -pub(crate) type EventLoopRunnerShared = Rc>>>; +pub(crate) type EventLoopRunnerShared = Rc>>; +pub(crate) enum ELRSharedOption { + Runner(*mut EventLoopRunner), + Buffer(Vec>) +} pub(crate) struct EventLoopRunner { event_loop: ::EventLoop, control_flow: ControlFlow, @@ -346,8 +364,10 @@ impl EventLoopRunner { } unsafe fn process_event(&mut self, event: Event) { + // If we're in the middle of a modal loop, only set the timer for zero if it hasn't been + // reset in a prior call to `process_event`. if let Some(ModalLoopData{hwnd, timer_handle}) = self.modal_loop_data { - if !self.event_processing_active() { + if self.runner_state != RunnerState::HandlingEvents { winuser::SetTimer(hwnd, timer_handle, 0, None); } } @@ -385,10 +405,9 @@ impl EventLoopRunner { ControlFlow::Poll | ControlFlow::Exit => unreachable!() } - - self.runner_state = RunnerState::HandlingEvents; } + self.runner_state = RunnerState::HandlingEvents; self.call_event_handler(event); } @@ -437,6 +456,12 @@ impl EventLoopRunner { unsafe fn call_event_handler(&mut self, event: Event) { if self.event_handler != mem::zeroed() { + match event { + Event::NewEvents(_) => self.event_loop.events_loop.trigger_newevents_on_redraw.store(true, Ordering::Relaxed), + Event::EventsCleared => self.event_loop.events_loop.trigger_newevents_on_redraw.store(false, Ordering::Relaxed), + _ => () + } + if self.control_flow != ControlFlow::Exit { (*self.event_handler)(event, &self.event_loop, &mut self.control_flow); } else { @@ -446,15 +471,6 @@ impl EventLoopRunner { panic!("Tried to call event handler with null handler"); } } - - fn event_processing_active(&self) -> bool { - match self.runner_state { - RunnerState::HandlingEvents => true, - RunnerState::DeferredNewEvents(..) | - RunnerState::New | - RunnerState::Idle(..) => false - } - } } // Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs @@ -490,7 +506,8 @@ impl Drop for EventLoop { } pub(crate) struct EventLoopThreadExecutor { - thread_id: DWORD + thread_id: DWORD, + trigger_newevents_on_redraw: Arc } impl EventLoopThreadExecutor { @@ -500,6 +517,10 @@ impl EventLoopThreadExecutor { self.thread_id == cur_thread_id } + pub(super) fn trigger_newevents_on_redraw(&self) -> bool { + !self.in_event_loop_thread() || self.trigger_newevents_on_redraw.load(Ordering::Relaxed) + } + /// Executes a function in the event loop thread. If we're already in the event loop thread, /// we just call the function directly. /// @@ -586,6 +607,12 @@ lazy_static! { winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR) } }; + // Message sent by a `Window` if it's requesting a redraw without sending a NewEvents. + pub static ref REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID: u32 = { + unsafe { + winuser::RegisterWindowMessageA("Winit::RequestRedrawNoNewevents\0".as_ptr() as LPCSTR) + } + }; static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = unsafe { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; @@ -708,7 +735,7 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_ENTERSIZEMOVE => { let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle; - if let Some(runner) = *subclass_input.event_loop_runner.borrow_mut() { + if let ELRSharedOption::Runner(runner) = *subclass_input.event_loop_runner.borrow_mut() { (*runner).modal_loop_data = Some(ModalLoopData { hwnd: window, timer_handle: modal_timer_handle @@ -719,7 +746,7 @@ unsafe extern "system" fn public_window_callback( }, winuser::WM_EXITSIZEMOVE => { let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle; - if let Some(runner) = *subclass_input.event_loop_runner.borrow_mut() { + if let ELRSharedOption::Runner(runner) = *subclass_input.event_loop_runner.borrow_mut() { (*runner).modal_loop_data = None; } winuser::SetTimer(window, modal_timer_handle, 0x7FFFFFFF, None); @@ -729,7 +756,7 @@ unsafe extern "system" fn public_window_callback( let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle; if wparam == modal_timer_handle { let runner = subclass_input.event_loop_runner.borrow_mut(); - if let Some(runner) = *runner { + if let ELRSharedOption::Runner(runner) = *runner { let runner = &mut *runner; if runner.modal_loop_data.is_some() { runner.events_cleared(); @@ -791,12 +818,44 @@ unsafe extern "system" fn public_window_callback( 0 }, + _ if msg == *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID => { + use events::WindowEvent::RedrawRequested; + let runner = subclass_input.event_loop_runner.borrow_mut(); + if let ELRSharedOption::Runner(runner) = *runner { + let runner = &mut *runner; + match runner.runner_state { + RunnerState::Idle(..) | + RunnerState::DeferredNewEvents(..) => runner.call_event_handler(Event::WindowEvent { + window_id: SuperWindowId(WindowId(window)), + event: RedrawRequested, + }), + _ => () + } + } + 0 + }, winuser::WM_PAINT => { - use events::WindowEvent::Redraw; - subclass_input.send_event(Event::WindowEvent { + use events::WindowEvent::RedrawRequested; + let event = || Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: Redraw, - }); + event: RedrawRequested, + }; + + let mut send_event = false; + { + let runner = subclass_input.event_loop_runner.borrow_mut(); + if let ELRSharedOption::Runner(runner) = *runner { + let runner = &mut *runner; + match runner.runner_state { + RunnerState::Idle(..) | + RunnerState::DeferredNewEvents(..) => runner.call_event_handler(event()), + _ => send_event = true + } + } + } + if send_event { + subclass_input.send_event(event()); + } commctrl::DefSubclassProc(window, msg, wparam, lparam) }, diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index bf4a37b476..ed66252f05 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -30,7 +30,8 @@ use { }; use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, WindowId}; use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi}; -use platform::platform::events_loop::{self, DESTROY_MSG_ID, EventLoop, INITIAL_DPI_MSG_ID, WindowState}; +use platform::platform::events_loop::{self, EventLoop, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID, REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID}; +use platform::platform::events_loop::WindowState; use platform::platform::icon::{self, IconType, WinIcon}; use platform::platform::monitor; use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input; @@ -140,6 +141,22 @@ impl Window { } } + #[inline] + pub fn request_redraw(&self) { + unsafe { + if self.thread_executor.trigger_newevents_on_redraw() { + winuser::RedrawWindow( + self.window.0, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT + ); + } else { + winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0); + } + } + } + pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> { util::get_window_rect(self.window.0) .map(|rect| (rect.left as i32, rect.top as i32)) diff --git a/src/window.rs b/src/window.rs index 6468099fd3..0abba65f14 100644 --- a/src/window.rs +++ b/src/window.rs @@ -205,6 +205,21 @@ impl Window { self.window.hide() } + /// Emits a `WindowEvent::RedrawRequested` event in the associated event loop after all OS + /// events have been processed by the event loop. + /// + /// This is the **strongly encouraged** method of redrawing windows, as it can integrates with + /// OS-requested redraws (e.g. when a window gets resized). + /// + /// This function can cause `RedrawRequested` events to be emitted after `Event::EventsCleared` + /// but before `Event::NewEvents` if called in the following circumstances: + /// * While processing `EventsCleared`. + /// * While processing a `RedrawRequested` event that was sent during `EventsCleared` or any + /// directly subsequent `RedrawRequested` event. + pub fn request_redraw(&self) { + self.window.request_redraw() + } + /// Returns the position of the top-left hand corner of the window relative to the /// top-left hand corner of the desktop. ///