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" ndk-glue = "0.7.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] [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] [target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9.3" core-foundation = "0.9.3"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,8 @@ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use objc::foundation::{NSInteger, NSUInteger}; use objc2::foundation::{NSInteger, NSUInteger};
use objc2::{class, msg_send};
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize}, 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 std::collections::HashMap;
use objc::{ use objc2::declare::ClassBuilder;
declare::ClassBuilder, use objc2::foundation::NSObject;
runtime::{Bool, Class, Object, Sel}, use objc2::runtime::{Bool, Class, Object, Sel};
}; use objc2::{class, declare_class, msg_send, sel, ClassType};
use super::uikit::{UIResponder, UIViewController, UIWindow};
use crate::{ use crate::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent}, event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
platform::ios::MonitorHandleExtIOS, platform::ios::MonitorHandleExtIOS,
platform_impl::platform::{ platform_impl::platform::{
app_state::{self, OSCapabilities}, app_state,
event_loop::{self, EventProxy, EventWrapper}, event_loop::{self, EventProxy, EventWrapper},
ffi::{ ffi::{
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask, id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
@ -22,64 +23,6 @@ use crate::{
window::{Fullscreen, WindowAttributes, WindowId as RootWindowId}, 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 // requires main thread
unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
static mut CLASSES: Option<HashMap<*const Class, &'static Class>> = None; 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 declare_class!(
unsafe fn get_view_controller_class() -> &'static Class { struct WinitViewController {
static mut CLASS: Option<&'static Class> = None; _prefers_status_bar_hidden: bool,
if CLASS.is_none() { _prefers_home_indicator_auto_hidden: bool,
_supported_orientations: UIInterfaceOrientationMask,
_preferred_screen_edges_deferring_system_gestures: UIRectEdge,
}
unsafe impl ClassType for WinitViewController {
#[inherits(UIResponder, NSObject)]
type Super = UIViewController;
const NAME: &'static str = "WinitUIViewController";
}
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(); let os_capabilities = app_state::os_capabilities();
if os_capabilities.home_indicator_hidden {
let uiviewcontroller_class = class!(UIViewController); unsafe {
let _: () = msg_send![self, setNeedsUpdateOfHomeIndicatorAutoHidden];
extern "C" fn should_autorotate(_: &Object, _: Sel) -> Bool { }
Bool::YES } else {
os_capabilities.home_indicator_hidden_err_msg("ignoring")
}
} }
let mut decl = ClassBuilder::new("WinitUIViewController", uiviewcontroller_class) #[sel(supportedInterfaceOrientations)]
.expect("Failed to declare class `WinitUIViewController`"); fn supported_orientations(&self) -> UIInterfaceOrientationMask {
decl.add_method( *self._supported_orientations
sel!(shouldAutorotate), }
should_autorotate as extern "C" fn(_, _) -> _,
#[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")
}
}
}
); );
add_property! {
decl, declare_class!(
prefers_status_bar_hidden: Bool, struct WinitUIWindow {}
setPrefersStatusBarHidden: |object| {
unsafe { unsafe impl ClassType for WinitUIWindow {
let _: () = msg_send![object, setNeedsStatusBarAppearanceUpdate]; #[inherits(UIResponder, NSObject)]
} type Super = UIWindow;
},
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());
}
CLASS.unwrap()
} }
// requires main thread unsafe impl WinitUIWindow {
unsafe fn get_window_class() -> &'static Class { #[sel(becomeKeyWindow)]
static mut CLASS: Option<&'static Class> = None; fn become_key_window(&self) {
if CLASS.is_none() {
let uiwindow_class = class!(UIWindow);
extern "C" fn become_key_window(object: &Object, _: Sel) {
unsafe { unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()), window_id: RootWindowId((&*****self).into()),
event: WindowEvent::Focused(true), 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 { unsafe {
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(object.into()), window_id: RootWindowId((&*****self).into()),
event: WindowEvent::Focused(false), 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 // requires main thread
pub(crate) unsafe fn create_view( pub(crate) unsafe fn create_view(
@ -449,7 +403,7 @@ pub(crate) unsafe fn create_view_controller(
platform_attributes: &PlatformSpecificWindowBuilderAttributes, platform_attributes: &PlatformSpecificWindowBuilderAttributes,
view: id, view: id,
) -> id { ) -> id {
let class = get_view_controller_class(); let class = WinitViewController::class();
let view_controller: id = msg_send![class, alloc]; let view_controller: id = msg_send![class, alloc];
assert!( assert!(
@ -499,9 +453,7 @@ pub(crate) unsafe fn create_window(
frame: CGRect, frame: CGRect,
view_controller: id, view_controller: id,
) -> id { ) -> id {
let class = get_window_class(); let window: id = msg_send![WinitUIWindow::class(), alloc];
let window: id = msg_send![class, alloc];
assert!(!window.is_null(), "Failed to create `UIWindow` instance"); assert!(!window.is_null(), "Failed to create `UIWindow` instance");
let window: id = msg_send![window, initWithFrame: frame]; let window: id = msg_send![window, initWithFrame: frame];
assert!( assert!(
@ -532,26 +484,40 @@ pub(crate) unsafe fn create_window(
window window
} }
pub fn create_delegate_class() { declare_class!(
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> Bool { pub struct WinitApplicationDelegate {}
unsafe impl ClassType for WinitApplicationDelegate {
type Super = NSObject;
}
// UIApplicationDelegate protocol
unsafe impl WinitApplicationDelegate {
#[sel(application:didFinishLaunchingWithOptions:)]
fn did_finish_launching(&self, _: id, _: id) -> bool {
unsafe { unsafe {
app_state::did_finish_launching(); app_state::did_finish_launching();
} }
Bool::YES true
} }
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) { #[sel(applicationDidBecomeActive:)]
fn did_become_active(&self, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) } unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
} }
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) { #[sel(applicationWillResignActive:)]
fn will_resign_active(&self, _: id) {
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) } unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
} }
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {} #[sel(applicationWillEnterForeground:)]
extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {} fn will_enter_foreground(&self, _: id) {}
#[sel(applicationDidEnterBackground:)]
fn did_enter_background(&self, _: id) {}
extern "C" fn will_terminate(_: &Object, _: Sel, _: id) { #[sel(applicationWillTerminate:)]
fn will_terminate(&self, _: id) {
unsafe { unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication]; let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows]; let windows: id = msg_send![app, windows];
@ -562,7 +528,7 @@ pub fn create_delegate_class() {
if window == nil { if window == nil {
break; break;
} }
let is_winit_window = msg_send![window, isKindOfClass: class!(WinitUIWindow)]; let is_winit_window = msg_send![window, isKindOfClass: WinitUIWindow::class()];
if is_winit_window { if is_winit_window {
events.push(EventWrapper::StaticEvent(Event::WindowEvent { events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()), window_id: RootWindowId(window.into()),
@ -574,39 +540,5 @@ pub fn create_delegate_class() {
app_state::terminated(); 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}, 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 raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
use crate::{ use crate::{