Skip to content

Commit

Permalink
determine resourcekind at runtime instead of via feature flags
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Jul 3, 2023
1 parent 041d427 commit a58525b
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 56 deletions.
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@ targets = [
members = [ "tao-macros" ]

[features]
default = [ "macos-open-files" ]
dox = [ "gtk/dox"]
tray = [ "libappindicator", "dirs-next" ]
macos-open-urls = []
macos-open-files = []

[build-dependencies]
cc = "1"
Expand Down
24 changes: 24 additions & 0 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@ impl From<ActivationPolicy> for NSApplicationActivationPolicy {
}
}

/// Kind of resource the application opens.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OpenResourceKind {
/// The application expects the user to open an URL with it.
Url,
/// The application expects the user to open files with it.
File,
}

impl Default for OpenResourceKind {
fn default() -> Self {
Self::File
}
}

pub trait CustomMenuItemExtMacOS {
fn set_native_image(&mut self, native_image: NativeImage);
}
Expand Down Expand Up @@ -511,7 +526,11 @@ pub trait EventLoopExtMacOS {
/// [`run`](crate::event_loop::EventLoop::run) or
/// [`run_return`](crate::platform::run_return::EventLoopExtRunReturn::run_return)
fn set_activate_ignoring_other_apps(&mut self, ignore: bool);

/// Sets which kind of resource the application opens.
fn set_application_open_resource_kind(&mut self, kind: OpenResourceKind);
}

impl<T> EventLoopExtMacOS for EventLoop<T> {
#[inline]
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
Expand All @@ -533,6 +552,11 @@ impl<T> EventLoopExtMacOS for EventLoop<T> {
get_aux_state_mut(&**self.event_loop.delegate).activate_ignoring_other_apps = ignore;
}
}

#[inline]
fn set_application_open_resource_kind(&mut self, kind: OpenResourceKind) {
self.event_loop.open_resource_kind = kind;
}
}

/// Additional methods on `MonitorHandle` that are specific to MacOS.
Expand Down
68 changes: 38 additions & 30 deletions src/platform_impl/macos/app_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,26 @@
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState};
use crate::{
platform::macos::{ActivationPolicy, OpenResourceKind},
platform_impl::platform::app_state::AppState,
};

use cocoa::base::id;
use cocoa::foundation::NSString;
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel},
runtime::{Class, Object, Sel, BOOL, YES},
};
use std::{
cell::{RefCell, RefMut},
os::raw::c_void,
};

#[cfg(any(feature = "macos-open-urls", feature = "macos-open-files"))]
use cocoa::foundation::NSArray;
#[cfg(all(feature = "macos-open-urls", not(feature = "macos-open-files")))]
use cocoa::foundation::NSURL;
#[cfg(any(feature = "macos-open-urls", feature = "macos-open-files"))]
use std::ffi::CStr;

#[cfg(all(feature = "macos-open-files", feature = "macos-open-urls"))]
compile_error!("`macos-open-files` and `macos-open-urls` feature flags can't be used at the same time, please choose only one of them.");

static AUX_DELEGATE_STATE_NAME: &str = "auxState";

pub struct AuxDelegateState {
Expand All @@ -42,10 +39,39 @@ pub struct AppDelegateClass(pub *const Class);
unsafe impl Send for AppDelegateClass {}
unsafe impl Sync for AppDelegateClass {}

pub unsafe fn app_delegate_class(
classname: &str,
open_resource_kind: OpenResourceKind,
) -> AppDelegateClass {
let superclass = &*APP_DELEGATE_CLASS.0;
let mut decl = ClassDecl::new(classname, superclass).unwrap();

match open_resource_kind {
OpenResourceKind::Url => {
decl.add_method(
sel!(application:openURLs:),
application_open_urls as extern "C" fn(&Object, Sel, id, id),
);
}
OpenResourceKind::File => {
decl.add_method(
sel!(application:openFile:),
application_open_file as extern "C" fn(&Object, Sel, id, id) -> cocoa::base::BOOL,
);
decl.add_method(
sel!(application:openFiles:),
application_open_files as extern "C" fn(&Object, Sel, id, id),
);
}
}

AppDelegateClass(decl.register())
}

lazy_static! {
pub static ref APP_DELEGATE_CLASS: AppDelegateClass = unsafe {
static ref APP_DELEGATE_CLASS: AppDelegateClass = unsafe {
let superclass = class!(NSResponder);
let mut decl = ClassDecl::new("TaoAppDelegate", superclass).unwrap();
let mut decl = ClassDecl::new("TaoAppDelegateParent", 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));
Expand All @@ -58,21 +84,6 @@ lazy_static! {
sel!(applicationWillTerminate:),
application_will_terminate as extern "C" fn(&Object, Sel, id),
);
#[cfg(all(feature = "macos-open-urls", not(feature = "macos-open-files")))]
decl.add_method(
sel!(application:openURLs:),
application_open_urls as extern "C" fn(&Object, Sel, id, id),
);
#[cfg(all(feature = "macos-open-files", not(feature = "macos-open-urls")))]
decl.add_method(
sel!(application:openFile:),
application_open_file as extern "C" fn(&Object, Sel, id, id) -> cocoa::base::BOOL,
);
#[cfg(all(feature = "macos-open-files", not(feature = "macos-open-urls")))]
decl.add_method(
sel!(application:openFiles:),
application_open_files as extern "C" fn(&Object, Sel, id, id),
);
decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME);

AppDelegateClass(decl.register())
Expand Down Expand Up @@ -123,7 +134,6 @@ extern "C" fn application_will_terminate(_: &Object, _: Sel, _: id) {
trace!("Completed `applicationWillTerminate`");
}

#[cfg(all(feature = "macos-open-urls", not(feature = "macos-open-files")))]
extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: id) -> () {
trace!("Trigger `application:openURLs:`");

Expand All @@ -142,8 +152,7 @@ extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: id) -> () {
trace!("Completed `application:openURLs:`");
}

#[cfg(all(feature = "macos-open-files", not(feature = "macos-open-urls")))]
extern "C" fn application_open_file(_: &Object, _: Sel, _: id, file: id) -> objc::runtime::BOOL {
extern "C" fn application_open_file(_: &Object, _: Sel, _: id, file: id) -> BOOL {
use std::{ffi::OsStr, os::unix::prelude::OsStrExt};

trace!("Trigger `application:openFile:`");
Expand All @@ -154,10 +163,9 @@ extern "C" fn application_open_file(_: &Object, _: Sel, _: id, file: id) -> objc
AppState::open_file(filename);
trace!("Completed `application:openFile:`");

objc::runtime::YES
YES
}

#[cfg(all(feature = "macos-open-files", not(feature = "macos-open-urls")))]
extern "C" fn application_open_files(_: &Object, _: Sel, _: id, files: id) -> () {
use std::{ffi::OsStr, os::unix::prelude::OsStrExt};

Expand Down
3 changes: 0 additions & 3 deletions src/platform_impl/macos/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,12 @@ impl AppState {
HANDLER.set_in_callback(false);
}

#[cfg(all(feature = "macos-open-urls", not(feature = "macos-open-files")))]
pub fn open_urls(urls: Vec<url::Url>) {
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Opened {
event: crate::event::OpenEvent::Url(urls),
}));
}

#[cfg(all(feature = "macos-open-files", not(feature = "macos-open-urls")))]
pub fn open_file(filename: std::path::PathBuf) {
if let Some(ref mut callback) = *HANDLER.callback.lock().unwrap() {
callback.handle_nonuser_event(
Expand All @@ -320,7 +318,6 @@ impl AppState {
)
}
}
#[cfg(all(feature = "macos-open-files", not(feature = "macos-open-urls")))]
pub fn open_files(files: Vec<std::path::PathBuf>) {
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Opened {
event: crate::event::OpenEvent::File(files),
Expand Down
51 changes: 31 additions & 20 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ use crate::{
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
monitor::MonitorHandle as RootMonitorHandle,
platform::macos::OpenResourceKind,
platform_impl::platform::{
app::APP_CLASS,
app_delegate::APP_DELEGATE_CLASS,
app_delegate::app_delegate_class,
app_state::AppState,
monitor::{self, MonitorHandle},
observer::*,
Expand Down Expand Up @@ -115,6 +116,7 @@ impl<T: 'static> EventLoopWindowTarget<T> {

pub struct EventLoop<T: 'static> {
pub(crate) delegate: IdRef,
pub(crate) open_resource_kind: OpenResourceKind,

window_target: Rc<RootWindowTarget<T>>,
panic_info: Rc<PanicInfo>,
Expand All @@ -130,28 +132,11 @@ pub struct EventLoop<T: 'static> {

impl<T> EventLoop<T> {
pub fn new() -> Self {
let delegate = unsafe {
let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread);
if is_main_thread == NO {
panic!("On macOS, `EventLoop` must be created on the main thread!");
}

// 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]);
let pool = NSAutoreleasePool::new(nil);
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
};
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(Rc::downgrade(&panic_info));
EventLoop {
delegate,
delegate: IdRef::new(nil),
open_resource_kind: Default::default(),
window_target: Rc::new(RootWindowTarget {
p: Default::default(),
_marker: PhantomData,
Expand All @@ -177,6 +162,32 @@ impl<T> EventLoop<T> {
where
F: FnMut(Event<'_, T>, &RootWindowTarget<T>, &mut ControlFlow),
{
// initialize app delegate if needed
if self.delegate.is_null() {
let delegate = unsafe {
let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread);
if is_main_thread == NO {
panic!("On macOS, `EventLoop` must be created on the main thread!");
}

// 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("TaoAppDelegate", self.open_resource_kind).0,
new
]);
let pool = NSAutoreleasePool::new(nil);
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
};
self.delegate = delegate;
}

// 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
Expand Down

0 comments on commit a58525b

Please sign in to comment.