From bd82b8f27bd64b09e1819263e2995f1bd3bc5c13 Mon Sep 17 00:00:00 2001 From: Ryan McGrath Date: Tue, 17 Mar 2020 19:11:03 -0700 Subject: [PATCH] Force-drain NSAutoReleasePool after run completes, feature-gate webview, refactor app module structure to match the rest --- appkit/Cargo.toml | 1 + appkit/build.rs | 2 + appkit/src/app/class.rs | 377 +--------------------- appkit/src/app/controller.rs | 381 +++++++++++++++++++++++ appkit/src/app/mod.rs | 18 +- appkit/src/constants.rs | 7 + appkit/src/foundation/autoreleasepool.rs | 2 +- appkit/src/lib.rs | 12 +- 8 files changed, 415 insertions(+), 385 deletions(-) create mode 100644 appkit/src/app/controller.rs diff --git a/appkit/Cargo.toml b/appkit/Cargo.toml index 92a17cb..233326d 100644 --- a/appkit/Cargo.toml +++ b/appkit/Cargo.toml @@ -17,4 +17,5 @@ url = "2.1.1" [features] cloudkit = [] user-notifications = ["uuid"] +webview = [] enable-webview-downloading = [] diff --git a/appkit/build.rs b/appkit/build.rs index 411e4f7..538f2b1 100644 --- a/appkit/build.rs +++ b/appkit/build.rs @@ -9,6 +9,8 @@ fn main() { println!("cargo:rustc-link-lib=framework=CoreGraphics"); println!("cargo:rustc-link-lib=framework=Security"); + + #[cfg(feature = "webview")] println!("cargo:rustc-link-lib=framework=WebKit"); #[cfg(feature = "cloudkit")] diff --git a/appkit/src/app/class.rs b/appkit/src/app/class.rs index ccd7baf..1e642b2 100644 --- a/appkit/src/app/class.rs +++ b/appkit/src/app/class.rs @@ -2,383 +2,12 @@ //! creates a custom `NSApplication` subclass that currently does nothing; this is meant as a hook //! for potential future use. -use std::ffi::c_void; use std::sync::Once; -use std::unreachable; -use block::Block; - -use objc::{class, msg_send, sel, sel_impl}; +use objc::class; +//, msg_send, sel, sel_impl}; use objc::declare::ClassDecl; -use objc::runtime::{Class, Object, Sel}; - -use url::Url; - -use crate::foundation::{id, nil, BOOL, YES, NO, NSUInteger, NSArray, NSString}; -use crate::app::traits::AppController; -use crate::constants::APP_PTR; -use crate::error::AppKitError; -use crate::printing::PrintSettings; -use crate::user_activity::UserActivity; - -#[cfg(feature = "cloudkit")] -use crate::cloudkit::share::CKShareMetaData; - -/// A handy method for grabbing our `AppController` from the pointer. This is different from our -/// standard `utils` version as this doesn't require `RefCell` backing. -fn app(this: &Object) -> &T { - unsafe { - let app_ptr: usize = *this.get_ivar(APP_PTR); - let app = app_ptr as *const T; - &*app - } -} - -/// Fires when the Application Delegate receives a `applicationWillFinishLaunching` notification. -extern fn will_finish_launching(this: &Object, _: Sel, _: id) { - app::(this).will_finish_launching(); -} - -/// Fires when the Application Delegate receives a `applicationDidFinishLaunching` notification. -extern fn did_finish_launching(this: &Object, _: Sel, _: id) { - app::(this).did_finish_launching(); -} - -/// Fires when the Application Delegate receives a `applicationWillBecomeActive` notification. -extern fn will_become_active(this: &Object, _: Sel, _: id) { - app::(this).will_become_active(); -} - -/// Fires when the Application Delegate receives a `applicationDidBecomeActive` notification. -extern fn did_become_active(this: &Object, _: Sel, _: id) { - app::(this).did_become_active(); -} - -/// Fires when the Application Delegate receives a `applicationWillResignActive` notification. -extern fn will_resign_active(this: &Object, _: Sel, _: id) { - app::(this).will_resign_active(); -} - -/// Fires when the Application Delegate receives a `applicationDidResignActive` notification. -extern fn did_resign_active(this: &Object, _: Sel, _: id) { - app::(this).did_resign_active(); -} - -/// Fires when the Application Delegate receives a 'applicationShouldTerminate:` notification. -extern fn should_terminate(this: &Object, _: Sel, _: id) -> NSUInteger { - app::(this).should_terminate().into() -} - -/// Fires when the Application Delegate receives a `applicationWillTerminate:` notification. -extern fn will_terminate(this: &Object, _: Sel, _: id) { - app::(this).will_terminate(); -} - -/// Fires when the Application Delegate receives a `applicationWillHide:` notification. -extern fn will_hide(this: &Object, _: Sel, _: id) { - app::(this).will_hide(); -} - -/// Fires when the Application Delegate receives a `applicationDidHide:` notification. -extern fn did_hide(this: &Object, _: Sel, _: id) { - app::(this).did_hide(); -} - -/// Fires when the Application Delegate receives a `applicationWillUnhide:` notification. -extern fn will_unhide(this: &Object, _: Sel, _: id) { - app::(this).will_unhide(); -} - -/// Fires when the Application Delegate receives a `applicationDidUnhide:` notification. -extern fn did_unhide(this: &Object, _: Sel, _: id) { - app::(this).did_unhide(); -} - -/// Fires when the Application Delegate receives a `applicationWillUpdate:` notification. -extern fn will_update(this: &Object, _: Sel, _: id) { - app::(this).will_update(); -} - -/// Fires when the Application Delegate receives a `applicationDidUpdate:` notification. -extern fn did_update(this: &Object, _: Sel, _: id) { - app::(this).did_update(); -} - -/// Fires when the Application Delegate receives a -/// `applicationShouldHandleReopen:hasVisibleWindows:` notification. -extern fn should_handle_reopen(this: &Object, _: Sel, _: id, has_visible_windows: BOOL) -> BOOL { - match app::(this).should_handle_reopen(match has_visible_windows { - YES => true, - NO => false, - _ => { unreachable!(); }, - }) { - true => YES, - false => NO - } -} - -/// Fires when the application delegate receives a `applicationDockMenu:` request. -extern fn dock_menu(this: &Object, _: Sel, _: id) -> id { - match app::(this).dock_menu() { - Some(mut menu) => &mut *menu.inner, - None => nil - } -} - -/// Fires when the application delegate receives a `application:willPresentError:` notification. -extern fn will_present_error(this: &Object, _: Sel, _: id, error: id) -> id { - let error = AppKitError::new(error); - app::(this).will_present_error(error).into_nserror() -} - -/// Fires when the application receives a `applicationDidChangeScreenParameters:` notification. -extern fn did_change_screen_parameters(this: &Object, _: Sel, _: id) { - app::(this).did_change_screen_parameters(); -} - -/// Fires when the application receives a `application:willContinueUserActivityWithType:` -/// notification. -extern fn will_continue_user_activity_with_type(this: &Object, _: Sel, _: id, activity_type: id) -> BOOL { - let activity = NSString::wrap(activity_type); - - match app::(this).will_continue_user_activity(activity.to_str()) { - true => YES, - false => NO - } -} - -/// Fires when the application receives a `application:continueUserActivity:restorationHandler:` notification. -extern fn continue_user_activity(this: &Object, _: Sel, _: id, activity: id, handler: id) -> BOOL { - // @TODO: This needs to support restorable objects, but it involves a larger question about how - // much `NSObject` wrapping we want to do here. For now, pass the handler for whenever it's - // useful. - let activity = UserActivity::with_inner(activity); - - match app::(this).continue_user_activity(activity, || unsafe { - let handler = handler as *const Block<(id,), c_void>; - (*handler).call((nil,)); - }) { - true => YES, - false => NO - } -} - -/// Fires when the application receives a -/// `application:didFailToContinueUserActivityWithType:error:` message. -extern fn failed_to_continue_user_activity(this: &Object, _: Sel, _: id, activity_type: id, error: id) { - app::(this).failed_to_continue_user_activity( - NSString::wrap(activity_type).to_str(), - AppKitError::new(error) - ); -} - -/// Fires when the application receives a `application:didUpdateUserActivity:` message. -extern fn did_update_user_activity(this: &Object, _: Sel, _: id, activity: id) { - let activity = UserActivity::with_inner(activity); - app::(this).updated_user_activity(activity); -} - -/// Fires when the application receives a `application:didRegisterForRemoteNotificationsWithDeviceToken:` message. -extern fn registered_for_remote_notifications(_this: &Object, _: Sel, _: id, _: id) { - -} - -/// Fires when the application receives a `application:didFailToRegisterForRemoteNotificationsWithError:` message. -extern fn failed_to_register_for_remote_notifications(this: &Object, _: Sel, _: id, error: id) { - app::(this).failed_to_register_for_remote_notifications(AppKitError::new(error)); -} - -/// Fires when the application receives a `application:didReceiveRemoteNotification:` message. -extern fn did_receive_remote_notification(_this: &Object, _: Sel, _: id, _: id) { - -} - -/// Fires when the application receives a `application:userDidAcceptCloudKitShareWithMetadata:` -/// message. -#[cfg(feature = "cloudkit")] -extern fn accepted_cloudkit_share(_this: &Object, _: Sel, _: id, metadata: id) { - let share = CKShareMetaData::with_inner(metadata); - app::(this).user_accepted_cloudkit_share(share); -} - -/// Fires when the application receives an `application:openURLs` message. -extern fn open_urls(this: &Object, _: Sel, _: id, file_urls: id) { - let urls = NSArray::wrap(file_urls).map(|url| { - let uri = NSString::wrap(unsafe { - msg_send![url, absoluteString] - }); - - Url::parse(uri.to_str()) - }).into_iter().filter_map(|url| url.ok()).collect(); - - app::(this).open_urls(urls); -} - -/// Fires when the application receives an `application:openFileWithoutUI:` message. -extern fn open_file_without_ui(this: &Object, _: Sel, _: id, file: id) -> BOOL { - let filename = NSString::wrap(file); - - match app::(this).open_file_without_ui(filename.to_str()) { - true => YES, - false => NO - } -} - -/// Fired when the application receives an `applicationShouldOpenUntitledFile:` message. -extern fn should_open_untitled_file(this: &Object, _: Sel, _: id) -> BOOL { - match app::(this).should_open_untitled_file() { - true => YES, - false => NO - } -} - -/// Fired when the application receives an `applicationOpenUntitledFile:` message. -extern fn open_untitled_file(this: &Object, _: Sel, _: id) -> BOOL { - match app::(this).open_untitled_file() { - true => YES, - false => NO - } -} - -/// Fired when the application receives an `application:openTempFile:` message. -extern fn open_temp_file(this: &Object, _: Sel, _: id, filename: id) -> BOOL { - let filename = NSString::wrap(filename); - - match app::(this).open_temp_file(filename.to_str()) { - true => YES, - false => NO - } -} - -/// Fired when the application receives an `application:printFile:` message. -extern fn print_file(this: &Object, _: Sel, _: id, file: id) -> BOOL { - let filename = NSString::wrap(file); - - match app::(this).print_file(filename.to_str()) { - true => YES, - false => NO - } -} - -/// Fired when the application receives an `application:printFiles:withSettings:showPrintPanels:` -/// message. -extern fn print_files(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger { - let files = NSArray::wrap(files).map(|file| { - NSString::wrap(file).to_str().to_string() - }); - - let settings = PrintSettings::with_inner(settings); - - app::(this).print_files(files, settings, match show_print_panels { - YES => true, - NO => false, - _ => { unreachable!(); } - }).into() -} - -/// Called when the application's occlusion state has changed. -extern fn did_change_occlusion_state(this: &Object, _: Sel, _: id) { - app::(this).occlusion_state_changed(); -} - -/// Called when the application receives an `application:delegateHandlesKey:` message. -/// Note: this may not fire in sandboxed applications. Apple's documentation is unclear on the -/// matter. -extern fn delegate_handles_key(this: &Object, _: Sel, _: id, key: id) -> BOOL { - let key = NSString::wrap(key); - - match app::(this).delegate_handles_key(key.to_str()) { - true => YES, - false => NO - } -} - -/// Registers an `NSObject` application delegate, and configures it for the various callbacks and -/// pointers we need to have. -pub(crate) fn register_app_controller_class() -> *const Class { - static mut DELEGATE_CLASS: *const Class = 0 as *const Class; - static INIT: Once = Once::new(); - - INIT.call_once(|| unsafe { - let superclass = class!(NSObject); - let mut decl = ClassDecl::new("RSTAppController", superclass).unwrap(); - - decl.add_ivar::(APP_PTR); - - // Launching Applications - decl.add_method(sel!(applicationWillFinishLaunching:), will_finish_launching:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidFinishLaunching:), did_finish_launching:: as extern fn(&Object, _, _)); - - // Managing Active Status - decl.add_method(sel!(applicationWillBecomeActive:), will_become_active:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidBecomeActive:), did_become_active:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationWillResignActive:), will_resign_active:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidResignActive:), did_resign_active:: as extern fn(&Object, _, _)); - - // Terminating Applications - decl.add_method(sel!(applicationShouldTerminate:), should_terminate:: as extern fn(&Object, _, _) -> NSUInteger); - decl.add_method(sel!(applicationWillTerminate:), will_terminate:: as extern fn(&Object, _, _)); - - // Hiding Applications - decl.add_method(sel!(applicationWillHide:), will_hide:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidHide:), did_hide:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationWillUnhide:), will_unhide:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidUnhide:), did_unhide:: as extern fn(&Object, _, _)); - - // Managing Windows - decl.add_method(sel!(applicationWillUpdate:), will_update:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidUpdate:), did_update:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationShouldHandleReopen:hasVisibleWindows:), should_handle_reopen:: as extern fn(&Object, _, _, BOOL) -> BOOL); - - // Dock Menu - decl.add_method(sel!(applicationDockMenu:), dock_menu:: as extern fn(&Object, _, _) -> id); - - // Displaying Errors - decl.add_method(sel!(application:willPresentError:), will_present_error:: as extern fn(&Object, _, _, id) -> id); - - // Managing the Screen - decl.add_method(sel!(applicationDidChangeScreenParameters:), did_change_screen_parameters:: as extern fn(&Object, _, _)); - decl.add_method(sel!(applicationDidChangeOcclusionState:), did_change_occlusion_state:: as extern fn(&Object, _, _)); - - // User Activities - decl.add_method(sel!(application:willContinueUserActivityWithType:), will_continue_user_activity_with_type:: as extern fn(&Object, _, _, id) -> BOOL); - decl.add_method(sel!(application:continueUserActivity:restorationHandler:), continue_user_activity:: as extern fn(&Object, _, _, id, id) -> BOOL); - decl.add_method(sel!(application:didFailToContinueUserActivityWithType:error:), failed_to_continue_user_activity:: as extern fn(&Object, _, _, id, id)); - decl.add_method(sel!(application:didUpdateUserActivity:), did_update_user_activity:: as extern fn(&Object, _, _, id)); - - // Handling push notifications - decl.add_method(sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:), registered_for_remote_notifications:: as extern fn(&Object, _, _, id)); - decl.add_method(sel!(application:didFailToRegisterForRemoteNotificationsWithError:), failed_to_register_for_remote_notifications:: as extern fn(&Object, _, _, id)); - decl.add_method(sel!(application:didReceiveRemoteNotification:), did_receive_remote_notification:: as extern fn(&Object, _, _, id)); - - // CloudKit - #[cfg(feature = "cloudkit")] - decl.add_method(sel!(application:userDidAcceptCloudKitShareWithMetadata:), accepted_cloudkit_share:: as extern fn(&Object, _, _, id)); - - // Opening Files - decl.add_method(sel!(application:openURLs:), open_urls:: as extern fn(&Object, _, _, id)); - decl.add_method(sel!(application:openFileWithoutUI:), open_file_without_ui:: as extern fn(&Object, _, _, id) -> BOOL); - decl.add_method(sel!(applicationShouldOpenUntitledFile:), should_open_untitled_file:: as extern fn(&Object, _, _) -> BOOL); - decl.add_method(sel!(applicationOpenUntitledFile:), open_untitled_file:: as extern fn(&Object, _, _) -> BOOL); - decl.add_method(sel!(application:openTempFile:), open_temp_file:: as extern fn(&Object, _, _, id) -> BOOL); - - // Printing - decl.add_method(sel!(application:printFile:), print_file:: as extern fn(&Object, _, _, id) -> BOOL); - decl.add_method(sel!(application:printFiles:withSettings:showPrintPanels:), print_files:: as extern fn(&Object, _, id, id, id, BOOL) -> NSUInteger); - - // @TODO: Restoring Application State - // Depends on NSCoder support, which is... welp. - - // Scripting - decl.add_method(sel!(application:delegateHandlesKey:), delegate_handles_key:: as extern fn(&Object, _, _, id) -> BOOL); - - DELEGATE_CLASS = decl.register(); - }); - - unsafe { - DELEGATE_CLASS - } -} +use objc::runtime::{Class}; /// Used for injecting a custom NSApplication. Currently does nothing. pub(crate) fn register_app_class() -> *const Class { diff --git a/appkit/src/app/controller.rs b/appkit/src/app/controller.rs new file mode 100644 index 0000000..e8d6482 --- /dev/null +++ b/appkit/src/app/controller.rs @@ -0,0 +1,381 @@ +//! This module implements forwarding methods for standard `NSApplicationDelegate` calls. It also +//! creates a custom `NSApplication` subclass that currently does nothing; this is meant as a hook +//! for potential future use. + +use std::ffi::c_void; +use std::sync::Once; +use std::unreachable; + +use block::Block; + +use objc::{class, msg_send, sel, sel_impl}; +use objc::declare::ClassDecl; +use objc::runtime::{Class, Object, Sel}; + +use url::Url; + +use crate::foundation::{id, nil, BOOL, YES, NO, NSUInteger, NSArray, NSString}; +use crate::app::traits::AppController; +use crate::constants::APP_PTR; +use crate::error::AppKitError; +use crate::printing::PrintSettings; +use crate::user_activity::UserActivity; + +#[cfg(feature = "cloudkit")] +use crate::cloudkit::share::CKShareMetaData; + +/// A handy method for grabbing our `AppController` from the pointer. This is different from our +/// standard `utils` version as this doesn't require `RefCell` backing. +fn app(this: &Object) -> &T { + unsafe { + let app_ptr: usize = *this.get_ivar(APP_PTR); + let app = app_ptr as *const T; + &*app + } +} + +/// Fires when the Application Delegate receives a `applicationWillFinishLaunching` notification. +extern fn will_finish_launching(this: &Object, _: Sel, _: id) { + app::(this).will_finish_launching(); +} + +/// Fires when the Application Delegate receives a `applicationDidFinishLaunching` notification. +extern fn did_finish_launching(this: &Object, _: Sel, _: id) { + app::(this).did_finish_launching(); +} + +/// Fires when the Application Delegate receives a `applicationWillBecomeActive` notification. +extern fn will_become_active(this: &Object, _: Sel, _: id) { + app::(this).will_become_active(); +} + +/// Fires when the Application Delegate receives a `applicationDidBecomeActive` notification. +extern fn did_become_active(this: &Object, _: Sel, _: id) { + app::(this).did_become_active(); +} + +/// Fires when the Application Delegate receives a `applicationWillResignActive` notification. +extern fn will_resign_active(this: &Object, _: Sel, _: id) { + app::(this).will_resign_active(); +} + +/// Fires when the Application Delegate receives a `applicationDidResignActive` notification. +extern fn did_resign_active(this: &Object, _: Sel, _: id) { + app::(this).did_resign_active(); +} + +/// Fires when the Application Delegate receives a 'applicationShouldTerminate:` notification. +extern fn should_terminate(this: &Object, _: Sel, _: id) -> NSUInteger { + app::(this).should_terminate().into() +} + +/// Fires when the Application Delegate receives a `applicationWillTerminate:` notification. +extern fn will_terminate(this: &Object, _: Sel, _: id) { + app::(this).will_terminate(); +} + +/// Fires when the Application Delegate receives a `applicationWillHide:` notification. +extern fn will_hide(this: &Object, _: Sel, _: id) { + app::(this).will_hide(); +} + +/// Fires when the Application Delegate receives a `applicationDidHide:` notification. +extern fn did_hide(this: &Object, _: Sel, _: id) { + app::(this).did_hide(); +} + +/// Fires when the Application Delegate receives a `applicationWillUnhide:` notification. +extern fn will_unhide(this: &Object, _: Sel, _: id) { + app::(this).will_unhide(); +} + +/// Fires when the Application Delegate receives a `applicationDidUnhide:` notification. +extern fn did_unhide(this: &Object, _: Sel, _: id) { + app::(this).did_unhide(); +} + +/// Fires when the Application Delegate receives a `applicationWillUpdate:` notification. +extern fn will_update(this: &Object, _: Sel, _: id) { + app::(this).will_update(); +} + +/// Fires when the Application Delegate receives a `applicationDidUpdate:` notification. +extern fn did_update(this: &Object, _: Sel, _: id) { + app::(this).did_update(); +} + +/// Fires when the Application Delegate receives a +/// `applicationShouldHandleReopen:hasVisibleWindows:` notification. +extern fn should_handle_reopen(this: &Object, _: Sel, _: id, has_visible_windows: BOOL) -> BOOL { + match app::(this).should_handle_reopen(match has_visible_windows { + YES => true, + NO => false, + _ => { unreachable!(); }, + }) { + true => YES, + false => NO + } +} + +/// Fires when the application delegate receives a `applicationDockMenu:` request. +extern fn dock_menu(this: &Object, _: Sel, _: id) -> id { + match app::(this).dock_menu() { + Some(mut menu) => &mut *menu.inner, + None => nil + } +} + +/// Fires when the application delegate receives a `application:willPresentError:` notification. +extern fn will_present_error(this: &Object, _: Sel, _: id, error: id) -> id { + let error = AppKitError::new(error); + app::(this).will_present_error(error).into_nserror() +} + +/// Fires when the application receives a `applicationDidChangeScreenParameters:` notification. +extern fn did_change_screen_parameters(this: &Object, _: Sel, _: id) { + app::(this).did_change_screen_parameters(); +} + +/// Fires when the application receives a `application:willContinueUserActivityWithType:` +/// notification. +extern fn will_continue_user_activity_with_type(this: &Object, _: Sel, _: id, activity_type: id) -> BOOL { + let activity = NSString::wrap(activity_type); + + match app::(this).will_continue_user_activity(activity.to_str()) { + true => YES, + false => NO + } +} + +/// Fires when the application receives a `application:continueUserActivity:restorationHandler:` notification. +extern fn continue_user_activity(this: &Object, _: Sel, _: id, activity: id, handler: id) -> BOOL { + // @TODO: This needs to support restorable objects, but it involves a larger question about how + // much `NSObject` wrapping we want to do here. For now, pass the handler for whenever it's + // useful. + let activity = UserActivity::with_inner(activity); + + match app::(this).continue_user_activity(activity, || unsafe { + let handler = handler as *const Block<(id,), c_void>; + (*handler).call((nil,)); + }) { + true => YES, + false => NO + } +} + +/// Fires when the application receives a +/// `application:didFailToContinueUserActivityWithType:error:` message. +extern fn failed_to_continue_user_activity(this: &Object, _: Sel, _: id, activity_type: id, error: id) { + app::(this).failed_to_continue_user_activity( + NSString::wrap(activity_type).to_str(), + AppKitError::new(error) + ); +} + +/// Fires when the application receives a `application:didUpdateUserActivity:` message. +extern fn did_update_user_activity(this: &Object, _: Sel, _: id, activity: id) { + let activity = UserActivity::with_inner(activity); + app::(this).updated_user_activity(activity); +} + +/// Fires when the application receives a `application:didRegisterForRemoteNotificationsWithDeviceToken:` message. +extern fn registered_for_remote_notifications(_this: &Object, _: Sel, _: id, _: id) { + +} + +/// Fires when the application receives a `application:didFailToRegisterForRemoteNotificationsWithError:` message. +extern fn failed_to_register_for_remote_notifications(this: &Object, _: Sel, _: id, error: id) { + app::(this).failed_to_register_for_remote_notifications(AppKitError::new(error)); +} + +/// Fires when the application receives a `application:didReceiveRemoteNotification:` message. +extern fn did_receive_remote_notification(_this: &Object, _: Sel, _: id, _: id) { + +} + +/// Fires when the application receives a `application:userDidAcceptCloudKitShareWithMetadata:` +/// message. +#[cfg(feature = "cloudkit")] +extern fn accepted_cloudkit_share(_this: &Object, _: Sel, _: id, metadata: id) { + let share = CKShareMetaData::with_inner(metadata); + app::(this).user_accepted_cloudkit_share(share); +} + +/// Fires when the application receives an `application:openURLs` message. +extern fn open_urls(this: &Object, _: Sel, _: id, file_urls: id) { + let urls = NSArray::wrap(file_urls).map(|url| { + let uri = NSString::wrap(unsafe { + msg_send![url, absoluteString] + }); + + Url::parse(uri.to_str()) + }).into_iter().filter_map(|url| url.ok()).collect(); + + app::(this).open_urls(urls); +} + +/// Fires when the application receives an `application:openFileWithoutUI:` message. +extern fn open_file_without_ui(this: &Object, _: Sel, _: id, file: id) -> BOOL { + let filename = NSString::wrap(file); + + match app::(this).open_file_without_ui(filename.to_str()) { + true => YES, + false => NO + } +} + +/// Fired when the application receives an `applicationShouldOpenUntitledFile:` message. +extern fn should_open_untitled_file(this: &Object, _: Sel, _: id) -> BOOL { + match app::(this).should_open_untitled_file() { + true => YES, + false => NO + } +} + +/// Fired when the application receives an `applicationOpenUntitledFile:` message. +extern fn open_untitled_file(this: &Object, _: Sel, _: id) -> BOOL { + match app::(this).open_untitled_file() { + true => YES, + false => NO + } +} + +/// Fired when the application receives an `application:openTempFile:` message. +extern fn open_temp_file(this: &Object, _: Sel, _: id, filename: id) -> BOOL { + let filename = NSString::wrap(filename); + + match app::(this).open_temp_file(filename.to_str()) { + true => YES, + false => NO + } +} + +/// Fired when the application receives an `application:printFile:` message. +extern fn print_file(this: &Object, _: Sel, _: id, file: id) -> BOOL { + let filename = NSString::wrap(file); + + match app::(this).print_file(filename.to_str()) { + true => YES, + false => NO + } +} + +/// Fired when the application receives an `application:printFiles:withSettings:showPrintPanels:` +/// message. +extern fn print_files(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger { + let files = NSArray::wrap(files).map(|file| { + NSString::wrap(file).to_str().to_string() + }); + + let settings = PrintSettings::with_inner(settings); + + app::(this).print_files(files, settings, match show_print_panels { + YES => true, + NO => false, + _ => { unreachable!(); } + }).into() +} + +/// Called when the application's occlusion state has changed. +extern fn did_change_occlusion_state(this: &Object, _: Sel, _: id) { + app::(this).occlusion_state_changed(); +} + +/// Called when the application receives an `application:delegateHandlesKey:` message. +/// Note: this may not fire in sandboxed applications. Apple's documentation is unclear on the +/// matter. +extern fn delegate_handles_key(this: &Object, _: Sel, _: id, key: id) -> BOOL { + let key = NSString::wrap(key); + + match app::(this).delegate_handles_key(key.to_str()) { + true => YES, + false => NO + } +} + +/// Registers an `NSObject` application delegate, and configures it for the various callbacks and +/// pointers we need to have. +pub(crate) fn register_app_controller_class() -> *const Class { + static mut DELEGATE_CLASS: *const Class = 0 as *const Class; + static INIT: Once = Once::new(); + + INIT.call_once(|| unsafe { + let superclass = class!(NSObject); + let mut decl = ClassDecl::new("RSTAppController", superclass).unwrap(); + + decl.add_ivar::(APP_PTR); + + // Launching Applications + decl.add_method(sel!(applicationWillFinishLaunching:), will_finish_launching:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidFinishLaunching:), did_finish_launching:: as extern fn(&Object, _, _)); + + // Managing Active Status + decl.add_method(sel!(applicationWillBecomeActive:), will_become_active:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidBecomeActive:), did_become_active:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationWillResignActive:), will_resign_active:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidResignActive:), did_resign_active:: as extern fn(&Object, _, _)); + + // Terminating Applications + decl.add_method(sel!(applicationShouldTerminate:), should_terminate:: as extern fn(&Object, _, _) -> NSUInteger); + decl.add_method(sel!(applicationWillTerminate:), will_terminate:: as extern fn(&Object, _, _)); + + // Hiding Applications + decl.add_method(sel!(applicationWillHide:), will_hide:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidHide:), did_hide:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationWillUnhide:), will_unhide:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidUnhide:), did_unhide:: as extern fn(&Object, _, _)); + + // Managing Windows + decl.add_method(sel!(applicationWillUpdate:), will_update:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidUpdate:), did_update:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationShouldHandleReopen:hasVisibleWindows:), should_handle_reopen:: as extern fn(&Object, _, _, BOOL) -> BOOL); + + // Dock Menu + decl.add_method(sel!(applicationDockMenu:), dock_menu:: as extern fn(&Object, _, _) -> id); + + // Displaying Errors + decl.add_method(sel!(application:willPresentError:), will_present_error:: as extern fn(&Object, _, _, id) -> id); + + // Managing the Screen + decl.add_method(sel!(applicationDidChangeScreenParameters:), did_change_screen_parameters:: as extern fn(&Object, _, _)); + decl.add_method(sel!(applicationDidChangeOcclusionState:), did_change_occlusion_state:: as extern fn(&Object, _, _)); + + // User Activities + decl.add_method(sel!(application:willContinueUserActivityWithType:), will_continue_user_activity_with_type:: as extern fn(&Object, _, _, id) -> BOOL); + decl.add_method(sel!(application:continueUserActivity:restorationHandler:), continue_user_activity:: as extern fn(&Object, _, _, id, id) -> BOOL); + decl.add_method(sel!(application:didFailToContinueUserActivityWithType:error:), failed_to_continue_user_activity:: as extern fn(&Object, _, _, id, id)); + decl.add_method(sel!(application:didUpdateUserActivity:), did_update_user_activity:: as extern fn(&Object, _, _, id)); + + // Handling push notifications + decl.add_method(sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:), registered_for_remote_notifications:: as extern fn(&Object, _, _, id)); + decl.add_method(sel!(application:didFailToRegisterForRemoteNotificationsWithError:), failed_to_register_for_remote_notifications:: as extern fn(&Object, _, _, id)); + decl.add_method(sel!(application:didReceiveRemoteNotification:), did_receive_remote_notification:: as extern fn(&Object, _, _, id)); + + // CloudKit + #[cfg(feature = "cloudkit")] + decl.add_method(sel!(application:userDidAcceptCloudKitShareWithMetadata:), accepted_cloudkit_share:: as extern fn(&Object, _, _, id)); + + // Opening Files + decl.add_method(sel!(application:openURLs:), open_urls:: as extern fn(&Object, _, _, id)); + decl.add_method(sel!(application:openFileWithoutUI:), open_file_without_ui:: as extern fn(&Object, _, _, id) -> BOOL); + decl.add_method(sel!(applicationShouldOpenUntitledFile:), should_open_untitled_file:: as extern fn(&Object, _, _) -> BOOL); + decl.add_method(sel!(applicationOpenUntitledFile:), open_untitled_file:: as extern fn(&Object, _, _) -> BOOL); + decl.add_method(sel!(application:openTempFile:), open_temp_file:: as extern fn(&Object, _, _, id) -> BOOL); + + // Printing + decl.add_method(sel!(application:printFile:), print_file:: as extern fn(&Object, _, _, id) -> BOOL); + decl.add_method(sel!(application:printFiles:withSettings:showPrintPanels:), print_files:: as extern fn(&Object, _, id, id, id, BOOL) -> NSUInteger); + + // @TODO: Restoring Application State + // Depends on NSCoder support, which is... welp. + + // Scripting + decl.add_method(sel!(application:delegateHandlesKey:), delegate_handles_key:: as extern fn(&Object, _, _, id) -> BOOL); + + DELEGATE_CLASS = decl.register(); + }); + + unsafe { + DELEGATE_CLASS + } +} diff --git a/appkit/src/app/mod.rs b/appkit/src/app/mod.rs index 254e9c7..ab520ef 100644 --- a/appkit/src/app/mod.rs +++ b/appkit/src/app/mod.rs @@ -5,14 +5,19 @@ use objc_id::Id; use objc::runtime::Object; use objc::{class, msg_send, sel, sel_impl}; -mod class; -pub mod traits; -pub mod enums; - use crate::foundation::{id, AutoReleasePool}; use crate::constants::APP_PTR; use crate::menu::Menu; -use class::{register_app_class, register_app_controller_class}; + +mod class; +use class::register_app_class; + +mod controller; +use controller::register_app_controller_class; + +pub mod enums; + +pub mod traits; pub use traits::{AppController, Dispatcher}; /// A wrapper for `NSApplication`. It holds (retains) pointers for the Objective-C runtime, @@ -104,9 +109,10 @@ impl App where M: Send + Sync + 'static, T: AppController + Dispatch pub fn run(&self) { unsafe { let current_app: id = msg_send![class!(NSRunningApplication), currentApplication]; - let _: () = msg_send![current_app, activateWithOptions:0]; + let _: () = msg_send![current_app, activateWithOptions:1<<1]; let shared_app: id = msg_send![class!(RSTApplication), sharedApplication]; let _: () = msg_send![shared_app, run]; + self.pool.drain(); } } } diff --git a/appkit/src/constants.rs b/appkit/src/constants.rs index af77297..9963966 100644 --- a/appkit/src/constants.rs +++ b/appkit/src/constants.rs @@ -5,7 +5,14 @@ pub(crate) static APP_PTR: &str = "rstAppPtr"; pub(crate) static BACKGROUND_COLOR: &str = "rstBackgroundColor"; pub(crate) static TOOLBAR_PTR: &str = "rstToolbarPtr"; pub(crate) static VIEW_CONTROLLER_PTR: &str = "rstViewControllerPtr"; + +#[cfg(feature = "webview")] pub(crate) static WEBVIEW_CONFIG_VAR: &str = "rstWebViewConfig"; + +#[cfg(feature = "webview")] pub(crate) static WEBVIEW_VAR: &str = "rstWebView"; + +#[cfg(feature = "webview")] pub(crate) static WEBVIEW_CONTROLLER_PTR: &str = "rstWebViewControllerPtr"; + pub(crate) static WINDOW_CONTROLLER_PTR: &str = "rstWindowController"; diff --git a/appkit/src/foundation/autoreleasepool.rs b/appkit/src/foundation/autoreleasepool.rs index 0f922b8..e623063 100644 --- a/appkit/src/foundation/autoreleasepool.rs +++ b/appkit/src/foundation/autoreleasepool.rs @@ -13,7 +13,7 @@ impl AutoReleasePool { }) } - pub fn drain(self) { + pub fn drain(&self) { let _: () = unsafe { msg_send![&*self.0, drain] }; } } diff --git a/appkit/src/lib.rs b/appkit/src/lib.rs index bdc94e6..e7cf31b 100644 --- a/appkit/src/lib.rs +++ b/appkit/src/lib.rs @@ -45,7 +45,10 @@ pub mod toolbar; pub mod user_activity; pub mod utils; pub mod view; -//pub mod webview; + +#[cfg(feature = "webview")] +pub mod webview; + pub mod window; // We re-export these so that they can be used without increasing build times. @@ -69,9 +72,10 @@ pub mod prelude { Window, WindowController, WindowHandle }; - //pub use crate::webview::{ - // WebView, WebViewConfig, WebViewController - //}; + #[cfg(feature = "webview")] + pub use crate::webview::{ + WebView, WebViewConfig, WebViewController + }; pub use crate::view::{View, ViewHandle, ViewController}; }