Further work on iOS support, mulling over how to make this cleaner
This commit is contained in:
parent
47ddf7f5a3
commit
0c604c2e84
|
@ -16,7 +16,7 @@ use url::Url;
|
||||||
|
|
||||||
use crate::error::AppKitError;
|
use crate::error::AppKitError;
|
||||||
use crate::foundation::{id, nil, BOOL, YES, NO, NSUInteger, NSArray, NSString};
|
use crate::foundation::{id, nil, BOOL, YES, NO, NSUInteger, NSArray, NSString};
|
||||||
use crate::ios::app::{APP_PTR, AppDelegate, APP_DELEGATE};
|
use crate::ios::app::{AppDelegate, APP_DELEGATE};
|
||||||
use crate::user_activity::UserActivity;
|
use crate::user_activity::UserActivity;
|
||||||
|
|
||||||
#[cfg(feature = "cloudkit")]
|
#[cfg(feature = "cloudkit")]
|
||||||
|
@ -49,8 +49,6 @@ pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
|
||||||
let superclass = class!(NSObject);
|
let superclass = class!(NSObject);
|
||||||
let mut decl = ClassDecl::new("RSTAppDelegate", superclass).unwrap();
|
let mut decl = ClassDecl::new("RSTAppDelegate", superclass).unwrap();
|
||||||
|
|
||||||
decl.add_ivar::<usize>(APP_PTR);
|
|
||||||
|
|
||||||
// Launching Applications
|
// Launching Applications
|
||||||
decl.add_method(sel!(application:didFinishLaunchingWithOptions:), did_finish_launching::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
decl.add_method(sel!(application:didFinishLaunchingWithOptions:), did_finish_launching::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
//! heavily by lifecycle events - in this case, your boilerplate would look something like this:
|
//! heavily by lifecycle events - in this case, your boilerplate would look something like this:
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use cacao::app::{App, AppDelegate};
|
//! use cacao::ios::app::{App, AppDelegate};
|
||||||
//! use cacao::window::Window;
|
//! use cacao::window::Window;
|
||||||
//!
|
//!
|
||||||
//! #[derive(Default)]
|
//! #[derive(Default)]
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! App::new("com.my.app", BasicApp::default()).run();
|
//! App::new(BasicApp::default()).run();
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -43,6 +43,7 @@ use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::foundation::{id, nil, YES, NO, NSString, NSUInteger, AutoReleasePool};
|
use crate::foundation::{id, nil, YES, NO, NSString, NSUInteger, AutoReleasePool};
|
||||||
use crate::notification_center::Dispatcher;
|
use crate::notification_center::Dispatcher;
|
||||||
|
use crate::utils::activate_cocoa_multithreading;
|
||||||
|
|
||||||
mod class;
|
mod class;
|
||||||
use class::register_app_class;
|
use class::register_app_class;
|
||||||
|
@ -56,11 +57,10 @@ pub use enums::*;
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::AppDelegate;
|
pub use traits::AppDelegate;
|
||||||
|
|
||||||
pub(crate) static APP_PTR: &str = "rstAppPtr";
|
|
||||||
pub(crate) static mut APP_DELEGATE: usize = 0;
|
pub(crate) static mut APP_DELEGATE: usize = 0;
|
||||||
|
|
||||||
//#[link(name="UIApplicationMain")]
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
/// Required for iOS applications to initialize.
|
||||||
fn UIApplicationMain(
|
fn UIApplicationMain(
|
||||||
argc: c_int,
|
argc: c_int,
|
||||||
argv: *const *const c_char,
|
argv: *const *const c_char,
|
||||||
|
@ -76,30 +76,9 @@ fn shared_application<F: Fn(id)>(handler: F) {
|
||||||
handler(app);
|
handler(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper method for ensuring that Cocoa is running in multi-threaded mode.
|
/// Wraps `UIApplication` and associated lifecycle pieces.
|
||||||
///
|
///
|
||||||
/// Why do we need this? According to Apple, if you're going to make use of standard POSIX threads,
|
/// Handles setting up a few necessary pieces:
|
||||||
/// you need to, before creating and using a POSIX thread, first create and immediately detach a
|
|
||||||
/// `NSThread`. This ensures that Cocoa utilizes proper locking in certain places where it might
|
|
||||||
/// not be doing so for performance reasons.
|
|
||||||
///
|
|
||||||
/// In general, you should aim to just start all of your work inside of your `AppDelegate` methods.
|
|
||||||
/// There are some cases where you might want to do things before that, though - and if you spawn a
|
|
||||||
/// thread there, just call this first... otherwise you may have some unexpected issues later on.
|
|
||||||
///
|
|
||||||
/// _(This is called inside the `App::new()` construct for you already, so as long as you're doing
|
|
||||||
/// nothing before your `AppDelegate`, you can pay this no mind)._
|
|
||||||
pub fn activate_cocoa_multithreading() {
|
|
||||||
unsafe {
|
|
||||||
let thread: id = msg_send![class!(NSThread), new];
|
|
||||||
let _: () = msg_send![thread, start];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper for `NSApplication` on macOS, and `UIApplication` on iOS.
|
|
||||||
///
|
|
||||||
/// It holds (retains) a pointer to the Objective-C runtime shared application object, as well as
|
|
||||||
/// handles setting up a few necessary pieces:
|
|
||||||
///
|
///
|
||||||
/// - It injects an `NSObject` subclass to act as a delegate for lifecycle events.
|
/// - It injects an `NSObject` subclass to act as a delegate for lifecycle events.
|
||||||
/// - It ensures that Cocoa, where appropriate, is operating in multi-threaded mode so POSIX
|
/// - It ensures that Cocoa, where appropriate, is operating in multi-threaded mode so POSIX
|
||||||
|
@ -109,40 +88,40 @@ pub fn activate_cocoa_multithreading() {
|
||||||
/// implement the `Dispatcher` trait to receive messages that you might dispatch from deeper in the
|
/// implement the `Dispatcher` trait to receive messages that you might dispatch from deeper in the
|
||||||
/// application.
|
/// application.
|
||||||
pub struct App<T = (), M = ()> {
|
pub struct App<T = (), M = ()> {
|
||||||
//pub inner: Id<Object>,
|
|
||||||
//pub objc_delegate: Id<Object>,
|
|
||||||
pub delegate: Box<T>,
|
pub delegate: Box<T>,
|
||||||
pub pool: AutoReleasePool,
|
pub pool: AutoReleasePool,
|
||||||
_t: std::marker::PhantomData<M>
|
_t: std::marker::PhantomData<M>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> App<T> {
|
impl<T> App<T> {
|
||||||
/// Kicks off the NSRunLoop for the NSApplication instance. This blocks when called.
|
/// Handles calling through to `UIApplicationMain()`, ensuring that it's using our custom
|
||||||
/// If you're wondering where to go from here... you need an `AppDelegate` that implements
|
/// `UIApplication` and `UIApplicationDelegate` classes.
|
||||||
/// `did_finish_launching`. :)
|
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
unsafe {
|
|
||||||
// create a vector of zero terminated strings
|
|
||||||
let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::<Vec<CString>>();
|
let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::<Vec<CString>>();
|
||||||
|
|
||||||
// convert the strings to raw pointers
|
|
||||||
let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>();
|
let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>();
|
||||||
|
|
||||||
let s = NSString::new("RSTApplication");
|
let s = NSString::new("RSTApplication");
|
||||||
let s2 = NSString::new("RSTAppDelegate");
|
let s2 = NSString::new("RSTAppDelegate");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
UIApplicationMain(c_args.len() as c_int, c_args.as_ptr(), s.into_inner(), s2.into_inner());
|
UIApplicationMain(c_args.len() as c_int, c_args.as_ptr(), s.into_inner(), s2.into_inner());
|
||||||
self.pool.drain();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.pool.drain();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> App<T> where T: AppDelegate + 'static {
|
impl<T> App<T> where T: AppDelegate + 'static {
|
||||||
/// Creates an NSAutoReleasePool, configures various NSApplication properties (e.g, activation
|
/// iOS manages creating a new Application (`UIApplication`) differently than you'd expect if
|
||||||
/// policies), injects an `NSObject` delegate wrapper, and retains everything on the
|
/// you were looking at the macOS side of things.
|
||||||
/// Objective-C side of things.
|
///
|
||||||
pub fn new(_bundle_id: &str, delegate: T) -> Self {
|
/// In this case, we're primarily concerned with shoving our `AppDelegate` to a place we can
|
||||||
// set_bundle_id(bundle_id);
|
/// retrieve it later on. While this is unsafe behavior, it's ultimately no different than
|
||||||
|
/// shoving the pointer onto the delegate like we do on the macOS side of things.
|
||||||
|
///
|
||||||
|
/// This also handles ensuring that our subclasses exist in the Objective-C runtime *before*
|
||||||
|
/// `UIApplicationMain` is called.
|
||||||
|
pub fn new(delegate: T) -> Self {
|
||||||
activate_cocoa_multithreading();
|
activate_cocoa_multithreading();
|
||||||
|
|
||||||
let pool = AutoReleasePool::new();
|
let pool = AutoReleasePool::new();
|
||||||
|
@ -154,23 +133,6 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
||||||
unsafe {
|
unsafe {
|
||||||
APP_DELEGATE = delegate_ptr as usize;
|
APP_DELEGATE = delegate_ptr as usize;
|
||||||
}
|
}
|
||||||
//(&mut *delegate).set_ivar(APP_PTR, delegate_ptr as usize);
|
|
||||||
|
|
||||||
|
|
||||||
/*let inner = unsafe {
|
|
||||||
let app: id = msg_send![register_app_class(), sharedApplication];
|
|
||||||
let _: () = msg_send![app, setActivationPolicy:0];
|
|
||||||
println!("1");
|
|
||||||
Id::from_ptr(app)
|
|
||||||
};
|
|
||||||
|
|
||||||
let objc_delegate = unsafe {
|
|
||||||
let delegate_class = register_app_delegate_class::<T>();
|
|
||||||
let delegate: id = msg_send![delegate_class, new];
|
|
||||||
let _: () = msg_send![&*inner, setDelegate:delegate];
|
|
||||||
println!("2");
|
|
||||||
Id::from_ptr(delegate)
|
|
||||||
};*/
|
|
||||||
|
|
||||||
App {
|
App {
|
||||||
delegate: app_delegate,
|
delegate: app_delegate,
|
||||||
|
@ -185,15 +147,5 @@ impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher
|
||||||
/// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate,
|
/// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate,
|
||||||
/// and passing back through there. All messages are currently dispatched on the main thread.
|
/// and passing back through there. All messages are currently dispatched on the main thread.
|
||||||
pub fn dispatch(message: M) {
|
pub fn dispatch(message: M) {
|
||||||
/*
|
|
||||||
let queue = dispatch::Queue::main();
|
|
||||||
|
|
||||||
queue.exec_async(move || unsafe {
|
|
||||||
let app: id = msg_send![register_app_class(), sharedApplication];
|
|
||||||
let app_delegate: id = msg_send![app, delegate];
|
|
||||||
let delegate_ptr: usize = *(*app_delegate).get_ivar(APP_PTR);
|
|
||||||
let delegate = delegate_ptr as *const T;
|
|
||||||
(&*delegate).on_message(message);
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
//! iOS.
|
//! iOS-specific implementations.
|
||||||
|
//!
|
||||||
|
//! In general, this framework tries to make things "just work" with regards to AppKit and UIKit
|
||||||
|
//! differences. With that said, there are certain things that just don't map between the two - for
|
||||||
|
//! iOS, these things are contained here.
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
pub use app::*;
|
pub use app::*;
|
||||||
|
|
|
@ -41,6 +41,7 @@ use objc::{class, msg_send, sel, sel_impl};
|
||||||
use crate::foundation::{id, nil, YES, NO, NSUInteger, AutoReleasePool};
|
use crate::foundation::{id, nil, YES, NO, NSUInteger, AutoReleasePool};
|
||||||
use crate::macos::menu::Menu;
|
use crate::macos::menu::Menu;
|
||||||
use crate::notification_center::Dispatcher;
|
use crate::notification_center::Dispatcher;
|
||||||
|
use crate::utils::activate_cocoa_multithreading;
|
||||||
|
|
||||||
mod class;
|
mod class;
|
||||||
use class::register_app_class;
|
use class::register_app_class;
|
||||||
|
@ -63,26 +64,6 @@ fn shared_application<F: Fn(id)>(handler: F) {
|
||||||
handler(app);
|
handler(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper method for ensuring that Cocoa is running in multi-threaded mode.
|
|
||||||
///
|
|
||||||
/// Why do we need this? According to Apple, if you're going to make use of standard POSIX threads,
|
|
||||||
/// you need to, before creating and using a POSIX thread, first create and immediately detach a
|
|
||||||
/// `NSThread`. This ensures that Cocoa utilizes proper locking in certain places where it might
|
|
||||||
/// not be doing so for performance reasons.
|
|
||||||
///
|
|
||||||
/// In general, you should aim to just start all of your work inside of your `AppDelegate` methods.
|
|
||||||
/// There are some cases where you might want to do things before that, though - and if you spawn a
|
|
||||||
/// thread there, just call this first... otherwise you may have some unexpected issues later on.
|
|
||||||
///
|
|
||||||
/// _(This is called inside the `App::new()` construct for you already, so as long as you're doing
|
|
||||||
/// nothing before your `AppDelegate`, you can pay this no mind)._
|
|
||||||
pub fn activate_cocoa_multithreading() {
|
|
||||||
unsafe {
|
|
||||||
let thread: id = msg_send![class!(NSThread), new];
|
|
||||||
let _: () = msg_send![thread, start];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper for `NSApplication` on macOS, and `UIApplication` on iOS.
|
/// A wrapper for `NSApplication` on macOS, and `UIApplication` on iOS.
|
||||||
///
|
///
|
||||||
/// It holds (retains) a pointer to the Objective-C runtime shared application object, as well as
|
/// It holds (retains) a pointer to the Objective-C runtime shared application object, as well as
|
||||||
|
|
25
src/utils.rs
25
src/utils.rs
|
@ -4,17 +4,20 @@
|
||||||
|
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::base::CGFloat;
|
||||||
|
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use objc::{Encode, Encoding};
|
use objc::{Encode, Encoding};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::foundation::id;
|
||||||
|
|
||||||
/// A generic trait that's used throughout multiple different controls in this framework - acts as
|
/// A generic trait that's used throughout multiple different controls in this framework - acts as
|
||||||
/// a guard for whether something is a (View|etc)Controller. Only needs to return the backing node.
|
/// a guard for whether something is a (View|etc)Controller. Only needs to return the backing node.
|
||||||
pub trait Controller {
|
pub trait Controller {
|
||||||
fn get_backing_node(&self) -> ShareId<Object>;
|
fn get_backing_node(&self) -> ShareId<Object>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Utility method for taking a pointer and grabbing the corresponding delegate in Rust. This is
|
/// Utility method for taking a pointer and grabbing the corresponding delegate in Rust. This is
|
||||||
/// theoretically safe:
|
/// theoretically safe:
|
||||||
///
|
///
|
||||||
|
@ -58,3 +61,23 @@ unsafe impl Encode for CGSize {
|
||||||
unsafe { Encoding::from_str(&encoding) }
|
unsafe { Encoding::from_str(&encoding) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper method for ensuring that Cocoa is running in multi-threaded mode.
|
||||||
|
///
|
||||||
|
/// Why do we need this? According to Apple, if you're going to make use of standard POSIX threads,
|
||||||
|
/// you need to, before creating and using a POSIX thread, first create and immediately detach a
|
||||||
|
/// `NSThread`. This ensures that Cocoa utilizes proper locking in certain places where it might
|
||||||
|
/// not be doing so for performance reasons.
|
||||||
|
///
|
||||||
|
/// In general, you should aim to just start all of your work inside of your `AppDelegate` methods.
|
||||||
|
/// There are some cases where you might want to do things before that, though - and if you spawn a
|
||||||
|
/// thread there, just call this first... otherwise you may have some unexpected issues later on.
|
||||||
|
///
|
||||||
|
/// _(This is called inside the `App::new()` construct for you already, so as long as you're doing
|
||||||
|
/// nothing before your `AppDelegate`, you can pay this no mind)._
|
||||||
|
pub fn activate_cocoa_multithreading() {
|
||||||
|
unsafe {
|
||||||
|
let thread: id = msg_send![class!(NSThread), new];
|
||||||
|
let _: () = msg_send![thread, start];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue