12
12
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
13
13
//! add a `WindowState` entry to a list of window to be used by the callback.
14
14
15
- use winapi:: shared:: basetsd:: DWORD_PTR ;
15
+ use winapi:: shared:: basetsd:: { DWORD_PTR , LONG_PTR } ;
16
16
use winapi:: shared:: basetsd:: UINT_PTR ;
17
17
use std:: { mem, ptr} ;
18
18
use std:: ffi:: OsString ;
@@ -101,7 +101,6 @@ pub struct WindowState {
101
101
pub maximized : bool ,
102
102
pub resizable : bool ,
103
103
pub mouse_buttons_down : u32 ,
104
- pub modal_timer_handle : UINT_PTR
105
104
}
106
105
107
106
impl WindowState {
@@ -191,7 +190,8 @@ impl<T> EventLoop<T> {
191
190
event_loop : self ,
192
191
control_flow : ControlFlow :: default ( ) ,
193
192
runner_state : RunnerState :: New ,
194
- modal_loop_data : None ,
193
+ in_modal_loop : false ,
194
+ modal_redraw_window : self . thread_msg_target ,
195
195
event_handler : unsafe {
196
196
// Transmute used to erase lifetimes.
197
197
mem:: transmute :: <
@@ -214,8 +214,6 @@ impl<T> EventLoop<T> {
214
214
}
215
215
216
216
unsafe {
217
- let timer_handle = winuser:: SetTimer ( ptr:: null_mut ( ) , 0 , 0x7FFFFFFF , None ) ;
218
-
219
217
let mut msg = mem:: uninitialized ( ) ;
220
218
let mut msg_unprocessed = false ;
221
219
@@ -242,16 +240,7 @@ impl<T> EventLoop<T> {
242
240
msg_unprocessed = true ;
243
241
}
244
242
ControlFlow :: WaitUntil ( resume_time) => {
245
- let now = Instant :: now ( ) ;
246
- if now <= resume_time {
247
- let duration = resume_time - now;
248
- winuser:: SetTimer ( ptr:: null_mut ( ) , timer_handle, dur2timeout ( duration) , None ) ;
249
- if 0 == winuser:: GetMessageW ( & mut msg, ptr:: null_mut ( ) , 0 , 0 ) {
250
- break ' main
251
- }
252
- winuser:: SetTimer ( ptr:: null_mut ( ) , timer_handle, 0x7FFFFFFF , None ) ;
253
- msg_unprocessed = true ;
254
- }
243
+ wait_until_time_or_msg ( resume_time) ;
255
244
} ,
256
245
ControlFlow :: Poll => ( )
257
246
}
@@ -287,15 +276,11 @@ pub(crate) struct EventLoopRunner<T> {
287
276
event_loop : * const EventLoop < T > ,
288
277
control_flow : ControlFlow ,
289
278
runner_state : RunnerState ,
290
- modal_loop_data : Option < ModalLoopData > ,
279
+ modal_redraw_window : HWND ,
280
+ in_modal_loop : bool ,
291
281
event_handler : * mut FnMut ( Event < T > , & RootEventLoop < T > , & mut ControlFlow )
292
282
}
293
283
294
- struct ModalLoopData {
295
- hwnd : HWND ,
296
- timer_handle : UINT_PTR
297
- }
298
-
299
284
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
300
285
enum RunnerState {
301
286
/// The event loop has just been created, and an `Init` event must be sent.
@@ -364,12 +349,18 @@ impl<T> EventLoopRunner<T> {
364
349
}
365
350
366
351
unsafe fn process_event ( & mut self , event : Event < T > ) {
367
- // If we're in the middle of a modal loop, only set the timer for zero if it hasn't been
368
- // reset in a prior call to `process_event`.
369
- if let Some ( ModalLoopData { hwnd, timer_handle} ) = self . modal_loop_data {
370
- if self . runner_state != RunnerState :: HandlingEvents {
371
- winuser:: SetTimer ( hwnd, timer_handle, 0 , None ) ;
372
- }
352
+ // If we're in the modal loop, we need to have some mechanism for finding when the event
353
+ // queue has been cleared so we can call `events_cleared`. Windows doesn't give any utilities
354
+ // for doing this, but it DOES guarantee that WM_PAINT will only occur after input events have
355
+ // been processed. So, we send WM_PAINT to a dummy window which calls `events_cleared` when
356
+ // the events queue has been emptied.
357
+ if self . in_modal_loop {
358
+ winuser:: RedrawWindow (
359
+ self . modal_redraw_window ,
360
+ ptr:: null ( ) ,
361
+ ptr:: null_mut ( ) ,
362
+ winuser:: RDW_INTERNALPAINT
363
+ ) ;
373
364
}
374
365
375
366
// If new event processing has to be done (i.e. call NewEvents or defer), do it. If we're
@@ -476,6 +467,29 @@ impl<T> EventLoopRunner<T> {
476
467
}
477
468
}
478
469
470
+ // Returns true if the wait time was reached, and false if a message must be processed.
471
+ unsafe fn wait_until_time_or_msg ( wait_until : Instant ) -> bool {
472
+ let mut msg = mem:: uninitialized ( ) ;
473
+ let now = Instant :: now ( ) ;
474
+ if now <= wait_until {
475
+ // MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract 1 millisecond
476
+ // from the requested time and spinlock for the remainder to compensate for that.
477
+ winuser:: MsgWaitForMultipleObjects (
478
+ 0 ,
479
+ ptr:: null ( ) ,
480
+ 1 ,
481
+ dur2timeout ( wait_until - now) . saturating_sub ( 1 ) ,
482
+ winuser:: QS_ALLINPUT
483
+ ) ;
484
+ while Instant :: now ( ) < wait_until {
485
+ if 0 != winuser:: PeekMessageW ( & mut msg, ptr:: null_mut ( ) , 0 , 0 , 0 ) {
486
+ return false ;
487
+ }
488
+ }
489
+ }
490
+
491
+ return true ;
492
+ }
479
493
// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
480
494
fn dur2timeout ( dur : Duration ) -> DWORD {
481
495
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
@@ -649,7 +663,7 @@ lazy_static! {
649
663
fn thread_event_target_window < T > ( event_loop_runner : EventLoopRunnerShared < T > ) -> ( HWND , Sender < T > ) {
650
664
unsafe {
651
665
let window = winuser:: CreateWindowExW (
652
- 0 ,
666
+ winuser :: WS_EX_NOACTIVATE | winuser :: WS_EX_TRANSPARENT | winuser :: WS_EX_LAYERED ,
653
667
THREAD_EVENT_TARGET_WINDOW_CLASS . as_ptr ( ) ,
654
668
ptr:: null_mut ( ) ,
655
669
0 ,
@@ -660,6 +674,14 @@ fn thread_event_target_window<T>(event_loop_runner: EventLoopRunnerShared<T>) ->
660
674
libloaderapi:: GetModuleHandleW ( ptr:: null ( ) ) ,
661
675
ptr:: null_mut ( )
662
676
) ;
677
+ winuser:: SetWindowLongPtrW (
678
+ window,
679
+ winuser:: GWL_STYLE ,
680
+ // The window technically has to be visible to receive WM_PAINT messages (which are used
681
+ // for delivering events during resizes), but it isn't displayed to the user because of
682
+ // the LAYERED style.
683
+ ( winuser:: WS_VISIBLE | winuser:: WS_POPUP ) as LONG_PTR
684
+ ) ;
663
685
664
686
let ( tx, rx) = mpsc:: channel ( ) ;
665
687
@@ -727,67 +749,20 @@ unsafe extern "system" fn public_window_callback<T>(
727
749
let subclass_input = & mut * ( subclass_input_ptr as * mut SubclassInput < T > ) ;
728
750
729
751
match msg {
730
- winuser:: WM_SYSCOMMAND => {
731
- {
732
- let mut window_state = subclass_input. window_state . lock ( ) ;
733
- if window_state. modal_timer_handle == 0 {
734
- window_state. modal_timer_handle = winuser:: SetTimer ( window, 0 , 0x7FFFFFFF , None ) ;
735
- }
736
- }
737
- commctrl:: DefSubclassProc ( window, msg, wparam, lparam)
738
- }
739
752
winuser:: WM_ENTERSIZEMOVE => {
740
- let modal_timer_handle = subclass_input. window_state . lock ( ) . modal_timer_handle ;
741
- if let ELRSharedOption :: Runner ( runner) = * subclass_input. event_loop_runner . borrow_mut ( ) {
742
- ( * runner) . modal_loop_data = Some ( ModalLoopData {
743
- hwnd : window,
744
- timer_handle : modal_timer_handle
745
- } ) ;
753
+ let runner = subclass_input. event_loop_runner . borrow_mut ( ) ;
754
+ if let ELRSharedOption :: Runner ( runner) = * runner {
755
+ ( * runner) . in_modal_loop = true ;
746
756
}
747
- winuser:: SetTimer ( window, modal_timer_handle, 0 , None ) ;
748
757
0
749
758
} ,
750
759
winuser:: WM_EXITSIZEMOVE => {
751
- let modal_timer_handle = subclass_input. window_state . lock ( ) . modal_timer_handle ;
752
- if let ELRSharedOption :: Runner ( runner) = * subclass_input . event_loop_runner . borrow_mut ( ) {
753
- ( * runner) . modal_loop_data = None ;
760
+ let runner = subclass_input. event_loop_runner . borrow_mut ( ) ;
761
+ if let ELRSharedOption :: Runner ( runner) = * runner {
762
+ ( * runner) . in_modal_loop = false ;
754
763
}
755
- winuser:: SetTimer ( window, modal_timer_handle, 0x7FFFFFFF , None ) ;
756
764
0
757
765
} ,
758
- winuser:: WM_TIMER => {
759
- let modal_timer_handle = subclass_input. window_state . lock ( ) . modal_timer_handle ;
760
- if wparam == modal_timer_handle {
761
- let runner = subclass_input. event_loop_runner . borrow_mut ( ) ;
762
- if let ELRSharedOption :: Runner ( runner) = * runner {
763
- let runner = & mut * runner;
764
- if runner. modal_loop_data . is_some ( ) {
765
- runner. events_cleared ( ) ;
766
- match runner. control_flow {
767
- ControlFlow :: Exit => ( ) ,
768
- ControlFlow :: Wait => {
769
- winuser:: SetTimer ( window, modal_timer_handle, 0x7FFFFFFF , None ) ;
770
- } ,
771
- ControlFlow :: WaitUntil ( resume_time) => {
772
- let now = Instant :: now ( ) ;
773
- let duration = match now <= resume_time {
774
- true => dur2timeout ( resume_time - now) ,
775
- false => 0
776
- } ;
777
- winuser:: SetTimer ( window, modal_timer_handle, duration, None ) ;
778
- } ,
779
- ControlFlow :: Poll => {
780
- winuser:: SetTimer ( window, modal_timer_handle, 0 , None ) ;
781
- }
782
- }
783
-
784
- runner. new_events ( ) ;
785
- }
786
- }
787
- }
788
- 0
789
- }
790
-
791
766
winuser:: WM_NCCREATE => {
792
767
enable_non_client_dpi_scaling ( window) ;
793
768
commctrl:: DefSubclassProc ( window, msg, wparam, lparam)
@@ -804,12 +779,6 @@ unsafe extern "system" fn public_window_callback<T>(
804
779
805
780
winuser:: WM_DESTROY => {
806
781
use event:: WindowEvent :: Destroyed ;
807
- {
808
- let window_state = subclass_input. window_state . lock ( ) ;
809
- if window_state. modal_timer_handle != 0 {
810
- winuser:: KillTimer ( window, window_state. modal_timer_handle ) ;
811
- }
812
- }
813
782
subclass_input. send_event ( Event :: WindowEvent {
814
783
window_id : RootWindowId ( WindowId ( window) ) ,
815
784
event : Destroyed
@@ -1537,6 +1506,76 @@ unsafe extern "system" fn thread_event_target_callback<T>(
1537
1506
drop ( subclass_input) ;
1538
1507
0
1539
1508
} ,
1509
+ // Because WM_PAINT comes after all other messages, we use it during modal loops to detect
1510
+ // when the event queue has been emptied. See `process_event` for more details.
1511
+ winuser:: WM_PAINT => {
1512
+ winuser:: ValidateRect ( window, ptr:: null ( ) ) ;
1513
+ let queue_call_again = || {
1514
+ winuser:: RedrawWindow (
1515
+ window,
1516
+ ptr:: null ( ) ,
1517
+ ptr:: null_mut ( ) ,
1518
+ winuser:: RDW_INTERNALPAINT
1519
+ ) ;
1520
+ } ;
1521
+ let in_modal_loop = {
1522
+ let runner = subclass_input. event_loop_runner . borrow_mut ( ) ;
1523
+ if let ELRSharedOption :: Runner ( runner) = * runner {
1524
+ ( * runner) . in_modal_loop
1525
+ } else {
1526
+ false
1527
+ }
1528
+ } ;
1529
+ if in_modal_loop {
1530
+ let mut msg = mem:: uninitialized ( ) ;
1531
+ loop {
1532
+ if 0 == winuser:: PeekMessageW ( & mut msg, ptr:: null_mut ( ) , 0 , 0 , 0 ) {
1533
+ break ;
1534
+ }
1535
+ // Clear all paint/timer messages from the queue before sending the events cleared message.
1536
+ match msg. message {
1537
+ // Flush the event queue of WM_PAINT messages.
1538
+ winuser:: WM_PAINT |
1539
+ winuser:: WM_TIMER => {
1540
+ // Remove the message from the message queue.
1541
+ winuser:: PeekMessageW ( & mut msg, ptr:: null_mut ( ) , 0 , 0 , 1 ) ;
1542
+
1543
+ if msg. hwnd != window {
1544
+ winuser:: TranslateMessage ( & mut msg) ;
1545
+ winuser:: DispatchMessageW ( & mut msg) ;
1546
+ }
1547
+ } ,
1548
+ // If the message isn't one of those three, it may be handled by the modal
1549
+ // loop so we should return control flow to it.
1550
+ _ => {
1551
+ queue_call_again ( ) ;
1552
+ return 0 ;
1553
+ }
1554
+ }
1555
+ }
1556
+
1557
+ let runner = subclass_input. event_loop_runner . borrow_mut ( ) ;
1558
+ if let ELRSharedOption :: Runner ( runner) = * runner {
1559
+ let runner = & mut * runner;
1560
+ runner. events_cleared ( ) ;
1561
+ match runner. control_flow {
1562
+ // Waiting is handled by the modal loop.
1563
+ ControlFlow :: Exit |
1564
+ ControlFlow :: Wait => runner. new_events ( ) ,
1565
+ ControlFlow :: WaitUntil ( resume_time) => {
1566
+ wait_until_time_or_msg ( resume_time) ;
1567
+ runner. new_events ( ) ;
1568
+ queue_call_again ( ) ;
1569
+ } ,
1570
+ ControlFlow :: Poll => {
1571
+ runner. new_events ( ) ;
1572
+ queue_call_again ( ) ;
1573
+ }
1574
+ }
1575
+ }
1576
+ }
1577
+ 0
1578
+ }
1540
1579
_ if msg == * USER_EVENT_MSG_ID => {
1541
1580
if let Ok ( event) = subclass_input. user_event_receiver . recv ( ) {
1542
1581
subclass_input. send_event ( Event :: UserEvent ( event) ) ;
0 commit comments