mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Add request_redraw
This commit is contained in:
28
examples/request_redraw.rs
Normal file
28
examples/request_redraw.rs
Normal 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))
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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<T> {
|
||||
|
||||
impl<T> SubclassInput<T> {
|
||||
unsafe fn send_event(&self, event: Event<T>) {
|
||||
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<T> {
|
||||
|
||||
impl<T> ThreadMsgTargetSubclassInput<T> {
|
||||
unsafe fn send_event(&self, event: Event<T>) {
|
||||
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<T> {
|
||||
thread_id: DWORD,
|
||||
thread_msg_target: HWND,
|
||||
thread_msg_sender: Sender<T>,
|
||||
trigger_newevents_on_redraw: Arc<AtomicBool>,
|
||||
pub(crate) runner_shared: EventLoopRunnerShared<T>,
|
||||
}
|
||||
|
||||
@@ -171,12 +171,13 @@ impl<T> EventLoop<T> {
|
||||
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<T> EventLoop<T> {
|
||||
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<T> EventLoop<T> {
|
||||
}
|
||||
|
||||
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<T> EventLoop<T> {
|
||||
#[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<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> {
|
||||
event_loop: ::EventLoop<T>,
|
||||
control_flow: ControlFlow,
|
||||
@@ -346,8 +364,10 @@ impl<T> EventLoopRunner<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 !self.event_processing_active() {
|
||||
if self.runner_state != RunnerState::HandlingEvents {
|
||||
winuser::SetTimer(hwnd, timer_handle, 0, None);
|
||||
}
|
||||
}
|
||||
@@ -385,10 +405,9 @@ impl<T> EventLoopRunner<T> {
|
||||
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<T> EventLoopRunner<T> {
|
||||
|
||||
unsafe fn call_event_handler(&mut self, event: Event<T>) {
|
||||
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<T> EventLoopRunner<T> {
|
||||
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<T> Drop for EventLoop<T> {
|
||||
}
|
||||
|
||||
pub(crate) struct EventLoopThreadExecutor {
|
||||
thread_id: DWORD
|
||||
thread_id: DWORD,
|
||||
trigger_newevents_on_redraw: Arc<AtomicBool>
|
||||
}
|
||||
|
||||
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<u16> = unsafe {
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
@@ -708,7 +735,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||
}
|
||||
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<T>(
|
||||
},
|
||||
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<T>(
|
||||
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<T>(
|
||||
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)
|
||||
},
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user