windows_async/
executor.rs

1use std::future::{Future};
2use std::pin::{Pin};
3use std::task::{Context, Poll, Waker, RawWaker, RawWakerVTable};
4
5use log;
6
7use windows::core::{
8    HSTRING,
9    PCWSTR,
10};
11
12/* required feature "Win32_Foundation" */
13use windows::Win32::Foundation::{
14    HWND,
15    WPARAM, LPARAM, LRESULT,
16    GetLastError,
17};
18
19/* required feature "Win32_System_LibraryLoader" */
20use windows::Win32::System::LibraryLoader::{
21    GetModuleHandleW,
22};
23
24/* required feature "Win32_UI_WindowsAndMessaging" */
25use windows::Win32::UI::WindowsAndMessaging::{
26    WNDCLASSEXW,
27    RegisterClassExW, /* required feature "Win32_Graphics_Gdi" */
28    CreateWindowExW,
29    DestroyWindow,
30    SetForegroundWindow,
31    WS_POPUP,
32    WS_EX_LEFT,
33    HMENU,
34    MSG,
35    GetMessageW,
36    TranslateMessage,
37    DispatchMessageW,
38    WM_USER,
39    CS_HREDRAW,
40    CS_VREDRAW,
41    DefWindowProcW,
42    IDI_APPLICATION,
43    IDC_ARROW,
44    HICON,
45    COLOR_WINDOW,
46    LoadIconW,
47    LoadCursorW,
48    PostMessageW,
49};
50
51use windows::Win32::Graphics::Gdi::{
52    HBRUSH,
53};
54
55const VTABLE: RawWakerVTable = RawWakerVTable::new(
56    /* clone */
57    |dummy_window| {
58        RawWaker::new(dummy_window, &VTABLE)
59    },
60
61    /* wake */
62    |dummy_window| {
63        let dummy_window:&DummyWindow = unsafe { &*(dummy_window as *const DummyWindow) };
64        crate::executor::post_message_asyncop_completed(dummy_window);
65    },
66
67    /* wake_by_ref */
68    |dummy_window| {
69        let dummy_window:&DummyWindow = unsafe { &*(dummy_window as *const DummyWindow) };
70        crate::executor::post_message_asyncop_completed(dummy_window);
71    },
72
73    /* drop */
74    |_| {},
75);
76
77const DUMMY_WINDOW_CLASSNAME:&str = "windows-msgloop-async:DummyWindow";
78const DUMMY_WINDOW_WINDOWNAME:&str = "windows-msgloop-async:DummyWindow";
79const WM_USER_ASYNCOP_COMPLETED:u32 = WM_USER + 1;
80
81static mut REGISTER_ATOM: Option<u16> = None;
82
83
84fn post_message_asyncop_completed(dummy_window: &DummyWindow) {
85    unsafe {
86        log::trace!("PostMessage(WM_USER_ASYNCOP_COMPLETED) to {:?}", dummy_window);
87        PostMessageW(dummy_window.hwnd(), WM_USER_ASYNCOP_COMPLETED, WPARAM(0), LPARAM(0));
88    }
89}
90
91extern "system" fn wndproc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
92    match msg {
93        WM_USER_ASYNCOP_COMPLETED => {
94            log::trace!("AsyncOperation/AsyncAction Completed ({:?})", hwnd);
95        }
96        _ => {}
97    }
98
99    unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
100}
101
102/// Run a future to completion on the current thread.
103/// 
104/// This function will run message-loop, until the given future has completed.
105pub fn block_on<T>(mut f: impl Future<Output=T>) -> T {
106    
107    // Create dummy window to receive window message.
108    let wnd = create_dummy_window();
109    let pwnd: *const DummyWindow = &wnd;
110
111    let waker = unsafe { Waker::from_raw(RawWaker::new(pwnd as *const (), &VTABLE)) };
112    let mut ctx = Context::from_waker(&waker);
113
114    let mut msg = MSG::default();
115    loop {
116        let f = unsafe { Pin::new_unchecked(&mut f) };
117        if let Poll::Ready(ret) = Future::poll(f, &mut ctx) {
118            return ret;
119        }
120
121        unsafe {
122            GetMessageW(&mut msg, None, 0, 0);
123            TranslateMessage(&mut msg);
124            DispatchMessageW(&mut msg);
125        }
126    }
127}
128
129/// Wrapper type of invisible window handle.
130#[derive(Debug)]
131#[repr(transparent)]
132pub struct DummyWindow(HWND);
133
134impl DummyWindow {
135    /// Get `HWND` of window.
136    pub fn hwnd(&self) -> HWND {
137        self.0.clone()
138    }
139}
140
141impl Drop for DummyWindow {
142    /// Destroy window by calling `DestroyWindow`.
143    fn drop(&mut self) {
144        unsafe {
145            DestroyWindow(self.0);
146        }
147    }
148}
149
150fn register_dummy_window() {
151    unsafe {
152        let hmodule = GetModuleHandleW(PCWSTR(std::ptr::null()));
153    
154        let wndcls = WNDCLASSEXW {
155            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
156            style: CS_HREDRAW | CS_VREDRAW,
157            lpfnWndProc: Some(wndproc),
158            cbClsExtra: 0,
159            cbWndExtra: 0,
160            hInstance: hmodule,
161            hIcon: LoadIconW(hmodule.clone(), IDI_APPLICATION),
162            hCursor: LoadCursorW(hmodule.clone(), IDC_ARROW),
163            hbrBackground: HBRUSH((COLOR_WINDOW.0 + 1) as isize),
164            lpszMenuName: PCWSTR(std::ptr::null()),
165            lpszClassName: PCWSTR(&HSTRING::from(DUMMY_WINDOW_CLASSNAME).as_wide()[0]),
166            hIconSm: HICON::default(),
167        };
168    
169        let ret = RegisterClassExW(&wndcls);
170        if ret == 0 {
171            let err = GetLastError();
172            log::debug!("RegisterClassExW failed (last_error={:?})", err);
173        }
174        else {
175            REGISTER_ATOM = Some(ret);
176        }
177    }
178}
179
180/// Create invisible dummy window.
181pub fn create_dummy_window() -> DummyWindow {
182    unsafe {
183        if REGISTER_ATOM.is_none() {
184            register_dummy_window();
185        }
186        let hmodule = GetModuleHandleW(PCWSTR(std::ptr::null()));
187        let hwnd = CreateWindowExW(
188            WS_EX_LEFT,
189            PCWSTR(&HSTRING::from(DUMMY_WINDOW_CLASSNAME).as_wide()[0]),
190            PCWSTR(&HSTRING::from(DUMMY_WINDOW_WINDOWNAME).as_wide()[0]),
191            WS_POPUP,
192            1, 1, 1, 1,
193            HWND::default(),
194            HMENU::default(),
195            hmodule,
196            std::ptr::null_mut(),
197        );
198
199        if hwnd.0 == 0 {
200            let err = GetLastError();
201            log::debug!("CreateWindowExW failed (last_error={:?})", err);
202        }
203        else {
204            log::trace!("Create Dummy window ({:?})", hwnd);
205        }
206
207        SetForegroundWindow(hwnd);
208        DummyWindow(hwnd)
209    }
210}