mirror of
https://github.com/rust-windowing/winit.git
synced 2026-09-01 06:10:07 -04:00
Allow the user to register the application delegate on iOS
iOS parts of #3758. This allows the user to override the application delegate themselves, which opens several doors for customization that were previously closed. To do this, we use notifications instead of a top-level application delegate. One effect of not providing an application delegate on iOS is that we no longer act as-if the application successfully open all URLs there. This is a breaking change, although unlikely to matter in practice, since the return value of `application:didFinishLaunchingWithOptions:` is seldom used by the system (and this is likely the preferred behaviour anyhow).
This commit is contained in:
committed by
Kirill Chibisov
parent
da7a09658a
commit
57baf72741
@@ -117,12 +117,12 @@ android-activity = "0.6.0"
|
|||||||
ndk = { version = "0.9.0", default-features = false }
|
ndk = { version = "0.9.0", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||||
|
block2 = "0.5.1"
|
||||||
core-foundation = "0.9.3"
|
core-foundation = "0.9.3"
|
||||||
objc2 = "0.5.2"
|
objc2 = "0.5.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-graphics = "0.23.1"
|
core-graphics = "0.23.1"
|
||||||
block2 = "0.5.1"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -180,11 +180,13 @@ features = [
|
|||||||
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
|
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
features = [
|
features = [
|
||||||
|
"block2",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"NSArray",
|
"NSArray",
|
||||||
"NSEnumerator",
|
"NSEnumerator",
|
||||||
"NSGeometry",
|
"NSGeometry",
|
||||||
"NSObjCRuntime",
|
"NSObjCRuntime",
|
||||||
|
"NSOperation",
|
||||||
"NSString",
|
"NSString",
|
||||||
"NSProcessInfo",
|
"NSProcessInfo",
|
||||||
"NSThread",
|
"NSThread",
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ changelog entry.
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- On macOS, no longer need control of the main `NSApplication` class (which means you can now override it yourself).
|
- On macOS, no longer need control of the main `NSApplication` class (which means you can now override it yourself).
|
||||||
|
- On iOS, remove custom application delegates. You are now allowed to override the
|
||||||
|
application delegate yourself.
|
||||||
|
- On iOS, no longer act as-if the application successfully open all URLs. Override
|
||||||
|
`application:didFinishLaunchingWithOptions:` and provide the desired behaviour yourself.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
|
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
|
||||||
//! iOS 9.3.
|
//! iOS 9.3.
|
||||||
//!
|
//!
|
||||||
|
//! ## Window initialization
|
||||||
|
//!
|
||||||
//! iOS's main `UIApplicationMain` does some init work that's required by all
|
//! iOS's main `UIApplicationMain` does some init work that's required by all
|
||||||
//! UI-related code (see issue [#1705]). It is best to create your windows
|
//! UI-related code (see issue [#1705]). It is best to create your windows
|
||||||
//! inside `Event::Resumed`.
|
//! inside [`ApplicationHandler::resumed`].
|
||||||
//!
|
//!
|
||||||
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
|
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
|
||||||
|
//! [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
|
||||||
//!
|
//!
|
||||||
//! ## Building app
|
//! ## Building app
|
||||||
//!
|
//!
|
||||||
@@ -63,6 +66,16 @@
|
|||||||
//! opengl will result in segfault.
|
//! opengl will result in segfault.
|
||||||
//!
|
//!
|
||||||
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
|
//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
|
||||||
|
//!
|
||||||
|
//! ## Custom `UIApplicationDelegate`
|
||||||
|
//!
|
||||||
|
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
|
||||||
|
//! though, you might want to access some of the more niche stuff that [the application
|
||||||
|
//! delegate][app-delegate] provides. This functionality is not exposed directly in Winit, since it
|
||||||
|
//! would increase the API surface by quite a lot. Instead, Winit guarantees that it will not
|
||||||
|
//! register an application delegate, so you can set up a custom one in a nib file instead.
|
||||||
|
//!
|
||||||
|
//! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,84 @@
|
|||||||
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
|
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
|
||||||
//! itself), and is regularly tested on macOS 10.14.
|
//! itself), and is regularly tested on macOS 10.14.
|
||||||
//!
|
//!
|
||||||
|
//! ## Window initialization
|
||||||
|
//!
|
||||||
//! A lot of functionality expects the application to be ready before you
|
//! A lot of functionality expects the application to be ready before you
|
||||||
//! start doing anything; this includes creating windows, fetching monitors,
|
//! start doing anything; this includes creating windows, fetching monitors,
|
||||||
//! drawing, and so on, see issues [#2238], [#2051] and [#2087].
|
//! drawing, and so on, see issues [#2238], [#2051] and [#2087].
|
||||||
//!
|
//!
|
||||||
//! If you encounter problems, you should try doing your initialization inside
|
//! If you encounter problems, you should try doing your initialization inside
|
||||||
//! `Event::Resumed`.
|
//! [`ApplicationHandler::resumed`].
|
||||||
//!
|
//!
|
||||||
//! [#2238]: https://github.com/rust-windowing/winit/issues/2238
|
//! [#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||||
//! [#2051]: https://github.com/rust-windowing/winit/issues/2051
|
//! [#2051]: https://github.com/rust-windowing/winit/issues/2051
|
||||||
//! [#2087]: https://github.com/rust-windowing/winit/issues/2087
|
//! [#2087]: https://github.com/rust-windowing/winit/issues/2087
|
||||||
|
//! [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
|
||||||
|
//!
|
||||||
|
//! ## Custom `NSApplicationDelegate`
|
||||||
|
//!
|
||||||
|
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
|
||||||
|
//! though, you might want to do more niche stuff, such as [handle when the user re-activates the
|
||||||
|
//! application][reopen]. Such functionality is not exposed directly in Winit, since it would
|
||||||
|
//! increase the API surface by quite a lot.
|
||||||
|
//!
|
||||||
|
//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
|
||||||
|
//!
|
||||||
|
//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
|
||||||
|
//! to register your own application delegate, as outlined in the following example (see
|
||||||
|
//! `objc2-app-kit` for more detailed information).
|
||||||
|
#![cfg_attr(target_os = "macos", doc = "```")]
|
||||||
|
#![cfg_attr(not(target_os = "macos"), doc = "```ignore")]
|
||||||
|
//! use objc2::rc::Retained;
|
||||||
|
//! use objc2::runtime::ProtocolObject;
|
||||||
|
//! use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||||
|
//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
|
||||||
|
//! use objc2_foundation::{NSArray, NSURL, MainThreadMarker, NSObject, NSObjectProtocol};
|
||||||
|
//! use winit::event_loop::EventLoop;
|
||||||
|
//!
|
||||||
|
//! declare_class!(
|
||||||
|
//! struct AppDelegate;
|
||||||
|
//!
|
||||||
|
//! unsafe impl ClassType for AppDelegate {
|
||||||
|
//! type Super = NSObject;
|
||||||
|
//! type Mutability = mutability::MainThreadOnly;
|
||||||
|
//! const NAME: &'static str = "MyAppDelegate";
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl DeclaredClass for AppDelegate {}
|
||||||
|
//!
|
||||||
|
//! unsafe impl NSObjectProtocol for AppDelegate {}
|
||||||
|
//!
|
||||||
|
//! unsafe impl NSApplicationDelegate for AppDelegate {
|
||||||
|
//! #[method(application:openURLs:)]
|
||||||
|
//! fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
|
||||||
|
//! // Note: To specifically get `application:openURLs:` to work, you _might_
|
||||||
|
//! // have to bundle your application. This is not done in this example.
|
||||||
|
//! println!("open urls: {application:?}, {urls:?}");
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! impl AppDelegate {
|
||||||
|
//! fn new(mtm: MainThreadMarker) -> Retained<Self> {
|
||||||
|
//! unsafe { msg_send_id![super(mtm.alloc().set_ivars(())), init] }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
//! let event_loop = EventLoop::new()?;
|
||||||
|
//!
|
||||||
|
//! let mtm = MainThreadMarker::new().unwrap();
|
||||||
|
//! let delegate = AppDelegate::new(mtm);
|
||||||
|
//! // Important: Call `sharedApplication` after `EventLoop::new`,
|
||||||
|
//! // doing it before is not yet supported.
|
||||||
|
//! let app = NSApplication::sharedApplication(mtm);
|
||||||
|
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||||
|
//!
|
||||||
|
//! // event_loop.run_app(&mut my_app);
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
|
||||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
|
||||||
use objc2_ui_kit::UIApplication;
|
|
||||||
|
|
||||||
use super::app_state::{self, send_occluded_event_for_all_windows, EventWrapper};
|
|
||||||
use crate::event::Event;
|
|
||||||
|
|
||||||
declare_class!(
|
|
||||||
pub struct AppDelegate;
|
|
||||||
|
|
||||||
unsafe impl ClassType for AppDelegate {
|
|
||||||
type Super = NSObject;
|
|
||||||
type Mutability = mutability::InteriorMutable;
|
|
||||||
const NAME: &'static str = "WinitApplicationDelegate";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeclaredClass for AppDelegate {}
|
|
||||||
|
|
||||||
// UIApplicationDelegate protocol
|
|
||||||
unsafe impl AppDelegate {
|
|
||||||
#[method(application:didFinishLaunchingWithOptions:)]
|
|
||||||
fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
|
|
||||||
app_state::did_finish_launching(MainThreadMarker::new().unwrap());
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(applicationDidBecomeActive:)]
|
|
||||||
fn did_become_active(&self, _application: &UIApplication) {
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(applicationWillResignActive:)]
|
|
||||||
fn will_resign_active(&self, _application: &UIApplication) {
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(applicationWillEnterForeground:)]
|
|
||||||
fn will_enter_foreground(&self, application: &UIApplication) {
|
|
||||||
send_occluded_event_for_all_windows(application, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(applicationDidEnterBackground:)]
|
|
||||||
fn did_enter_background(&self, application: &UIApplication) {
|
|
||||||
send_occluded_event_for_all_windows(application, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(applicationWillTerminate:)]
|
|
||||||
fn will_terminate(&self, application: &UIApplication) {
|
|
||||||
app_state::terminated(application);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[method(applicationDidReceiveMemoryWarning:)]
|
|
||||||
fn did_receive_memory_warning(&self, _application: &UIApplication) {
|
|
||||||
let mtm = MainThreadMarker::new().unwrap();
|
|
||||||
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -13,8 +13,14 @@ use core_foundation::runloop::{
|
|||||||
};
|
};
|
||||||
use objc2::rc::Retained;
|
use objc2::rc::Retained;
|
||||||
use objc2::{msg_send_id, ClassType};
|
use objc2::{msg_send_id, ClassType};
|
||||||
use objc2_foundation::{MainThreadMarker, NSString};
|
use objc2_foundation::{MainThreadMarker, NSNotificationCenter, NSObject};
|
||||||
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
|
use objc2_ui_kit::{
|
||||||
|
UIApplication, UIApplicationDidBecomeActiveNotification,
|
||||||
|
UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification,
|
||||||
|
UIApplicationDidReceiveMemoryWarningNotification, UIApplicationMain,
|
||||||
|
UIApplicationWillEnterForegroundNotification, UIApplicationWillResignActiveNotification,
|
||||||
|
UIApplicationWillTerminateNotification, UIDevice, UIScreen, UIUserInterfaceIdiom,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::error::EventLoopError;
|
use crate::error::EventLoopError;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
@@ -25,8 +31,8 @@ use crate::platform::ios::Idiom;
|
|||||||
use crate::platform_impl::ios::app_state::{EventLoopHandler, HandlePendingUserEvents};
|
use crate::platform_impl::ios::app_state::{EventLoopHandler, HandlePendingUserEvents};
|
||||||
use crate::window::{CustomCursor, CustomCursorSource, Theme};
|
use crate::window::{CustomCursor, CustomCursorSource, Theme};
|
||||||
|
|
||||||
use super::app_delegate::AppDelegate;
|
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
|
||||||
use super::app_state::AppState;
|
use super::notification_center::create_observer;
|
||||||
use super::{app_state, monitor, MonitorHandle};
|
use super::{app_state, monitor, MonitorHandle};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -132,6 +138,18 @@ pub struct EventLoop<T: 'static> {
|
|||||||
sender: Sender<T>,
|
sender: Sender<T>,
|
||||||
receiver: Receiver<T>,
|
receiver: Receiver<T>,
|
||||||
window_target: RootActiveEventLoop,
|
window_target: RootActiveEventLoop,
|
||||||
|
|
||||||
|
// Since iOS 9.0, we no longer need to remove the observers before they are deallocated; the
|
||||||
|
// system instead cleans it up next time it would have posted a notification to it.
|
||||||
|
//
|
||||||
|
// Though we do still need to keep the observers around to prevent them from being deallocated.
|
||||||
|
_did_finish_launching_observer: Retained<NSObject>,
|
||||||
|
_did_become_active_observer: Retained<NSObject>,
|
||||||
|
_will_resign_active_observer: Retained<NSObject>,
|
||||||
|
_will_enter_foreground_observer: Retained<NSObject>,
|
||||||
|
_did_enter_background_observer: Retained<NSObject>,
|
||||||
|
_will_terminate_observer: Retained<NSObject>,
|
||||||
|
_did_receive_memory_warning_observer: Retained<NSObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -158,11 +176,97 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
// this line sets up the main run loop before `UIApplicationMain`
|
// this line sets up the main run loop before `UIApplicationMain`
|
||||||
setup_control_flow_observers();
|
setup_control_flow_observers();
|
||||||
|
|
||||||
|
let center = unsafe { NSNotificationCenter::defaultCenter() };
|
||||||
|
|
||||||
|
let _did_finish_launching_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `application:didFinishLaunchingWithOptions:`
|
||||||
|
unsafe { UIApplicationDidFinishLaunchingNotification },
|
||||||
|
move |_| {
|
||||||
|
app_state::did_finish_launching(mtm);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _did_become_active_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `applicationDidBecomeActive:`
|
||||||
|
unsafe { UIApplicationDidBecomeActiveNotification },
|
||||||
|
move |_| {
|
||||||
|
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _will_resign_active_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `applicationWillResignActive:`
|
||||||
|
unsafe { UIApplicationWillResignActiveNotification },
|
||||||
|
move |_| {
|
||||||
|
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _will_enter_foreground_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `applicationWillEnterForeground:`
|
||||||
|
unsafe { UIApplicationWillEnterForegroundNotification },
|
||||||
|
move |notification| {
|
||||||
|
let app = unsafe { notification.object() }.expect(
|
||||||
|
"UIApplicationWillEnterForegroundNotification to have application object",
|
||||||
|
);
|
||||||
|
// SAFETY: The `object` in `UIApplicationWillEnterForegroundNotification` is
|
||||||
|
// documented to be `UIApplication`.
|
||||||
|
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||||
|
send_occluded_event_for_all_windows(&app, false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _did_enter_background_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `applicationDidEnterBackground:`
|
||||||
|
unsafe { UIApplicationDidEnterBackgroundNotification },
|
||||||
|
move |notification| {
|
||||||
|
let app = unsafe { notification.object() }.expect(
|
||||||
|
"UIApplicationDidEnterBackgroundNotification to have application object",
|
||||||
|
);
|
||||||
|
// SAFETY: The `object` in `UIApplicationDidEnterBackgroundNotification` is
|
||||||
|
// documented to be `UIApplication`.
|
||||||
|
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||||
|
send_occluded_event_for_all_windows(&app, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _will_terminate_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `applicationWillTerminate:`
|
||||||
|
unsafe { UIApplicationWillTerminateNotification },
|
||||||
|
move |notification| {
|
||||||
|
let app = unsafe { notification.object() }
|
||||||
|
.expect("UIApplicationWillTerminateNotification to have application object");
|
||||||
|
// SAFETY: The `object` in `UIApplicationWillTerminateNotification` is
|
||||||
|
// (somewhat) documented to be `UIApplication`.
|
||||||
|
let app: Retained<UIApplication> = unsafe { Retained::cast(app) };
|
||||||
|
app_state::terminated(&app);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _did_receive_memory_warning_observer = create_observer(
|
||||||
|
¢er,
|
||||||
|
// `applicationDidReceiveMemoryWarning:`
|
||||||
|
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
|
||||||
|
move |_| {
|
||||||
|
app_state::handle_nonuser_event(
|
||||||
|
mtm,
|
||||||
|
EventWrapper::StaticEvent(Event::MemoryWarning),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Ok(EventLoop {
|
Ok(EventLoop {
|
||||||
mtm,
|
mtm,
|
||||||
sender,
|
sender,
|
||||||
receiver,
|
receiver,
|
||||||
window_target: RootActiveEventLoop { p: ActiveEventLoop { mtm }, _marker: PhantomData },
|
window_target: RootActiveEventLoop { p: ActiveEventLoop { mtm }, _marker: PhantomData },
|
||||||
|
_did_finish_launching_observer,
|
||||||
|
_did_become_active_observer,
|
||||||
|
_will_resign_active_observer,
|
||||||
|
_will_enter_foreground_observer,
|
||||||
|
_did_enter_background_observer,
|
||||||
|
_will_terminate_observer,
|
||||||
|
_did_receive_memory_warning_observer,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,9 +296,6 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
|
|
||||||
app_state::will_launch(self.mtm, handler);
|
app_state::will_launch(self.mtm, handler);
|
||||||
|
|
||||||
// Ensure application delegate is initialized
|
|
||||||
let _ = AppDelegate::class();
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// These functions are in crt_externs.h.
|
// These functions are in crt_externs.h.
|
||||||
fn _NSGetArgc() -> *mut c_int;
|
fn _NSGetArgc() -> *mut c_int;
|
||||||
@@ -205,8 +306,10 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
UIApplicationMain(
|
UIApplicationMain(
|
||||||
*_NSGetArgc(),
|
*_NSGetArgc(),
|
||||||
NonNull::new(*_NSGetArgv()).unwrap(),
|
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||||
|
// We intentionally override neither the application nor the delegate, to allow the
|
||||||
|
// user to do so themselves!
|
||||||
|
None,
|
||||||
None,
|
None,
|
||||||
Some(&NSString::from_str(AppDelegate::NAME)),
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#![allow(clippy::let_unit_value)]
|
#![allow(clippy::let_unit_value)]
|
||||||
|
|
||||||
mod app_delegate;
|
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod monitor;
|
mod monitor;
|
||||||
|
mod notification_center;
|
||||||
mod view;
|
mod view;
|
||||||
mod view_controller;
|
mod view_controller;
|
||||||
mod window;
|
mod window;
|
||||||
|
|||||||
27
src/platform_impl/ios/notification_center.rs
Normal file
27
src/platform_impl/ios/notification_center.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use block2::RcBlock;
|
||||||
|
use objc2::rc::Retained;
|
||||||
|
use objc2_foundation::{NSNotification, NSNotificationCenter, NSNotificationName, NSObject};
|
||||||
|
|
||||||
|
/// Observe the given notification.
|
||||||
|
///
|
||||||
|
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
|
||||||
|
/// give the user full control over those.
|
||||||
|
pub fn create_observer(
|
||||||
|
center: &NSNotificationCenter,
|
||||||
|
name: &NSNotificationName,
|
||||||
|
handler: impl Fn(&NSNotification) + 'static,
|
||||||
|
) -> Retained<NSObject> {
|
||||||
|
let block = RcBlock::new(move |notification: NonNull<NSNotification>| {
|
||||||
|
handler(unsafe { notification.as_ref() });
|
||||||
|
});
|
||||||
|
unsafe {
|
||||||
|
center.addObserverForName_object_queue_usingBlock(
|
||||||
|
Some(name),
|
||||||
|
None, // No sender filter
|
||||||
|
None, // No queue, run on posting thread (i.e. main thread)
|
||||||
|
&block,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user