Add request_redraw

This commit is contained in:
Osspial
2018-08-22 17:55:27 -04:00
parent a0fef1a5fa
commit 99c0f84a9f
5 changed files with 157 additions and 38 deletions

View File

@@ -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))
},
_ => ()
}
});
}

View File

@@ -140,8 +140,8 @@ pub enum WindowEvent {
/// Motion on some analog axis. May report data redundant to other, more specific events. /// Motion on some analog axis. May report data redundant to other, more specific events.
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 }, AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 },
/// The window needs to be redrawn. /// The OS or application has requested that the window be redrawn.
Redraw, RedrawRequested,
/// Touch event has been received /// Touch event has been received
Touch(Touch), Touch(Touch),

View File

@@ -16,6 +16,7 @@ use winapi::shared::basetsd::DWORD_PTR;
use winapi::shared::basetsd::UINT_PTR; use winapi::shared::basetsd::UINT_PTR;
use std::{mem, ptr}; use std::{mem, ptr};
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
@@ -129,11 +130,10 @@ pub(crate) struct SubclassInput<T> {
impl<T> SubclassInput<T> { impl<T> SubclassInput<T> {
unsafe fn send_event(&self, event: Event<T>) { unsafe fn send_event(&self, event: Event<T>) {
let runner = self.event_loop_runner.borrow_mut(); let mut runner = self.event_loop_runner.borrow_mut();
if let Some(runner) = *runner { match *runner {
(*runner).process_event(event); ELRSharedOption::Runner(runner) => (*runner).process_event(event),
} else { ELRSharedOption::Buffer(ref mut buffer) => buffer.push(event)
panic!("Attempted to send event without active runner");
} }
} }
} }
@@ -145,11 +145,10 @@ struct ThreadMsgTargetSubclassInput<T> {
impl<T> ThreadMsgTargetSubclassInput<T> { impl<T> ThreadMsgTargetSubclassInput<T> {
unsafe fn send_event(&self, event: Event<T>) { unsafe fn send_event(&self, event: Event<T>) {
let runner = self.event_loop_runner.borrow_mut(); let mut runner = self.event_loop_runner.borrow_mut();
if let Some(runner) = *runner { match *runner {
(*runner).process_event(event); ELRSharedOption::Runner(runner) => (*runner).process_event(event),
} else { ELRSharedOption::Buffer(ref mut buffer) => buffer.push(event)
panic!("Attempted to send event without active runner");
} }
} }
} }
@@ -159,6 +158,7 @@ pub struct EventLoop<T> {
thread_id: DWORD, thread_id: DWORD,
thread_msg_target: HWND, thread_msg_target: HWND,
thread_msg_sender: Sender<T>, thread_msg_sender: Sender<T>,
trigger_newevents_on_redraw: Arc<AtomicBool>,
pub(crate) runner_shared: EventLoopRunnerShared<T>, pub(crate) runner_shared: EventLoopRunnerShared<T>,
} }
@@ -171,12 +171,13 @@ impl<T> EventLoop<T> {
become_dpi_aware(dpi_aware); become_dpi_aware(dpi_aware);
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; 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()); let (thread_msg_target, thread_msg_sender) = thread_event_target_window(runner_shared.clone());
EventLoop { EventLoop {
thread_id, thread_id,
thread_msg_target, thread_msg_sender, thread_msg_target, thread_msg_sender,
trigger_newevents_on_redraw: Arc::new(AtomicBool::new(true)),
runner_shared runner_shared
} }
} }
@@ -197,7 +198,19 @@ impl<T> EventLoop<T> {
modal_loop_data: None, modal_loop_data: None,
event_handler: &mut event_handler 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 timer_handle = winuser::SetTimer(ptr::null_mut(), 0, 0x7FFFFFFF, None);
let mut msg = mem::uninitialized(); let mut msg = mem::uninitialized();
@@ -242,7 +255,7 @@ impl<T> EventLoop<T> {
} }
runner.call_event_handler(Event::LoopDestroyed); 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); drop(event_handler);
@@ -259,12 +272,17 @@ impl<T> EventLoop<T> {
#[inline(always)] #[inline(always)]
pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor { pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
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<T> = Rc<RefCell<Option<*mut EventLoopRunner<T>>>>; pub(crate) type EventLoopRunnerShared<T> = Rc<RefCell<ELRSharedOption<T>>>;
pub(crate) enum ELRSharedOption<T> {
Runner(*mut EventLoopRunner<T>),
Buffer(Vec<Event<T>>)
}
pub(crate) struct EventLoopRunner<T> { pub(crate) struct EventLoopRunner<T> {
event_loop: ::EventLoop<T>, event_loop: ::EventLoop<T>,
control_flow: ControlFlow, control_flow: ControlFlow,
@@ -346,8 +364,10 @@ impl<T> EventLoopRunner<T> {
} }
unsafe fn process_event(&mut self, event: Event<T>) { unsafe fn process_event(&mut self, event: Event<T>) {
// 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 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); winuser::SetTimer(hwnd, timer_handle, 0, None);
} }
} }
@@ -385,10 +405,9 @@ impl<T> EventLoopRunner<T> {
ControlFlow::Poll | ControlFlow::Poll |
ControlFlow::Exit => unreachable!() ControlFlow::Exit => unreachable!()
} }
self.runner_state = RunnerState::HandlingEvents;
} }
self.runner_state = RunnerState::HandlingEvents;
self.call_event_handler(event); self.call_event_handler(event);
} }
@@ -437,6 +456,12 @@ impl<T> EventLoopRunner<T> {
unsafe fn call_event_handler(&mut self, event: Event<T>) { unsafe fn call_event_handler(&mut self, event: Event<T>) {
if self.event_handler != mem::zeroed() { 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 { if self.control_flow != ControlFlow::Exit {
(*self.event_handler)(event, &self.event_loop, &mut self.control_flow); (*self.event_handler)(event, &self.event_loop, &mut self.control_flow);
} else { } else {
@@ -446,15 +471,6 @@ impl<T> EventLoopRunner<T> {
panic!("Tried to call event handler with null handler"); 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 // Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
@@ -490,7 +506,8 @@ impl<T> Drop for EventLoop<T> {
} }
pub(crate) struct EventLoopThreadExecutor { pub(crate) struct EventLoopThreadExecutor {
thread_id: DWORD thread_id: DWORD,
trigger_newevents_on_redraw: Arc<AtomicBool>
} }
impl EventLoopThreadExecutor { impl EventLoopThreadExecutor {
@@ -500,6 +517,10 @@ impl EventLoopThreadExecutor {
self.thread_id == cur_thread_id 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, /// Executes a function in the event loop thread. If we're already in the event loop thread,
/// we just call the function directly. /// we just call the function directly.
/// ///
@@ -586,6 +607,12 @@ lazy_static! {
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR) 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<u16> = unsafe { static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec<u16> = unsafe {
use std::ffi::OsStr; use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
@@ -708,7 +735,7 @@ unsafe extern "system" fn public_window_callback<T>(
} }
winuser::WM_ENTERSIZEMOVE => { winuser::WM_ENTERSIZEMOVE => {
let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle; 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 { (*runner).modal_loop_data = Some(ModalLoopData {
hwnd: window, hwnd: window,
timer_handle: modal_timer_handle timer_handle: modal_timer_handle
@@ -719,7 +746,7 @@ unsafe extern "system" fn public_window_callback<T>(
}, },
winuser::WM_EXITSIZEMOVE => { winuser::WM_EXITSIZEMOVE => {
let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle; 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; (*runner).modal_loop_data = None;
} }
winuser::SetTimer(window, modal_timer_handle, 0x7FFFFFFF, None); winuser::SetTimer(window, modal_timer_handle, 0x7FFFFFFF, None);
@@ -729,7 +756,7 @@ unsafe extern "system" fn public_window_callback<T>(
let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle; let modal_timer_handle = subclass_input.window_state.lock().modal_timer_handle;
if wparam == modal_timer_handle { if wparam == modal_timer_handle {
let runner = subclass_input.event_loop_runner.borrow_mut(); let runner = subclass_input.event_loop_runner.borrow_mut();
if let Some(runner) = *runner { if let ELRSharedOption::Runner(runner) = *runner {
let runner = &mut *runner; let runner = &mut *runner;
if runner.modal_loop_data.is_some() { if runner.modal_loop_data.is_some() {
runner.events_cleared(); runner.events_cleared();
@@ -791,12 +818,44 @@ unsafe extern "system" fn public_window_callback<T>(
0 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 => { winuser::WM_PAINT => {
use events::WindowEvent::Redraw; use events::WindowEvent::RedrawRequested;
subclass_input.send_event(Event::WindowEvent { let event = || Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)), 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) commctrl::DefSubclassProc(window, msg, wparam, lparam)
}, },

View File

@@ -30,7 +30,8 @@ use {
}; };
use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, WindowId}; use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, WindowId};
use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi}; 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::icon::{self, IconType, WinIcon};
use platform::platform::monitor; use platform::platform::monitor;
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input; 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)> { pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> {
util::get_window_rect(self.window.0) util::get_window_rect(self.window.0)
.map(|rect| (rect.left as i32, rect.top as i32)) .map(|rect| (rect.left as i32, rect.top as i32))

View File

@@ -205,6 +205,21 @@ impl Window {
self.window.hide() 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 /// Returns the position of the top-left hand corner of the window relative to the
/// top-left hand corner of the desktop. /// top-left hand corner of the desktop.
/// ///