2019-05-26 11:10:41 +10:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
use objc::{
|
|
|
|
declare::ClassDecl,
|
|
|
|
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
2019-06-22 01:33:15 +10:00
|
|
|
|
|
|
|
use crate::{
|
2019-08-14 08:12:13 +10:00
|
|
|
event::{DeviceId as RootDeviceId, Event, Force, Touch, TouchPhase, WindowEvent},
|
2019-06-22 01:33:15 +10:00
|
|
|
platform::ios::MonitorHandleExtIOS,
|
2019-07-30 04:16:14 +10:00
|
|
|
platform_impl::platform::{
|
2019-09-05 07:23:11 +10:00
|
|
|
app_state::{self, OSCapabilities},
|
2019-10-19 02:31:26 +11:00
|
|
|
event_loop::{self, EventProxy, EventWrapper},
|
2019-07-31 16:57:31 +10:00
|
|
|
ffi::{
|
2019-08-14 08:12:13 +10:00
|
|
|
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
|
|
|
|
UIRectEdge, UITouchPhase, UITouchType,
|
2019-07-31 16:57:31 +10:00
|
|
|
},
|
2019-07-30 04:16:14 +10:00
|
|
|
window::PlatformSpecificWindowBuilderAttributes,
|
|
|
|
DeviceId,
|
|
|
|
},
|
|
|
|
window::{Fullscreen, WindowAttributes, WindowId as RootWindowId},
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
|
|
|
|
2019-07-31 16:57:31 +10:00
|
|
|
macro_rules! add_property {
|
|
|
|
(
|
|
|
|
$decl:ident,
|
|
|
|
$name:ident: $t:ty,
|
|
|
|
$setter_name:ident: |$object:ident| $after_set:expr,
|
|
|
|
$getter_name:ident,
|
2019-08-27 08:47:23 +10:00
|
|
|
) => {
|
|
|
|
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,
|
2019-07-31 16:57:31 +10:00
|
|
|
) => {
|
|
|
|
{
|
|
|
|
const VAR_NAME: &'static str = concat!("_", stringify!($name));
|
|
|
|
$decl.add_ivar::<$t>(VAR_NAME);
|
2019-08-27 08:47:23 +10:00
|
|
|
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
|
2019-07-31 16:57:31 +10:00
|
|
|
}
|
2019-08-27 08:47:23 +10:00
|
|
|
$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
|
|
|
|
};
|
2019-07-31 16:57:31 +10:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
|
|
|
|
unsafe { *$object.get_ivar::<$t>(VAR_NAME) }
|
|
|
|
}
|
|
|
|
$decl.add_method(
|
|
|
|
sel!($setter_name:),
|
2019-08-27 08:47:23 +10:00
|
|
|
setter as extern "C" fn(&mut Object, Sel, $t),
|
2019-07-31 16:57:31 +10:00
|
|
|
);
|
|
|
|
$decl.add_method(
|
|
|
|
sel!($getter_name),
|
|
|
|
$getter_name as extern "C" fn(&Object, Sel) -> $t,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
// 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;
|
|
|
|
static mut ID: usize = 0;
|
2019-05-30 11:29:54 +10:00
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
if CLASSES.is_none() {
|
|
|
|
CLASSES = Some(HashMap::default());
|
|
|
|
}
|
|
|
|
|
|
|
|
let classes = CLASSES.as_mut().unwrap();
|
|
|
|
|
|
|
|
classes.entry(root_view_class).or_insert_with(move || {
|
|
|
|
let uiview_class = class!(UIView);
|
2019-06-22 01:33:15 +10:00
|
|
|
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class];
|
|
|
|
assert_eq!(
|
|
|
|
is_uiview, YES,
|
|
|
|
"`root_view_class` must inherit from `UIView`"
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
|
|
|
let window: id = msg_send![object, window];
|
2019-09-17 04:27:46 +10:00
|
|
|
assert!(!window.is_null());
|
2019-12-22 11:35:18 +11:00
|
|
|
app_state::handle_nonuser_events(
|
2020-01-04 17:33:07 +11:00
|
|
|
std::iter::once(EventWrapper::StaticEvent(Event::RedrawRequested(
|
|
|
|
RootWindowId(window.into()),
|
|
|
|
)))
|
|
|
|
.chain(std::iter::once(EventWrapper::StaticEvent(
|
|
|
|
Event::RedrawEventsCleared,
|
|
|
|
))),
|
2019-12-22 11:35:18 +11:00
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
let superclass: &'static Class = msg_send![object, superclass];
|
|
|
|
let () = msg_send![super(object, superclass), drawRect: rect];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn layout_subviews(object: &Object, _: Sel) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-09-05 07:23:11 +10:00
|
|
|
let superclass: &'static Class = msg_send![object, superclass];
|
|
|
|
let () = msg_send![super(object, superclass), layoutSubviews];
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
let window: id = msg_send![object, window];
|
2019-09-17 04:27:46 +10:00
|
|
|
assert!(!window.is_null());
|
2019-05-26 11:10:41 +10:00
|
|
|
let bounds: CGRect = msg_send![window, bounds];
|
|
|
|
let screen: id = msg_send![window, screen];
|
|
|
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
2019-06-22 01:33:15 +10:00
|
|
|
let screen_frame: CGRect =
|
|
|
|
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
|
2019-10-19 02:31:26 +11:00
|
|
|
let dpi_factor: CGFloat = msg_send![screen, scale];
|
2019-05-26 11:10:41 +10:00
|
|
|
let size = crate::dpi::LogicalSize {
|
2020-01-04 17:33:07 +11:00
|
|
|
width: screen_frame.size.width as f64,
|
|
|
|
height: screen_frame.size.height as f64,
|
2019-10-19 02:31:26 +11:00
|
|
|
}
|
|
|
|
.to_physical(dpi_factor.into());
|
|
|
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
2019-05-26 11:10:41 +10:00
|
|
|
window_id: RootWindowId(window.into()),
|
|
|
|
event: WindowEvent::Resized(size),
|
2019-10-19 02:31:26 +11:00
|
|
|
}));
|
2019-09-05 07:23:11 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn set_content_scale_factor(
|
|
|
|
object: &mut Object,
|
|
|
|
_: Sel,
|
2020-01-04 06:52:27 +11:00
|
|
|
untrusted_scale_factor: CGFloat,
|
2019-09-05 07:23:11 +10:00
|
|
|
) {
|
|
|
|
unsafe {
|
2019-05-26 11:10:41 +10:00
|
|
|
let superclass: &'static Class = msg_send![object, superclass];
|
2019-09-05 07:23:11 +10:00
|
|
|
let () = msg_send![
|
|
|
|
super(object, superclass),
|
2020-01-04 06:52:27 +11:00
|
|
|
setContentScaleFactor: untrusted_scale_factor
|
2019-09-05 07:23:11 +10:00
|
|
|
];
|
|
|
|
|
2019-09-17 04:27:46 +10:00
|
|
|
let window: id = msg_send![object, window];
|
|
|
|
// `window` is null when `setContentScaleFactor` is invoked prior to `[UIWindow
|
|
|
|
// makeKeyAndVisible]` at window creation time (either manually or internally by
|
|
|
|
// UIKit when the `UIView` is first created), in which case we send no events here
|
|
|
|
if window.is_null() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// `setContentScaleFactor` may be called with a value of 0, which means "reset the
|
|
|
|
// content scale factor to a device-specific default value", so we can't use the
|
|
|
|
// parameter here. We can query the actual factor using the getter
|
2019-10-19 02:31:26 +11:00
|
|
|
let dpi_factor: CGFloat = msg_send![object, contentScaleFactor];
|
2019-09-05 07:23:11 +10:00
|
|
|
assert!(
|
2019-10-19 02:31:26 +11:00
|
|
|
!dpi_factor.is_nan()
|
|
|
|
&& dpi_factor.is_finite()
|
|
|
|
&& dpi_factor.is_sign_positive()
|
|
|
|
&& dpi_factor > 0.0,
|
2020-01-04 06:52:27 +11:00
|
|
|
"invalid scale_factor set on UIView",
|
2019-09-05 07:23:11 +10:00
|
|
|
);
|
2020-01-04 06:52:27 +11:00
|
|
|
let scale_factor: f64 = dpi_factor.into();
|
2019-09-05 07:23:11 +10:00
|
|
|
let bounds: CGRect = msg_send![object, bounds];
|
|
|
|
let screen: id = msg_send![window, screen];
|
|
|
|
let screen_space: id = msg_send![screen, coordinateSpace];
|
|
|
|
let screen_frame: CGRect =
|
|
|
|
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
|
|
|
|
let size = crate::dpi::LogicalSize {
|
|
|
|
width: screen_frame.size.width as _,
|
|
|
|
height: screen_frame.size.height as _,
|
|
|
|
};
|
|
|
|
app_state::handle_nonuser_events(
|
2020-01-04 06:52:27 +11:00
|
|
|
std::iter::once(EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
|
|
|
|
window_id: window,
|
|
|
|
scale_factor,
|
|
|
|
suggested_size: size,
|
|
|
|
}))
|
2019-10-19 02:31:26 +11:00
|
|
|
.chain(std::iter::once(EventWrapper::StaticEvent(
|
|
|
|
Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(window.into()),
|
2020-01-04 06:52:27 +11:00
|
|
|
event: WindowEvent::Resized(size.to_physical(scale_factor)),
|
2019-10-19 02:31:26 +11:00
|
|
|
},
|
|
|
|
))),
|
2019-09-05 07:23:11 +10:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
|
|
|
|
unsafe {
|
|
|
|
let window: id = msg_send![object, window];
|
2019-09-17 04:27:46 +10:00
|
|
|
assert!(!window.is_null());
|
2019-09-05 07:23:11 +10:00
|
|
|
let uiscreen: id = msg_send![window, screen];
|
|
|
|
let touches_enum: id = msg_send![touches, objectEnumerator];
|
|
|
|
let mut touch_events = Vec::new();
|
|
|
|
let os_supports_force = app_state::os_capabilities().force_touch;
|
|
|
|
loop {
|
|
|
|
let touch: id = msg_send![touches_enum, nextObject];
|
|
|
|
if touch == nil {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let location: CGPoint = msg_send![touch, locationInView: nil];
|
|
|
|
let touch_type: UITouchType = msg_send![touch, type];
|
|
|
|
let force = if os_supports_force {
|
|
|
|
let trait_collection: id = msg_send![object, traitCollection];
|
|
|
|
let touch_capability: UIForceTouchCapability =
|
|
|
|
msg_send![trait_collection, forceTouchCapability];
|
|
|
|
// Both the OS _and_ the device need to be checked for force touch support.
|
|
|
|
if touch_capability == UIForceTouchCapability::Available {
|
|
|
|
let force: CGFloat = msg_send![touch, force];
|
|
|
|
let max_possible_force: CGFloat =
|
|
|
|
msg_send![touch, maximumPossibleForce];
|
|
|
|
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
|
|
|
let angle: CGFloat = msg_send![touch, altitudeAngle];
|
|
|
|
Some(angle as _)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
Some(Force::Calibrated {
|
|
|
|
force: force as _,
|
|
|
|
max_possible_force: max_possible_force as _,
|
|
|
|
altitude_angle,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let touch_id = touch as u64;
|
|
|
|
let phase: UITouchPhase = msg_send![touch, phase];
|
|
|
|
let phase = match phase {
|
|
|
|
UITouchPhase::Began => TouchPhase::Started,
|
|
|
|
UITouchPhase::Moved => TouchPhase::Moved,
|
|
|
|
// 2 is UITouchPhase::Stationary and is not expected here
|
|
|
|
UITouchPhase::Ended => TouchPhase::Ended,
|
|
|
|
UITouchPhase::Cancelled => TouchPhase::Cancelled,
|
|
|
|
_ => panic!("unexpected touch phase: {:?}", phase as i32),
|
|
|
|
};
|
|
|
|
|
2019-10-19 02:31:26 +11:00
|
|
|
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
2019-09-05 07:23:11 +10:00
|
|
|
window_id: RootWindowId(window.into()),
|
|
|
|
event: WindowEvent::Touch(Touch {
|
|
|
|
device_id: RootDeviceId(DeviceId { uiscreen }),
|
|
|
|
id: touch_id,
|
|
|
|
location: (location.x as f64, location.y as f64).into(),
|
|
|
|
force,
|
|
|
|
phase,
|
|
|
|
}),
|
2019-10-19 02:31:26 +11:00
|
|
|
}));
|
2019-09-05 07:23:11 +10:00
|
|
|
}
|
|
|
|
app_state::handle_nonuser_events(touch_events);
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut decl = ClassDecl::new(&format!("WinitUIView{}", ID), root_view_class)
|
|
|
|
.expect("Failed to declare class `WinitUIView`");
|
|
|
|
ID += 1;
|
2019-06-22 01:33:15 +10:00
|
|
|
decl.add_method(
|
|
|
|
sel!(drawRect:),
|
|
|
|
draw_rect as extern "C" fn(&Object, Sel, CGRect),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(layoutSubviews),
|
|
|
|
layout_subviews as extern "C" fn(&Object, Sel),
|
|
|
|
);
|
2019-09-05 07:23:11 +10:00
|
|
|
decl.add_method(
|
|
|
|
sel!(setContentScaleFactor:),
|
|
|
|
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
|
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(touchesBegan:withEvent:),
|
|
|
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(touchesMoved:withEvent:),
|
|
|
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(touchesEnded:withEvent:),
|
|
|
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(touchesCancelled:withEvent:),
|
|
|
|
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
|
|
|
|
);
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
decl.register()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
unsafe fn get_view_controller_class() -> &'static Class {
|
|
|
|
static mut CLASS: Option<&'static Class> = None;
|
|
|
|
if CLASS.is_none() {
|
2019-08-27 08:47:23 +10:00
|
|
|
let os_capabilities = app_state::os_capabilities();
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
let uiviewcontroller_class = class!(UIViewController);
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn should_autorotate(_: &Object, _: Sel) -> BOOL {
|
2019-05-26 11:10:41 +10:00
|
|
|
YES
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut decl = ClassDecl::new("WinitUIViewController", uiviewcontroller_class)
|
|
|
|
.expect("Failed to declare class `WinitUIViewController`");
|
2019-06-22 01:33:15 +10:00
|
|
|
decl.add_method(
|
|
|
|
sel!(shouldAutorotate),
|
|
|
|
should_autorotate as extern "C" fn(&Object, Sel) -> BOOL,
|
|
|
|
);
|
2019-07-31 16:57:31 +10:00
|
|
|
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,
|
2019-08-27 08:47:23 +10:00
|
|
|
setPrefersHomeIndicatorAutoHidden:
|
|
|
|
os_capabilities.home_indicator_hidden,
|
|
|
|
OSCapabilities::home_indicator_hidden_err_msg;
|
|
|
|
|object| {
|
|
|
|
unsafe {
|
|
|
|
let () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden];
|
|
|
|
}
|
|
|
|
},
|
2019-07-31 16:57:31 +10:00
|
|
|
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,
|
2019-08-27 08:47:23 +10:00
|
|
|
setPreferredScreenEdgesDeferringSystemGestures:
|
|
|
|
os_capabilities.defer_system_gestures,
|
|
|
|
OSCapabilities::defer_system_gestures_err_msg;
|
|
|
|
|object| {
|
|
|
|
unsafe {
|
|
|
|
let () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
|
|
|
}
|
|
|
|
},
|
2019-07-31 16:57:31 +10:00
|
|
|
preferredScreenEdgesDeferringSystemGestures,
|
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
CLASS = Some(decl.register());
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn become_key_window(object: &Object, _: Sel) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-10-19 02:31:26 +11:00
|
|
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
2019-05-26 11:10:41 +10:00
|
|
|
window_id: RootWindowId(object.into()),
|
|
|
|
event: WindowEvent::Focused(true),
|
2019-10-19 02:31:26 +11:00
|
|
|
}));
|
2019-05-26 11:10:41 +10:00
|
|
|
let () = msg_send![super(object, class!(UIWindow)), becomeKeyWindow];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn resign_key_window(object: &Object, _: Sel) {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-10-19 02:31:26 +11:00
|
|
|
app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
2019-05-26 11:10:41 +10:00
|
|
|
window_id: RootWindowId(object.into()),
|
|
|
|
event: WindowEvent::Focused(false),
|
2019-10-19 02:31:26 +11:00
|
|
|
}));
|
2019-05-26 11:10:41 +10:00
|
|
|
let () = msg_send![super(object, class!(UIWindow)), resignKeyWindow];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class)
|
|
|
|
.expect("Failed to declare class `WinitUIWindow`");
|
2019-06-22 01:33:15 +10:00
|
|
|
decl.add_method(
|
|
|
|
sel!(becomeKeyWindow),
|
|
|
|
become_key_window as extern "C" fn(&Object, Sel),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(resignKeyWindow),
|
|
|
|
resign_key_window as extern "C" fn(&Object, Sel),
|
|
|
|
);
|
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
CLASS = Some(decl.register());
|
|
|
|
}
|
|
|
|
CLASS.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn create_view(
|
2019-05-30 11:29:54 +10:00
|
|
|
_window_attributes: &WindowAttributes,
|
2019-05-26 11:10:41 +10:00
|
|
|
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
|
|
|
frame: CGRect,
|
|
|
|
) -> id {
|
|
|
|
let class = get_view_class(platform_attributes.root_view_class);
|
|
|
|
|
|
|
|
let view: id = msg_send![class, alloc];
|
|
|
|
assert!(!view.is_null(), "Failed to create `UIView` instance");
|
2019-06-22 01:33:15 +10:00
|
|
|
let view: id = msg_send![view, initWithFrame: frame];
|
2019-05-26 11:10:41 +10:00
|
|
|
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![view, setMultipleTouchEnabled: YES];
|
2020-01-04 06:52:27 +11:00
|
|
|
if let Some(scale_factor) = platform_attributes.scale_factor {
|
|
|
|
let () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat];
|
2019-09-05 07:23:11 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
|
|
|
|
view
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn create_view_controller(
|
2019-08-09 09:10:54 +10:00
|
|
|
_window_attributes: &WindowAttributes,
|
2019-05-26 11:10:41 +10:00
|
|
|
platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
|
|
|
view: id,
|
|
|
|
) -> id {
|
|
|
|
let class = get_view_controller_class();
|
|
|
|
|
|
|
|
let view_controller: id = msg_send![class, alloc];
|
2019-06-22 01:33:15 +10:00
|
|
|
assert!(
|
|
|
|
!view_controller.is_null(),
|
|
|
|
"Failed to create `UIViewController` instance"
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
let view_controller: id = msg_send![view_controller, init];
|
2019-06-22 01:33:15 +10:00
|
|
|
assert!(
|
|
|
|
!view_controller.is_null(),
|
|
|
|
"Failed to initialize `UIViewController` instance"
|
|
|
|
);
|
2019-08-09 09:10:54 +10:00
|
|
|
let status_bar_hidden = if platform_attributes.prefers_status_bar_hidden {
|
2019-05-26 11:10:41 +10:00
|
|
|
YES
|
2019-08-09 09:10:54 +10:00
|
|
|
} else {
|
|
|
|
NO
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
|
|
|
let idiom = event_loop::get_idiom();
|
|
|
|
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
|
|
|
|
platform_attributes.valid_orientations,
|
|
|
|
idiom,
|
|
|
|
);
|
2019-07-31 16:57:31 +10:00
|
|
|
let prefers_home_indicator_hidden = if platform_attributes.prefers_home_indicator_hidden {
|
|
|
|
YES
|
|
|
|
} else {
|
|
|
|
NO
|
|
|
|
};
|
|
|
|
let edges: UIRectEdge = platform_attributes
|
|
|
|
.preferred_screen_edges_deferring_system_gestures
|
|
|
|
.into();
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![
|
|
|
|
view_controller,
|
|
|
|
setPrefersStatusBarHidden: status_bar_hidden
|
|
|
|
];
|
|
|
|
let () = msg_send![
|
|
|
|
view_controller,
|
|
|
|
setSupportedInterfaceOrientations: supported_orientations
|
|
|
|
];
|
2019-07-31 16:57:31 +10:00
|
|
|
let () = msg_send![
|
|
|
|
view_controller,
|
|
|
|
setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden
|
|
|
|
];
|
|
|
|
let () = msg_send![
|
|
|
|
view_controller,
|
|
|
|
setPreferredScreenEdgesDeferringSystemGestures: edges
|
|
|
|
];
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![view_controller, setView: view];
|
2019-05-26 11:10:41 +10:00
|
|
|
view_controller
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn create_window(
|
|
|
|
window_attributes: &WindowAttributes,
|
2019-09-05 07:23:11 +10:00
|
|
|
_platform_attributes: &PlatformSpecificWindowBuilderAttributes,
|
2019-05-26 11:10:41 +10:00
|
|
|
frame: CGRect,
|
|
|
|
view_controller: id,
|
|
|
|
) -> id {
|
|
|
|
let class = get_window_class();
|
|
|
|
|
|
|
|
let window: id = msg_send![class, alloc];
|
|
|
|
assert!(!window.is_null(), "Failed to create `UIWindow` instance");
|
2019-06-22 01:33:15 +10:00
|
|
|
let window: id = msg_send![window, initWithFrame: frame];
|
|
|
|
assert!(
|
|
|
|
!window.is_null(),
|
|
|
|
"Failed to initialize `UIWindow` instance"
|
|
|
|
);
|
|
|
|
let () = msg_send![window, setRootViewController: view_controller];
|
2019-07-30 04:16:14 +10:00
|
|
|
match window_attributes.fullscreen {
|
2019-07-31 16:57:31 +10:00
|
|
|
Some(Fullscreen::Exclusive(ref video_mode)) => {
|
|
|
|
let uiscreen = video_mode.monitor().ui_screen() as id;
|
2019-12-22 19:39:22 +11:00
|
|
|
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0];
|
2019-07-31 16:57:31 +10:00
|
|
|
msg_send![window, setScreen:video_mode.monitor().ui_screen()]
|
|
|
|
}
|
2020-01-04 17:33:07 +11:00
|
|
|
Some(Fullscreen::Borderless(ref monitor)) => {
|
|
|
|
msg_send![window, setScreen:monitor.ui_screen()]
|
|
|
|
}
|
2019-07-30 04:16:14 +10:00
|
|
|
None => (),
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
window
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_delegate_class() {
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-09-05 07:23:11 +10:00
|
|
|
app_state::did_finish_launching();
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
YES
|
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
|
2019-10-19 02:31:26 +11:00
|
|
|
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) }
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
|
2019-10-19 02:31:26 +11:00
|
|
|
unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) }
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
|
|
|
|
extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {}
|
2019-05-26 11:10:41 +10:00
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
|
2019-05-26 11:10:41 +10:00
|
|
|
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 {
|
2019-06-22 01:33:15 +10:00
|
|
|
break;
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
let is_winit_window: BOOL = msg_send![window, isKindOfClass: class!(WinitUIWindow)];
|
2019-05-26 11:10:41 +10:00
|
|
|
if is_winit_window == YES {
|
2019-10-19 02:31:26 +11:00
|
|
|
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
2019-05-26 11:10:41 +10:00
|
|
|
window_id: RootWindowId(window.into()),
|
|
|
|
event: WindowEvent::Destroyed,
|
2019-10-19 02:31:26 +11:00
|
|
|
}));
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
2019-09-05 07:23:11 +10:00
|
|
|
app_state::handle_nonuser_events(events);
|
|
|
|
app_state::terminated();
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let ui_responder = class!(UIResponder);
|
2019-06-22 01:33:15 +10:00
|
|
|
let mut decl =
|
|
|
|
ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
|
2019-05-26 11:10:41 +10:00
|
|
|
|
|
|
|
unsafe {
|
2019-06-22 01:33:15 +10:00
|
|
|
decl.add_method(
|
|
|
|
sel!(application:didFinishLaunchingWithOptions:),
|
|
|
|
did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL,
|
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationDidBecomeActive:),
|
|
|
|
did_become_active as extern "C" fn(&Object, Sel, id),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationWillResignActive:),
|
|
|
|
will_resign_active as extern "C" fn(&Object, Sel, id),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationWillEnterForeground:),
|
|
|
|
will_enter_foreground as extern "C" fn(&Object, Sel, id),
|
|
|
|
);
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationDidEnterBackground:),
|
|
|
|
did_enter_background as extern "C" fn(&Object, Sel, id),
|
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationWillTerminate:),
|
|
|
|
will_terminate as extern "C" fn(&Object, Sel, id),
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
|
|
|
|
decl.register();
|
|
|
|
}
|
|
|
|
}
|