Skip to content

Commit 2f27f64

Browse files
authored
On Windows, fix request_redraw() related panics (#1461)
* On Windows, fix request_redraw() related panics These panics were introduced by 6a330a2 Fixes #1391 Fixes #1400 Fixes #1466 Probably fixes other related issues See #1429 * On Windows, replace all calls to UpdateWindow by calls to InvalidateRgn This avoids directly sending a WM_PAINT message, which might cause buffering of RedrawRequested events. We don't want to buffer RedrawRequested events because: - we wan't to handle RedrawRequested during processing of WM_PAINT messages - state transitionning is broken when handling buffered RedrawRequested events Fixes #1469 * On Windows, panic if we are trying to buffer a RedrawRequested event * On Windows, move modal loop jumpstart to set_modal_loop() method This fixes a panic. Note that the WM_PAINT event is now sent to the modal_redraw_method which is more correct and avoids an unecessary redraw of the window. Relates to but does does not fix #1484 * On Window, filter by paint messages when draining paint messages This seems to prevent PeekMessage from dispatching unrelated sent messages * Change recently added panic/assert calls with warn calls This makes the code less panicky... And actually, winit's Windoww callbacks should not panic because the panic will unwind into Windows code. It is currently undefined behavior to unwind from Rust code into foreign code. See https://doc.rust-lang.org/std/panic/fn.catch_unwind.html * add comments to clarify WM_PAINT handling in non modal loop * made redraw_events_cleared more explicit and more comments
1 parent cbb60d2 commit 2f27f64

File tree

6 files changed

+154
-273
lines changed

6 files changed

+154
-273
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased
22

33
- On Windows, fix minor timing issue in wait_until_time_or_msg
4+
- On Windows, rework handling of request_redraw() to address panics.
45
- On macOS, fix `set_simple_screen` to remember frame excluding title bar.
56
- On Wayland, fix coordinates in touch events when scale factor isn't 1.
67
- On Wayland, fix color from `close_button_icon_color` not applying.

src/platform_impl/windows/event_loop.rs

Lines changed: 67 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -220,67 +220,66 @@ impl<T: 'static> EventLoop<T> {
220220
runner.new_events();
221221
loop {
222222
if !unread_message_exists {
223-
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
223+
if 0 == winuser::PeekMessageW(
224+
&mut msg,
225+
ptr::null_mut(),
226+
0,
227+
0,
228+
winuser::PM_REMOVE,
229+
) {
224230
break;
225231
}
226232
}
227-
if msg.message == winuser::WM_PAINT {
228-
unread_message_exists = true;
229-
break;
230-
}
231233
winuser::TranslateMessage(&mut msg);
232234
winuser::DispatchMessageW(&mut msg);
233235

234236
unread_message_exists = false;
237+
238+
if msg.message == winuser::WM_PAINT {
239+
// An "external" redraw was requested.
240+
// Note that the WM_PAINT has been dispatched and
241+
// has caused the event loop to emit the MainEventsCleared event.
242+
// See EventLoopRunner::process_event().
243+
// The call to main_events_cleared() below will do nothing.
244+
break;
245+
}
235246
}
247+
// Make sure we emit the MainEventsCleared event if no WM_PAINT message was received.
236248
runner.main_events_cleared();
249+
// Drain eventual WM_PAINT messages sent if user called request_redraw()
250+
// during handling of MainEventsCleared.
237251
loop {
238-
if !unread_message_exists {
239-
if 0 == winuser::PeekMessageW(
240-
&mut msg,
241-
ptr::null_mut(),
242-
winuser::WM_PAINT,
243-
winuser::WM_PAINT,
244-
1,
245-
) {
246-
break;
247-
}
252+
if 0 == winuser::PeekMessageW(
253+
&mut msg,
254+
ptr::null_mut(),
255+
winuser::WM_PAINT,
256+
winuser::WM_PAINT,
257+
winuser::PM_QS_PAINT | winuser::PM_REMOVE,
258+
) {
259+
break;
248260
}
249261

250262
winuser::TranslateMessage(&mut msg);
251263
winuser::DispatchMessageW(&mut msg);
252-
253-
unread_message_exists = false;
254-
}
255-
if runner.redraw_events_cleared().events_buffered() {
256-
if runner.control_flow() == ControlFlow::Exit {
257-
break 'main;
258-
}
259-
continue;
260264
}
261-
262-
if !unread_message_exists {
263-
let control_flow = runner.control_flow();
264-
match control_flow {
265-
ControlFlow::Exit => break 'main,
266-
ControlFlow::Wait => {
267-
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
268-
break 'main;
269-
}
270-
unread_message_exists = true;
271-
}
272-
ControlFlow::WaitUntil(resume_time) => {
273-
wait_until_time_or_msg(resume_time);
265+
runner.redraw_events_cleared();
266+
match runner.control_flow() {
267+
ControlFlow::Exit => break 'main,
268+
ControlFlow::Wait => {
269+
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
270+
break 'main;
274271
}
275-
ControlFlow::Poll => (),
272+
unread_message_exists = true;
276273
}
274+
ControlFlow::WaitUntil(resume_time) => {
275+
wait_until_time_or_msg(resume_time);
276+
}
277+
ControlFlow::Poll => (),
277278
}
278279
}
279280
}
280281

281-
unsafe {
282-
runner.call_event_handler(Event::LoopDestroyed);
283-
}
282+
runner.destroy_loop();
284283
runner.destroy_runner();
285284
}
286285

@@ -652,13 +651,6 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
652651
}
653652

654653
winuser::WM_NCLBUTTONDOWN => {
655-
// jumpstart the modal loop
656-
winuser::RedrawWindow(
657-
window,
658-
ptr::null(),
659-
ptr::null_mut(),
660-
winuser::RDW_INTERNALPAINT,
661-
);
662654
if wparam == winuser::HTCAPTION as _ {
663655
winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0);
664656
}
@@ -688,7 +680,6 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
688680
}
689681

690682
winuser::WM_PAINT => {
691-
subclass_input.event_loop_runner.main_events_cleared();
692683
subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
693684
commctrl::DefSubclassProc(window, msg, wparam, lparam)
694685
}
@@ -1780,50 +1771,39 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
17801771
};
17811772
let in_modal_loop = subclass_input.event_loop_runner.in_modal_loop();
17821773
if in_modal_loop {
1774+
let runner = &subclass_input.event_loop_runner;
1775+
runner.main_events_cleared();
1776+
// Drain eventual WM_PAINT messages sent if user called request_redraw()
1777+
// during handling of MainEventsCleared.
17831778
let mut msg = mem::zeroed();
1784-
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
1785-
if msg.message != 0 && msg.message != winuser::WM_PAINT {
1786-
queue_call_again();
1787-
return 0;
1779+
loop {
1780+
if 0 == winuser::PeekMessageW(
1781+
&mut msg,
1782+
ptr::null_mut(),
1783+
winuser::WM_PAINT,
1784+
winuser::WM_PAINT,
1785+
winuser::PM_QS_PAINT | winuser::PM_REMOVE,
1786+
) {
1787+
break;
17881788
}
17891789

1790-
subclass_input.event_loop_runner.main_events_cleared();
1791-
loop {
1792-
if 0 == winuser::PeekMessageW(
1793-
&mut msg,
1794-
ptr::null_mut(),
1795-
winuser::WM_PAINT,
1796-
winuser::WM_PAINT,
1797-
1,
1798-
) {
1799-
break;
1800-
}
1801-
1802-
if msg.hwnd != window {
1803-
winuser::TranslateMessage(&mut msg);
1804-
winuser::DispatchMessageW(&mut msg);
1805-
}
1790+
if msg.hwnd != window {
1791+
winuser::TranslateMessage(&mut msg);
1792+
winuser::DispatchMessageW(&mut msg);
18061793
}
18071794
}
1808-
1809-
// we don't borrow until here because TODO SAFETY
1810-
let runner = &subclass_input.event_loop_runner;
1811-
if runner.redraw_events_cleared().events_buffered() {
1812-
queue_call_again();
1813-
runner.new_events();
1814-
} else {
1815-
match runner.control_flow() {
1816-
// Waiting is handled by the modal loop.
1817-
ControlFlow::Exit | ControlFlow::Wait => runner.new_events(),
1818-
ControlFlow::WaitUntil(resume_time) => {
1819-
wait_until_time_or_msg(resume_time);
1820-
runner.new_events();
1821-
queue_call_again();
1822-
}
1823-
ControlFlow::Poll => {
1824-
runner.new_events();
1825-
queue_call_again();
1826-
}
1795+
runner.redraw_events_cleared();
1796+
match runner.control_flow() {
1797+
// Waiting is handled by the modal loop.
1798+
ControlFlow::Exit | ControlFlow::Wait => runner.new_events(),
1799+
ControlFlow::WaitUntil(resume_time) => {
1800+
wait_until_time_or_msg(resume_time);
1801+
runner.new_events();
1802+
queue_call_again();
1803+
}
1804+
ControlFlow::Poll => {
1805+
runner.new_events();
1806+
queue_call_again();
18271807
}
18281808
}
18291809
}

0 commit comments

Comments
 (0)