Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow listening to file open events on macos #1759

Closed
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Change default file open callback behaviour.
Now setting the file open callback to `None` behaves identically to not
specifying the delegate methods at all.
ArturKovacs committed Apr 8, 2021
commit 330da43a33adb530e02e65f69fae4095072146dc
34 changes: 26 additions & 8 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@

use std::{os::raw::c_void, path::PathBuf};

use cocoa::{base::nil, foundation::NSAutoreleasePool};

use crate::{
dpi::LogicalSize,
event_loop::EventLoopWindowTarget,
@@ -231,9 +233,10 @@ pub trait EventLoopWindowTargetExtMacOS {
///
/// Other systems usually provide the path as a program argument.
/// See: `std::env::args`.
fn set_file_open_callback<F>(&self, callback: Option<F>)
where
F: FnMut(Vec<PathBuf>) -> FileOpenResult + Send + 'static;
fn set_file_open_callback(
&self,
callback: Option<Box<dyn FnMut(Vec<PathBuf>) -> FileOpenResult + Send + 'static>>,
);
}

impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
@@ -249,10 +252,25 @@ impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
unsafe { msg_send![app, hideOtherApplications: 0] }
}

fn set_file_open_callback<F>(&self, callback: Option<F>)
where
F: FnMut(Vec<PathBuf>) -> FileOpenResult + Send + 'static,
{
AppState::set_file_open_callback(callback);
fn set_file_open_callback(
&self,
callback: Option<Box<dyn FnMut(Vec<PathBuf>) -> FileOpenResult + Send + 'static>>,
) {
let has_callback = callback.is_some();
let should_change_delegate = AppState::set_file_open_callback(callback);
if should_change_delegate {
unsafe {
let cls = class!(NSApplication);
let app: cocoa::base::id = msg_send![cls, sharedApplication];
let delegate = if has_callback {
*self.p.delegate_with_file_open
} else {
*self.p.delegate
};
let pool = NSAutoreleasePool::new(nil);
let () = msg_send![app, setDelegate: delegate];
let () = msg_send![pool, drain];
}
}
}
}
17 changes: 13 additions & 4 deletions src/platform_impl/macos/app_delegate.rs
Original file line number Diff line number Diff line change
@@ -20,10 +20,10 @@ pub struct AppDelegateClass(pub *const Class);
unsafe impl Send for AppDelegateClass {}
unsafe impl Sync for AppDelegateClass {}

lazy_static! {
pub static ref APP_DELEGATE_CLASS: AppDelegateClass = unsafe {
fn get_app_deleget_class_decl(name: &str) -> ClassDecl {
unsafe {
let superclass = class!(NSResponder);
let mut decl = ClassDecl::new("WinitAppDelegate", superclass).unwrap();
let mut decl = ClassDecl::new(name, superclass).unwrap();

decl.add_class_method(sel!(new), new as extern "C" fn(&Class, Sel) -> id);
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
@@ -45,7 +45,17 @@ lazy_static! {
sel!(activationHackMouseMoved:),
activation_hack::mouse_moved as extern "C" fn(&Object, Sel, id),
);
decl
}
}

lazy_static! {
pub static ref APP_DELEGATE_CLASS: AppDelegateClass = {
let decl = get_app_deleget_class_decl("WinitAppDelegate");
AppDelegateClass(decl.register())
};
pub static ref APP_DELEGATE_CLASS_WITH_FILE_OPEN: AppDelegateClass = unsafe {
let mut decl = get_app_deleget_class_decl("WinitAppDelegateWithFileOpen");
decl.add_method(
sel!(application:openFile:),
application_open_file as extern "C" fn(&Object, Sel, id, id) -> BOOL,
@@ -54,7 +64,6 @@ lazy_static! {
sel!(application:openFiles:),
application_open_files as extern "C" fn(&Object, Sel, id, id),
);

AppDelegateClass(decl.register())
};
}
14 changes: 9 additions & 5 deletions src/platform_impl/macos/app_state.rs
Original file line number Diff line number Diff line change
@@ -268,11 +268,15 @@ impl AppState {
}));
}

pub fn set_file_open_callback<F>(callback: Option<F>)
where
F: FnMut(Vec<PathBuf>) -> FileOpenResult + Send + 'static,
{
*HANDLER.file_open_callback.lock().unwrap() = callback.map(|c| Box::new(c) as _);
/// Returns `true` if the previous value of `callback.is_some()` is different
/// from its new value.
pub fn set_file_open_callback(
callback: Option<Box<dyn FnMut(Vec<PathBuf>) -> FileOpenResult + Send + 'static>>,
) -> bool {
let mut guard: MutexGuard<'_, _> = HANDLER.file_open_callback.lock().unwrap();
let was_some = guard.is_some();
*guard = callback;
was_some != guard.is_some()
}

pub fn exit() {
43 changes: 30 additions & 13 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
@@ -23,13 +23,16 @@ use crate::{
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{
app::APP_CLASS,
app_delegate::APP_DELEGATE_CLASS,
app_state::AppState,
monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
platform_impl::{
self,
platform::{
app::APP_CLASS,
app_delegate::{APP_DELEGATE_CLASS, APP_DELEGATE_CLASS_WITH_FILE_OPEN},
app_state::AppState,
monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
},
},
};

@@ -64,12 +67,19 @@ impl PanicInfo {
pub struct EventLoopWindowTarget<T: 'static> {
pub sender: mpsc::Sender<T>, // this is only here to be cloned elsewhere
pub receiver: mpsc::Receiver<T>,
pub delegate: IdRef,
pub delegate_with_file_open: IdRef,
}
Comment on lines 71 to 76
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't entirely sure about adding the application delegates here, but this was the only reasonable way I could make these accessible at set_file_open_callback().


impl<T> Default for EventLoopWindowTarget<T> {
fn default() -> Self {
impl<T> EventLoopWindowTarget<T> {
pub fn new(delegate: IdRef, delegate_with_file_open: IdRef) -> Self {
let (sender, receiver) = mpsc::channel();
EventLoopWindowTarget { sender, receiver }
EventLoopWindowTarget {
sender,
receiver,
delegate,
delegate_with_file_open,
}
}
}

@@ -98,11 +108,12 @@ pub struct EventLoop<T: 'static> {
/// strong reference should be dropped as soon as possible.
_callback: Option<Rc<RefCell<dyn FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow)>>>,
_delegate: IdRef,
_delegate_with_file_open: IdRef,
}

impl<T> EventLoop<T> {
pub fn new() -> Self {
let delegate = unsafe {
let (delegate, delegate_with_file_open) = unsafe {
if !msg_send![class!(NSThread), isMainThread] {
panic!("On macOS, `EventLoop` must be created on the main thread!");
}
@@ -114,21 +125,27 @@ impl<T> EventLoop<T> {
let app: id = msg_send![APP_CLASS.0, sharedApplication];

let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
let delegate_with_file_open =
IdRef::new(msg_send![APP_DELEGATE_CLASS_WITH_FILE_OPEN.0, new]);
let pool = NSAutoreleasePool::new(nil);
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
(delegate, delegate_with_file_open)
};
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(Rc::downgrade(&panic_info));
EventLoop {
window_target: Rc::new(RootWindowTarget {
p: Default::default(),
p: platform_impl::EventLoopWindowTarget::new(
delegate.clone(),
delegate_with_file_open.clone(),
),
_marker: PhantomData,
}),
panic_info,
_callback: None,
_delegate: delegate,
_delegate_with_file_open: delegate_with_file_open,
}
}