1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
use std::mem;
use std::ptr::null_mut;

use winapi::shared::minwindef::BOOL;
use winapi::shared::windef::HACCEL;
use winapi::um::winuser::{DispatchMessageW, GetMessageW, TranslateAcceleratorW, TranslateMessage};

/// A basic winapi runloop.
///
/// This runloop blocks on receiving messages and dispatches them to windows. It exits
/// on [`WM_QUIT`].
///
/// It is tempting to try to get fancier with runloops, for example waiting on semaphores
/// or other events, but these strategies are risky. In particular, the main runloop is not
/// always in control; when the window is being resized, or a modal dialog is open, then
/// that runloop takes precedence. For waking the UI thread from another thread,
/// [`SendMessage`] is probably the best bet.
///
/// # Safety
///
/// The `accel` argument must be a valid HACCEL handle (though `null_mut()` is valid).
///
/// [`WM_QUIT`]: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-quit
/// [`SendMessage`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage
pub unsafe fn runloop(accel: HACCEL) -> BOOL {
    loop {
        let mut msg = mem::MaybeUninit::uninit();
        let res = GetMessageW(msg.as_mut_ptr(), null_mut(), 0, 0);
        if res <= 0 {
            return res;
        }
        let mut msg = msg.assume_init();
        if accel.is_null() || TranslateAcceleratorW(msg.hwnd, accel, &mut msg) == 0 {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }
}