Skip to content

Commit f6e99e9

Browse files
author
Billy Messenger
authored
add ability to close window from user code, add HostWindowHandle (#103)
* add ability to close window from user code, add HostWindowHandle * fix manual close method for Mac, rename HostWindowHandle to ChildWindowHandle * fix rustfmt.toml and run cargo format * fix merge conflict mistake * fix more merge conflict mistakes * implement requested changes (with a non-broken commit this time) * implement requested changes * slight reordering of impls
1 parent 2a894c6 commit f6e99e9

7 files changed

Lines changed: 423 additions & 65 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ objc = "0.2.7"
3333
uuid = { version = "0.8", features = ["v4"] }
3434

3535
[dev-dependencies]
36-
rtrb = "0.1.1"
36+
rtrb = "0.2"

examples/open_window.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn main() {
3838
scale: WindowScalePolicy::SystemScaleFactor,
3939
};
4040

41-
let (mut tx, rx) = RingBuffer::new(128).split();
41+
let (mut tx, rx) = RingBuffer::new(128);
4242

4343
::std::thread::spawn(move || loop {
4444
::std::thread::sleep(Duration::from_secs(5));

src/macos/view.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ extern "C" fn release(this: &mut Object, _sel: Sel) {
178178
let retain_count_after_build = WindowState::from_field(this).retain_count_after_build;
179179

180180
if retain_count <= retain_count_after_build {
181-
WindowState::from_field(this).remove_timer();
181+
WindowState::from_field(this).stop();
182182

183183
this.set_ivar(BASEVIEW_STATE_IVAR, ::std::ptr::null() as *const c_void);
184184

src/macos/window.rs

Lines changed: 147 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use std::ffi::c_void;
2+
use std::marker::PhantomData;
3+
use std::sync::atomic::{AtomicBool, Ordering};
4+
use std::sync::Arc;
25

36
use cocoa::appkit::{
47
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSWindow,
@@ -15,21 +18,94 @@ use objc::{msg_send, runtime::Object, sel, sel_impl};
1518

1619
use 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

2026
use super::keyboard::KeyboardState;
2127
use 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+
2395
pub 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

31107
impl 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

Comments
 (0)