Clean up iOS class declaration (#2462)

* Begin abstraction over UIKit

* Clean up UIWindow override declaration

* Clean up UIApplication delegate declaration

* Clean up UIViewController override declaration

* Finalize objc -> objc2 rename
This commit is contained in:
Mads Marquart 2022-09-08 20:30:34 +02:00 committed by GitHub
parent da7bf8e29b
commit fb248eaadc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 250 additions and 249 deletions

View file

@ -62,7 +62,7 @@ ndk = "0.7.0"
ndk-glue = "0.7.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
objc = { version = "=0.3.0-beta.3", package = "objc2" }
objc2 = "=0.3.0-beta.3"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9.3"

View file

@ -145,11 +145,6 @@ extern crate log;
extern crate serde;
#[macro_use]
extern crate bitflags;
#[cfg(target_os = "ios")]
#[macro_use]
extern crate objc;
#[cfg(target_os = "macos")]
extern crate objc as objc2;
pub mod dpi;
#[macro_use]

View file

@ -9,8 +9,9 @@ use std::{
time::Instant,
};
use objc::foundation::{NSInteger, NSUInteger};
use objc::runtime::Object;
use objc2::foundation::{NSInteger, NSUInteger};
use objc2::runtime::Object;
use objc2::{class, msg_send, sel};
use once_cell::sync::Lazy;
use crate::{

View file

@ -7,7 +7,8 @@ use std::{
sync::mpsc::{self, Receiver, Sender},
};
use objc::runtime::Object;
use objc2::runtime::Object;
use objc2::{class, msg_send, ClassType};
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
use crate::{
@ -91,7 +92,6 @@ impl<T: 'static> EventLoop<T> {
`EventLoopProxy` might be helpful"
);
SINGLETON_INIT = true;
view::create_delegate_class();
}
let (sender_to_clone, receiver) = mpsc::channel();
@ -128,11 +128,14 @@ impl<T: 'static> EventLoop<T> {
event_loop: self.window_target,
}));
// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
UIApplicationMain(
0,
ptr::null(),
nil,
NSStringRust::alloc(nil).init_str("AppDelegate"),
NSStringRust::alloc(nil).init_str("WinitApplicationDelegate"),
);
unreachable!()
}

View file

@ -2,8 +2,10 @@
use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*};
use objc::foundation::{NSInteger, NSUInteger};
use objc::{runtime::Object, Encode, Encoding};
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{NSInteger, NSUInteger};
use objc2::runtime::Object;
use objc2::{class, msg_send};
use crate::{
dpi::LogicalSize,

View file

@ -63,7 +63,7 @@
// window size/position.
macro_rules! assert_main_thread {
($($t:tt)*) => {
if !::objc::foundation::is_main_thread() {
if !::objc2::foundation::is_main_thread() {
panic!($($t)*);
}
};
@ -73,6 +73,7 @@ mod app_state;
mod event_loop;
mod ffi;
mod monitor;
mod uikit;
mod view;
mod window;

View file

@ -4,7 +4,8 @@ use std::{
ops::{Deref, DerefMut},
};
use objc::foundation::{NSInteger, NSUInteger};
use objc2::foundation::{NSInteger, NSUInteger};
use objc2::{class, msg_send};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},

View file

@ -0,0 +1,12 @@
#![deny(unsafe_op_in_unsafe_fn)]
mod responder;
mod view;
mod view_controller;
mod window;
pub(crate) use self::responder::UIResponder;
#[allow(unused)]
pub(crate) use self::view::UIView;
pub(crate) use self::view_controller::UIViewController;
pub(crate) use self::window::UIWindow;

View file

@ -0,0 +1,11 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIResponder;
unsafe impl ClassType for UIResponder {
type Super = NSObject;
}
);

View file

@ -0,0 +1,14 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use super::UIResponder;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIView;
unsafe impl ClassType for UIView {
#[inherits(NSObject)]
type Super = UIResponder;
}
);

View file

@ -0,0 +1,14 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use super::UIResponder;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIViewController;
unsafe impl ClassType for UIViewController {
#[inherits(NSObject)]
type Super = UIResponder;
}
);

View file

@ -0,0 +1,14 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use super::UIResponder;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIWindow;
unsafe impl ClassType for UIWindow {
#[inherits(NSObject)]
type Super = UIResponder;
}
);

View file

@ -1,16 +1,17 @@
use std::collections::HashMap;
use objc::{
declare::ClassBuilder,
runtime::{Bool, Class, Object, Sel},
};
use objc2::declare::ClassBuilder;
use objc2::foundation::NSObject;
use objc2::runtime::{Bool, Class, Object, Sel};
use objc2::{class, declare_class, msg_send, sel, ClassType};
use super::uikit::{UIResponder, UIViewController, UIWindow};
use crate::{
dpi::PhysicalPosition,
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
platform::ios::MonitorHandleExtIOS,
platform_impl::platform::{
app_state::{self, OSCapabilities},
app_state,
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
@ -22,64 +23,6 @@ use crate::{
window::{Fullscreen, WindowAttributes, WindowId as RootWindowId},
};
macro_rules! add_property {
(
$decl:ident,
$name:ident: $t:ty,
$setter_name:ident: |$object:ident| $after_set:expr,
$getter_name:ident,
) => {
add_property!(
$decl,
$name: $t,
$setter_name: true, |_, _|{}; |$object| $after_set,
$getter_name,
)
};
(
$decl:ident,
$name:ident: $t:ty,
$setter_name:ident: $capability:expr, $err:expr; |$object:ident| $after_set:expr,
$getter_name:ident,
) => {
{
const VAR_NAME: &'static str = concat!("_", stringify!($name));
$decl.add_ivar::<$t>(VAR_NAME);
let setter = if $capability {
#[allow(non_snake_case)]
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
unsafe {
$object.set_ivar::<$t>(VAR_NAME, value);
}
$after_set
}
$setter_name
} else {
#[allow(non_snake_case)]
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
unsafe {
$object.set_ivar::<$t>(VAR_NAME, value);
}
$err(&app_state::os_capabilities(), "ignoring")
}
$setter_name
};
#[allow(non_snake_case)]
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
unsafe { *$object.ivar::<$t>(VAR_NAME) }
}
$decl.add_method(
sel!($setter_name:),
setter as extern "C" fn(_, _, _),
);
$decl.add_method(
sel!($getter_name),
$getter_name as extern "C" fn(_, _) -> _,
);
}
};
}
// requires main thread
unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
static mut CLASSES: Option<HashMap<*const Class, &'static Class>> = None;
@ -312,116 +255,127 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
})
}
// requires main thread
unsafe fn get_view_controller_class() -> &'static Class {
static mut CLASS: Option<&'static Class> = None;
if CLASS.is_none() {
let os_capabilities = app_state::os_capabilities();
let uiviewcontroller_class = class!(UIViewController);
extern "C" fn should_autorotate(_: &Object, _: Sel) -> Bool {
Bool::YES
}
let mut decl = ClassBuilder::new("WinitUIViewController", uiviewcontroller_class)
.expect("Failed to declare class `WinitUIViewController`");
decl.add_method(
sel!(shouldAutorotate),
should_autorotate as extern "C" fn(_, _) -> _,
);
add_property! {
decl,
prefers_status_bar_hidden: Bool,
setPrefersStatusBarHidden: |object| {
unsafe {
let _: () = msg_send![object, setNeedsStatusBarAppearanceUpdate];
}
},
prefersStatusBarHidden,
}
add_property! {
decl,
prefers_home_indicator_auto_hidden: Bool,
setPrefersHomeIndicatorAutoHidden:
os_capabilities.home_indicator_hidden,
OSCapabilities::home_indicator_hidden_err_msg;
|object| {
unsafe {
let _: () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden];
}
},
prefersHomeIndicatorAutoHidden,
}
add_property! {
decl,
supported_orientations: UIInterfaceOrientationMask,
setSupportedInterfaceOrientations: |object| {
unsafe {
let _: () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation];
}
},
supportedInterfaceOrientations,
}
add_property! {
decl,
preferred_screen_edges_deferring_system_gestures: UIRectEdge,
setPreferredScreenEdgesDeferringSystemGestures:
os_capabilities.defer_system_gestures,
OSCapabilities::defer_system_gestures_err_msg;
|object| {
unsafe {
let _: () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
},
preferredScreenEdgesDeferringSystemGestures,
}
CLASS = Some(decl.register());
declare_class!(
struct WinitViewController {
_prefers_status_bar_hidden: bool,
_prefers_home_indicator_auto_hidden: bool,
_supported_orientations: UIInterfaceOrientationMask,
_preferred_screen_edges_deferring_system_gestures: UIRectEdge,
}
CLASS.unwrap()
}
// requires main thread
unsafe fn get_window_class() -> &'static Class {
static mut CLASS: Option<&'static Class> = None;
if CLASS.is_none() {
let uiwindow_class = class!(UIWindow);
unsafe impl ClassType for WinitViewController {
#[inherits(UIResponder, NSObject)]
type Super = UIViewController;
const NAME: &'static str = "WinitUIViewController";
}
extern "C" fn become_key_window(object: &Object, _: Sel) {
unsafe impl WinitViewController {
#[sel(shouldAutorotate)]
fn should_autorotate(&self) -> bool {
true
}
}
unsafe impl WinitViewController {
#[sel(prefersStatusBarHidden)]
fn prefers_status_bar_hidden(&self) -> bool {
*self._prefers_status_bar_hidden
}
#[sel(setPrefersStatusBarHidden:)]
fn set_prefers_status_bar_hidden(&mut self, val: bool) {
*self._prefers_status_bar_hidden = val;
unsafe {
let _: () = msg_send![self, setNeedsStatusBarAppearanceUpdate];
}
}
#[sel(prefersHomeIndicatorAutoHidden)]
fn prefers_home_indicator_auto_hidden(&self) -> bool {
*self._prefers_home_indicator_auto_hidden
}
#[sel(setPrefersHomeIndicatorAutoHidden:)]
fn set_prefers_home_indicator_auto_hidden(&mut self, val: bool) {
*self._prefers_home_indicator_auto_hidden = val;
let os_capabilities = app_state::os_capabilities();
if os_capabilities.home_indicator_hidden {
unsafe {
let _: () = msg_send![self, setNeedsUpdateOfHomeIndicatorAutoHidden];
}
} else {
os_capabilities.home_indicator_hidden_err_msg("ignoring")
}
}
#[sel(supportedInterfaceOrientations)]
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
*self._supported_orientations
}
#[sel(setSupportedInterfaceOrientations:)]
fn set_supported_orientations(&mut self, val: UIInterfaceOrientationMask) {
*self._supported_orientations = val;
unsafe {
let _: () = msg_send![
UIViewController::class(),
attemptRotationToDeviceOrientation
];
}
}
#[sel(preferredScreenEdgesDeferringSystemGestures)]
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
*self._preferred_screen_edges_deferring_system_gestures
}
#[sel(setPreferredScreenEdgesDeferringSystemGestures:)]
fn set_preferred_screen_edges_deferring_system_gestures(&mut self, val: UIRectEdge) {
*self._preferred_screen_edges_deferring_system_gestures = val;
let os_capabilities = app_state::os_capabilities();
if os_capabilities.defer_system_gestures {
unsafe {
let _: () = msg_send![self, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
} else {
os_capabilities.defer_system_gestures_err_msg("ignoring")
}
}
}
);
declare_class!(
struct WinitUIWindow {}
unsafe impl ClassType for WinitUIWindow {
#[inherits(UIResponder, NSObject)]
type Super = UIWindow;
}
unsafe impl WinitUIWindow {
#[sel(becomeKeyWindow)]
fn become_key_window(&self) {
unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()),
window_id: RootWindowId((&*****self).into()),
event: WindowEvent::Focused(true),
}));
let _: () = msg_send![super(object, class!(UIWindow)), becomeKeyWindow];
let _: () = msg_send![super(self), becomeKeyWindow];
}
}
extern "C" fn resign_key_window(object: &Object, _: Sel) {
#[sel(resignKeyWindow)]
fn resign_key_window(&self) {
unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()),
window_id: RootWindowId((&*****self).into()),
event: WindowEvent::Focused(false),
}));
let _: () = msg_send![super(object, class!(UIWindow)), resignKeyWindow];
let _: () = msg_send![super(self), resignKeyWindow];
}
}
let mut decl = ClassBuilder::new("WinitUIWindow", uiwindow_class)
.expect("Failed to declare class `WinitUIWindow`");
decl.add_method(
sel!(becomeKeyWindow),
become_key_window as extern "C" fn(_, _),
);
decl.add_method(
sel!(resignKeyWindow),
resign_key_window as extern "C" fn(_, _),
);
CLASS = Some(decl.register());
}
CLASS.unwrap()
}
);
// requires main thread
pub(crate) unsafe fn create_view(
@ -449,7 +403,7 @@ pub(crate) unsafe fn create_view_controller(
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: id,
) -> id {
let class = get_view_controller_class();
let class = WinitViewController::class();
let view_controller: id = msg_send![class, alloc];
assert!(
@ -499,9 +453,7 @@ pub(crate) unsafe fn create_window(
frame: CGRect,
view_controller: id,
) -> id {
let class = get_window_class();
let window: id = msg_send![class, alloc];
let window: id = msg_send![WinitUIWindow::class(), alloc];
assert!(!window.is_null(), "Failed to create `UIWindow` instance");
let window: id = msg_send![window, initWithFrame: frame];
assert!(
@ -532,81 +484,61 @@ pub(crate) unsafe fn create_window(
window
}
pub fn create_delegate_class() {
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> Bool {
unsafe {
app_state::did_finish_launching();
}
Bool::YES
declare_class!(
pub struct WinitApplicationDelegate {}
unsafe impl ClassType for WinitApplicationDelegate {
type Super = NSObject;
}
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
}
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
}
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {}
extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows];
let windows_enum: id = msg_send![windows, objectEnumerator];
let mut events = Vec::new();
loop {
let window: id = msg_send![windows_enum, nextObject];
if window == nil {
break;
}
let is_winit_window = msg_send![window, isKindOfClass: class!(WinitUIWindow)];
if is_winit_window {
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Destroyed,
}));
}
// UIApplicationDelegate protocol
unsafe impl WinitApplicationDelegate {
#[sel(application:didFinishLaunchingWithOptions:)]
fn did_finish_launching(&self, _: id, _: id) -> bool {
unsafe {
app_state::did_finish_launching();
}
true
}
#[sel(applicationDidBecomeActive:)]
fn did_become_active(&self, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
}
#[sel(applicationWillResignActive:)]
fn will_resign_active(&self, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
}
#[sel(applicationWillEnterForeground:)]
fn will_enter_foreground(&self, _: id) {}
#[sel(applicationDidEnterBackground:)]
fn did_enter_background(&self, _: id) {}
#[sel(applicationWillTerminate:)]
fn will_terminate(&self, _: id) {
unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows];
let windows_enum: id = msg_send![windows, objectEnumerator];
let mut events = Vec::new();
loop {
let window: id = msg_send![windows_enum, nextObject];
if window == nil {
break;
}
let is_winit_window = msg_send![window, isKindOfClass: WinitUIWindow::class()];
if is_winit_window {
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Destroyed,
}));
}
}
app_state::handle_nonuser_events(events);
app_state::terminated();
}
app_state::handle_nonuser_events(events);
app_state::terminated();
}
}
let ui_responder = class!(UIResponder);
let mut decl = ClassBuilder::new("AppDelegate", ui_responder)
.expect("Failed to declare class `AppDelegate`");
unsafe {
decl.add_method(
sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern "C" fn(_, _, _, _) -> _,
);
decl.add_method(
sel!(applicationDidBecomeActive:),
did_become_active as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillResignActive:),
will_resign_active as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillEnterForeground:),
will_enter_foreground as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationDidEnterBackground:),
did_enter_background as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillTerminate:),
will_terminate as extern "C" fn(_, _, _),
);
decl.register();
}
}
);

View file

@ -3,7 +3,8 @@ use std::{
ops::{Deref, DerefMut},
};
use objc::runtime::{Class, Object};
use objc2::runtime::{Class, Object};
use objc2::{class, msg_send};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
use crate::{