Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions winit-core/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,6 @@ impl EventLoopProxy {
///
/// [`proxy_wake_up`]: crate::application::ApplicationHandler::proxy_wake_up
/// [`ApplicationHandler::proxy_wake_up()`]: crate::application::ApplicationHandler::proxy_wake_up
///
/// # Platform-specific
///
/// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
///
/// [#3687]: https://github.com/rust-windowing/winit/pull/3687
pub fn wake_up(&self) {
self.proxy.wake_up();
}
Expand Down
30 changes: 21 additions & 9 deletions winit-win32/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,10 @@ impl ActiveEventLoop {

impl RootActiveEventLoop for ActiveEventLoop {
fn create_proxy(&self) -> RootEventLoopProxy {
let event_loop_proxy = EventLoopProxy { target_window: self.0.thread_msg_target };
let event_loop_proxy = EventLoopProxy {
has_sent_wakeup_msg: self.0.has_sent_wakeup_msg.clone(),
target_window: self.0.thread_msg_target,
};
RootEventLoopProxy::new(Arc::new(event_loop_proxy))
}

Expand Down Expand Up @@ -766,6 +769,7 @@ type ThreadExecFn = Box<Box<dyn FnMut()>>;

#[derive(Debug)]
pub struct EventLoopProxy {
has_sent_wakeup_msg: Arc<AtomicBool>,
target_window: HWND,
}

Expand All @@ -774,7 +778,18 @@ unsafe impl Sync for EventLoopProxy {}

impl EventLoopProxyProvider for EventLoopProxy {
fn wake_up(&self) {
unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) };
if self.has_sent_wakeup_msg.swap(true, Ordering::AcqRel) {
// Do not send a wakeup event if one has already been sent, but hasn't been processed
// yet. This prevents errors when the internal message queue fills up, and effectively
// coalesces wakeups.
tracing::trace!("avoided sending wake up, previous wake-up has yet to be processed");
return;
}
if unsafe { PostMessageW(self.target_window, PROXY_WAKEUP_MSG_ID.get(), 0, 0) } == 0 {
// _can_ technically fail, but realistically won't, since we've prevented the most
// common case (queue full) above.
tracing::error!("failed waking event loop: {}", std::io::Error::last_os_error());
}
}
}

Expand Down Expand Up @@ -830,7 +845,7 @@ impl LazyMessageId {

// Message sent by the `EventLoopProxy` when we want to wake up the thread.
// WPARAM and LPARAM are unused.
static USER_EVENT_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0");
static PROXY_WAKEUP_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0");
// Message sent when we want to execute a closure in the thread.
// WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
// and LPARAM is unused.
Expand Down Expand Up @@ -2471,12 +2486,9 @@ unsafe extern "system" fn thread_event_target_callback(
unsafe { DefWindowProcW(window, msg, wparam, lparam) }
},

_ if msg == USER_EVENT_MSG_ID.get() => {
// synthesis a placeholder UserEvent, so that if the callback is
// re-entered it can be buffered for later delivery. the real
// user event is still in the mpsc channel and will be pulled
// once the placeholder event is delivered to the wrapper
// `event_handler`
_ if msg == PROXY_WAKEUP_MSG_ID.get() => {
// Reset the sent state, allowing new `PROXY_WAKEUP_MSG_ID` messages to be sent.
userdata.event_loop_runner.has_sent_wakeup_msg.store(false, Ordering::Release);
userdata.send_wakeup();
0
},
Expand Down
8 changes: 6 additions & 2 deletions winit-win32/src/event_loop/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::any::Any;
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Instant;
use std::{fmt, mem, panic};
Expand Down Expand Up @@ -30,6 +31,8 @@ pub(crate) struct EventLoopRunner {
// can't stall an external loop beyond a frame
pub(super) interrupt_msg_dispatch: Cell<bool>,

pub(super) has_sent_wakeup_msg: Arc<AtomicBool>,

control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>,
runner_state: Cell<RunnerState>,
Expand Down Expand Up @@ -69,8 +72,6 @@ pub(crate) enum Event {
Device { device_id: DeviceId, event: DeviceEvent },
Window { window_id: WindowId, event: WindowEvent },
BufferedScaleFactorChanged(HWND, f64, PhysicalSize<u32>),
// FIXME(madsmtm): Coalesce these into a flag (or similar) instead of handling them as events.
// https://github.com/rust-windowing/winit/pull/3687
WakeUp,
}

Expand All @@ -80,6 +81,7 @@ impl EventLoopRunner {
thread_id,
thread_msg_target,
interrupt_msg_dispatch: Cell::new(false),
has_sent_wakeup_msg: Arc::new(AtomicBool::new(false)),
runner_state: Cell::new(RunnerState::Uninitialized),
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None),
Expand Down Expand Up @@ -131,6 +133,7 @@ impl EventLoopRunner {
thread_id: _,
thread_msg_target: _,
interrupt_msg_dispatch,
has_sent_wakeup_msg,
runner_state,
panic_error,
control_flow: _,
Expand All @@ -140,6 +143,7 @@ impl EventLoopRunner {
event_buffer: _,
} = self;
interrupt_msg_dispatch.set(false);
has_sent_wakeup_msg.store(false, Ordering::Release);
runner_state.set(RunnerState::Uninitialized);
panic_error.set(None);
exit.set(None);
Expand Down
1 change: 1 addition & 0 deletions winit/src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,4 @@ changelog entry.
- On Windows, `Window::theme` will return the correct theme after setting it through `Window::set_theme`.
- On Windows, `Window::set_theme` will change the title bar color immediately now.
- On Windows 11, prevent incorrect shifting when dragging window onto a monitor with different DPI.
- On Windows, coalesce wake-up events to avoid filling the message queue.
Loading