1414
1515//! Windows implementation of features at the application scope.
1616
17+ use std:: cell:: RefCell ;
18+ use std:: collections:: HashSet ;
1719use std:: mem;
1820use std:: ptr;
21+ use std:: rc:: Rc ;
1922
20- use winapi:: shared:: minwindef:: HINSTANCE ;
23+ use winapi:: shared:: minwindef:: { FALSE , HINSTANCE } ;
2124use winapi:: shared:: ntdef:: LPCWSTR ;
22- use winapi:: shared:: windef:: HCURSOR ;
25+ use winapi:: shared:: windef:: { HCURSOR , HWND } ;
26+ use winapi:: shared:: winerror:: HRESULT_FROM_WIN32 ;
27+ use winapi:: um:: errhandlingapi:: GetLastError ;
2328use winapi:: um:: shellscalingapi:: PROCESS_SYSTEM_DPI_AWARE ;
2429use winapi:: um:: wingdi:: CreateSolidBrush ;
2530use winapi:: um:: winuser:: {
26- DispatchMessageW , GetAncestor , GetMessageW , LoadIconW , PostQuitMessage , RegisterClassW ,
31+ DispatchMessageW , GetAncestor , GetMessageW , LoadIconW , PostMessageW , RegisterClassW ,
2732 TranslateAcceleratorW , TranslateMessage , GA_ROOT , IDI_APPLICATION , MSG , WNDCLASSW ,
2833} ;
2934
3035use crate :: application:: AppHandler ;
3136
3237use super :: accels;
3338use super :: clipboard:: Clipboard ;
39+ use super :: error:: Error ;
3440use super :: util:: { self , ToWide , CLASS_NAME , OPTIONAL_FUNCTIONS } ;
35- use super :: window:: win_proc_dispatch;
41+ use super :: window:: { self , DS_REQUEST_QUIT } ;
42+
43+ thread_local ! {
44+ static GLOBAL_STATE : RefCell <Option <AppState >> = RefCell :: new( None ) ;
45+ }
46+
47+ #[ derive( Clone ) ]
48+ pub struct AppState {
49+ state : Rc < RefCell < State > > ,
50+ }
51+
52+ struct State {
53+ quitting : bool ,
54+ app_hwnd : Option < HWND > ,
55+ windows : HashSet < HWND > ,
56+ }
3657
3758pub struct Application ;
3859
60+ impl AppState {
61+ pub ( crate ) fn new ( ) -> AppState {
62+ let state = Rc :: new ( RefCell :: new ( State {
63+ quitting : false ,
64+ app_hwnd : None ,
65+ windows : HashSet :: new ( ) ,
66+ } ) ) ;
67+ AppState { state }
68+ }
69+
70+ pub ( crate ) fn quitting ( & self ) -> bool {
71+ self . state . borrow ( ) . quitting
72+ }
73+
74+ pub ( crate ) fn set_quitting ( & self , quitting : bool ) {
75+ self . state . borrow_mut ( ) . quitting = quitting;
76+ }
77+
78+ pub ( crate ) fn app_hwnd ( & self ) -> Option < HWND > {
79+ self . state . borrow ( ) . app_hwnd
80+ }
81+
82+ pub ( crate ) fn set_app_hwnd ( & self , app_hwnd : Option < HWND > ) {
83+ self . state . borrow_mut ( ) . app_hwnd = app_hwnd;
84+ }
85+
86+ /// Returns a set of `HWND` for all the current normal windows.
87+ ///
88+ /// The returned set should be treated with extremely limited lifetime.
89+ /// The window handles it contains can become stale quickly.
90+ #[ allow( clippy:: mutable_key_type) ]
91+ pub ( crate ) unsafe fn windows ( & self ) -> HashSet < HWND > {
92+ self . state . borrow ( ) . windows . clone ( )
93+ }
94+
95+ pub ( crate ) fn add_window ( & self , hwnd : HWND ) -> bool {
96+ self . state . borrow_mut ( ) . windows . insert ( hwnd)
97+ }
98+
99+ pub ( crate ) fn remove_window ( & self , hwnd : HWND ) -> bool {
100+ self . state . borrow_mut ( ) . windows . remove ( & hwnd)
101+ }
102+ }
103+
39104impl Application {
40- pub fn new ( _handler : Option < Box < dyn AppHandler > > ) -> Application {
105+ pub fn new ( state : AppState , _handler : Option < Box < dyn AppHandler > > ) -> Application {
106+ util:: claim_main_thread ( ) ;
107+ GLOBAL_STATE . with ( |global_state| {
108+ * global_state. borrow_mut ( ) = Some ( state. clone ( ) ) ;
109+ } ) ;
41110 Application :: init ( ) ;
111+ window:: build_app_window ( state) . expect ( "Failed to build main message window" ) ;
42112 Application
43113 }
44114
@@ -49,14 +119,19 @@ impl Application {
49119 let mut msg = mem:: MaybeUninit :: uninit ( ) ;
50120 let res = GetMessageW ( msg. as_mut_ptr ( ) , ptr:: null_mut ( ) , 0 , 0 ) ;
51121 if res <= 0 {
52- return ;
122+ if res == -1 {
123+ log:: error!(
124+ "GetMessageW failed: {}" ,
125+ Error :: Hr ( HRESULT_FROM_WIN32 ( GetLastError ( ) ) )
126+ ) ;
127+ }
128+ break ;
53129 }
54130 let mut msg: MSG = msg. assume_init ( ) ;
55131 let accels = accels:: find_accels ( GetAncestor ( msg. hwnd , GA_ROOT ) ) ;
56132 let translated = accels. map_or ( false , |it| {
57133 TranslateAcceleratorW ( msg. hwnd , it. handle ( ) , & mut msg) != 0
58134 } ) ;
59-
60135 if !translated {
61136 TranslateMessage ( & msg) ;
62137 DispatchMessageW ( & msg) ;
@@ -67,6 +142,7 @@ impl Application {
67142
68143 /// Initialize the app. At the moment, this is mostly needed for hi-dpi.
69144 fn init ( ) {
145+ util:: assert_main_thread ( ) ;
70146 util:: attach_console ( ) ;
71147 if let Some ( func) = OPTIONAL_FUNCTIONS . SetProcessDpiAwareness {
72148 // This function is only supported on windows 10
@@ -81,7 +157,7 @@ impl Application {
81157 let brush = CreateSolidBrush ( 0xff_ff_ff ) ;
82158 let wnd = WNDCLASSW {
83159 style : 0 ,
84- lpfnWndProc : Some ( win_proc_dispatch) ,
160+ lpfnWndProc : Some ( window :: win_proc_dispatch) ,
85161 cbClsExtra : 0 ,
86162 cbWndExtra : 0 ,
87163 hInstance : 0 as HINSTANCE ,
@@ -99,9 +175,21 @@ impl Application {
99175 }
100176
101177 pub fn quit ( ) {
102- unsafe {
103- PostQuitMessage ( 0 ) ;
104- }
178+ util:: assert_main_thread ( ) ;
179+ GLOBAL_STATE . with ( |global_state| {
180+ if let Some ( global_state) = global_state. borrow ( ) . as_ref ( ) {
181+ if let Some ( app_hwnd) = global_state. app_hwnd ( ) {
182+ unsafe {
183+ if PostMessageW ( app_hwnd, DS_REQUEST_QUIT , 0 , 0 ) == FALSE {
184+ log:: error!(
185+ "PostMessageW failed: {}" ,
186+ Error :: Hr ( HRESULT_FROM_WIN32 ( GetLastError ( ) ) )
187+ ) ;
188+ }
189+ }
190+ }
191+ }
192+ } ) ;
105193 }
106194
107195 pub fn clipboard ( ) -> Clipboard {
@@ -113,3 +201,12 @@ impl Application {
113201 "en-US" . into ( )
114202 }
115203}
204+
205+ impl Drop for Application {
206+ fn drop ( & mut self ) {
207+ GLOBAL_STATE . with ( |global_state| {
208+ * global_state. borrow_mut ( ) = None ;
209+ } ) ;
210+ util:: release_main_thread ( ) ;
211+ }
212+ }
0 commit comments