11use std:: ffi:: c_void;
2+ use std:: marker:: PhantomData ;
3+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
4+ use std:: sync:: Arc ;
25
36use cocoa:: appkit:: {
47 NSApp , NSApplication , NSApplicationActivationPolicyRegular , NSBackingStoreBuffered , NSWindow ,
@@ -15,21 +18,94 @@ use objc::{msg_send, runtime::Object, sel, sel_impl};
1518
1619use raw_window_handle:: { macos:: MacOSHandle , HasRawWindowHandle , RawWindowHandle } ;
1720
18- use crate :: { Event , EventStatus , WindowHandler , WindowInfo , WindowOpenOptions , WindowScalePolicy } ;
21+ use crate :: {
22+ Event , EventStatus , WindowEvent , WindowHandler , WindowInfo , WindowOpenOptions ,
23+ WindowScalePolicy ,
24+ } ;
1925
2026use super :: keyboard:: KeyboardState ;
2127use super :: view:: { create_view, BASEVIEW_STATE_IVAR } ;
2228
29+ pub struct WindowHandle {
30+ raw_window_handle : Option < RawWindowHandle > ,
31+ close_requested : Arc < AtomicBool > ,
32+ is_open : Arc < AtomicBool > ,
33+
34+ // Ensure handle is !Send
35+ _phantom : PhantomData < * mut ( ) > ,
36+ }
37+
38+ impl WindowHandle {
39+ pub fn close ( & mut self ) {
40+ if let Some ( _) = self . raw_window_handle . take ( ) {
41+ self . close_requested . store ( true , Ordering :: Relaxed ) ;
42+ }
43+ }
44+
45+ pub fn is_open ( & self ) -> bool {
46+ self . is_open . load ( Ordering :: Relaxed )
47+ }
48+ }
49+
50+ unsafe impl HasRawWindowHandle for WindowHandle {
51+ fn raw_window_handle ( & self ) -> RawWindowHandle {
52+ if let Some ( raw_window_handle) = self . raw_window_handle {
53+ if self . is_open . load ( Ordering :: Relaxed ) {
54+ return raw_window_handle;
55+ }
56+ }
57+
58+ RawWindowHandle :: MacOS ( MacOSHandle { ..MacOSHandle :: empty ( ) } )
59+ }
60+ }
61+
62+ struct ParentHandle {
63+ _close_requested : Arc < AtomicBool > ,
64+ is_open : Arc < AtomicBool > ,
65+ }
66+
67+ impl ParentHandle {
68+ pub fn new ( raw_window_handle : RawWindowHandle ) -> ( Self , WindowHandle ) {
69+ let close_requested = Arc :: new ( AtomicBool :: new ( false ) ) ;
70+ let is_open = Arc :: new ( AtomicBool :: new ( true ) ) ;
71+
72+ let handle = WindowHandle {
73+ raw_window_handle : Some ( raw_window_handle) ,
74+ close_requested : Arc :: clone ( & close_requested) ,
75+ is_open : Arc :: clone ( & is_open) ,
76+ _phantom : PhantomData :: default ( ) ,
77+ } ;
78+
79+ ( Self { _close_requested : close_requested, is_open } , handle)
80+ }
81+
82+ /*
83+ pub fn parent_did_drop(&self) -> bool {
84+ self.close_requested.load(Ordering::Relaxed)
85+ }
86+ */
87+ }
88+
89+ impl Drop for ParentHandle {
90+ fn drop ( & mut self ) {
91+ self . is_open . store ( false , Ordering :: Relaxed ) ;
92+ }
93+ }
94+
2395pub struct Window {
96+ /// Only set if we created the parent window, i.e. we are running in
97+ /// parentless mode
98+ ns_app : Option < id > ,
2499 /// Only set if we created the parent window, i.e. we are running in
25100 /// parentless mode
26101 ns_window : Option < id > ,
27102 /// Our subclassed NSView
28103 ns_view : id ,
104+ close_requested : bool ,
29105}
30106
31107impl Window {
32- pub fn open_parented < P , H , B > ( parent : & P , options : WindowOpenOptions , build : B )
108+ pub fn open_parented < P , H , B > ( parent : & P , options : WindowOpenOptions , build : B ) -> WindowHandle
33109 where
34110 P : HasRawWindowHandle ,
35111 H : WindowHandler + ' static ,
@@ -46,19 +122,21 @@ impl Window {
46122
47123 let ns_view = unsafe { create_view ( & options) } ;
48124
49- let window = Window { ns_window : None , ns_view } ;
125+ let window = Window { ns_app : None , ns_window : None , ns_view, close_requested : false } ;
50126
51- Self :: init ( window, build) ;
127+ let window_handle = Self :: init ( true , window, build) ;
52128
53129 unsafe {
54130 let _: id = msg_send ! [ handle. ns_view as * mut Object , addSubview: ns_view] ;
55131 let ( ) = msg_send ! [ ns_view as id, release] ;
56132
57133 let ( ) = msg_send ! [ pool, drain] ;
58134 }
135+
136+ window_handle
59137 }
60138
61- pub fn open_as_if_parented < H , B > ( options : WindowOpenOptions , build : B ) -> RawWindowHandle
139+ pub fn open_as_if_parented < H , B > ( options : WindowOpenOptions , build : B ) -> WindowHandle
62140 where
63141 H : WindowHandler + ' static ,
64142 B : FnOnce ( & mut crate :: Window ) -> H ,
@@ -68,17 +146,15 @@ impl Window {
68146
69147 let ns_view = unsafe { create_view ( & options) } ;
70148
71- let window = Window { ns_window : None , ns_view } ;
72-
73- let raw_window_handle = window. raw_window_handle ( ) ;
149+ let window = Window { ns_app : None , ns_window : None , ns_view, close_requested : false } ;
74150
75- Self :: init ( window, build) ;
151+ let window_handle = Self :: init ( true , window, build) ;
76152
77153 unsafe {
78154 let ( ) = msg_send ! [ pool, drain] ;
79155 }
80156
81- raw_window_handle
157+ window_handle
82158 }
83159
84160 pub fn open_blocking < H , B > ( options : WindowOpenOptions , build : B )
@@ -118,7 +194,9 @@ impl Window {
118194 let ns_window = unsafe {
119195 let ns_window = NSWindow :: alloc ( nil) . initWithContentRect_styleMask_backing_defer_ (
120196 rect,
121- NSWindowStyleMask :: NSTitledWindowMask ,
197+ NSWindowStyleMask :: NSTitledWindowMask
198+ | NSWindowStyleMask :: NSClosableWindowMask
199+ | NSWindowStyleMask :: NSMiniaturizableWindowMask ,
122200 NSBackingStoreBuffered ,
123201 NO ,
124202 ) ;
@@ -134,28 +212,36 @@ impl Window {
134212
135213 let ns_view = unsafe { create_view ( & options) } ;
136214
137- let window = Window { ns_window : Some ( ns_window) , ns_view } ;
215+ let window = Window {
216+ ns_app : Some ( app) ,
217+ ns_window : Some ( ns_window) ,
218+ ns_view,
219+ close_requested : false ,
220+ } ;
138221
139- Self :: init ( window, build) ;
222+ let _ = Self :: init ( false , window, build) ;
140223
141224 unsafe {
142225 ns_window. setContentView_ ( ns_view) ;
143- let ( ) = msg_send ! [ ns_view as id, release] ;
144226
227+ let ( ) = msg_send ! [ ns_view as id, release] ;
145228 let ( ) = msg_send ! [ pool, drain] ;
146229
147230 app. run ( ) ;
148231 }
149232 }
150233
151- fn init < H , B > ( mut window : Window , build : B )
234+ fn init < H , B > ( parented : bool , mut window : Window , build : B ) -> WindowHandle
152235 where
153236 H : WindowHandler + ' static ,
154237 B : FnOnce ( & mut crate :: Window ) -> H ,
155238 B : Send + ' static ,
156239 {
157240 let window_handler = Box :: new ( build ( & mut crate :: Window :: new ( & mut window) ) ) ;
158241
242+ let ( parent_handle, window_handle) = ParentHandle :: new ( window. raw_window_handle ( ) ) ;
243+ let parent_handle = if parented { Some ( parent_handle) } else { None } ;
244+
159245 let retain_count_after_build: usize = unsafe { msg_send ! [ window. ns_view, retainCount] } ;
160246
161247 let window_state_ptr = Box :: into_raw ( Box :: new ( WindowState {
@@ -164,6 +250,7 @@ impl Window {
164250 keyboard_state : KeyboardState :: new ( ) ,
165251 frame_timer : None ,
166252 retain_count_after_build,
253+ _parent_handle : parent_handle,
167254 } ) ) ;
168255
169256 unsafe {
@@ -172,6 +259,12 @@ impl Window {
172259
173260 WindowState :: setup_timer ( window_state_ptr) ;
174261 }
262+
263+ window_handle
264+ }
265+
266+ pub fn close ( & mut self ) {
267+ self . close_requested = true ;
175268 }
176269}
177270
@@ -180,6 +273,7 @@ pub(super) struct WindowState {
180273 window_handler : Box < dyn WindowHandler > ,
181274 keyboard_state : KeyboardState ,
182275 frame_timer : Option < CFRunLoopTimer > ,
276+ _parent_handle : Option < ParentHandle > ,
183277 pub retain_count_after_build : usize ,
184278}
185279
@@ -201,6 +295,36 @@ impl WindowState {
201295
202296 pub ( super ) fn trigger_frame ( & mut self ) {
203297 self . window_handler . on_frame ( & mut crate :: Window :: new ( & mut self . window ) ) ;
298+
299+ let mut do_close = false ;
300+
301+ /* FIXME: Is it even necessary to check if the parent dropped the handle
302+ // in MacOS?
303+ // Check if the parent handle was dropped
304+ if let Some(parent_handle) = &self.parent_handle {
305+ if parent_handle.parent_did_drop() {
306+ do_close = true;
307+ self.window.close_requested = false;
308+ }
309+ }
310+ */
311+
312+ // Check if the user requested the window to close
313+ if self . window . close_requested {
314+ do_close = true ;
315+ self . window . close_requested = false ;
316+ }
317+
318+ if do_close {
319+ unsafe {
320+ if let Some ( ns_window) = self . window . ns_window . take ( ) {
321+ ns_window. close ( ) ;
322+ } else {
323+ // FIXME: How do we close a non-parented window? Is this even
324+ // possible in a DAW host usecase?
325+ }
326+ }
327+ }
204328 }
205329
206330 pub ( super ) fn process_native_key_event ( & mut self , event : * mut Object ) -> Option < KeyboardEvent > {
@@ -235,10 +359,17 @@ impl WindowState {
235359 }
236360
237361 /// Call when freeing view
238- pub ( super ) unsafe fn remove_timer ( & mut self ) {
362+ pub ( super ) unsafe fn stop ( & mut self ) {
239363 if let Some ( frame_timer) = self . frame_timer . take ( ) {
240364 CFRunLoop :: get_current ( ) . remove_timer ( & frame_timer, kCFRunLoopDefaultMode) ;
241365 }
366+
367+ self . trigger_event ( Event :: Window ( WindowEvent :: WillClose ) ) ;
368+
369+ // If in non-parented mode, we want to also quit the app altogether
370+ if let Some ( app) = self . window . ns_app . take ( ) {
371+ app. stop_ ( app) ;
372+ }
242373 }
243374}
244375
0 commit comments