2019-05-01 17:03:30 -06:00
|
|
|
use std::{
|
2021-03-08 19:56:39 +01:00
|
|
|
any::Any,
|
|
|
|
|
cell::{Cell, RefCell},
|
|
|
|
|
collections::VecDeque,
|
|
|
|
|
marker::PhantomData,
|
|
|
|
|
mem,
|
|
|
|
|
os::raw::c_void,
|
|
|
|
|
panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe},
|
|
|
|
|
process, ptr,
|
|
|
|
|
rc::{Rc, Weak},
|
2019-08-23 12:30:53 +03:00
|
|
|
sync::mpsc,
|
2019-05-01 17:03:30 -06:00
|
|
|
};
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use cocoa::{
|
2022-01-10 21:39:17 +01:00
|
|
|
appkit::{NSApp, NSEventModifierFlags, NSEventSubtype, NSEventType::NSApplicationDefined},
|
|
|
|
|
base::{id, nil, BOOL, NO, YES},
|
|
|
|
|
foundation::{NSInteger, NSPoint, NSTimeInterval},
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
2021-05-27 17:38:41 +02:00
|
|
|
use objc::rc::autoreleasepool;
|
2021-03-08 19:56:39 +01:00
|
|
|
|
2019-06-18 02:27:00 +08:00
|
|
|
use crate::{
|
2019-05-01 17:03:30 -06:00
|
|
|
event::Event,
|
|
|
|
|
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
|
2020-09-07 20:20:47 +03:00
|
|
|
monitor::MonitorHandle as RootMonitorHandle,
|
2022-02-16 22:09:03 +01:00
|
|
|
platform::macos::ActivationPolicy,
|
|
|
|
|
platform_impl::{
|
|
|
|
|
get_aux_state_mut,
|
|
|
|
|
platform::{
|
|
|
|
|
app::APP_CLASS,
|
|
|
|
|
app_delegate::APP_DELEGATE_CLASS,
|
|
|
|
|
app_state::{AppState, Callback},
|
|
|
|
|
monitor::{self, MonitorHandle},
|
|
|
|
|
observer::*,
|
|
|
|
|
util::IdRef,
|
|
|
|
|
},
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
2019-05-01 17:03:30 -06:00
|
|
|
};
|
|
|
|
|
|
2021-03-08 19:56:39 +01:00
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct PanicInfo {
|
|
|
|
|
inner: Cell<Option<Box<dyn Any + Send + 'static>>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WARNING:
|
|
|
|
|
// As long as this struct is used through its `impl`, it is UnwindSafe.
|
|
|
|
|
// (If `get_mut` is called on `inner`, unwind safety may get broken.)
|
|
|
|
|
impl UnwindSafe for PanicInfo {}
|
|
|
|
|
impl RefUnwindSafe for PanicInfo {}
|
|
|
|
|
impl PanicInfo {
|
|
|
|
|
pub fn is_panicking(&self) -> bool {
|
|
|
|
|
let inner = self.inner.take();
|
|
|
|
|
let result = inner.is_some();
|
|
|
|
|
self.inner.set(inner);
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
/// Overwrites the curret state if the current state is not panicking
|
|
|
|
|
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
|
|
|
|
|
if !self.is_panicking() {
|
|
|
|
|
self.inner.set(Some(p));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn take(&self) -> Option<Box<dyn Any + Send + 'static>> {
|
|
|
|
|
self.inner.take()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
pub struct EventLoopWindowTarget<T: 'static> {
|
|
|
|
|
pub sender: mpsc::Sender<T>, // this is only here to be cloned elsewhere
|
|
|
|
|
pub receiver: mpsc::Receiver<T>,
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
impl<T> Default for EventLoopWindowTarget<T> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
let (sender, receiver) = mpsc::channel();
|
|
|
|
|
EventLoopWindowTarget { sender, receiver }
|
|
|
|
|
}
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2020-07-04 15:46:41 -04:00
|
|
|
impl<T: 'static> EventLoopWindowTarget<T> {
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
|
|
|
monitor::available_monitors()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-09-07 20:20:47 +03:00
|
|
|
pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
|
|
|
|
|
let monitor = monitor::primary_monitor();
|
|
|
|
|
Some(RootMonitorHandle { inner: monitor })
|
2020-07-04 15:46:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-01 12:20:56 +01:00
|
|
|
impl<T> EventLoopWindowTarget<T> {
|
|
|
|
|
pub(crate) fn hide_application(&self) {
|
|
|
|
|
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
|
|
|
|
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
|
|
|
|
unsafe { msg_send![app, hide: 0] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn hide_other_applications(&self) {
|
|
|
|
|
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
|
|
|
|
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
|
|
|
|
unsafe { msg_send![app, hideOtherApplications: 0] }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
pub struct EventLoop<T: 'static> {
|
2022-02-16 22:09:03 +01:00
|
|
|
/// The delegate is only weakly referenced by NSApplication, so we keep
|
|
|
|
|
/// it around here as well.
|
|
|
|
|
_delegate: IdRef,
|
2021-04-30 11:31:28 +02:00
|
|
|
|
2019-08-23 12:30:53 +03:00
|
|
|
window_target: Rc<RootWindowTarget<T>>,
|
2021-03-08 19:56:39 +01:00
|
|
|
panic_info: Rc<PanicInfo>,
|
|
|
|
|
|
|
|
|
|
/// We make sure that the callback closure is dropped during a panic
|
|
|
|
|
/// by making the event loop own it.
|
|
|
|
|
///
|
|
|
|
|
/// Every other reference should be a Weak reference which is only upgraded
|
|
|
|
|
/// into a strong reference in order to call the callback but then the
|
|
|
|
|
/// strong reference should be dropped as soon as possible.
|
2022-01-16 11:14:59 +11:00
|
|
|
_callback: Option<Rc<Callback<T>>>,
|
2017-06-09 22:13:30 +10:00
|
|
|
}
|
|
|
|
|
|
2022-06-10 13:43:33 +03:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
2022-02-16 22:09:03 +01:00
|
|
|
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
|
|
|
|
pub(crate) activation_policy: ActivationPolicy,
|
|
|
|
|
pub(crate) default_menu: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for PlatformSpecificEventLoopAttributes {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
activation_policy: Default::default(), // Regular
|
|
|
|
|
default_menu: true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
impl<T> EventLoop<T> {
|
2022-02-16 22:09:03 +01:00
|
|
|
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
|
2019-05-01 17:03:30 -06:00
|
|
|
let delegate = unsafe {
|
2022-01-10 21:39:17 +01:00
|
|
|
let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread);
|
|
|
|
|
if is_main_thread == NO {
|
2019-05-01 17:03:30 -06:00
|
|
|
panic!("On macOS, `EventLoop` must be created on the main thread!");
|
2017-06-09 22:13:30 +10:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
// This must be done before `NSApp()` (equivalent to sending
|
|
|
|
|
// `sharedApplication`) is called anywhere else, or we'll end up
|
|
|
|
|
// with the wrong `NSApplication` class and the wrong thread could
|
|
|
|
|
// be marked as main.
|
|
|
|
|
let app: id = msg_send![APP_CLASS.0, sharedApplication];
|
|
|
|
|
|
|
|
|
|
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
|
2022-02-16 22:09:03 +01:00
|
|
|
|
|
|
|
|
let mut aux_state = get_aux_state_mut(&**delegate);
|
|
|
|
|
aux_state.activation_policy = attributes.activation_policy;
|
|
|
|
|
aux_state.default_menu = attributes.default_menu;
|
|
|
|
|
|
2021-05-27 17:38:41 +02:00
|
|
|
autoreleasepool(|| {
|
|
|
|
|
let _: () = msg_send![app, setDelegate:*delegate];
|
|
|
|
|
});
|
2022-02-16 22:09:03 +01:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
delegate
|
|
|
|
|
};
|
2021-03-08 19:56:39 +01:00
|
|
|
let panic_info: Rc<PanicInfo> = Default::default();
|
|
|
|
|
setup_control_flow_observers(Rc::downgrade(&panic_info));
|
2019-05-01 17:03:30 -06:00
|
|
|
EventLoop {
|
2022-02-16 22:09:03 +01:00
|
|
|
_delegate: delegate,
|
2019-08-23 12:30:53 +03:00
|
|
|
window_target: Rc::new(RootWindowTarget {
|
2019-05-01 17:03:30 -06:00
|
|
|
p: Default::default(),
|
|
|
|
|
_marker: PhantomData,
|
2019-08-23 12:30:53 +03:00
|
|
|
}),
|
2021-03-08 19:56:39 +01:00
|
|
|
panic_info,
|
|
|
|
|
_callback: None,
|
2017-06-09 22:13:30 +10:00
|
|
|
}
|
|
|
|
|
}
|
2017-02-05 12:51:09 +11:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
pub fn window_target(&self) -> &RootWindowTarget<T> {
|
|
|
|
|
&self.window_target
|
2017-02-05 12:51:09 +11:00
|
|
|
}
|
|
|
|
|
|
2019-08-23 12:30:53 +03:00
|
|
|
pub fn run<F>(mut self, callback: F) -> !
|
2019-06-21 11:33:15 -04:00
|
|
|
where
|
2020-01-04 01:32:34 -05:00
|
|
|
F: 'static + FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
|
2019-08-23 12:30:53 +03:00
|
|
|
{
|
2022-01-11 01:23:20 +01:00
|
|
|
let exit_code = self.run_return(callback);
|
|
|
|
|
process::exit(exit_code);
|
2019-08-23 12:30:53 +03:00
|
|
|
}
|
|
|
|
|
|
2022-01-11 01:23:20 +01:00
|
|
|
pub fn run_return<F>(&mut self, callback: F) -> i32
|
2019-08-23 12:30:53 +03:00
|
|
|
where
|
2020-01-04 01:32:34 -05:00
|
|
|
F: FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
|
2017-02-03 23:05:57 +11:00
|
|
|
{
|
2021-03-08 19:56:39 +01:00
|
|
|
// This transmute is always safe, in case it was reached through `run`, since our
|
|
|
|
|
// lifetime will be already 'static. In other cases caller should ensure that all data
|
|
|
|
|
// they passed to callback will actually outlive it, some apps just can't move
|
|
|
|
|
// everything to event loop, so this is something that they should care about.
|
|
|
|
|
let callback = unsafe {
|
|
|
|
|
mem::transmute::<
|
|
|
|
|
Rc<RefCell<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
|
|
|
|
|
Rc<RefCell<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
|
|
|
|
|
>(Rc::new(RefCell::new(callback)))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self._callback = Some(Rc::clone(&callback));
|
|
|
|
|
|
2022-01-11 01:23:20 +01:00
|
|
|
let exit_code = autoreleasepool(|| unsafe {
|
2019-05-01 17:03:30 -06:00
|
|
|
let app = NSApp();
|
|
|
|
|
assert_ne!(app, nil);
|
2021-03-08 19:56:39 +01:00
|
|
|
|
|
|
|
|
// A bit of juggling with the callback references to make sure
|
|
|
|
|
// that `self.callback` is the only owner of the callback.
|
|
|
|
|
let weak_cb: Weak<_> = Rc::downgrade(&callback);
|
|
|
|
|
mem::drop(callback);
|
|
|
|
|
|
|
|
|
|
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
|
2022-06-10 13:43:33 +03:00
|
|
|
let _: () = msg_send![app, run];
|
2021-03-08 19:56:39 +01:00
|
|
|
|
|
|
|
|
if let Some(panic) = self.panic_info.take() {
|
2021-06-13 14:25:06 +02:00
|
|
|
drop(self._callback.take());
|
2021-03-08 19:56:39 +01:00
|
|
|
resume_unwind(panic);
|
|
|
|
|
}
|
2022-01-11 01:23:20 +01:00
|
|
|
AppState::exit()
|
2021-05-27 17:38:41 +02:00
|
|
|
});
|
2021-06-13 14:25:06 +02:00
|
|
|
drop(self._callback.take());
|
2022-01-11 01:23:20 +01:00
|
|
|
|
|
|
|
|
exit_code
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
|
|
|
|
EventLoopProxy::new(self.window_target.p.sender.clone())
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
}
|
2017-02-04 00:51:38 +11:00
|
|
|
|
2021-03-08 19:56:39 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub unsafe fn post_dummy_event(target: id) {
|
|
|
|
|
let event_class = class!(NSEvent);
|
|
|
|
|
let dummy_event: id = msg_send![
|
|
|
|
|
event_class,
|
|
|
|
|
otherEventWithType: NSApplicationDefined
|
|
|
|
|
location: NSPoint::new(0.0, 0.0)
|
2022-01-10 21:39:17 +01:00
|
|
|
modifierFlags: NSEventModifierFlags::empty()
|
|
|
|
|
timestamp: 0 as NSTimeInterval
|
|
|
|
|
windowNumber: 0 as NSInteger
|
2021-03-08 19:56:39 +01:00
|
|
|
context: nil
|
2022-01-10 21:39:17 +01:00
|
|
|
subtype: NSEventSubtype::NSWindowExposedEventType
|
|
|
|
|
data1: 0 as NSInteger
|
|
|
|
|
data2: 0 as NSInteger
|
2021-03-08 19:56:39 +01:00
|
|
|
];
|
2022-06-10 13:43:33 +03:00
|
|
|
let _: () = msg_send![target, postEvent: dummy_event atStart: YES];
|
2021-03-08 19:56:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Catches panics that happen inside `f` and when a panic
|
|
|
|
|
/// happens, stops the `sharedApplication`
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
|
|
|
|
panic_info: Weak<PanicInfo>,
|
|
|
|
|
f: F,
|
|
|
|
|
) -> Option<R> {
|
|
|
|
|
match catch_unwind(f) {
|
|
|
|
|
Ok(r) => Some(r),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
// It's important that we set the panic before requesting a `stop`
|
|
|
|
|
// because some callback are still called during the `stop` message
|
|
|
|
|
// and we need to know in those callbacks if the application is currently
|
|
|
|
|
// panicking
|
|
|
|
|
{
|
|
|
|
|
let panic_info = panic_info.upgrade().unwrap();
|
|
|
|
|
panic_info.set_panic(e);
|
|
|
|
|
}
|
|
|
|
|
unsafe {
|
|
|
|
|
let app_class = class!(NSApplication);
|
|
|
|
|
let app: id = msg_send![app_class, sharedApplication];
|
2022-06-10 13:43:33 +03:00
|
|
|
let _: () = msg_send![app, stop: nil];
|
2021-03-08 19:56:39 +01:00
|
|
|
|
|
|
|
|
// Posting a dummy event to get `stop` to take effect immediately.
|
|
|
|
|
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
|
|
|
|
|
post_dummy_event(app);
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
pub struct EventLoopProxy<T> {
|
2019-05-01 17:03:30 -06:00
|
|
|
sender: mpsc::Sender<T>,
|
|
|
|
|
source: CFRunLoopSourceRef,
|
|
|
|
|
}
|
2017-02-04 00:51:38 +11:00
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
|
2017-02-04 00:51:38 +11:00
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
impl<T> Drop for EventLoopProxy<T> {
|
2020-04-20 23:48:42 +02:00
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
CFRelease(self.source as _);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
impl<T> Clone for EventLoopProxy<T> {
|
2019-08-06 05:51:42 +09:00
|
|
|
fn clone(&self) -> Self {
|
2022-03-18 14:09:39 +01:00
|
|
|
EventLoopProxy::new(self.sender.clone())
|
2019-08-06 05:51:42 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
impl<T> EventLoopProxy<T> {
|
2019-05-01 17:03:30 -06:00
|
|
|
fn new(sender: mpsc::Sender<T>) -> Self {
|
|
|
|
|
unsafe {
|
2019-07-13 01:05:07 +02:00
|
|
|
// just wake up the eventloop
|
2019-05-01 17:03:30 -06:00
|
|
|
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
|
|
|
|
|
|
|
|
|
|
// adding a Source to the main CFRunLoop lets us wake it up and
|
|
|
|
|
// process user events through the normal OS EventLoop mechanisms.
|
|
|
|
|
let rl = CFRunLoopGetMain();
|
|
|
|
|
let mut context: CFRunLoopSourceContext = mem::zeroed();
|
2019-11-11 14:50:31 -08:00
|
|
|
context.perform = Some(event_loop_proxy_handler);
|
2019-06-21 11:33:15 -04:00
|
|
|
let source =
|
|
|
|
|
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
2019-05-01 17:03:30 -06:00
|
|
|
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
|
|
|
|
CFRunLoopWakeUp(rl);
|
|
|
|
|
|
2022-03-18 14:09:39 +01:00
|
|
|
EventLoopProxy { sender, source }
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-07 18:22:03 +01:00
|
|
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
|
|
|
|
self.sender
|
|
|
|
|
.send(event)
|
|
|
|
|
.map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
|
2017-05-31 15:00:49 +10:00
|
|
|
unsafe {
|
2019-05-01 17:03:30 -06:00
|
|
|
// let the main thread know there's a new event
|
|
|
|
|
CFRunLoopSourceSignal(self.source);
|
|
|
|
|
let rl = CFRunLoopGetMain();
|
|
|
|
|
CFRunLoopWakeUp(rl);
|
2017-05-31 15:00:49 +10:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2017-02-03 23:05:57 +11:00
|
|
|
}
|