1use std::cell::Cell;
4use windows::Win32::Foundation::BOOL;
5use windows::Win32::UI::WindowsAndMessaging::{
6 DispatchMessageW,
7 GetMessageW,
8 PostQuitMessage,
9 TranslateMessage,
10 MSG,
11 WM_QUIT,
12};
13
14use std::io;
15
16use crate::internal::ReturnValue;
17
18pub enum ThreadMessageLoop {}
22
23impl ThreadMessageLoop {
24 thread_local! {
25 static RUNNING: Cell<bool> = const { Cell::new(false) };
26 pub(crate) static ENABLE_CALLBACK_ONCE: Cell<bool> = const { Cell::new(false) };
27 }
28
29 pub fn run_thread_message_loop<F>(mut loop_callback: F) -> io::Result<()>
40 where
41 F: FnMut() -> io::Result<()>,
42 {
43 Self::RUNNING.with(|running| {
44 if running.get() {
45 panic!("Cannot run two thread message loops on the same thread");
46 }
47 running.set(true);
48 });
49 let mut msg: MSG = Default::default();
50 loop {
51 unsafe {
52 GetMessageW(&mut msg, None, 0, 0)
53 .if_eq_to_error(BOOL(-1), io::Error::last_os_error)?;
54 }
55 if msg.message == WM_QUIT {
56 Self::RUNNING.with(|running| running.set(false));
57 break;
58 }
59 unsafe {
60 let _ = TranslateMessage(&msg);
61 DispatchMessageW(&msg);
62 }
63 if Self::ENABLE_CALLBACK_ONCE.with(|x| x.take()) {
64 loop_callback()?;
65 }
66 }
67 Ok(())
68 }
69
70 pub fn post_quit_message() {
78 if !ThreadMessageLoop::is_loop_running() {
79 panic!("Cannot post quit message because thread message loop is not running");
80 }
81 unsafe {
82 PostQuitMessage(0);
83 }
84 }
85
86 #[inline(always)]
87 fn is_loop_running() -> bool {
88 Self::RUNNING.with(|running| running.get())
89 }
90}