|
17 | 17 | use std::mem; |
18 | 18 | use std::ptr; |
19 | 19 |
|
| 20 | +use winapi::shared::minwindef::FALSE; |
20 | 21 | use winapi::shared::minwindef::HINSTANCE; |
21 | 22 | use winapi::shared::ntdef::LPCWSTR; |
22 | 23 | use winapi::shared::windef::HCURSOR; |
| 24 | +use winapi::shared::winerror::HRESULT_FROM_WIN32; |
| 25 | +use winapi::um::errhandlingapi::GetLastError; |
23 | 26 | use winapi::um::shellscalingapi::PROCESS_SYSTEM_DPI_AWARE; |
24 | 27 | use winapi::um::wingdi::CreateSolidBrush; |
25 | 28 | use winapi::um::winuser::{ |
26 | | - DispatchMessageW, GetAncestor, GetMessageW, LoadIconW, PostQuitMessage, RegisterClassW, |
27 | | - TranslateAcceleratorW, TranslateMessage, GA_ROOT, IDI_APPLICATION, MSG, WNDCLASSW, |
| 29 | + DispatchMessageW, GetAncestor, GetMessageW, LoadIconW, PostQuitMessage, PostThreadMessageW, |
| 30 | + RegisterClassW, TranslateAcceleratorW, TranslateMessage, GA_ROOT, IDI_APPLICATION, MSG, |
| 31 | + WNDCLASSW, |
28 | 32 | }; |
29 | 33 |
|
| 34 | +use log::{error, warn}; |
| 35 | + |
30 | 36 | use crate::application::AppHandler; |
31 | 37 |
|
32 | 38 | use super::accels; |
33 | 39 | use super::clipboard::Clipboard; |
34 | | -use super::util::{self, ToWide, CLASS_NAME, OPTIONAL_FUNCTIONS}; |
35 | | -use super::window::win_proc_dispatch; |
| 40 | +use super::error::Error; |
| 41 | +use super::util::{ |
| 42 | + self, claim_main_thread, main_thread_id, release_main_thread, ToWide, CLASS_NAME, |
| 43 | + OPTIONAL_FUNCTIONS, |
| 44 | +}; |
| 45 | +use super::window::{win_proc_dispatch, DS_REQUEST_QUIT}; |
36 | 46 |
|
37 | | -pub struct Application; |
| 47 | +pub struct Application { |
| 48 | + handler: Option<Box<dyn AppHandler>>, |
| 49 | +} |
38 | 50 |
|
39 | 51 | impl Application { |
40 | | - pub fn new(_handler: Option<Box<dyn AppHandler>>) -> Application { |
| 52 | + pub fn new(handler: Option<Box<dyn AppHandler>>) -> Application { |
41 | 53 | Application::init(); |
42 | | - Application |
| 54 | + Application { handler } |
43 | 55 | } |
44 | 56 |
|
45 | 57 | pub fn run(&mut self) { |
| 58 | + claim_main_thread(); |
46 | 59 | unsafe { |
47 | 60 | // Handle windows messages |
48 | 61 | loop { |
49 | 62 | let mut msg = mem::MaybeUninit::uninit(); |
50 | 63 | let res = GetMessageW(msg.as_mut_ptr(), ptr::null_mut(), 0, 0); |
51 | 64 | if res <= 0 { |
52 | | - return; |
| 65 | + if res == -1 { |
| 66 | + error!( |
| 67 | + "GetMessageW failed: {}", |
| 68 | + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) |
| 69 | + ); |
| 70 | + } |
| 71 | + break; |
53 | 72 | } |
54 | 73 | let mut msg: MSG = msg.assume_init(); |
55 | 74 | let accels = accels::find_accels(GetAncestor(msg.hwnd, GA_ROOT)); |
56 | 75 | let translated = accels.map_or(false, |it| { |
57 | 76 | TranslateAcceleratorW(msg.hwnd, it.handle(), &mut msg) != 0 |
58 | 77 | }); |
59 | | - |
60 | 78 | if !translated { |
61 | | - TranslateMessage(&msg); |
62 | | - DispatchMessageW(&msg); |
| 79 | + // We check for DS_REQUEST_QUIT here because thread messages |
| 80 | + // will not be forwarded to any window procedures by DispatchMessageW. |
| 81 | + if msg.message == DS_REQUEST_QUIT { |
| 82 | + if let Some(handler) = &mut self.handler { |
| 83 | + // We want to queue up the destruction of all open windows. |
| 84 | + // Failure to do so will lead to resource leaks |
| 85 | + // and an eventual error code exit for the process. |
| 86 | + handler.close_all_windows(); |
| 87 | + } |
| 88 | + // PostQuitMessage sets a quit request flag in the OS. |
| 89 | + // The actual WM_QUIT message is queued but won't be sent |
| 90 | + // until all other important events have been handled. |
| 91 | + PostQuitMessage(0); |
| 92 | + } else { |
| 93 | + TranslateMessage(&msg); |
| 94 | + DispatchMessageW(&msg); |
| 95 | + } |
63 | 96 | } |
64 | 97 | } |
65 | 98 | } |
| 99 | + release_main_thread(); |
66 | 100 | } |
67 | 101 |
|
68 | 102 | /// Initialize the app. At the moment, this is mostly needed for hi-dpi. |
@@ -99,8 +133,15 @@ impl Application { |
99 | 133 | } |
100 | 134 |
|
101 | 135 | pub fn quit() { |
102 | | - unsafe { |
103 | | - PostQuitMessage(0); |
| 136 | + if let Some(thread_id) = main_thread_id() { |
| 137 | + unsafe { |
| 138 | + if PostThreadMessageW(thread_id, DS_REQUEST_QUIT, 0, 0) == FALSE { |
| 139 | + warn!( |
| 140 | + "PostThreadMessageW failed: {}", |
| 141 | + Error::Hr(HRESULT_FROM_WIN32(GetLastError())) |
| 142 | + ); |
| 143 | + } |
| 144 | + } |
104 | 145 | } |
105 | 146 | } |
106 | 147 |
|
|
0 commit comments