mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 13:31:29 +11:00
iOS os version checking around certain APIs (#1094)
* iOS os version checking * iOS, fix some incorrect msg_send return types * address nits, and fix OS version check for unsupported os versions * source for 60fps guarantee
This commit is contained in:
parent
7b707e7d75
commit
f53683f01f
|
@ -29,6 +29,7 @@
|
||||||
- On iOS, add touch pressure information for touch events.
|
- On iOS, add touch pressure information for touch events.
|
||||||
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
|
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
|
||||||
- On macOS, fix the signature of `-[NSView drawRect:]`.
|
- On macOS, fix the signature of `-[NSView drawRect:]`.
|
||||||
|
- On iOS, fix improper `msg_send` usage that was UB and/or would break if `!` is stabilized.
|
||||||
|
|
||||||
# 0.20.0 Alpha 2 (2019-07-09)
|
# 0.20.0 Alpha 2 (2019-07-09)
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||||
* Base window size
|
* Base window size
|
||||||
|
|
||||||
### iOS
|
### iOS
|
||||||
|
* `winit` has a minimum OS requirement of iOS 8
|
||||||
* Get the `UIWindow` object pointer
|
* Get the `UIWindow` object pointer
|
||||||
* Get the `UIViewController` object pointer
|
* Get the `UIViewController` object pointer
|
||||||
* Get the `UIView` object pointer
|
* Get the `UIView` object pointer
|
||||||
|
|
|
@ -329,7 +329,7 @@ pub struct Touch {
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - Only available on **iOS**.
|
/// - Only available on **iOS** 9.0+.
|
||||||
pub force: Option<Force>,
|
pub force: Option<Force>,
|
||||||
/// Unique identifier of a finger.
|
/// Unique identifier of a finger.
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
|
|
|
@ -70,6 +70,8 @@ pub trait WindowExtIOS {
|
||||||
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc),
|
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc),
|
||||||
/// and then calls
|
/// and then calls
|
||||||
/// [`-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc).
|
/// [`-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc).
|
||||||
|
///
|
||||||
|
/// This only has an effect on iOS 11.0+.
|
||||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool);
|
fn set_prefers_home_indicator_hidden(&self, hidden: bool);
|
||||||
|
|
||||||
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
||||||
|
@ -79,6 +81,8 @@ pub trait WindowExtIOS {
|
||||||
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc),
|
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc),
|
||||||
/// and then calls
|
/// and then calls
|
||||||
/// [`-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc).
|
/// [`-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc).
|
||||||
|
///
|
||||||
|
/// This only has an effect on iOS 11.0+.
|
||||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge);
|
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge);
|
||||||
|
|
||||||
/// Sets whether the [`Window`] prefers the status bar hidden.
|
/// Sets whether the [`Window`] prefers the status bar hidden.
|
||||||
|
@ -167,6 +171,8 @@ pub trait WindowBuilderExtIOS {
|
||||||
///
|
///
|
||||||
/// This sets the initial value returned by
|
/// This sets the initial value returned by
|
||||||
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
|
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
|
||||||
|
///
|
||||||
|
/// This only has an effect on iOS 11.0+.
|
||||||
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> WindowBuilder;
|
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> WindowBuilder;
|
||||||
|
|
||||||
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
||||||
|
@ -174,6 +180,8 @@ pub trait WindowBuilderExtIOS {
|
||||||
///
|
///
|
||||||
/// This sets the initial value returned by
|
/// This sets the initial value returned by
|
||||||
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
|
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
|
||||||
|
///
|
||||||
|
/// This only has an effect on iOS 11.0+.
|
||||||
fn with_preferred_screen_edges_deferring_system_gestures(
|
fn with_preferred_screen_edges_deferring_system_gestures(
|
||||||
self,
|
self,
|
||||||
edges: ScreenEdge,
|
edges: ScreenEdge,
|
||||||
|
|
|
@ -6,6 +6,8 @@ use std::{
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use objc::runtime::{BOOL, YES};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::{Event, StartCause},
|
event::{Event, StartCause},
|
||||||
event_loop::ControlFlow,
|
event_loop::ControlFlow,
|
||||||
|
@ -16,7 +18,8 @@ use crate::platform_impl::platform::{
|
||||||
ffi::{
|
ffi::{
|
||||||
id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
|
id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
|
||||||
CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
||||||
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, NSUInteger,
|
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, NSInteger, NSOperatingSystemVersion,
|
||||||
|
NSUInteger,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +129,7 @@ impl AppState {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
queued_windows.push(window);
|
queued_windows.push(window);
|
||||||
msg_send![window, retain];
|
let _: id = msg_send![window, retain];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
&mut AppStateImpl::ProcessingEvents { .. } => {}
|
&mut AppStateImpl::ProcessingEvents { .. } => {}
|
||||||
|
@ -199,7 +202,7 @@ impl AppState {
|
||||||
// completed. This may result in incorrect visual appearance.
|
// completed. This may result in incorrect visual appearance.
|
||||||
// ```
|
// ```
|
||||||
let screen: id = msg_send![window, screen];
|
let screen: id = msg_send![window, screen];
|
||||||
let () = msg_send![screen, retain];
|
let _: id = msg_send![screen, retain];
|
||||||
let () = msg_send![window, setScreen:0 as id];
|
let () = msg_send![window, setScreen:0 as id];
|
||||||
let () = msg_send![window, setScreen: screen];
|
let () = msg_send![window, setScreen: screen];
|
||||||
let () = msg_send![screen, release];
|
let () = msg_send![screen, release];
|
||||||
|
@ -618,3 +621,95 @@ impl EventLoopWaker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! os_capabilities {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$error_name:ident: $objc_call:literal,
|
||||||
|
$name:ident: $major:literal-$minor:literal
|
||||||
|
),*
|
||||||
|
$(,)*
|
||||||
|
) => {
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct OSCapabilities {
|
||||||
|
$(
|
||||||
|
pub $name: bool,
|
||||||
|
)*
|
||||||
|
|
||||||
|
os_version: NSOperatingSystemVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NSOperatingSystemVersion> for OSCapabilities {
|
||||||
|
fn from(os_version: NSOperatingSystemVersion) -> OSCapabilities {
|
||||||
|
$(let $name = os_version.meets_requirements($major, $minor);)*
|
||||||
|
OSCapabilities { $($name,)* os_version, }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OSCapabilities {$(
|
||||||
|
$(#[$attr])*
|
||||||
|
pub fn $error_name(&self, extra_msg: &str) {
|
||||||
|
log::warn!(
|
||||||
|
concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"),
|
||||||
|
$major, $minor, self.os_version.major, self.os_version.minor, self.os_version.patch,
|
||||||
|
extra_msg
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
os_capabilities! {
|
||||||
|
/// https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
|
||||||
|
#[allow(unused)] // error message unused
|
||||||
|
safe_area_err_msg: "-[UIView safeAreaInsets]",
|
||||||
|
safe_area: 11-0,
|
||||||
|
/// https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc
|
||||||
|
home_indicator_hidden_err_msg: "-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]",
|
||||||
|
home_indicator_hidden: 11-0,
|
||||||
|
/// https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc
|
||||||
|
defer_system_gestures_err_msg: "-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystem]",
|
||||||
|
defer_system_gestures: 11-0,
|
||||||
|
/// https://developer.apple.com/documentation/uikit/uiscreen/2806814-maximumframespersecond?language=objc
|
||||||
|
maximum_frames_per_second_err_msg: "-[UIScreen maximumFramesPerSecond]",
|
||||||
|
maximum_frames_per_second: 10-3,
|
||||||
|
/// https://developer.apple.com/documentation/uikit/uitouch/1618110-force?language=objc
|
||||||
|
#[allow(unused)] // error message unused
|
||||||
|
force_touch_err_msg: "-[UITouch force]",
|
||||||
|
force_touch: 9-0,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NSOperatingSystemVersion {
|
||||||
|
fn meets_requirements(&self, required_major: NSInteger, required_minor: NSInteger) -> bool {
|
||||||
|
(self.major, self.minor) >= (required_major, required_minor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn os_capabilities() -> OSCapabilities {
|
||||||
|
lazy_static! {
|
||||||
|
static ref OS_CAPABILITIES: OSCapabilities = {
|
||||||
|
let version: NSOperatingSystemVersion = unsafe {
|
||||||
|
let process_info: id = msg_send![class!(NSProcessInfo), processInfo];
|
||||||
|
let atleast_ios_8: BOOL = msg_send![
|
||||||
|
process_info,
|
||||||
|
respondsToSelector: sel!(operatingSystemVersion)
|
||||||
|
];
|
||||||
|
// winit requires atleast iOS 8 because no one has put the time into supporting earlier os versions.
|
||||||
|
// Older iOS versions are increasingly difficult to test. For example, Xcode 11 does not support
|
||||||
|
// debugging on devices with an iOS version of less than 8. Another example, in order to use an iOS
|
||||||
|
// simulator older than iOS 8, you must download an older version of Xcode (<9), and at least Xcode 7
|
||||||
|
// has been tested to not even run on macOS 10.15 - Xcode 8 might?
|
||||||
|
//
|
||||||
|
// The minimum required iOS version is likely to grow in the future.
|
||||||
|
assert!(
|
||||||
|
atleast_ios_8 == YES,
|
||||||
|
"`winit` requires iOS version 8 or greater"
|
||||||
|
);
|
||||||
|
msg_send![process_info, operatingSystemVersion]
|
||||||
|
};
|
||||||
|
version.into()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
OS_CAPABILITIES.clone()
|
||||||
|
}
|
||||||
|
|
|
@ -23,8 +23,7 @@ use crate::platform_impl::platform::{
|
||||||
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext,
|
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext,
|
||||||
CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef,
|
CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef,
|
||||||
CFRunLoopSourceSignal, CFRunLoopWakeUp, NSOperatingSystemVersion, NSString,
|
CFRunLoopSourceSignal, CFRunLoopWakeUp, NSString, UIApplicationMain, UIUserInterfaceIdiom,
|
||||||
UIApplicationMain, UIUserInterfaceIdiom,
|
|
||||||
},
|
},
|
||||||
monitor, view, MonitorHandle,
|
monitor, view, MonitorHandle,
|
||||||
};
|
};
|
||||||
|
@ -32,13 +31,6 @@ use crate::platform_impl::platform::{
|
||||||
pub struct EventLoopWindowTarget<T: 'static> {
|
pub struct EventLoopWindowTarget<T: 'static> {
|
||||||
receiver: Receiver<T>,
|
receiver: Receiver<T>,
|
||||||
sender_to_clone: Sender<T>,
|
sender_to_clone: Sender<T>,
|
||||||
capabilities: Capabilities,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static> EventLoopWindowTarget<T> {
|
|
||||||
pub fn capabilities(&self) -> &Capabilities {
|
|
||||||
&self.capabilities
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventLoop<T: 'static> {
|
pub struct EventLoop<T: 'static> {
|
||||||
|
@ -64,18 +56,11 @@ 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 version: NSOperatingSystemVersion = unsafe {
|
|
||||||
let process_info: id = msg_send![class!(NSProcessInfo), processInfo];
|
|
||||||
msg_send![process_info, operatingSystemVersion]
|
|
||||||
};
|
|
||||||
let capabilities = version.into();
|
|
||||||
|
|
||||||
EventLoop {
|
EventLoop {
|
||||||
window_target: RootEventLoopWindowTarget {
|
window_target: RootEventLoopWindowTarget {
|
||||||
p: EventLoopWindowTarget {
|
p: EventLoopWindowTarget {
|
||||||
receiver,
|
receiver,
|
||||||
sender_to_clone,
|
sender_to_clone,
|
||||||
capabilities,
|
|
||||||
},
|
},
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
},
|
},
|
||||||
|
@ -296,20 +281,3 @@ pub unsafe fn get_idiom() -> Idiom {
|
||||||
let raw_idiom: UIUserInterfaceIdiom = msg_send![device, userInterfaceIdiom];
|
let raw_idiom: UIUserInterfaceIdiom = msg_send![device, userInterfaceIdiom];
|
||||||
raw_idiom.into()
|
raw_idiom.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Capabilities {
|
|
||||||
pub supports_safe_area: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NSOperatingSystemVersion> for Capabilities {
|
|
||||||
fn from(os_version: NSOperatingSystemVersion) -> Capabilities {
|
|
||||||
assert!(
|
|
||||||
os_version.major >= 8,
|
|
||||||
"`winit` current requires iOS version 8 or greater"
|
|
||||||
);
|
|
||||||
|
|
||||||
let supports_safe_area = os_version.major >= 11;
|
|
||||||
|
|
||||||
Capabilities { supports_safe_area }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,10 @@ use std::{
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize},
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||||
platform_impl::platform::ffi::{id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger},
|
platform_impl::platform::{
|
||||||
|
app_state,
|
||||||
|
ffi::{id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -35,7 +38,7 @@ impl Drop for VideoMode {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_main_thread!("`VideoMode` can only be dropped on the main thread on iOS");
|
assert_main_thread!("`VideoMode` can only be dropped on the main thread on iOS");
|
||||||
msg_send![self.screen_mode, release];
|
let () = msg_send![self.screen_mode, release];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +46,23 @@ impl Drop for VideoMode {
|
||||||
impl VideoMode {
|
impl VideoMode {
|
||||||
unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode {
|
unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode {
|
||||||
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
||||||
let refresh_rate: NSInteger = msg_send![uiscreen, maximumFramesPerSecond];
|
let os_capabilities = app_state::os_capabilities();
|
||||||
|
let refresh_rate: NSInteger = if os_capabilities.maximum_frames_per_second {
|
||||||
|
msg_send![uiscreen, maximumFramesPerSecond]
|
||||||
|
} else {
|
||||||
|
// https://developer.apple.com/library/archive/technotes/tn2460/_index.html
|
||||||
|
// https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison
|
||||||
|
//
|
||||||
|
// All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not
|
||||||
|
// supported, they are all guaranteed to have 60hz refresh rates. This does not
|
||||||
|
// correctly handle external displays. ProMotion displays support 120fps, but they were
|
||||||
|
// introduced at the same time as the `maximumFramesPerSecond` API.
|
||||||
|
//
|
||||||
|
// FIXME: earlier OSs could calculate the refresh rate using
|
||||||
|
// `-[CADisplayLink duration]`.
|
||||||
|
os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps");
|
||||||
|
60
|
||||||
|
};
|
||||||
let size: CGSize = msg_send![screen_mode, size];
|
let size: CGSize = msg_send![screen_mode, size];
|
||||||
VideoMode {
|
VideoMode {
|
||||||
size: (size.width as u32, size.height as u32),
|
size: (size.width as u32, size.height as u32),
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
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::AppState,
|
app_state::{self, AppState, OSCapabilities},
|
||||||
event_loop,
|
event_loop,
|
||||||
ffi::{
|
ffi::{
|
||||||
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
|
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
|
||||||
|
@ -27,24 +27,49 @@ macro_rules! add_property {
|
||||||
$name:ident: $t:ty,
|
$name:ident: $t:ty,
|
||||||
$setter_name:ident: |$object:ident| $after_set:expr,
|
$setter_name:ident: |$object:ident| $after_set:expr,
|
||||||
$getter_name:ident,
|
$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));
|
const VAR_NAME: &'static str = concat!("_", stringify!($name));
|
||||||
$decl.add_ivar::<$t>(VAR_NAME);
|
$decl.add_ivar::<$t>(VAR_NAME);
|
||||||
#[allow(non_snake_case)]
|
let setter = if $capability {
|
||||||
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
|
#[allow(non_snake_case)]
|
||||||
unsafe {
|
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
|
||||||
$object.set_ivar::<$t>(VAR_NAME, value);
|
unsafe {
|
||||||
|
$object.set_ivar::<$t>(VAR_NAME, value);
|
||||||
|
}
|
||||||
|
$after_set
|
||||||
}
|
}
|
||||||
$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)]
|
#[allow(non_snake_case)]
|
||||||
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
|
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
|
||||||
unsafe { *$object.get_ivar::<$t>(VAR_NAME) }
|
unsafe { *$object.get_ivar::<$t>(VAR_NAME) }
|
||||||
}
|
}
|
||||||
$decl.add_method(
|
$decl.add_method(
|
||||||
sel!($setter_name:),
|
sel!($setter_name:),
|
||||||
$setter_name as extern "C" fn(&mut Object, Sel, $t),
|
setter as extern "C" fn(&mut Object, Sel, $t),
|
||||||
);
|
);
|
||||||
$decl.add_method(
|
$decl.add_method(
|
||||||
sel!($getter_name),
|
sel!($getter_name),
|
||||||
|
@ -125,6 +150,8 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
||||||
unsafe fn get_view_controller_class() -> &'static Class {
|
unsafe fn get_view_controller_class() -> &'static Class {
|
||||||
static mut CLASS: Option<&'static Class> = None;
|
static mut CLASS: Option<&'static Class> = None;
|
||||||
if CLASS.is_none() {
|
if CLASS.is_none() {
|
||||||
|
let os_capabilities = app_state::os_capabilities();
|
||||||
|
|
||||||
let uiviewcontroller_class = class!(UIViewController);
|
let uiviewcontroller_class = class!(UIViewController);
|
||||||
|
|
||||||
extern "C" fn should_autorotate(_: &Object, _: Sel) -> BOOL {
|
extern "C" fn should_autorotate(_: &Object, _: Sel) -> BOOL {
|
||||||
|
@ -150,11 +177,14 @@ unsafe fn get_view_controller_class() -> &'static Class {
|
||||||
add_property! {
|
add_property! {
|
||||||
decl,
|
decl,
|
||||||
prefers_home_indicator_auto_hidden: BOOL,
|
prefers_home_indicator_auto_hidden: BOOL,
|
||||||
setPrefersHomeIndicatorAutoHidden: |object| {
|
setPrefersHomeIndicatorAutoHidden:
|
||||||
unsafe {
|
os_capabilities.home_indicator_hidden,
|
||||||
let () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden];
|
OSCapabilities::home_indicator_hidden_err_msg;
|
||||||
}
|
|object| {
|
||||||
},
|
unsafe {
|
||||||
|
let () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden];
|
||||||
|
}
|
||||||
|
},
|
||||||
prefersHomeIndicatorAutoHidden,
|
prefersHomeIndicatorAutoHidden,
|
||||||
}
|
}
|
||||||
add_property! {
|
add_property! {
|
||||||
|
@ -170,11 +200,14 @@ unsafe fn get_view_controller_class() -> &'static Class {
|
||||||
add_property! {
|
add_property! {
|
||||||
decl,
|
decl,
|
||||||
preferred_screen_edges_deferring_system_gestures: UIRectEdge,
|
preferred_screen_edges_deferring_system_gestures: UIRectEdge,
|
||||||
setPreferredScreenEdgesDeferringSystemGestures: |object| {
|
setPreferredScreenEdgesDeferringSystemGestures:
|
||||||
unsafe {
|
os_capabilities.defer_system_gestures,
|
||||||
let () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
OSCapabilities::defer_system_gestures_err_msg;
|
||||||
}
|
|object| {
|
||||||
},
|
unsafe {
|
||||||
|
let () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
||||||
|
}
|
||||||
|
},
|
||||||
preferredScreenEdgesDeferringSystemGestures,
|
preferredScreenEdgesDeferringSystemGestures,
|
||||||
}
|
}
|
||||||
CLASS = Some(decl.register());
|
CLASS = Some(decl.register());
|
||||||
|
@ -213,6 +246,7 @@ unsafe fn get_window_class() -> &'static Class {
|
||||||
let uiscreen = msg_send![object, screen];
|
let uiscreen = msg_send![object, screen];
|
||||||
let touches_enum: id = msg_send![touches, objectEnumerator];
|
let touches_enum: id = msg_send![touches, objectEnumerator];
|
||||||
let mut touch_events = Vec::new();
|
let mut touch_events = Vec::new();
|
||||||
|
let os_supports_force = app_state::os_capabilities().force_touch;
|
||||||
loop {
|
loop {
|
||||||
let touch: id = msg_send![touches_enum, nextObject];
|
let touch: id = msg_send![touches_enum, nextObject];
|
||||||
if touch == nil {
|
if touch == nil {
|
||||||
|
@ -220,23 +254,29 @@ unsafe fn get_window_class() -> &'static Class {
|
||||||
}
|
}
|
||||||
let location: CGPoint = msg_send![touch, locationInView: nil];
|
let location: CGPoint = msg_send![touch, locationInView: nil];
|
||||||
let touch_type: UITouchType = msg_send![touch, type];
|
let touch_type: UITouchType = msg_send![touch, type];
|
||||||
let trait_collection: id = msg_send![object, traitCollection];
|
let force = if os_supports_force {
|
||||||
let touch_capability: UIForceTouchCapability =
|
let trait_collection: id = msg_send![object, traitCollection];
|
||||||
msg_send![trait_collection, forceTouchCapability];
|
let touch_capability: UIForceTouchCapability =
|
||||||
let force = if touch_capability == UIForceTouchCapability::Available {
|
msg_send![trait_collection, forceTouchCapability];
|
||||||
let force: CGFloat = msg_send![touch, force];
|
// Both the OS _and_ the device need to be checked for force touch support.
|
||||||
let max_possible_force: CGFloat = msg_send![touch, maximumPossibleForce];
|
if touch_capability == UIForceTouchCapability::Available {
|
||||||
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
|
let force: CGFloat = msg_send![touch, force];
|
||||||
let angle: CGFloat = msg_send![touch, altitudeAngle];
|
let max_possible_force: CGFloat =
|
||||||
Some(angle as _)
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
}
|
||||||
Some(Force::Calibrated {
|
|
||||||
force: force as _,
|
|
||||||
max_possible_force: max_possible_force as _,
|
|
||||||
altitude_angle,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
||||||
monitor::MonitorHandle as RootMonitorHandle,
|
monitor::MonitorHandle as RootMonitorHandle,
|
||||||
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
|
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
app_state::AppState,
|
app_state::{self, AppState},
|
||||||
event_loop,
|
event_loop,
|
||||||
ffi::{
|
ffi::{
|
||||||
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
|
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask,
|
||||||
|
@ -28,7 +28,6 @@ pub struct Inner {
|
||||||
pub window: id,
|
pub window: id,
|
||||||
pub view_controller: id,
|
pub view_controller: id,
|
||||||
pub view: id,
|
pub view: id,
|
||||||
supports_safe_area: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Inner {
|
impl Drop for Inner {
|
||||||
|
@ -300,7 +299,7 @@ impl DerefMut for Window {
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new<T>(
|
pub fn new<T>(
|
||||||
event_loop: &EventLoopWindowTarget<T>,
|
_event_loop: &EventLoopWindowTarget<T>,
|
||||||
window_attributes: WindowAttributes,
|
window_attributes: WindowAttributes,
|
||||||
platform_attributes: PlatformSpecificWindowBuilderAttributes,
|
platform_attributes: PlatformSpecificWindowBuilderAttributes,
|
||||||
) -> Result<Window, RootOsError> {
|
) -> Result<Window, RootOsError> {
|
||||||
|
@ -347,14 +346,11 @@ impl Window {
|
||||||
view_controller,
|
view_controller,
|
||||||
);
|
);
|
||||||
|
|
||||||
let supports_safe_area = event_loop.capabilities().supports_safe_area;
|
|
||||||
|
|
||||||
let result = Window {
|
let result = Window {
|
||||||
inner: Inner {
|
inner: Inner {
|
||||||
window,
|
window,
|
||||||
view_controller,
|
view_controller,
|
||||||
view,
|
view,
|
||||||
supports_safe_area,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
AppState::set_key_window(window);
|
AppState::set_key_window(window);
|
||||||
|
@ -396,7 +392,7 @@ impl Inner {
|
||||||
msg_send![
|
msg_send![
|
||||||
self.view_controller,
|
self.view_controller,
|
||||||
setSupportedInterfaceOrientations: supported_orientations
|
setSupportedInterfaceOrientations: supported_orientations
|
||||||
];
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +458,7 @@ impl Inner {
|
||||||
// requires main thread
|
// requires main thread
|
||||||
unsafe fn safe_area_screen_space(&self) -> CGRect {
|
unsafe fn safe_area_screen_space(&self) -> CGRect {
|
||||||
let bounds: CGRect = msg_send![self.window, bounds];
|
let bounds: CGRect = msg_send![self.window, bounds];
|
||||||
if self.supports_safe_area {
|
if app_state::os_capabilities().safe_area {
|
||||||
let safe_area: UIEdgeInsets = msg_send![self.window, safeAreaInsets];
|
let safe_area: UIEdgeInsets = msg_send![self.window, safeAreaInsets];
|
||||||
let safe_bounds = CGRect {
|
let safe_bounds = CGRect {
|
||||||
origin: CGPoint {
|
origin: CGPoint {
|
||||||
|
|
Loading…
Reference in a new issue