diff --git a/Cargo.lock b/Cargo.lock index 962feae4..33455e3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1836,30 +1836,6 @@ dependencies = [ "objc2-quartz-core", ] -[[package]] -name = "objc2-cloud-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" -dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-core-location", - "objc2-foundation", -] - -[[package]] -name = "objc2-contacts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", -] - [[package]] name = "objc2-core-data" version = "0.2.2" @@ -1884,18 +1860,6 @@ dependencies = [ "objc2-metal", ] -[[package]] -name = "objc2-core-location" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" -dependencies = [ - "block2", - "objc2", - "objc2-contacts", - "objc2-foundation", -] - [[package]] name = "objc2-encode" version = "4.0.3" @@ -1915,18 +1879,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "objc2-link-presentation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" -dependencies = [ - "block2", - "objc2", - "objc2-app-kit", - "objc2-foundation", -] - [[package]] name = "objc2-metal" version = "0.2.2" @@ -1952,61 +1904,6 @@ dependencies = [ "objc2-metal", ] -[[package]] -name = "objc2-symbols" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-ui-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" -dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", - "objc2-core-location", - "objc2-foundation", - "objc2-link-presentation", - "objc2-quartz-core", - "objc2-symbols", - "objc2-uniform-type-identifiers", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-uniform-type-identifiers" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" -dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-core-location", - "objc2-foundation", -] - [[package]] name = "objc_id" version = "0.1.1" @@ -2511,7 +2408,6 @@ dependencies = [ "objc2", "objc2-app-kit", "objc2-foundation", - "objc2-ui-kit", "orbclient", "percent-encoding", "pin-project", diff --git a/rio-window/Cargo.toml b/rio-window/Cargo.toml index 959977af..e6470374 100644 --- a/rio-window/Cargo.toml +++ b/rio-window/Cargo.toml @@ -42,8 +42,6 @@ tracing = { version = "0.1.40", default-features = false } image = { version = "0.25.0", default-features = false, features = ["png"] } tracing = { version = "0.1.40", default-features = false, features = ["log"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } - -[target.'cfg(not(any(target_os = "ios")))'.dev-dependencies] softbuffer = { version = "0.4.0", default-features = false, features = [ "x11", "x11-dlopen", @@ -51,11 +49,9 @@ softbuffer = { version = "0.4.0", default-features = false, features = [ "wayland-dlopen", ] } -[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] +[target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9.3" objc2 = "0.5.2" - -[target.'cfg(target_os = "macos")'.dependencies] core-graphics = "0.23.1" [target.'cfg(target_os = "macos")'.dependencies.objc2-foundation] @@ -109,43 +105,6 @@ features = [ "NSWindowTabGroup", ] -[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation] -version = "0.2.2" -features = [ - "dispatch", - "NSArray", - "NSEnumerator", - "NSGeometry", - "NSObjCRuntime", - "NSString", - "NSProcessInfo", - "NSThread", - "NSSet", -] - -[target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit] -version = "0.2.2" -features = [ - "UIApplication", - "UIDevice", - "UIEvent", - "UIGeometry", - "UIGestureRecognizer", - "UIOrientation", - "UIPanGestureRecognizer", - "UIPinchGestureRecognizer", - "UIResponder", - "UIRotationGestureRecognizer", - "UIScreen", - "UIScreenMode", - "UITapGestureRecognizer", - "UITouch", - "UITraitCollection", - "UIView", - "UIViewController", - "UIWindow", -] - [target.'cfg(target_os = "windows")'.dependencies] unicode-segmentation = "1.7.1" @@ -178,7 +137,7 @@ features = [ "Win32_UI_WindowsAndMessaging", ] -[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "ios", target_os = "macos"))))'.dependencies] +[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "macos"))))'.dependencies] ahash = { version = "0.8.7", features = ["no-rng"], optional = true } bytemuck = { version = "1.13.1", default-features = false, optional = true } calloop = "0.13.0" diff --git a/rio-window/src/event_loop.rs b/rio-window/src/event_loop.rs index 74dac033..dcaf55aa 100644 --- a/rio-window/src/event_loop.rs +++ b/rio-window/src/event_loop.rs @@ -518,7 +518,7 @@ impl raw_window_handle::HasDisplayHandle for ActiveEventLoop { &self, ) -> Result, raw_window_handle::HandleError> { - let raw = self.p.raw_display_handle_rwh_06()?; + let raw = self.p.raw_display_handle_raw_window_handle()?; // SAFETY: The display will never be deallocated while the event loop is alive. Ok(unsafe { raw_window_handle::DisplayHandle::borrow_raw(raw) }) } @@ -555,7 +555,7 @@ impl raw_window_handle::HasDisplayHandle for OwnedDisplayHandle { &self, ) -> Result, raw_window_handle::HandleError> { - let raw = self.platform.raw_display_handle_rwh_06()?; + let raw = self.platform.raw_display_handle_raw_window_handle()?; // SAFETY: The underlying display handle should be safe. let handle = unsafe { raw_window_handle::DisplayHandle::borrow_raw(raw) }; diff --git a/rio-window/src/platform/ios.rs b/rio-window/src/platform/ios.rs deleted file mode 100644 index 1cf27556..00000000 --- a/rio-window/src/platform/ios.rs +++ /dev/null @@ -1,444 +0,0 @@ -//! # iOS / UIKit -//! -//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on -//! iOS 9.3. -//! -//! iOS's main `UIApplicationMain` does some init work that's required by all -//! UI-related code (see issue [#1705]). It is best to create your windows -//! inside `Event::Resumed`. -//! -//! [#1705]: https://github.com/rust-windowing/winit/issues/1705 -//! -//! ## Building app -//! -//! To build ios app you will need rustc built for this targets: -//! -//! - armv7-apple-ios -//! - armv7s-apple-ios -//! - i386-apple-ios -//! - aarch64-apple-ios -//! - x86_64-apple-ios -//! -//! Then -//! -//! ``` -//! cargo build --target=... -//! ``` -//! The simplest way to integrate your app into xcode environment is to build it -//! as a static library. Wrap your main function and export it. -//! -//! ```rust, ignore -//! #[no_mangle] -//! pub extern fn start_winit_app() { -//! start_inner() -//! } -//! -//! fn start_inner() { -//! ... -//! } -//! ``` -//! -//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode. -//! -//! ```ignore -//! void start_winit_app(); -//! ``` -//! -//! Use start_winit_app inside your xcode's main function. -//! -//! -//! ## App lifecycle and events -//! -//! iOS environment is very different from other platforms and you must be very -//! careful with it's events. Familiarize yourself with -//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/). -//! -//! This is how those event are represented in winit: -//! -//! - applicationDidBecomeActive is Resumed -//! - applicationWillResignActive is Suspended -//! - applicationWillTerminate is LoopExiting -//! -//! Keep in mind that after LoopExiting event is received every attempt to draw with -//! opengl will result in segfault. -//! -//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed. - -use std::os::raw::c_void; - -use crate::event_loop::EventLoop; -use crate::monitor::{MonitorHandle, VideoModeHandle}; -use crate::window::{Window, WindowAttributes}; - -/// Additional methods on [`EventLoop`] that are specific to iOS. -pub trait EventLoopExtIOS { - /// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device. - fn idiom(&self) -> Idiom; -} - -impl EventLoopExtIOS for EventLoop { - fn idiom(&self) -> Idiom { - self.event_loop.idiom() - } -} - -/// Additional methods on [`Window`] that are specific to iOS. -pub trait WindowExtIOS { - /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`. - /// - /// The default value is device dependent, and it's recommended GLES or Metal applications set - /// this to [`MonitorHandle::scale_factor()`]. - /// - /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc - /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc - fn set_scale_factor(&self, scale_factor: f64); - - /// Sets the valid orientations for the [`Window`]. - /// - /// The default value is [`ValidOrientations::LandscapeAndPortrait`]. - /// - /// This changes the value returned by - /// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc), - /// and then calls - /// [`-[UIViewController attemptRotationToDeviceOrientation]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621400-attemptrotationtodeviceorientati?language=objc). - fn set_valid_orientations(&self, valid_orientations: ValidOrientations); - - /// Sets whether the [`Window`] prefers the home indicator hidden. - /// - /// The default is to prefer showing the home indicator. - /// - /// This changes the value returned by - /// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc), - /// and then calls - /// [`-[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); - - /// Sets the screen edges for which the system gestures will take a lower priority than the - /// application's touch handling. - /// - /// This changes the value returned by - /// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc), - /// and then calls - /// [`-[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); - - /// Sets whether the [`Window`] prefers the status bar hidden. - /// - /// The default is to prefer showing the status bar. - /// - /// This sets the value of the - /// [`prefersStatusBarHidden`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc) - /// property. - /// - /// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc) - /// is also called for you. - fn set_prefers_status_bar_hidden(&self, hidden: bool); - - /// Sets the preferred status bar style for the [`Window`]. - /// - /// The default is system-defined. - /// - /// This sets the value of the - /// [`preferredStatusBarStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc) - /// property. - /// - /// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc) - /// is also called for you. - fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle); - - /// Sets whether the [`Window`] should recognize pinch gestures. - /// - /// The default is to not recognize gestures. - fn recognize_pinch_gesture(&self, should_recognize: bool); - - /// Sets whether the [`Window`] should recognize pan gestures. - /// - /// The default is to not recognize gestures. - /// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view - /// - /// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches) - /// - /// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches) - fn recognize_pan_gesture( - &self, - should_recognize: bool, - minimum_number_of_touches: u8, - maximum_number_of_touches: u8, - ); - - /// Sets whether the [`Window`] should recognize double tap gestures. - /// - /// The default is to not recognize gestures. - fn recognize_doubletap_gesture(&self, should_recognize: bool); - - /// Sets whether the [`Window`] should recognize rotation gestures. - /// - /// The default is to not recognize gestures. - fn recognize_rotation_gesture(&self, should_recognize: bool); -} - -impl WindowExtIOS for Window { - #[inline] - fn set_scale_factor(&self, scale_factor: f64) { - self.window - .maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor)) - } - - #[inline] - fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { - self.window - .maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations)) - } - - #[inline] - fn set_prefers_home_indicator_hidden(&self, hidden: bool) { - self.window - .maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden)) - } - - #[inline] - fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) { - self.window.maybe_queue_on_main(move |w| { - w.set_preferred_screen_edges_deferring_system_gestures(edges) - }) - } - - #[inline] - fn set_prefers_status_bar_hidden(&self, hidden: bool) { - self.window - .maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden)) - } - - #[inline] - fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { - self.window.maybe_queue_on_main(move |w| { - w.set_preferred_status_bar_style(status_bar_style) - }) - } - - #[inline] - fn recognize_pinch_gesture(&self, should_recognize: bool) { - self.window - .maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); - } - - #[inline] - fn recognize_pan_gesture( - &self, - should_recognize: bool, - minimum_number_of_touches: u8, - maximum_number_of_touches: u8, - ) { - self.window.maybe_queue_on_main(move |w| { - w.recognize_pan_gesture( - should_recognize, - minimum_number_of_touches, - maximum_number_of_touches, - ) - }); - } - - #[inline] - fn recognize_doubletap_gesture(&self, should_recognize: bool) { - self.window.maybe_queue_on_main(move |w| { - w.recognize_doubletap_gesture(should_recognize) - }); - } - - #[inline] - fn recognize_rotation_gesture(&self, should_recognize: bool) { - self.window - .maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize)); - } -} - -/// Additional methods on [`WindowAttributes`] that are specific to iOS. -pub trait WindowAttributesExtIOS { - /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`. - /// - /// The default value is device dependent, and it's recommended GLES or Metal applications set - /// this to [`MonitorHandle::scale_factor()`]. - /// - /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc - /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc - fn with_scale_factor(self, scale_factor: f64) -> Self; - - /// Sets the valid orientations for the [`Window`]. - /// - /// The default value is [`ValidOrientations::LandscapeAndPortrait`]. - /// - /// This sets the initial value returned by - /// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc). - fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> Self; - - /// Sets whether the [`Window`] prefers the home indicator hidden. - /// - /// The default is to prefer showing the home indicator. - /// - /// This sets the initial value returned by - /// [`-[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) -> Self; - - /// Sets the screen edges for which the system gestures will take a lower priority than the - /// application's touch handling. - /// - /// This sets the initial value returned by - /// [`-[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( - self, - edges: ScreenEdge, - ) -> Self; - - /// Sets whether the [`Window`] prefers the status bar hidden. - /// - /// The default is to prefer showing the status bar. - /// - /// This sets the initial value returned by - /// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc). - fn with_prefers_status_bar_hidden(self, hidden: bool) -> Self; - - /// Sets the style of the [`Window`]'s status bar. - /// - /// The default is system-defined. - /// - /// This sets the initial value returned by - /// [`-[UIViewController preferredStatusBarStyle]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc), - fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self; -} - -impl WindowAttributesExtIOS for WindowAttributes { - #[inline] - fn with_scale_factor(mut self, scale_factor: f64) -> Self { - self.platform_specific.scale_factor = Some(scale_factor); - self - } - - #[inline] - fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self { - self.platform_specific.valid_orientations = valid_orientations; - self - } - - #[inline] - fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self { - self.platform_specific.prefers_home_indicator_hidden = hidden; - self - } - - #[inline] - fn with_preferred_screen_edges_deferring_system_gestures( - mut self, - edges: ScreenEdge, - ) -> Self { - self.platform_specific - .preferred_screen_edges_deferring_system_gestures = edges; - self - } - - #[inline] - fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self { - self.platform_specific.prefers_status_bar_hidden = hidden; - self - } - - #[inline] - fn with_preferred_status_bar_style( - mut self, - status_bar_style: StatusBarStyle, - ) -> Self { - self.platform_specific.preferred_status_bar_style = status_bar_style; - self - } -} - -/// Additional methods on [`MonitorHandle`] that are specific to iOS. -pub trait MonitorHandleExtIOS { - /// Returns a pointer to the [`UIScreen`] that is used by this monitor. - /// - /// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc - fn ui_screen(&self) -> *mut c_void; - - /// Returns the preferred [`VideoModeHandle`] for this monitor. - /// - /// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc). - fn preferred_video_mode(&self) -> VideoModeHandle; -} - -impl MonitorHandleExtIOS for MonitorHandle { - #[inline] - fn ui_screen(&self) -> *mut c_void { - // SAFETY: The marker is only used to get the pointer of the screen - let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; - objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void - } - - #[inline] - fn preferred_video_mode(&self) -> VideoModeHandle { - VideoModeHandle { - video_mode: self.inner.preferred_video_mode(), - } - } -} - -/// Valid orientations for a particular [`Window`]. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum ValidOrientations { - /// Excludes `PortraitUpsideDown` on iphone - #[default] - LandscapeAndPortrait, - - Landscape, - - /// Excludes `PortraitUpsideDown` on iphone - Portrait, -} - -/// The device [idiom]. -/// -/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Idiom { - Unspecified, - - /// iPhone and iPod touch. - Phone, - - /// iPad. - Pad, - - /// tvOS and Apple TV. - TV, - CarPlay, -} - -bitflags::bitflags! { - /// The [edges] of a screen. - /// - /// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct ScreenEdge: u8 { - const NONE = 0; - const TOP = 1 << 0; - const LEFT = 1 << 1; - const BOTTOM = 1 << 2; - const RIGHT = 1 << 3; - const ALL = ScreenEdge::TOP.bits() | ScreenEdge::LEFT.bits() - | ScreenEdge::BOTTOM.bits() | ScreenEdge::RIGHT.bits(); - } -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum StatusBarStyle { - #[default] - Default, - LightContent, - DarkContent, -} diff --git a/rio-window/src/platform/mod.rs b/rio-window/src/platform/mod.rs index c31eafcc..6cd87fe8 100644 --- a/rio-window/src/platform/mod.rs +++ b/rio-window/src/platform/mod.rs @@ -2,8 +2,6 @@ //! //! Only the modules corresponding to the platform you're compiling to will be available. -#[cfg(any(ios_platform, docsrs))] -pub mod ios; #[cfg(any(macos_platform, docsrs))] pub mod macos; #[cfg(any(orbital_platform, docsrs))] diff --git a/rio-window/src/platform_impl/ios/app_delegate.rs b/rio-window/src/platform_impl/ios/app_delegate.rs deleted file mode 100644 index 6a363027..00000000 --- a/rio-window/src/platform_impl/ios/app_delegate.rs +++ /dev/null @@ -1,107 +0,0 @@ -use objc2::{declare_class, mutability, ClassType, DeclaredClass}; -use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol}; -use objc2_ui_kit::{UIApplication, UIWindow}; - -use super::app_state::{self, EventWrapper}; -use super::window::WinitUIWindow; -use crate::event::{Event, WindowEvent}; -use crate::window::WindowId as RootWindowId; - -declare_class!( - pub struct AppDelegate; - - unsafe impl ClassType for AppDelegate { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "WinitApplicationDelegate"; - } - - impl DeclaredClass for AppDelegate {} - - // UIApplicationDelegate protocol - unsafe impl AppDelegate { - #[method(application:didFinishLaunchingWithOptions:)] - fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool { - app_state::did_finish_launching(MainThreadMarker::new().unwrap()); - true - } - - #[method(applicationDidBecomeActive:)] - fn did_become_active(&self, _application: &UIApplication) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed)) - } - - #[method(applicationWillResignActive:)] - fn will_resign_active(&self, _application: &UIApplication) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended)) - } - - #[method(applicationWillEnterForeground:)] - fn will_enter_foreground(&self, application: &UIApplication) { - self.send_occluded_event_for_all_windows(application, false); - } - - #[method(applicationDidEnterBackground:)] - fn did_enter_background(&self, application: &UIApplication) { - self.send_occluded_event_for_all_windows(application, true); - } - - #[method(applicationWillTerminate:)] - fn will_terminate(&self, application: &UIApplication) { - let mut events = Vec::new(); - #[allow(deprecated)] - for window in application.windows().iter() { - if window.is_kind_of::() { - // SAFETY: We just checked that the window is a `winit` window - let window = unsafe { - let ptr: *const UIWindow = window; - let ptr: *const WinitUIWindow = ptr.cast(); - &*ptr - }; - events.push(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::Destroyed, - })); - } - } - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_events(mtm, events); - app_state::terminated(mtm); - } - - #[method(applicationDidReceiveMemoryWarning:)] - fn did_receive_memory_warning(&self, _application: &UIApplication) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning)) - } - } -); - -impl AppDelegate { - fn send_occluded_event_for_all_windows( - &self, - application: &UIApplication, - occluded: bool, - ) { - let mut events = Vec::new(); - #[allow(deprecated)] - for window in application.windows().iter() { - if window.is_kind_of::() { - // SAFETY: We just checked that the window is a `winit` window - let window = unsafe { - let ptr: *const UIWindow = window; - let ptr: *const WinitUIWindow = ptr.cast(); - &*ptr - }; - events.push(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::Occluded(occluded), - })); - } - } - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_events(mtm, events); - } -} diff --git a/rio-window/src/platform_impl/ios/app_state.rs b/rio-window/src/platform_impl/ios/app_state.rs deleted file mode 100644 index fb56344f..00000000 --- a/rio-window/src/platform_impl/ios/app_state.rs +++ /dev/null @@ -1,994 +0,0 @@ -#![deny(unused_results)] - -use std::cell::{RefCell, RefMut}; -use std::collections::HashSet; -use std::os::raw::c_void; -use std::sync::{Arc, Mutex, OnceLock}; -use std::time::Instant; -use std::{fmt, mem, ptr}; - -use core_foundation::base::CFRelease; -use core_foundation::date::CFAbsoluteTimeGetCurrent; -use core_foundation::runloop::{ - kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, - CFRunLoopTimerCreate, CFRunLoopTimerInvalidate, CFRunLoopTimerRef, - CFRunLoopTimerSetNextFireDate, -}; -use objc2::rc::Retained; -use objc2::runtime::AnyObject; -use objc2::{msg_send, sel}; -use objc2_foundation::{ - CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, - NSOperatingSystemVersion, NSProcessInfo, -}; -use objc2_ui_kit::{UICoordinateSpace, UIView}; - -use super::window::WinitUIWindow; -use crate::dpi::PhysicalSize; -use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent}; -use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow}; -use crate::window::WindowId as RootWindowId; - -macro_rules! bug { - ($($msg:tt)*) => { - panic!("winit iOS bug, file an issue: {}", format!($($msg)*)) - }; -} - -macro_rules! bug_assert { - ($test:expr, $($msg:tt)*) => { - assert!($test, "winit iOS bug, file an issue: {}", format!($($msg)*)) - }; -} - -#[derive(Debug)] -pub(crate) struct HandlePendingUserEvents; - -pub(crate) struct EventLoopHandler { - #[allow(clippy::type_complexity)] - pub(crate) handler: - Box, &RootActiveEventLoop)>, - pub(crate) event_loop: RootActiveEventLoop, -} - -impl fmt::Debug for EventLoopHandler { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EventLoopHandler") - .field("handler", &"...") - .field("event_loop", &self.event_loop) - .finish() - } -} - -impl EventLoopHandler { - fn handle_event(&mut self, event: Event) { - (self.handler)(event, &self.event_loop) - } -} - -#[derive(Debug)] -pub(crate) enum EventWrapper { - StaticEvent(Event), - ScaleFactorChanged(ScaleFactorChanged), -} - -#[derive(Debug)] -pub struct ScaleFactorChanged { - pub(super) window: Retained, - pub(super) suggested_size: PhysicalSize, - pub(super) scale_factor: f64, -} - -enum UserCallbackTransitionResult<'a> { - Success { - handler: EventLoopHandler, - active_control_flow: ControlFlow, - processing_redraws: bool, - }, - ReentrancyPrevented { - queued_events: &'a mut Vec, - }, -} - -impl Event { - fn is_redraw(&self) -> bool { - matches!( - self, - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } - ) - } -} - -// this is the state machine for the app lifecycle -#[derive(Debug)] -#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"] -enum AppStateImpl { - NotLaunched { - queued_windows: Vec>, - queued_events: Vec, - queued_gpu_redraws: HashSet>, - }, - Launching { - queued_windows: Vec>, - queued_events: Vec, - queued_handler: EventLoopHandler, - queued_gpu_redraws: HashSet>, - }, - ProcessingEvents { - handler: EventLoopHandler, - queued_gpu_redraws: HashSet>, - active_control_flow: ControlFlow, - }, - // special state to deal with reentrancy and prevent mutable aliasing. - InUserCallback { - queued_events: Vec, - queued_gpu_redraws: HashSet>, - }, - ProcessingRedraws { - handler: EventLoopHandler, - active_control_flow: ControlFlow, - }, - Waiting { - waiting_handler: EventLoopHandler, - start: Instant, - }, - PollFinished { - waiting_handler: EventLoopHandler, - }, - Terminated, -} - -pub(crate) struct AppState { - // This should never be `None`, except for briefly during a state transition. - app_state: Option, - control_flow: ControlFlow, - waker: EventLoopWaker, -} - -impl AppState { - pub(crate) fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> { - // basically everything in UIKit requires the main thread, so it's pointless to use the - // std::sync APIs. - // must be mut because plain `static` requires `Sync` - static mut APP_STATE: RefCell> = RefCell::new(None); - - let mut guard = unsafe { APP_STATE.borrow_mut() }; - if guard.is_none() { - #[inline(never)] - #[cold] - fn init_guard(guard: &mut RefMut<'static, Option>) { - let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() }); - **guard = Some(AppState { - app_state: Some(AppStateImpl::NotLaunched { - queued_windows: Vec::new(), - queued_events: Vec::new(), - queued_gpu_redraws: HashSet::new(), - }), - control_flow: ControlFlow::default(), - waker, - }); - } - init_guard(&mut guard); - } - RefMut::map(guard, |state| state.as_mut().unwrap()) - } - - fn state(&self) -> &AppStateImpl { - match &self.app_state { - Some(ref state) => state, - None => bug!("`AppState` previously failed a state transition"), - } - } - - fn state_mut(&mut self) -> &mut AppStateImpl { - match &mut self.app_state { - Some(ref mut state) => state, - None => bug!("`AppState` previously failed a state transition"), - } - } - - fn take_state(&mut self) -> AppStateImpl { - match self.app_state.take() { - Some(state) => state, - None => bug!("`AppState` previously failed a state transition"), - } - } - - fn set_state(&mut self, new_state: AppStateImpl) { - bug_assert!( - self.app_state.is_none(), - "attempted to set an `AppState` without calling `take_state` first {:?}", - self.app_state - ); - self.app_state = Some(new_state) - } - - fn replace_state(&mut self, new_state: AppStateImpl) -> AppStateImpl { - match &mut self.app_state { - Some(ref mut state) => mem::replace(state, new_state), - None => bug!("`AppState` previously failed a state transition"), - } - } - - fn has_launched(&self) -> bool { - !matches!( - self.state(), - AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. } - ) - } - - fn has_terminated(&self) -> bool { - matches!(self.state(), AppStateImpl::Terminated) - } - - fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) { - let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() - { - AppStateImpl::NotLaunched { - queued_windows, - queued_events, - queued_gpu_redraws, - } => (queued_windows, queued_events, queued_gpu_redraws), - s => bug!("unexpected state {:?}", s), - }; - self.set_state(AppStateImpl::Launching { - queued_windows, - queued_events, - queued_handler, - queued_gpu_redraws, - }); - } - - fn did_finish_launching_transition( - &mut self, - ) -> (Vec>, Vec) { - let (windows, events, handler, queued_gpu_redraws) = match self.take_state() { - AppStateImpl::Launching { - queued_windows, - queued_events, - queued_handler, - queued_gpu_redraws, - } => ( - queued_windows, - queued_events, - queued_handler, - queued_gpu_redraws, - ), - s => bug!("unexpected state {:?}", s), - }; - self.set_state(AppStateImpl::ProcessingEvents { - handler, - active_control_flow: self.control_flow, - queued_gpu_redraws, - }); - (windows, events) - } - - fn wakeup_transition(&mut self) -> Option { - // before `AppState::did_finish_launching` is called, pretend there is no running - // event loop. - if !self.has_launched() || self.has_terminated() { - return None; - } - - let (handler, event) = match (self.control_flow, self.take_state()) { - (ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => ( - waiting_handler, - EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)), - ), - ( - ControlFlow::Wait, - AppStateImpl::Waiting { - waiting_handler, - start, - }, - ) => ( - waiting_handler, - EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { - start, - requested_resume: None, - })), - ), - ( - ControlFlow::WaitUntil(requested_resume), - AppStateImpl::Waiting { - waiting_handler, - start, - }, - ) => { - let event = if Instant::now() >= requested_resume { - EventWrapper::StaticEvent(Event::NewEvents( - StartCause::ResumeTimeReached { - start, - requested_resume, - }, - )) - } else { - EventWrapper::StaticEvent(Event::NewEvents( - StartCause::WaitCancelled { - start, - requested_resume: Some(requested_resume), - }, - )) - }; - (waiting_handler, event) - } - s => bug!("`EventHandler` unexpectedly woke up {:?}", s), - }; - - self.set_state(AppStateImpl::ProcessingEvents { - handler, - queued_gpu_redraws: Default::default(), - active_control_flow: self.control_flow, - }); - Some(event) - } - - fn try_user_callback_transition(&mut self) -> UserCallbackTransitionResult<'_> { - // If we're not able to process an event due to recursion or `Init` not having been sent out - // yet, then queue the events up. - match self.state_mut() { - &mut AppStateImpl::Launching { - ref mut queued_events, - .. - } - | &mut AppStateImpl::NotLaunched { - ref mut queued_events, - .. - } - | &mut AppStateImpl::InUserCallback { - ref mut queued_events, - .. - } => { - // A lifetime cast: early returns are not currently handled well with NLL, but - // polonius handles them well. This transmute is a safe workaround. - return unsafe { - mem::transmute::< - UserCallbackTransitionResult<'_>, - UserCallbackTransitionResult<'_>, - >( - UserCallbackTransitionResult::ReentrancyPrevented { - queued_events, - }, - ) - }; - } - - &mut AppStateImpl::ProcessingEvents { .. } - | &mut AppStateImpl::ProcessingRedraws { .. } => {} - - s @ &mut AppStateImpl::PollFinished { .. } - | s @ &mut AppStateImpl::Waiting { .. } - | s @ &mut AppStateImpl::Terminated => { - bug!("unexpected attempted to process an event {:?}", s) - } - } - - let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) = - match self.take_state() { - AppStateImpl::Launching { .. } - | AppStateImpl::NotLaunched { .. } - | AppStateImpl::InUserCallback { .. } => unreachable!(), - AppStateImpl::ProcessingEvents { - handler, - queued_gpu_redraws, - active_control_flow, - } => (handler, queued_gpu_redraws, active_control_flow, false), - AppStateImpl::ProcessingRedraws { - handler, - active_control_flow, - } => (handler, Default::default(), active_control_flow, true), - AppStateImpl::PollFinished { .. } - | AppStateImpl::Waiting { .. } - | AppStateImpl::Terminated => unreachable!(), - }; - self.set_state(AppStateImpl::InUserCallback { - queued_events: Vec::new(), - queued_gpu_redraws, - }); - UserCallbackTransitionResult::Success { - handler, - active_control_flow, - processing_redraws, - } - } - - fn main_events_cleared_transition(&mut self) -> HashSet> { - let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() { - AppStateImpl::ProcessingEvents { - handler, - queued_gpu_redraws, - active_control_flow, - } => (handler, queued_gpu_redraws, active_control_flow), - s => bug!("unexpected state {:?}", s), - }; - self.set_state(AppStateImpl::ProcessingRedraws { - handler, - active_control_flow, - }); - queued_gpu_redraws - } - - fn events_cleared_transition(&mut self) { - if !self.has_launched() || self.has_terminated() { - return; - } - let (waiting_handler, old) = match self.take_state() { - AppStateImpl::ProcessingRedraws { - handler, - active_control_flow, - } => (handler, active_control_flow), - s => bug!("unexpected state {:?}", s), - }; - - let new = self.control_flow; - match (old, new) { - (ControlFlow::Wait, ControlFlow::Wait) => { - let start = Instant::now(); - self.set_state(AppStateImpl::Waiting { - waiting_handler, - start, - }); - } - ( - ControlFlow::WaitUntil(old_instant), - ControlFlow::WaitUntil(new_instant), - ) if old_instant == new_instant => { - let start = Instant::now(); - self.set_state(AppStateImpl::Waiting { - waiting_handler, - start, - }); - } - (_, ControlFlow::Wait) => { - let start = Instant::now(); - self.set_state(AppStateImpl::Waiting { - waiting_handler, - start, - }); - self.waker.stop() - } - (_, ControlFlow::WaitUntil(new_instant)) => { - let start = Instant::now(); - self.set_state(AppStateImpl::Waiting { - waiting_handler, - start, - }); - self.waker.start_at(new_instant) - } - // Unlike on macOS, handle Poll to Poll transition here to call the waker - (_, ControlFlow::Poll) => { - self.set_state(AppStateImpl::PollFinished { waiting_handler }); - self.waker.start() - } - } - } - - fn terminated_transition(&mut self) -> EventLoopHandler { - match self.replace_state(AppStateImpl::Terminated) { - AppStateImpl::ProcessingEvents { handler, .. } => handler, - s => bug!("`LoopExiting` happened while not processing events {:?}", s), - } - } - - pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) { - self.control_flow = control_flow; - } - - pub(crate) fn control_flow(&self) -> ControlFlow { - self.control_flow - } -} - -pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained) { - let mut this = AppState::get_mut(mtm); - match this.state_mut() { - &mut AppStateImpl::NotLaunched { - ref mut queued_windows, - .. - } => return queued_windows.push(window.clone()), - &mut AppStateImpl::ProcessingEvents { .. } - | &mut AppStateImpl::InUserCallback { .. } - | &mut AppStateImpl::ProcessingRedraws { .. } => {} - s @ &mut AppStateImpl::Launching { .. } - | s @ &mut AppStateImpl::Waiting { .. } - | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), - &mut AppStateImpl::Terminated => { - panic!("Attempt to create a `Window` after the app has terminated") - } - } - drop(this); - window.makeKeyAndVisible(); -} - -pub(crate) fn queue_gl_or_metal_redraw( - mtm: MainThreadMarker, - window: Retained, -) { - let mut this = AppState::get_mut(mtm); - match this.state_mut() { - &mut AppStateImpl::NotLaunched { - ref mut queued_gpu_redraws, - .. - } - | &mut AppStateImpl::Launching { - ref mut queued_gpu_redraws, - .. - } - | &mut AppStateImpl::ProcessingEvents { - ref mut queued_gpu_redraws, - .. - } - | &mut AppStateImpl::InUserCallback { - ref mut queued_gpu_redraws, - .. - } => { - let _ = queued_gpu_redraws.insert(window); - } - s @ &mut AppStateImpl::ProcessingRedraws { .. } - | s @ &mut AppStateImpl::Waiting { .. } - | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), - &mut AppStateImpl::Terminated => { - panic!("Attempt to create a `Window` after the app has terminated") - } - } -} - -pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) { - AppState::get_mut(mtm).will_launch_transition(queued_handler) -} - -pub fn did_finish_launching(mtm: MainThreadMarker) { - let mut this = AppState::get_mut(mtm); - let windows = match this.state_mut() { - AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows), - s => bug!("unexpected state {:?}", s), - }; - - this.waker.start(); - - // have to drop RefMut because the window setup code below can trigger new events - drop(this); - - for window in windows { - // Do a little screen dance here to account for windows being created before - // `UIApplicationMain` is called. This fixes visual issues such as being - // offcenter and sized incorrectly. Additionally, to fix orientation issues, we - // gotta reset the `rootViewController`. - // - // relevant iOS log: - // ``` - // [ApplicationLifecycle] Windows were created before application initialization - // completed. This may result in incorrect visual appearance. - // ``` - let screen = window.screen(); - let _: () = unsafe { msg_send![&window, setScreen: ptr::null::()] }; - window.setScreen(&screen); - - let controller = window.rootViewController(); - window.setRootViewController(None); - window.setRootViewController(controller.as_deref()); - - window.makeKeyAndVisible(); - } - - let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition(); - - let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents( - StartCause::Init, - ))) - .chain(events); - handle_nonuser_events(mtm, events); - - // the above window dance hack, could possibly trigger new windows to be created. - // we can just set those windows up normally, as they were created after didFinishLaunching - for window in windows { - window.makeKeyAndVisible(); - } -} - -// AppState::did_finish_launching handles the special transition `Init` -pub fn handle_wakeup_transition(mtm: MainThreadMarker) { - let mut this = AppState::get_mut(mtm); - let wakeup_event = match this.wakeup_transition() { - None => return, - Some(wakeup_event) => wakeup_event, - }; - drop(this); - - handle_nonuser_event(mtm, wakeup_event) -} - -pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) { - handle_nonuser_events(mtm, std::iter::once(event)) -} - -pub(crate) fn handle_nonuser_events>( - mtm: MainThreadMarker, - events: I, -) { - let mut this = AppState::get_mut(mtm); - if this.has_terminated() { - return; - } - - let (mut handler, active_control_flow, processing_redraws) = - match this.try_user_callback_transition() { - UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { - queued_events.extend(events); - return; - } - UserCallbackTransitionResult::Success { - handler, - active_control_flow, - processing_redraws, - } => (handler, active_control_flow, processing_redraws), - }; - drop(this); - - for wrapper in events { - match wrapper { - EventWrapper::StaticEvent(event) => { - if !processing_redraws && event.is_redraw() { - tracing::info!( - "processing `RedrawRequested` during the main event loop" - ); - } else if processing_redraws && !event.is_redraw() { - tracing::warn!( - "processing non `RedrawRequested` event after the main event loop: {:#?}", - event - ); - } - handler.handle_event(event) - } - EventWrapper::ScaleFactorChanged(event) => { - handle_hidpi_proxy(&mut handler, event) - } - } - } - - loop { - let mut this = AppState::get_mut(mtm); - let queued_events = match this.state_mut() { - &mut AppStateImpl::InUserCallback { - ref mut queued_events, - queued_gpu_redraws: _, - } => mem::take(queued_events), - s => bug!("unexpected state {:?}", s), - }; - if queued_events.is_empty() { - let queued_gpu_redraws = match this.take_state() { - AppStateImpl::InUserCallback { - queued_events: _, - queued_gpu_redraws, - } => queued_gpu_redraws, - _ => unreachable!(), - }; - this.app_state = Some(if processing_redraws { - bug_assert!( - queued_gpu_redraws.is_empty(), - "redraw queued while processing redraws" - ); - AppStateImpl::ProcessingRedraws { - handler, - active_control_flow, - } - } else { - AppStateImpl::ProcessingEvents { - handler, - queued_gpu_redraws, - active_control_flow, - } - }); - break; - } - drop(this); - - for wrapper in queued_events { - match wrapper { - EventWrapper::StaticEvent(event) => { - if !processing_redraws && event.is_redraw() { - tracing::info!( - "processing `RedrawRequested` during the main event loop" - ); - } else if processing_redraws && !event.is_redraw() { - tracing::warn!( - "processing non-`RedrawRequested` event after the main event loop: \ - {:#?}", - event - ); - } - handler.handle_event(event) - } - EventWrapper::ScaleFactorChanged(event) => { - handle_hidpi_proxy(&mut handler, event) - } - } - } - } -} - -fn handle_user_events(mtm: MainThreadMarker) { - let mut this = AppState::get_mut(mtm); - let (mut handler, active_control_flow, processing_redraws) = - match this.try_user_callback_transition() { - UserCallbackTransitionResult::ReentrancyPrevented { .. } => { - bug!("unexpected attempted to process an event") - } - UserCallbackTransitionResult::Success { - handler, - active_control_flow, - processing_redraws, - } => (handler, active_control_flow, processing_redraws), - }; - if processing_redraws { - bug!("user events attempted to be sent out while `ProcessingRedraws`"); - } - drop(this); - - handler.handle_event(Event::UserEvent(HandlePendingUserEvents)); - - loop { - let mut this = AppState::get_mut(mtm); - let queued_events = match this.state_mut() { - &mut AppStateImpl::InUserCallback { - ref mut queued_events, - queued_gpu_redraws: _, - } => mem::take(queued_events), - s => bug!("unexpected state {:?}", s), - }; - if queued_events.is_empty() { - let queued_gpu_redraws = match this.take_state() { - AppStateImpl::InUserCallback { - queued_events: _, - queued_gpu_redraws, - } => queued_gpu_redraws, - _ => unreachable!(), - }; - this.app_state = Some(AppStateImpl::ProcessingEvents { - handler, - queued_gpu_redraws, - active_control_flow, - }); - break; - } - drop(this); - - for wrapper in queued_events { - match wrapper { - EventWrapper::StaticEvent(event) => handler.handle_event(event), - EventWrapper::ScaleFactorChanged(event) => { - handle_hidpi_proxy(&mut handler, event) - } - } - } - - handler.handle_event(Event::UserEvent(HandlePendingUserEvents)); - } -} - -pub fn handle_main_events_cleared(mtm: MainThreadMarker) { - let mut this = AppState::get_mut(mtm); - if !this.has_launched() || this.has_terminated() { - return; - } - match this.state_mut() { - AppStateImpl::ProcessingEvents { .. } => {} - _ => bug!("`ProcessingRedraws` happened unexpectedly"), - }; - drop(this); - - handle_user_events(mtm); - - let mut this = AppState::get_mut(mtm); - let redraw_events: Vec = this - .main_events_cleared_transition() - .into_iter() - .map(|window| { - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::RedrawRequested, - }) - }) - .collect(); - drop(this); - - handle_nonuser_events(mtm, redraw_events); - handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait)); -} - -pub fn handle_events_cleared(mtm: MainThreadMarker) { - AppState::get_mut(mtm).events_cleared_transition(); -} - -pub fn terminated(mtm: MainThreadMarker) { - let mut this = AppState::get_mut(mtm); - let mut handler = this.terminated_transition(); - drop(this); - - handler.handle_event(Event::LoopExiting) -} - -fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) { - let ScaleFactorChanged { - suggested_size, - scale_factor, - window, - } = event; - let new_inner_size = Arc::new(Mutex::new(suggested_size)); - let event = Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::ScaleFactorChanged { - scale_factor, - inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), - }, - }; - handler.handle_event(event); - let (view, screen_frame) = get_view_and_screen_frame(&window); - let physical_size = *new_inner_size.lock().unwrap(); - drop(new_inner_size); - let logical_size = physical_size.to_logical(scale_factor); - let size = CGSize::new(logical_size.width, logical_size.height); - let new_frame: CGRect = CGRect::new(screen_frame.origin, size); - view.setFrame(new_frame); -} - -fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained, CGRect) { - let view_controller = window.rootViewController().unwrap(); - let view = view_controller.view().unwrap(); - let bounds = window.bounds(); - let screen = window.screen(); - let screen_space = screen.coordinateSpace(); - let screen_frame = window.convertRect_toCoordinateSpace(bounds, &screen_space); - (view, screen_frame) -} - -struct EventLoopWaker { - timer: CFRunLoopTimerRef, -} - -impl Drop for EventLoopWaker { - fn drop(&mut self) { - unsafe { - CFRunLoopTimerInvalidate(self.timer); - CFRelease(self.timer as _); - } - } -} - -impl EventLoopWaker { - fn new(rl: CFRunLoopRef) -> EventLoopWaker { - extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {} - unsafe { - // Create a timer with a 0.1µs interval (1ns does not work) to mimic polling. - // It is initially setup with a first fire time really far into the - // future, but that gets changed to fire immediately in did_finish_launching - let timer = CFRunLoopTimerCreate( - ptr::null_mut(), - std::f64::MAX, - 0.000_000_1, - 0, - 0, - wakeup_main_loop, - ptr::null_mut(), - ); - CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes); - - EventLoopWaker { timer } - } - } - - fn stop(&mut self) { - unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MAX) } - } - - fn start(&mut self) { - unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MIN) } - } - - fn start_at(&mut self, instant: Instant) { - let now = Instant::now(); - if now >= instant { - self.start(); - } else { - unsafe { - let current = CFAbsoluteTimeGetCurrent(); - let duration = instant - now; - let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0 - + duration.as_secs() as f64; - CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs) - } - } - } -} - -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 OSCapabilities { - fn from_os_version(os_version: NSOperatingSystemVersion) -> Self { - $(let $name = meets_requirements(os_version, $major, $minor);)* - Self { $($name,)* os_version, } - } - } - - impl OSCapabilities {$( - $(#[$attr])* - pub fn $error_name(&self, extra_msg: &str) { - tracing::warn!( - concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"), - $major, $minor, self.os_version.majorVersion, self.os_version.minorVersion, self.os_version.patchVersion, - extra_msg - ) - } - )*} - }; -} - -os_capabilities! { - /// - #[allow(unused)] // error message unused - safe_area_err_msg: "-[UIView safeAreaInsets]", - safe_area: 11-0, - /// - home_indicator_hidden_err_msg: "-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]", - home_indicator_hidden: 11-0, - /// - defer_system_gestures_err_msg: "-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystem]", - defer_system_gestures: 11-0, - /// - maximum_frames_per_second_err_msg: "-[UIScreen maximumFramesPerSecond]", - maximum_frames_per_second: 10-3, - /// - #[allow(unused)] // error message unused - force_touch_err_msg: "-[UITouch force]", - force_touch: 9-0, -} - -fn meets_requirements( - version: NSOperatingSystemVersion, - required_major: NSInteger, - required_minor: NSInteger, -) -> bool { - (version.majorVersion, version.minorVersion) >= (required_major, required_minor) -} - -fn get_version() -> NSOperatingSystemVersion { - let process_info = NSProcessInfo::processInfo(); - let atleast_ios_8 = 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, "`winit` requires iOS version 8 or greater"); - process_info.operatingSystemVersion() -} - -pub fn os_capabilities() -> OSCapabilities { - // Cache the version lookup for efficiency - static OS_CAPABILITIES: OnceLock = OnceLock::new(); - OS_CAPABILITIES - .get_or_init(|| OSCapabilities::from_os_version(get_version())) - .clone() -} diff --git a/rio-window/src/platform_impl/ios/event_loop.rs b/rio-window/src/platform_impl/ios/event_loop.rs deleted file mode 100644 index 4656d3c6..00000000 --- a/rio-window/src/platform_impl/ios/event_loop.rs +++ /dev/null @@ -1,393 +0,0 @@ -use std::collections::VecDeque; -use std::ffi::{c_char, c_int, c_void}; -use std::marker::PhantomData; -use std::ptr::{self, NonNull}; -use std::sync::mpsc::{self, Receiver, Sender}; - -use core_foundation::base::{CFIndex, CFRelease}; -use core_foundation::runloop::{ - kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, - kCFRunLoopDefaultMode, kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, - CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopObserverCreate, CFRunLoopObserverRef, - CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, - CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp, -}; -use objc2::rc::Retained; -use objc2::{msg_send_id, ClassType}; -use objc2_foundation::{MainThreadMarker, NSString}; -use objc2_ui_kit::{ - UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom, -}; - -use crate::error::EventLoopError; -use crate::event::Event; -use crate::event_loop::{ - ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed, -}; -use crate::platform::ios::Idiom; -use crate::platform_impl::platform::app_state::{ - EventLoopHandler, HandlePendingUserEvents, -}; -use crate::window::{CustomCursor, CustomCursorSource}; - -use super::app_delegate::AppDelegate; -use super::app_state::AppState; -use super::{app_state, monitor, MonitorHandle}; - -#[derive(Debug)] -pub struct ActiveEventLoop { - pub(super) mtm: MainThreadMarker, -} - -impl ActiveEventLoop { - pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor { - let _ = source.inner; - CustomCursor { - inner: super::PlatformCustomCursor, - } - } - - pub fn available_monitors(&self) -> VecDeque { - monitor::uiscreens(self.mtm) - } - - pub fn primary_monitor(&self) -> Option { - #[allow(deprecated)] - Some(MonitorHandle::new(UIScreen::mainScreen(self.mtm))) - } - - #[inline] - pub fn listen_device_events(&self, _allowed: DeviceEvents) {} - - #[inline] - pub fn raw_display_handle_rwh_06( - &self, - ) -> Result { - Ok(raw_window_handle::RawDisplayHandle::UiKit( - raw_window_handle::UiKitDisplayHandle::new(), - )) - } - - pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { - AppState::get_mut(self.mtm).set_control_flow(control_flow) - } - - pub(crate) fn control_flow(&self) -> ControlFlow { - AppState::get_mut(self.mtm).control_flow() - } - - pub(crate) fn exit(&self) { - // https://developer.apple.com/library/archive/qa/qa1561/_index.html - // it is not possible to quit an iOS app gracefully and programmatically - tracing::warn!("`ControlFlow::Exit` ignored on iOS"); - } - - pub(crate) fn exiting(&self) -> bool { - false - } - - pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle { - OwnedDisplayHandle - } -} - -#[derive(Clone)] -pub(crate) struct OwnedDisplayHandle; - -impl OwnedDisplayHandle { - #[inline] - pub fn raw_display_handle_rwh_06( - &self, - ) -> Result { - Ok(raw_window_handle::UiKitDisplayHandle::new().into()) - } -} - -fn map_user_event( - mut handler: impl FnMut(Event, &RootActiveEventLoop), - receiver: mpsc::Receiver, -) -> impl FnMut(Event, &RootActiveEventLoop) { - move |event, window_target| match event.map_nonuser_event() { - Ok(event) => (handler)(event, window_target), - Err(_) => { - for event in receiver.try_iter() { - (handler)(Event::UserEvent(event), window_target); - } - } - } -} - -pub struct EventLoop { - mtm: MainThreadMarker, - sender: Sender, - receiver: Receiver, - window_target: RootActiveEventLoop, -} - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct PlatformSpecificEventLoopAttributes {} - -impl EventLoop { - pub(crate) fn new( - _: &PlatformSpecificEventLoopAttributes, - ) -> Result, EventLoopError> { - let mtm = MainThreadMarker::new() - .expect("On iOS, `EventLoop` must be created on the main thread"); - - static mut SINGLETON_INIT: bool = false; - unsafe { - assert!( - !SINGLETON_INIT, - "Only one `EventLoop` is supported on iOS. `EventLoopProxy` might be helpful" - ); - SINGLETON_INIT = true; - } - - let (sender, receiver) = mpsc::channel(); - - // this line sets up the main run loop before `UIApplicationMain` - setup_control_flow_observers(); - - Ok(EventLoop { - mtm, - sender, - receiver, - window_target: RootActiveEventLoop { - p: ActiveEventLoop { mtm }, - _marker: PhantomData, - }, - }) - } - - pub fn run(self, handler: F) -> ! - where - F: FnMut(Event, &RootActiveEventLoop), - { - let application: Option> = - unsafe { msg_send_id![UIApplication::class(), sharedApplication] }; - assert!( - application.is_none(), - "\ - `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\nNote: \ - `EventLoop::run_app` calls `UIApplicationMain` on iOS", - ); - - let handler = map_user_event(handler, self.receiver); - - let handler = unsafe { - std::mem::transmute::< - Box, &RootActiveEventLoop)>, - Box, &RootActiveEventLoop)>, - >(Box::new(handler)) - }; - - let handler = EventLoopHandler { - handler, - event_loop: self.window_target, - }; - - app_state::will_launch(self.mtm, handler); - - // Ensure application delegate is initialized - let _ = AppDelegate::class(); - - extern "C" { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut c_int; - fn _NSGetArgv() -> *mut *mut *mut c_char; - } - - unsafe { - UIApplicationMain( - *_NSGetArgc(), - NonNull::new(*_NSGetArgv()).unwrap(), - None, - Some(&NSString::from_str(AppDelegate::NAME)), - ) - }; - unreachable!() - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.sender.clone()) - } - - pub fn window_target(&self) -> &RootActiveEventLoop { - &self.window_target - } -} - -// EventLoopExtIOS -impl EventLoop { - pub fn idiom(&self) -> Idiom { - match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() { - UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified, - UIUserInterfaceIdiom::Phone => Idiom::Phone, - UIUserInterfaceIdiom::Pad => Idiom::Pad, - UIUserInterfaceIdiom::TV => Idiom::TV, - UIUserInterfaceIdiom::CarPlay => Idiom::CarPlay, - _ => Idiom::Unspecified, - } - } -} - -pub struct EventLoopProxy { - sender: Sender, - source: CFRunLoopSourceRef, -} - -unsafe impl Send for EventLoopProxy {} -unsafe impl Sync for EventLoopProxy {} - -impl Clone for EventLoopProxy { - fn clone(&self) -> EventLoopProxy { - EventLoopProxy::new(self.sender.clone()) - } -} - -impl Drop for EventLoopProxy { - fn drop(&mut self) { - unsafe { - CFRunLoopSourceInvalidate(self.source); - CFRelease(self.source as _); - } - } -} - -impl EventLoopProxy { - fn new(sender: Sender) -> EventLoopProxy { - unsafe { - // just wake up the eventloop - extern "C" fn event_loop_proxy_handler(_: *const c_void) {} - - // adding a Source to the main CFRunLoop lets us wake it up and - // process user events through the normal OS EventLoop mechanisms. - let rl = CFRunLoopGetMain(); - let mut context = CFRunLoopSourceContext { - version: 0, - info: ptr::null_mut(), - retain: None, - release: None, - copyDescription: None, - equal: None, - hash: None, - schedule: None, - cancel: None, - perform: event_loop_proxy_handler, - }; - let source = CFRunLoopSourceCreate( - ptr::null_mut(), - CFIndex::max_value() - 1, - &mut context, - ); - CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); - CFRunLoopWakeUp(rl); - - EventLoopProxy { sender, source } - } - } - - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender - .send(event) - .map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?; - unsafe { - // let the main thread know there's a new event - CFRunLoopSourceSignal(self.source); - let rl = CFRunLoopGetMain(); - CFRunLoopWakeUp(rl); - } - Ok(()) - } -} - -fn setup_control_flow_observers() { - unsafe { - // begin is queued with the highest priority to ensure it is processed before other - // observers - extern "C" fn control_flow_begin_handler( - _: CFRunLoopObserverRef, - activity: CFRunLoopActivity, - _: *mut c_void, - ) { - let mtm = MainThreadMarker::new().unwrap(); - #[allow(non_upper_case_globals)] - match activity { - kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm), - _ => unreachable!(), - } - } - - // Core Animation registers its `CFRunLoopObserver` that performs drawing operations in - // `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end - // priority to be 0, in order to send AboutToWait before RedrawRequested. This value was - // chosen conservatively to guard against apple using different priorities for their redraw - // observers in different OS's or on different devices. If it so happens that it's too - // conservative, the main symptom would be non-redraw events coming in after `AboutToWait`. - // - // The value of `0x1e8480` was determined by inspecting stack traces and the associated - // registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4. - // - // Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4. - extern "C" fn control_flow_main_end_handler( - _: CFRunLoopObserverRef, - activity: CFRunLoopActivity, - _: *mut c_void, - ) { - let mtm = MainThreadMarker::new().unwrap(); - #[allow(non_upper_case_globals)] - match activity { - kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm), - kCFRunLoopExit => {} // may happen when running on macOS - _ => unreachable!(), - } - } - - // end is queued with the lowest priority to ensure it is processed after other observers - extern "C" fn control_flow_end_handler( - _: CFRunLoopObserverRef, - activity: CFRunLoopActivity, - _: *mut c_void, - ) { - let mtm = MainThreadMarker::new().unwrap(); - #[allow(non_upper_case_globals)] - match activity { - kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm), - kCFRunLoopExit => {} // may happen when running on macOS - _ => unreachable!(), - } - } - - let main_loop = CFRunLoopGetMain(); - - let begin_observer = CFRunLoopObserverCreate( - ptr::null_mut(), - kCFRunLoopAfterWaiting, - 1, // repeat = true - CFIndex::min_value(), - control_flow_begin_handler, - ptr::null_mut(), - ); - CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode); - - let main_end_observer = CFRunLoopObserverCreate( - ptr::null_mut(), - kCFRunLoopExit | kCFRunLoopBeforeWaiting, - 1, // repeat = true - 0, // see comment on `control_flow_main_end_handler` - control_flow_main_end_handler, - ptr::null_mut(), - ); - CFRunLoopAddObserver(main_loop, main_end_observer, kCFRunLoopDefaultMode); - - let end_observer = CFRunLoopObserverCreate( - ptr::null_mut(), - kCFRunLoopExit | kCFRunLoopBeforeWaiting, - 1, // repeat = true - CFIndex::max_value(), - control_flow_end_handler, - ptr::null_mut(), - ); - CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode); - } -} diff --git a/rio-window/src/platform_impl/ios/mod.rs b/rio-window/src/platform_impl/ios/mod.rs deleted file mode 100644 index bf2c011f..00000000 --- a/rio-window/src/platform_impl/ios/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![cfg(ios_platform)] -#![allow(clippy::let_unit_value)] - -mod app_delegate; -mod app_state; -mod event_loop; -mod monitor; -mod view; -mod view_controller; -mod window; - -use std::fmt; - -use crate::event::DeviceId as RootDeviceId; - -pub(crate) use self::event_loop::{ - ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle, - PlatformSpecificEventLoopAttributes, -}; -pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle}; -pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window, WindowId}; -pub(crate) use crate::cursor::{ - NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, -}; -pub(crate) use crate::icon::NoIcon as PlatformIcon; -pub(crate) use crate::platform_impl::Fullscreen; - -/// There is no way to detect which device that performed a certain event in -/// UIKit (i.e. you can't differentiate between different external keyboards, -/// or whether it was the main touchscreen, assistive technologies, or some -/// other pointer device that caused a touch event). -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId; - -impl DeviceId { - pub const unsafe fn dummy() -> Self { - DeviceId - } -} - -pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId); - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct KeyEventExtra {} - -#[derive(Debug)] -pub enum OsError {} - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "os error") - } -} diff --git a/rio-window/src/platform_impl/ios/monitor.rs b/rio-window/src/platform_impl/ios/monitor.rs deleted file mode 100644 index 6765b068..00000000 --- a/rio-window/src/platform_impl/ios/monitor.rs +++ /dev/null @@ -1,262 +0,0 @@ -#![allow(clippy::unnecessary_cast)] - -use std::collections::{BTreeSet, VecDeque}; -use std::{fmt, hash, ptr}; - -use objc2::mutability::IsRetainable; -use objc2::rc::Retained; -use objc2::Message; -use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger}; -use objc2_ui_kit::{UIScreen, UIScreenMode}; - -use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::monitor::VideoModeHandle as RootVideoModeHandle; -use crate::platform_impl::platform::app_state; - -// Workaround for `MainThreadBound` implementing almost no traits -#[derive(Debug)] -struct MainThreadBoundDelegateImpls(MainThreadBound>); - -impl Clone for MainThreadBoundDelegateImpls { - fn clone(&self) -> Self { - Self(run_on_main(|mtm| { - MainThreadBound::new(Retained::clone(self.0.get(mtm)), mtm) - })) - } -} - -impl hash::Hash for MainThreadBoundDelegateImpls { - fn hash(&self, state: &mut H) { - // SAFETY: Marker only used to get the pointer - let mtm = unsafe { MainThreadMarker::new_unchecked() }; - Retained::as_ptr(self.0.get(mtm)).hash(state); - } -} - -impl PartialEq for MainThreadBoundDelegateImpls { - fn eq(&self, other: &Self) -> bool { - // SAFETY: Marker only used to get the pointer - let mtm = unsafe { MainThreadMarker::new_unchecked() }; - Retained::as_ptr(self.0.get(mtm)) == Retained::as_ptr(other.0.get(mtm)) - } -} - -impl Eq for MainThreadBoundDelegateImpls {} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct VideoModeHandle { - pub(crate) size: (u32, u32), - pub(crate) bit_depth: u16, - pub(crate) refresh_rate_millihertz: u32, - screen_mode: MainThreadBoundDelegateImpls, - pub(crate) monitor: MonitorHandle, -} - -impl VideoModeHandle { - fn new( - uiscreen: Retained, - screen_mode: Retained, - mtm: MainThreadMarker, - ) -> VideoModeHandle { - let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); - let size = screen_mode.size(); - VideoModeHandle { - size: (size.width as u32, size.height as u32), - bit_depth: 32, - refresh_rate_millihertz, - screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new( - screen_mode, - mtm, - )), - monitor: MonitorHandle::new(uiscreen), - } - } - - pub fn size(&self) -> PhysicalSize { - self.size.into() - } - - pub fn bit_depth(&self) -> u16 { - self.bit_depth - } - - pub fn refresh_rate_millihertz(&self) -> u32 { - self.refresh_rate_millihertz - } - - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() - } - - pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained { - self.screen_mode.0.get(mtm) - } -} - -pub struct MonitorHandle { - ui_screen: MainThreadBound>, -} - -impl Clone for MonitorHandle { - fn clone(&self) -> Self { - run_on_main(|mtm| Self { - ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm), - }) - } -} - -impl hash::Hash for MonitorHandle { - fn hash(&self, state: &mut H) { - (self as *const Self).hash(state); - } -} - -impl PartialEq for MonitorHandle { - fn eq(&self, other: &Self) -> bool { - ptr::eq(self, other) - } -} - -impl Eq for MonitorHandle {} - -impl PartialOrd for MonitorHandle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for MonitorHandle { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - // TODO: Make a better ordering - (self as *const Self).cmp(&(other as *const Self)) - } -} - -impl fmt::Debug for MonitorHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MonitorHandle") - .field("name", &self.name()) - .field("size", &self.size()) - .field("position", &self.position()) - .field("scale_factor", &self.scale_factor()) - .field("refresh_rate_millihertz", &self.refresh_rate_millihertz()) - .finish_non_exhaustive() - } -} - -impl MonitorHandle { - pub(crate) fn new(ui_screen: Retained) -> Self { - // Holding `Retained` implies we're on the main thread. - let mtm = MainThreadMarker::new().unwrap(); - Self { - ui_screen: MainThreadBound::new(ui_screen, mtm), - } - } - - pub fn name(&self) -> Option { - run_on_main(|mtm| { - #[allow(deprecated)] - let main = UIScreen::mainScreen(mtm); - if *self.ui_screen(mtm) == main { - Some("Primary".to_string()) - } else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() { - Some("Mirrored".to_string()) - } else { - #[allow(deprecated)] - UIScreen::screens(mtm) - .iter() - .position(|rhs| rhs == &**self.ui_screen(mtm)) - .map(|idx| idx.to_string()) - } - }) - } - - pub fn size(&self) -> PhysicalSize { - let bounds = self - .ui_screen - .get_on_main(|ui_screen| ui_screen.nativeBounds()); - PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32) - } - - pub fn position(&self) -> PhysicalPosition { - let bounds = self - .ui_screen - .get_on_main(|ui_screen| ui_screen.nativeBounds()); - (bounds.origin.x as f64, bounds.origin.y as f64).into() - } - - pub fn scale_factor(&self) -> f64 { - self.ui_screen - .get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 - } - - pub fn refresh_rate_millihertz(&self) -> Option { - Some( - self.ui_screen - .get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)), - ) - } - - pub fn video_modes(&self) -> impl Iterator { - run_on_main(|mtm| { - let ui_screen = self.ui_screen(mtm); - // Use Ord impl of RootVideoModeHandle - - let modes: BTreeSet<_> = ui_screen - .availableModes() - .into_iter() - .map(|mode| RootVideoModeHandle { - video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm), - }) - .collect(); - - modes.into_iter().map(|mode| mode.video_mode) - }) - } - - pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained { - self.ui_screen.get(mtm) - } - - pub fn preferred_video_mode(&self) -> VideoModeHandle { - run_on_main(|mtm| { - VideoModeHandle::new( - self.ui_screen(mtm).clone(), - self.ui_screen(mtm).preferredMode().unwrap(), - mtm, - ) - }) - } -} - -fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 { - let refresh_rate_millihertz: NSInteger = { - let os_capabilities = app_state::os_capabilities(); - if os_capabilities.maximum_frames_per_second { - 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 - } - }; - - refresh_rate_millihertz as u32 * 1000 -} - -pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque { - #[allow(deprecated)] - UIScreen::screens(mtm) - .into_iter() - .map(MonitorHandle::new) - .collect() -} diff --git a/rio-window/src/platform_impl/ios/view.rs b/rio-window/src/platform_impl/ios/view.rs deleted file mode 100644 index 1ef54ef9..00000000 --- a/rio-window/src/platform_impl/ios/view.rs +++ /dev/null @@ -1,525 +0,0 @@ -#![allow(clippy::unnecessary_cast)] -use std::cell::{Cell, RefCell}; - -use objc2::rc::Retained; -use objc2::runtime::{NSObjectProtocol, ProtocolObject}; -use objc2::{ - declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, -}; -use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet}; -use objc2_ui_kit::{ - UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer, - UIGestureRecognizerDelegate, UIGestureRecognizerState, UIPanGestureRecognizer, - UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, - UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType, UITraitEnvironment, - UIView, -}; - -use super::app_state::{self, EventWrapper}; -use super::window::WinitUIWindow; -use crate::dpi::PhysicalPosition; -use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent}; -use crate::platform_impl::platform::DEVICE_ID; -use crate::window::{WindowAttributes, WindowId as RootWindowId}; - -pub struct WinitViewState { - pinch_gesture_recognizer: RefCell>>, - doubletap_gesture_recognizer: RefCell>>, - rotation_gesture_recognizer: RefCell>>, - pan_gesture_recognizer: RefCell>>, - - // for iOS delta references the start of the Gesture - rotation_last_delta: Cell, - pinch_last_delta: Cell, - pan_last_delta: Cell, -} - -declare_class!( - pub(crate) struct WinitView; - - unsafe impl ClassType for WinitView { - #[inherits(UIResponder, NSObject)] - type Super = UIView; - type Mutability = mutability::MainThreadOnly; - const NAME: &'static str = "WinitUIView"; - } - - impl DeclaredClass for WinitView { - type Ivars = WinitViewState; - } - - unsafe impl WinitView { - #[method(drawRect:)] - fn draw_rect(&self, rect: CGRect) { - let mtm = MainThreadMarker::new().unwrap(); - let window = self.window().unwrap(); - app_state::handle_nonuser_event( - mtm, - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::RedrawRequested, - }), - ); - let _: () = unsafe { msg_send![super(self), drawRect: rect] }; - } - - #[method(layoutSubviews)] - fn layout_subviews(&self) { - let mtm = MainThreadMarker::new().unwrap(); - let _: () = unsafe { msg_send![super(self), layoutSubviews] }; - - let window = self.window().unwrap(); - let window_bounds = window.bounds(); - let screen = window.screen(); - let screen_space = screen.coordinateSpace(); - let screen_frame = self.convertRect_toCoordinateSpace(window_bounds, &screen_space); - let scale_factor = screen.scale(); - let size = crate::dpi::LogicalSize { - width: screen_frame.size.width as f64, - height: screen_frame.size.height as f64, - } - .to_physical(scale_factor as f64); - - // If the app is started in landscape, the view frame and window bounds can be mismatched. - // The view frame will be in portrait and the window bounds in landscape. So apply the - // window bounds to the view frame to make it consistent. - let view_frame = self.frame(); - if view_frame != window_bounds { - self.setFrame(window_bounds); - } - - app_state::handle_nonuser_event( - mtm, - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::Resized(size), - }), - ); - } - - #[method(setContentScaleFactor:)] - fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) { - let mtm = MainThreadMarker::new().unwrap(); - let _: () = - unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] }; - - // `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 - let window = match self.window() { - Some(window) => window, - None => 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 - let scale_factor = self.contentScaleFactor(); - assert!( - !scale_factor.is_nan() - && scale_factor.is_finite() - && scale_factor.is_sign_positive() - && scale_factor > 0.0, - "invalid scale_factor set on UIView", - ); - let scale_factor = scale_factor as f64; - let bounds = self.bounds(); - let screen = window.screen(); - let screen_space = screen.coordinateSpace(); - let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space); - let size = crate::dpi::LogicalSize { - width: screen_frame.size.width as f64, - height: screen_frame.size.height as f64, - }; - let window_id = RootWindowId(window.id()); - app_state::handle_nonuser_events( - mtm, - std::iter::once(EventWrapper::ScaleFactorChanged( - app_state::ScaleFactorChanged { - window, - scale_factor, - suggested_size: size.to_physical(scale_factor), - }, - )) - .chain(std::iter::once(EventWrapper::StaticEvent( - Event::WindowEvent { - window_id, - event: WindowEvent::Resized(size.to_physical(scale_factor)), - }, - ))), - ); - } - - #[method(touchesBegan:withEvent:)] - fn touches_began(&self, touches: &NSSet, _event: Option<&UIEvent>) { - self.handle_touches(touches) - } - - #[method(touchesMoved:withEvent:)] - fn touches_moved(&self, touches: &NSSet, _event: Option<&UIEvent>) { - self.handle_touches(touches) - } - - #[method(touchesEnded:withEvent:)] - fn touches_ended(&self, touches: &NSSet, _event: Option<&UIEvent>) { - self.handle_touches(touches) - } - - #[method(touchesCancelled:withEvent:)] - fn touches_cancelled(&self, touches: &NSSet, _event: Option<&UIEvent>) { - self.handle_touches(touches) - } - - #[method(pinchGesture:)] - fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) { - let window = self.window().unwrap(); - - let (phase, delta) = match recognizer.state() { - UIGestureRecognizerState::Began => { - self.ivars().pinch_last_delta.set(recognizer.scale()); - (TouchPhase::Started, 0.0) - } - UIGestureRecognizerState::Changed => { - let last_scale: f64 = self.ivars().pinch_last_delta.replace(recognizer.scale()); - (TouchPhase::Moved, recognizer.scale() - last_scale) - } - UIGestureRecognizerState::Ended => { - let last_scale: f64 = self.ivars().pinch_last_delta.replace(0.0); - (TouchPhase::Moved, recognizer.scale() - last_scale) - } - UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => { - self.ivars().rotation_last_delta.set(0.0); - // Pass -delta so that action is reversed - (TouchPhase::Cancelled, -recognizer.scale()) - } - state => panic!("unexpected recognizer state: {:?}", state), - }; - - let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::PinchGesture { - device_id: DEVICE_ID, - delta: delta as f64, - phase, - }, - }); - - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, gesture_event); - } - - #[method(doubleTapGesture:)] - fn double_tap_gesture(&self, recognizer: &UITapGestureRecognizer) { - let window = self.window().unwrap(); - - if recognizer.state() == UIGestureRecognizerState::Ended { - let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::DoubleTapGesture { - device_id: DEVICE_ID, - }, - }); - - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, gesture_event); - } - } - - #[method(rotationGesture:)] - fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) { - let window = self.window().unwrap(); - - let (phase, delta) = match recognizer.state() { - UIGestureRecognizerState::Began => { - self.ivars().rotation_last_delta.set(0.0); - - (TouchPhase::Started, 0.0) - } - UIGestureRecognizerState::Changed => { - let last_rotation = self.ivars().rotation_last_delta.replace(recognizer.rotation()); - - (TouchPhase::Moved, recognizer.rotation() - last_rotation) - } - UIGestureRecognizerState::Ended => { - let last_rotation = self.ivars().rotation_last_delta.replace(0.0); - - (TouchPhase::Ended, recognizer.rotation() - last_rotation) - } - UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => { - self.ivars().rotation_last_delta.set(0.0); - - // Pass -delta so that action is reversed - (TouchPhase::Cancelled, -recognizer.rotation()) - } - state => panic!("unexpected recognizer state: {:?}", state), - }; - - // Make delta negative to match macos, convert to degrees - let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::RotationGesture { - device_id: DEVICE_ID, - delta: -delta.to_degrees() as _, - phase, - }, - }); - - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, gesture_event); - } - - #[method(panGesture:)] - fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) { - let window = self.window().unwrap(); - - let translation = recognizer.translationInView(Some(self)); - - let (phase, dx, dy) = match recognizer.state() { - UIGestureRecognizerState::Began => { - self.ivars().pan_last_delta.set(translation); - - (TouchPhase::Started, 0.0, 0.0) - } - UIGestureRecognizerState::Changed => { - let last_pan: CGPoint = self.ivars().pan_last_delta.replace(translation); - - let dx = translation.x - last_pan.x; - let dy = translation.y - last_pan.y; - - (TouchPhase::Moved, dx, dy) - } - UIGestureRecognizerState::Ended => { - let last_pan: CGPoint = self.ivars().pan_last_delta.replace(CGPoint{x:0.0, y:0.0}); - - let dx = translation.x - last_pan.x; - let dy = translation.y - last_pan.y; - - (TouchPhase::Ended, dx, dy) - } - UIGestureRecognizerState::Cancelled | UIGestureRecognizerState::Failed => { - let last_pan: CGPoint = self.ivars().pan_last_delta.replace(CGPoint{x:0.0, y:0.0}); - - // Pass -delta so that action is reversed - (TouchPhase::Cancelled, -last_pan.x, -last_pan.y) - } - state => panic!("unexpected recognizer state: {:?}", state), - }; - - - let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::PanGesture { - device_id: DEVICE_ID, - delta: PhysicalPosition::new(dx as _, dy as _), - phase, - }, - }); - - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, gesture_event); - } - } - - unsafe impl NSObjectProtocol for WinitView {} - - unsafe impl UIGestureRecognizerDelegate for WinitView { - #[method(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)] - fn should_recognize_simultaneously(&self, _gesture_recognizer: &UIGestureRecognizer, _other_gesture_recognizer: &UIGestureRecognizer) -> bool { - true - } - } -); - -impl WinitView { - pub(crate) fn new( - mtm: MainThreadMarker, - window_attributes: &WindowAttributes, - frame: CGRect, - ) -> Retained { - let this = mtm.alloc().set_ivars(WinitViewState { - pinch_gesture_recognizer: RefCell::new(None), - doubletap_gesture_recognizer: RefCell::new(None), - rotation_gesture_recognizer: RefCell::new(None), - pan_gesture_recognizer: RefCell::new(None), - - rotation_last_delta: Cell::new(0.0), - pinch_last_delta: Cell::new(0.0), - pan_last_delta: Cell::new(CGPoint { x: 0.0, y: 0.0 }), - }); - let this: Retained = - unsafe { msg_send_id![super(this), initWithFrame: frame] }; - - this.setMultipleTouchEnabled(true); - - if let Some(scale_factor) = window_attributes.platform_specific.scale_factor { - this.setContentScaleFactor(scale_factor as _); - } - - this - } - - fn window(&self) -> Option> { - // SAFETY: `WinitView`s are always installed in a `WinitUIWindow` - (**self) - .window() - .map(|window| unsafe { Retained::cast(window) }) - } - - pub(crate) fn recognize_pinch_gesture(&self, should_recognize: bool) { - let mtm = MainThreadMarker::from(self); - if should_recognize { - if self.ivars().pinch_gesture_recognizer.borrow().is_none() { - let pinch = unsafe { - UIPinchGestureRecognizer::initWithTarget_action( - mtm.alloc(), - Some(self), - Some(sel!(pinchGesture:)), - ) - }; - pinch.setDelegate(Some(ProtocolObject::from_ref(self))); - self.addGestureRecognizer(&pinch); - self.ivars().pinch_gesture_recognizer.replace(Some(pinch)); - } - } else if let Some(recognizer) = self.ivars().pinch_gesture_recognizer.take() { - self.removeGestureRecognizer(&recognizer); - } - } - - pub(crate) fn recognize_pan_gesture( - &self, - should_recognize: bool, - minimum_number_of_touches: u8, - maximum_number_of_touches: u8, - ) { - let mtm = MainThreadMarker::from(self); - if should_recognize { - if self.ivars().pan_gesture_recognizer.borrow().is_none() { - let pan = unsafe { - UIPanGestureRecognizer::initWithTarget_action( - mtm.alloc(), - Some(self), - Some(sel!(panGesture:)), - ) - }; - pan.setDelegate(Some(ProtocolObject::from_ref(self))); - pan.setMinimumNumberOfTouches(minimum_number_of_touches as _); - pan.setMaximumNumberOfTouches(maximum_number_of_touches as _); - self.addGestureRecognizer(&pan); - self.ivars().pan_gesture_recognizer.replace(Some(pan)); - } - } else if let Some(recognizer) = self.ivars().pan_gesture_recognizer.take() { - self.removeGestureRecognizer(&recognizer); - } - } - - pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) { - let mtm = MainThreadMarker::from(self); - if should_recognize { - if self.ivars().doubletap_gesture_recognizer.borrow().is_none() { - let tap = unsafe { - UITapGestureRecognizer::initWithTarget_action( - mtm.alloc(), - Some(self), - Some(sel!(doubleTapGesture:)), - ) - }; - tap.setDelegate(Some(ProtocolObject::from_ref(self))); - tap.setNumberOfTapsRequired(2); - tap.setNumberOfTouchesRequired(1); - self.addGestureRecognizer(&tap); - self.ivars().doubletap_gesture_recognizer.replace(Some(tap)); - } - } else if let Some(recognizer) = self.ivars().doubletap_gesture_recognizer.take() - { - self.removeGestureRecognizer(&recognizer); - } - } - - pub(crate) fn recognize_rotation_gesture(&self, should_recognize: bool) { - let mtm = MainThreadMarker::from(self); - if should_recognize { - if self.ivars().rotation_gesture_recognizer.borrow().is_none() { - let rotation = unsafe { - UIRotationGestureRecognizer::initWithTarget_action( - mtm.alloc(), - Some(self), - Some(sel!(rotationGesture:)), - ) - }; - rotation.setDelegate(Some(ProtocolObject::from_ref(self))); - self.addGestureRecognizer(&rotation); - self.ivars() - .rotation_gesture_recognizer - .replace(Some(rotation)); - } - } else if let Some(recognizer) = self.ivars().rotation_gesture_recognizer.take() { - self.removeGestureRecognizer(&recognizer); - } - } - - fn handle_touches(&self, touches: &NSSet) { - let window = self.window().unwrap(); - let mut touch_events = Vec::new(); - let os_supports_force = app_state::os_capabilities().force_touch; - for touch in touches { - let logical_location = touch.locationInView(None); - let touch_type = touch.r#type(); - let force = if os_supports_force { - let trait_collection = self.traitCollection(); - let touch_capability = trait_collection.forceTouchCapability(); - // Both the OS _and_ the device need to be checked for force touch support. - if touch_capability == UIForceTouchCapability::Available - || touch_type == UITouchType::Pencil - { - let force = touch.force(); - let max_possible_force = touch.maximumPossibleForce(); - let altitude_angle: Option = if touch_type == UITouchType::Pencil - { - let angle = 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 *const UITouch as u64; - let phase = 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:?}"), - }; - - let physical_location = { - let scale_factor = self.contentScaleFactor(); - PhysicalPosition::from_logical::<(f64, f64), f64>( - (logical_location.x as _, logical_location.y as _), - scale_factor as f64, - ) - }; - touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.id()), - event: WindowEvent::Touch(Touch { - device_id: DEVICE_ID, - id: touch_id, - location: physical_location, - force, - phase, - }), - })); - } - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_events(mtm, touch_events); - } -} diff --git a/rio-window/src/platform_impl/ios/view_controller.rs b/rio-window/src/platform_impl/ios/view_controller.rs deleted file mode 100644 index d22a651c..00000000 --- a/rio-window/src/platform_impl/ios/view_controller.rs +++ /dev/null @@ -1,200 +0,0 @@ -use std::cell::Cell; - -use objc2::rc::Retained; -use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; -use objc2_foundation::{MainThreadMarker, NSObject}; -use objc2_ui_kit::{ - UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle, - UIUserInterfaceIdiom, UIView, UIViewController, -}; - -use super::app_state::{self}; -use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; -use crate::window::WindowAttributes; - -pub struct ViewControllerState { - prefers_status_bar_hidden: Cell, - preferred_status_bar_style: Cell, - prefers_home_indicator_auto_hidden: Cell, - supported_orientations: Cell, - preferred_screen_edges_deferring_system_gestures: Cell, -} - -declare_class!( - pub(crate) struct WinitViewController; - - unsafe impl ClassType for WinitViewController { - #[inherits(UIResponder, NSObject)] - type Super = UIViewController; - type Mutability = mutability::MainThreadOnly; - const NAME: &'static str = "WinitUIViewController"; - } - - impl DeclaredClass for WinitViewController { - type Ivars = ViewControllerState; - } - - unsafe impl WinitViewController { - #[method(shouldAutorotate)] - fn should_autorotate(&self) -> bool { - true - } - - #[method(prefersStatusBarHidden)] - fn prefers_status_bar_hidden(&self) -> bool { - self.ivars().prefers_status_bar_hidden.get() - } - - #[method(preferredStatusBarStyle)] - fn preferred_status_bar_style(&self) -> UIStatusBarStyle { - self.ivars().preferred_status_bar_style.get() - } - - #[method(prefersHomeIndicatorAutoHidden)] - fn prefers_home_indicator_auto_hidden(&self) -> bool { - self.ivars().prefers_home_indicator_auto_hidden.get() - } - - #[method(supportedInterfaceOrientations)] - fn supported_orientations(&self) -> UIInterfaceOrientationMask { - self.ivars().supported_orientations.get() - } - - #[method(preferredScreenEdgesDeferringSystemGestures)] - fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge { - self.ivars() - .preferred_screen_edges_deferring_system_gestures - .get() - } - } -); - -impl WinitViewController { - pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) { - self.ivars().prefers_status_bar_hidden.set(val); - self.setNeedsStatusBarAppearanceUpdate(); - } - - pub(crate) fn set_preferred_status_bar_style(&self, val: StatusBarStyle) { - let val = match val { - StatusBarStyle::Default => UIStatusBarStyle::Default, - StatusBarStyle::LightContent => UIStatusBarStyle::LightContent, - StatusBarStyle::DarkContent => UIStatusBarStyle::DarkContent, - }; - self.ivars().preferred_status_bar_style.set(val); - self.setNeedsStatusBarAppearanceUpdate(); - } - - pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) { - self.ivars().prefers_home_indicator_auto_hidden.set(val); - let os_capabilities = app_state::os_capabilities(); - if os_capabilities.home_indicator_hidden { - self.setNeedsUpdateOfHomeIndicatorAutoHidden(); - } else { - os_capabilities.home_indicator_hidden_err_msg("ignoring") - } - } - - pub(crate) fn set_preferred_screen_edges_deferring_system_gestures( - &self, - val: ScreenEdge, - ) { - let val = { - assert_eq!( - val.bits() & !ScreenEdge::ALL.bits(), - 0, - "invalid `ScreenEdge`" - ); - UIRectEdge(val.bits().into()) - }; - self.ivars() - .preferred_screen_edges_deferring_system_gestures - .set(val); - let os_capabilities = app_state::os_capabilities(); - if os_capabilities.defer_system_gestures { - self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures(); - } else { - os_capabilities.defer_system_gestures_err_msg("ignoring") - } - } - - pub(crate) fn set_supported_interface_orientations( - &self, - mtm: MainThreadMarker, - valid_orientations: ValidOrientations, - ) { - let mask = match ( - valid_orientations, - UIDevice::currentDevice(mtm).userInterfaceIdiom(), - ) { - (ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => { - UIInterfaceOrientationMask::AllButUpsideDown - } - (ValidOrientations::LandscapeAndPortrait, _) => { - UIInterfaceOrientationMask::All - } - (ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape, - (ValidOrientations::Portrait, UIUserInterfaceIdiom::Phone) => { - UIInterfaceOrientationMask::Portrait - } - (ValidOrientations::Portrait, _) => { - UIInterfaceOrientationMask::Portrait - | UIInterfaceOrientationMask::PortraitUpsideDown - } - }; - self.ivars().supported_orientations.set(mask); - #[allow(deprecated)] - UIViewController::attemptRotationToDeviceOrientation(mtm); - } - - pub(crate) fn new( - mtm: MainThreadMarker, - window_attributes: &WindowAttributes, - view: &UIView, - ) -> Retained { - // These are set properly below, we just to set them to something in the meantime. - let this = mtm.alloc().set_ivars(ViewControllerState { - prefers_status_bar_hidden: Cell::new(false), - preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default), - prefers_home_indicator_auto_hidden: Cell::new(false), - supported_orientations: Cell::new(UIInterfaceOrientationMask::All), - preferred_screen_edges_deferring_system_gestures: Cell::new( - UIRectEdge::empty(), - ), - }); - let this: Retained = unsafe { msg_send_id![super(this), init] }; - - this.set_prefers_status_bar_hidden( - window_attributes - .platform_specific - .prefers_status_bar_hidden, - ); - - this.set_preferred_status_bar_style( - window_attributes - .platform_specific - .preferred_status_bar_style, - ); - - this.set_supported_interface_orientations( - mtm, - window_attributes.platform_specific.valid_orientations, - ); - - this.set_prefers_home_indicator_auto_hidden( - window_attributes - .platform_specific - .prefers_home_indicator_hidden, - ); - - this.set_preferred_screen_edges_deferring_system_gestures( - window_attributes - .platform_specific - .preferred_screen_edges_deferring_system_gestures, - ); - - this.setView(Some(view)); - - this - } -} diff --git a/rio-window/src/platform_impl/ios/window.rs b/rio-window/src/platform_impl/ios/window.rs deleted file mode 100644 index 184d2273..00000000 --- a/rio-window/src/platform_impl/ios/window.rs +++ /dev/null @@ -1,769 +0,0 @@ -#![allow(clippy::unnecessary_cast)] - -use std::collections::VecDeque; - -use objc2::rc::Retained; -use objc2::runtime::{AnyObject, NSObject}; -use objc2::{ - class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass, -}; -use objc2_foundation::{ - CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObjectProtocol, -}; -use objc2_ui_kit::{ - UIApplication, UICoordinateSpace, UIResponder, UIScreen, - UIScreenOverscanCompensation, UIViewController, UIWindow, -}; -use tracing::{debug, warn}; - -use super::app_state::EventWrapper; -use super::view::WinitView; -use super::view_controller::WinitViewController; -use crate::cursor::Cursor; -use crate::dpi::{ - LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, -}; -use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; -use crate::event::{Event, WindowEvent}; -use crate::icon::Icon; -use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; -use crate::platform_impl::platform::{ - app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle, -}; -use crate::window::{ - CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, - WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel, -}; - -declare_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct WinitUIWindow; - - unsafe impl ClassType for WinitUIWindow { - #[inherits(UIResponder, NSObject)] - type Super = UIWindow; - type Mutability = mutability::MainThreadOnly; - const NAME: &'static str = "WinitUIWindow"; - } - - impl DeclaredClass for WinitUIWindow {} - - unsafe impl WinitUIWindow { - #[method(becomeKeyWindow)] - fn become_key_window(&self) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event( - mtm, - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(self.id()), - event: WindowEvent::Focused(true), - }), - ); - let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; - } - - #[method(resignKeyWindow)] - fn resign_key_window(&self) { - let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event( - mtm, - EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(self.id()), - event: WindowEvent::Focused(false), - }), - ); - let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; - } - } -); - -impl WinitUIWindow { - pub(crate) fn new( - mtm: MainThreadMarker, - window_attributes: &WindowAttributes, - frame: CGRect, - view_controller: &UIViewController, - ) -> Retained { - let this: Retained = - unsafe { msg_send_id![mtm.alloc(), initWithFrame: frame] }; - - this.setRootViewController(Some(view_controller)); - - match window_attributes.fullscreen.clone().map(Into::into) { - Some(Fullscreen::Exclusive(ref video_mode)) => { - let monitor = video_mode.monitor(); - let screen = monitor.ui_screen(mtm); - screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); - this.setScreen(screen); - } - Some(Fullscreen::Borderless(Some(ref monitor))) => { - let screen = monitor.ui_screen(mtm); - this.setScreen(screen); - } - _ => (), - } - - this - } - - pub(crate) fn id(&self) -> WindowId { - (self as *const Self as usize as u64).into() - } -} - -pub struct Inner { - window: Retained, - view_controller: Retained, - view: Retained, - gl_or_metal_backed: bool, -} - -impl Inner { - pub fn set_title(&self, _title: &str) { - debug!("`Window::set_title` is ignored on iOS") - } - - pub fn set_transparent(&self, _transparent: bool) { - debug!("`Window::set_transparent` is ignored on iOS") - } - - pub fn set_blur(&self, _blur: bool) { - debug!("`Window::set_blur` is ignored on iOS") - } - - pub fn set_visible(&self, visible: bool) { - self.window.setHidden(!visible) - } - - pub fn is_visible(&self) -> Option { - warn!("`Window::is_visible` is ignored on iOS"); - None - } - - pub fn request_redraw(&self) { - if self.gl_or_metal_backed { - let mtm = MainThreadMarker::new().unwrap(); - // `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or - // CAMetalLayer. Ordinarily the OS sets up a bunch of UIKit state before - // calling drawRect: on a UIView, but when using raw or gl/metal for drawing - // this work is completely avoided. - // - // The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been - // confirmed via testing. - // - // https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc - app_state::queue_gl_or_metal_redraw(mtm, self.window.clone()); - } else { - self.view.setNeedsDisplay(); - } - } - - pub fn pre_present_notify(&self) {} - - pub fn inner_position(&self) -> Result, NotSupportedError> { - let safe_area = self.safe_area_screen_space(); - let position = LogicalPosition { - x: safe_area.origin.x as f64, - y: safe_area.origin.y as f64, - }; - let scale_factor = self.scale_factor(); - Ok(position.to_physical(scale_factor)) - } - - pub fn outer_position(&self) -> Result, NotSupportedError> { - let screen_frame = self.screen_frame(); - let position = LogicalPosition { - x: screen_frame.origin.x as f64, - y: screen_frame.origin.y as f64, - }; - let scale_factor = self.scale_factor(); - Ok(position.to_physical(scale_factor)) - } - - pub fn set_outer_position(&self, physical_position: Position) { - let scale_factor = self.scale_factor(); - let position = physical_position.to_logical::(scale_factor); - let screen_frame = self.screen_frame(); - let new_screen_frame = CGRect { - origin: CGPoint { - x: position.x as _, - y: position.y as _, - }, - size: screen_frame.size, - }; - let bounds = self.rect_from_screen_space(new_screen_frame); - self.window.setBounds(bounds); - } - - pub fn inner_size(&self) -> PhysicalSize { - let scale_factor = self.scale_factor(); - let safe_area = self.safe_area_screen_space(); - let size = LogicalSize { - width: safe_area.size.width as f64, - height: safe_area.size.height as f64, - }; - size.to_physical(scale_factor) - } - - pub fn outer_size(&self) -> PhysicalSize { - let scale_factor = self.scale_factor(); - let screen_frame = self.screen_frame(); - let size = LogicalSize { - width: screen_frame.size.width as f64, - height: screen_frame.size.height as f64, - }; - size.to_physical(scale_factor) - } - - pub fn request_inner_size(&self, _size: Size) -> Option> { - Some(self.inner_size()) - } - - pub fn set_min_inner_size(&self, _dimensions: Option) { - warn!("`Window::set_min_inner_size` is ignored on iOS") - } - - pub fn set_max_inner_size(&self, _dimensions: Option) { - warn!("`Window::set_max_inner_size` is ignored on iOS") - } - - pub fn resize_increments(&self) -> Option> { - None - } - - #[inline] - pub fn set_resize_increments(&self, _increments: Option) { - warn!("`Window::set_resize_increments` is ignored on iOS") - } - - pub fn set_resizable(&self, _resizable: bool) { - warn!("`Window::set_resizable` is ignored on iOS") - } - - pub fn is_resizable(&self) -> bool { - warn!("`Window::is_resizable` is ignored on iOS"); - false - } - - #[inline] - pub fn set_enabled_buttons(&self, _buttons: WindowButtons) { - warn!("`Window::set_enabled_buttons` is ignored on iOS"); - } - - #[inline] - pub fn enabled_buttons(&self) -> WindowButtons { - warn!("`Window::enabled_buttons` is ignored on iOS"); - WindowButtons::all() - } - - pub fn scale_factor(&self) -> f64 { - self.view.contentScaleFactor() as _ - } - - pub fn set_cursor(&self, _cursor: Cursor) { - debug!("`Window::set_cursor` ignored on iOS") - } - - pub fn set_cursor_position(&self, _position: Position) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) - } - - pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) - } - - pub fn set_cursor_visible(&self, _visible: bool) { - debug!("`Window::set_cursor_visible` is ignored on iOS") - } - - pub fn drag_window(&self) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) - } - - pub fn drag_resize_window( - &self, - _direction: ResizeDirection, - ) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) - } - - #[inline] - pub fn show_window_menu(&self, _position: Position) {} - - pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) - } - - pub fn set_minimized(&self, _minimized: bool) { - warn!("`Window::set_minimized` is ignored on iOS") - } - - pub fn is_minimized(&self) -> Option { - warn!("`Window::is_minimized` is ignored on iOS"); - None - } - - pub fn set_maximized(&self, _maximized: bool) { - warn!("`Window::set_maximized` is ignored on iOS") - } - - pub fn is_maximized(&self) -> bool { - warn!("`Window::is_maximized` is ignored on iOS"); - false - } - - pub(crate) fn set_fullscreen(&self, monitor: Option) { - let mtm = MainThreadMarker::new().unwrap(); - let uiscreen = match &monitor { - Some(Fullscreen::Exclusive(video_mode)) => { - let uiscreen = video_mode.monitor.ui_screen(mtm); - uiscreen.setCurrentMode(Some(video_mode.screen_mode(mtm))); - uiscreen.clone() - } - Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen(mtm).clone(), - Some(Fullscreen::Borderless(None)) => { - self.current_monitor_inner().ui_screen(mtm).clone() - } - None => { - warn!("`Window::set_fullscreen(None)` ignored on iOS"); - return; - } - }; - - // this is pretty slow on iOS, so avoid doing it if we can - let current = self.window.screen(); - if uiscreen != current { - self.window.setScreen(&uiscreen); - } - - let bounds = uiscreen.bounds(); - self.window.setFrame(bounds); - - // For external displays, we must disable overscan compensation or - // the displayed image will have giant black bars surrounding it on - // each side - uiscreen.setOverscanCompensation(UIScreenOverscanCompensation::None); - } - - pub(crate) fn fullscreen(&self) -> Option { - let mtm = MainThreadMarker::new().unwrap(); - let monitor = self.current_monitor_inner(); - let uiscreen = monitor.ui_screen(mtm); - let screen_space_bounds = self.screen_frame(); - let screen_bounds = uiscreen.bounds(); - - // TODO: track fullscreen instead of relying on brittle float comparisons - if screen_space_bounds.origin.x == screen_bounds.origin.x - && screen_space_bounds.origin.y == screen_bounds.origin.y - && screen_space_bounds.size.width == screen_bounds.size.width - && screen_space_bounds.size.height == screen_bounds.size.height - { - Some(Fullscreen::Borderless(Some(monitor))) - } else { - None - } - } - - pub fn set_decorations(&self, _decorations: bool) {} - - pub fn is_decorated(&self) -> bool { - true - } - - pub fn set_window_level(&self, _level: WindowLevel) { - warn!("`Window::set_window_level` is ignored on iOS") - } - - pub fn set_window_icon(&self, _icon: Option) { - warn!("`Window::set_window_icon` is ignored on iOS") - } - - pub fn set_ime_cursor_area(&self, _position: Position, _size: Size) { - warn!("`Window::set_ime_cursor_area` is ignored on iOS") - } - - pub fn set_ime_allowed(&self, _allowed: bool) { - warn!("`Window::set_ime_allowed` is ignored on iOS") - } - - pub fn set_ime_purpose(&self, _purpose: ImePurpose) { - warn!("`Window::set_ime_allowed` is ignored on iOS") - } - - pub fn focus_window(&self) { - warn!("`Window::set_focus` is ignored on iOS") - } - - pub fn request_user_attention(&self, _request_type: Option) { - warn!("`Window::request_user_attention` is ignored on iOS") - } - - // Allow directly accessing the current monitor internally without unwrapping. - fn current_monitor_inner(&self) -> MonitorHandle { - MonitorHandle::new(self.window.screen()) - } - - pub fn current_monitor(&self) -> Option { - Some(self.current_monitor_inner()) - } - - pub fn available_monitors(&self) -> VecDeque { - monitor::uiscreens(MainThreadMarker::new().unwrap()) - } - - pub fn primary_monitor(&self) -> Option { - #[allow(deprecated)] - Some(MonitorHandle::new(UIScreen::mainScreen( - MainThreadMarker::new().unwrap(), - ))) - } - - pub fn id(&self) -> WindowId { - self.window.id() - } - - pub fn raw_window_handle_rwh_06(&self) -> raw_window_handle::RawWindowHandle { - let mut window_handle = raw_window_handle::UiKitWindowHandle::new({ - let ui_view = Retained::as_ptr(&self.view) as _; - std::ptr::NonNull::new(ui_view).expect("Retained should never be null") - }); - window_handle.ui_view_controller = - std::ptr::NonNull::new(Retained::as_ptr(&self.view_controller) as _); - raw_window_handle::RawWindowHandle::UiKit(window_handle) - } - - pub fn theme(&self) -> Option { - warn!("`Window::theme` is ignored on iOS"); - None - } - - pub fn set_content_protected(&self, _protected: bool) {} - - pub fn has_focus(&self) -> bool { - self.window.isKeyWindow() - } - - #[inline] - pub fn set_theme(&self, _theme: Option) { - warn!("`Window::set_theme` is ignored on iOS"); - } - - pub fn title(&self) -> String { - warn!("`Window::title` is ignored on iOS"); - String::new() - } - - pub fn reset_dead_keys(&self) { - // Noop - } -} - -pub struct Window { - inner: MainThreadBound, -} - -impl Window { - pub(crate) fn new( - event_loop: &ActiveEventLoop, - window_attributes: WindowAttributes, - ) -> Result { - let mtm = event_loop.mtm; - - if window_attributes.min_inner_size.is_some() { - warn!("`WindowAttributes::min_inner_size` is ignored on iOS"); - } - if window_attributes.max_inner_size.is_some() { - warn!("`WindowAttributes::max_inner_size` is ignored on iOS"); - } - - // TODO: transparency, visible - - #[allow(deprecated)] - let main_screen = UIScreen::mainScreen(mtm); - let fullscreen = window_attributes.fullscreen.clone().map(Into::into); - let screen = match fullscreen { - Some(Fullscreen::Exclusive(ref video_mode)) => { - video_mode.monitor.ui_screen(mtm) - } - Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm), - Some(Fullscreen::Borderless(None)) | None => &main_screen, - }; - - let screen_bounds = screen.bounds(); - - let frame = match window_attributes.inner_size { - Some(dim) => { - let scale_factor = screen.scale(); - let size = dim.to_logical::(scale_factor as f64); - CGRect { - origin: screen_bounds.origin, - size: CGSize { - width: size.width as _, - height: size.height as _, - }, - } - } - None => screen_bounds, - }; - - let view = WinitView::new(mtm, &window_attributes, frame); - - let gl_or_metal_backed = view.isKindOfClass(class!(CAMetalLayer)) - || view.isKindOfClass(class!(CAEAGLLayer)); - - let view_controller = WinitViewController::new(mtm, &window_attributes, &view); - let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller); - - app_state::set_key_window(mtm, &window); - - // Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized` - // event on window creation if the DPI factor != 1.0 - let scale_factor = view.contentScaleFactor(); - let scale_factor = scale_factor as f64; - if scale_factor != 1.0 { - let bounds = view.bounds(); - let screen = window.screen(); - let screen_space = screen.coordinateSpace(); - let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space); - let size = LogicalSize { - width: screen_frame.size.width as f64, - height: screen_frame.size.height as f64, - }; - let window_id = RootWindowId(window.id()); - app_state::handle_nonuser_events( - mtm, - std::iter::once(EventWrapper::ScaleFactorChanged( - app_state::ScaleFactorChanged { - window: window.clone(), - scale_factor, - suggested_size: size.to_physical(scale_factor), - }, - )) - .chain(std::iter::once(EventWrapper::StaticEvent( - Event::WindowEvent { - window_id, - event: WindowEvent::Resized(size.to_physical(scale_factor)), - }, - ))), - ); - } - - let inner = Inner { - window, - view_controller, - view, - gl_or_metal_backed, - }; - Ok(Window { - inner: MainThreadBound::new(inner, mtm), - }) - } - - pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) { - // For now, don't actually do queuing, since it may be less predictable - self.maybe_wait_on_main(f) - } - - pub(crate) fn maybe_wait_on_main( - &self, - f: impl FnOnce(&Inner) -> R + Send, - ) -> R { - self.inner.get_on_main(|inner| f(inner)) - } - - #[inline] - pub(crate) fn raw_window_handle_rwh_06( - &self, - ) -> Result { - if let Some(mtm) = MainThreadMarker::new() { - Ok(self.inner.get(mtm).raw_window_handle_rwh_06()) - } else { - Err(raw_window_handle::HandleError::Unavailable) - } - } - - #[inline] - pub(crate) fn raw_display_handle_rwh_06( - &self, - ) -> Result { - Ok(raw_window_handle::RawDisplayHandle::UiKit( - raw_window_handle::UiKitDisplayHandle::new(), - )) - } -} - -// WindowExtIOS -impl Inner { - pub fn set_scale_factor(&self, scale_factor: f64) { - assert!( - dpi::validate_scale_factor(scale_factor), - "`WindowExtIOS::set_scale_factor` received an invalid hidpi factor" - ); - let scale_factor = scale_factor as CGFloat; - self.view.setContentScaleFactor(scale_factor); - } - - pub fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { - self.view_controller.set_supported_interface_orientations( - MainThreadMarker::new().unwrap(), - valid_orientations, - ); - } - - pub fn set_prefers_home_indicator_hidden(&self, hidden: bool) { - self.view_controller - .set_prefers_home_indicator_auto_hidden(hidden); - } - - pub fn set_preferred_screen_edges_deferring_system_gestures( - &self, - edges: ScreenEdge, - ) { - self.view_controller - .set_preferred_screen_edges_deferring_system_gestures(edges); - } - - pub fn set_prefers_status_bar_hidden(&self, hidden: bool) { - self.view_controller.set_prefers_status_bar_hidden(hidden); - } - - pub fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { - self.view_controller - .set_preferred_status_bar_style(status_bar_style); - } - - pub fn recognize_pinch_gesture(&self, should_recognize: bool) { - self.view.recognize_pinch_gesture(should_recognize); - } - - pub fn recognize_pan_gesture( - &self, - should_recognize: bool, - minimum_number_of_touches: u8, - maximum_number_of_touches: u8, - ) { - self.view.recognize_pan_gesture( - should_recognize, - minimum_number_of_touches, - maximum_number_of_touches, - ); - } - - pub fn recognize_doubletap_gesture(&self, should_recognize: bool) { - self.view.recognize_doubletap_gesture(should_recognize); - } - - pub fn recognize_rotation_gesture(&self, should_recognize: bool) { - self.view.recognize_rotation_gesture(should_recognize); - } -} - -impl Inner { - fn screen_frame(&self) -> CGRect { - self.rect_to_screen_space(self.window.bounds()) - } - - fn rect_to_screen_space(&self, rect: CGRect) -> CGRect { - let screen_space = self.window.screen().coordinateSpace(); - self.window - .convertRect_toCoordinateSpace(rect, &screen_space) - } - - fn rect_from_screen_space(&self, rect: CGRect) -> CGRect { - let screen_space = self.window.screen().coordinateSpace(); - self.window - .convertRect_fromCoordinateSpace(rect, &screen_space) - } - - fn safe_area_screen_space(&self) -> CGRect { - let bounds = self.window.bounds(); - if app_state::os_capabilities().safe_area { - let safe_area = self.window.safeAreaInsets(); - let safe_bounds = CGRect { - origin: CGPoint { - x: bounds.origin.x + safe_area.left, - y: bounds.origin.y + safe_area.top, - }, - size: CGSize { - width: bounds.size.width - safe_area.left - safe_area.right, - height: bounds.size.height - safe_area.top - safe_area.bottom, - }, - }; - self.rect_to_screen_space(safe_bounds) - } else { - let screen_frame = self.rect_to_screen_space(bounds); - let status_bar_frame = { - let app = - UIApplication::sharedApplication(MainThreadMarker::new().unwrap()); - #[allow(deprecated)] - app.statusBarFrame() - }; - let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height { - (screen_frame.origin.y, screen_frame.size.height) - } else { - let y = status_bar_frame.size.height; - let height = screen_frame.size.height - - (status_bar_frame.size.height - screen_frame.origin.y); - (y, height) - }; - CGRect { - origin: CGPoint { - x: screen_frame.origin.x, - y, - }, - size: CGSize { - width: screen_frame.size.width, - height, - }, - } - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId { - window: *mut WinitUIWindow, -} - -impl WindowId { - pub const unsafe fn dummy() -> Self { - WindowId { - window: std::ptr::null_mut(), - } - } -} - -impl From for u64 { - fn from(window_id: WindowId) -> Self { - window_id.window as u64 - } -} - -impl From for WindowId { - fn from(raw_id: u64) -> Self { - Self { - window: raw_id as _, - } - } -} - -unsafe impl Send for WindowId {} -unsafe impl Sync for WindowId {} - -impl From<&AnyObject> for WindowId { - fn from(window: &AnyObject) -> WindowId { - WindowId { - window: window as *const _ as _, - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct PlatformSpecificWindowAttributes { - pub scale_factor: Option, - pub valid_orientations: ValidOrientations, - pub prefers_home_indicator_hidden: bool, - pub prefers_status_bar_hidden: bool, - pub preferred_status_bar_style: StatusBarStyle, - pub preferred_screen_edges_deferring_system_gestures: ScreenEdge, -} diff --git a/rio-window/src/platform_impl/linux/mod.rs b/rio-window/src/platform_impl/linux/mod.rs index a1dc34f4..5d7b4b1c 100644 --- a/rio-window/src/platform_impl/linux/mod.rs +++ b/rio-window/src/platform_impl/linux/mod.rs @@ -596,17 +596,17 @@ impl Window { } #[inline] - pub fn raw_window_handle_rwh_06( + pub fn raw_window_handle_raw_window_handle( &self, ) -> Result { - x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_06()) + x11_or_wayland!(match self; Window(window) => window.raw_window_handle_raw_window_handle()) } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { - x11_or_wayland!(match self; Window(window) => window.raw_display_handle_rwh_06()) + x11_or_wayland!(match self; Window(window) => window.raw_display_handle_raw_window_handle()) } #[inline] @@ -901,10 +901,10 @@ impl ActiveEventLoop { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { - x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_06()) + x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_raw_window_handle()) } pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { @@ -958,7 +958,7 @@ pub(crate) enum OwnedDisplayHandle { impl OwnedDisplayHandle { #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { use std::ptr::NonNull; diff --git a/rio-window/src/platform_impl/linux/wayland/event_loop/mod.rs b/rio-window/src/platform_impl/linux/wayland/event_loop/mod.rs index 159a1031..c804cad0 100644 --- a/rio-window/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/rio-window/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -734,7 +734,7 @@ impl ActiveEventLoop { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { use sctk::reexports::client::Proxy; diff --git a/rio-window/src/platform_impl/linux/wayland/window/mod.rs b/rio-window/src/platform_impl/linux/wayland/window/mod.rs index 7c93998b..c5fd37f7 100644 --- a/rio-window/src/platform_impl/linux/wayland/window/mod.rs +++ b/rio-window/src/platform_impl/linux/wayland/window/mod.rs @@ -679,7 +679,7 @@ impl Window { } #[inline] - pub fn raw_window_handle_rwh_06( + pub fn raw_window_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::WaylandWindowHandle::new({ @@ -690,7 +690,7 @@ impl Window { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::WaylandDisplayHandle::new({ diff --git a/rio-window/src/platform_impl/linux/x11/mod.rs b/rio-window/src/platform_impl/linux/x11/mod.rs index de0e3a67..d4b7528a 100644 --- a/rio-window/src/platform_impl/linux/x11/mod.rs +++ b/rio-window/src/platform_impl/linux/x11/mod.rs @@ -727,7 +727,7 @@ impl ActiveEventLoop { .expect_then_ignore_error("Failed to update device event filter"); } - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { let display_handle = raw_window_handle::XlibDisplayHandle::new( diff --git a/rio-window/src/platform_impl/linux/x11/window.rs b/rio-window/src/platform_impl/linux/x11/window.rs index aa0de7fc..67ec596c 100644 --- a/rio-window/src/platform_impl/linux/x11/window.rs +++ b/rio-window/src/platform_impl/linux/x11/window.rs @@ -1996,7 +1996,7 @@ impl UnownedWindow { } #[inline] - pub fn raw_window_handle_rwh_06( + pub fn raw_window_handle_raw_window_handle( &self, ) -> Result { let mut window_handle = @@ -2006,7 +2006,7 @@ impl UnownedWindow { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::XlibDisplayHandle::new( diff --git a/rio-window/src/platform_impl/macos/event_loop.rs b/rio-window/src/platform_impl/macos/event_loop.rs index 8f8f4ebc..148fd557 100644 --- a/rio-window/src/platform_impl/macos/event_loop.rs +++ b/rio-window/src/platform_impl/macos/event_loop.rs @@ -106,7 +106,7 @@ impl ActiveEventLoop { pub fn listen_device_events(&self, _allowed: DeviceEvents) {} #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::AppKit( @@ -390,7 +390,7 @@ pub(crate) struct OwnedDisplayHandle; impl OwnedDisplayHandle { #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::AppKitDisplayHandle::new().into()) diff --git a/rio-window/src/platform_impl/macos/window.rs b/rio-window/src/platform_impl/macos/window.rs index 6e942a6d..f7e38ac9 100644 --- a/rio-window/src/platform_impl/macos/window.rs +++ b/rio-window/src/platform_impl/macos/window.rs @@ -54,18 +54,18 @@ impl Window { } #[inline] - pub(crate) fn raw_window_handle_rwh_06( + pub(crate) fn raw_window_handle_raw_window_handle( &self, ) -> Result { if let Some(mtm) = MainThreadMarker::new() { - Ok(self.delegate.get(mtm).raw_window_handle_rwh_06()) + Ok(self.delegate.get(mtm).raw_window_handle_raw_window_handle()) } else { Err(raw_window_handle::HandleError::Unavailable) } } #[inline] - pub(crate) fn raw_display_handle_rwh_06( + pub(crate) fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::AppKit( diff --git a/rio-window/src/platform_impl/macos/window_delegate.rs b/rio-window/src/platform_impl/macos/window_delegate.rs index e2982ba7..2d882b76 100644 --- a/rio-window/src/platform_impl/macos/window_delegate.rs +++ b/rio-window/src/platform_impl/macos/window_delegate.rs @@ -1603,7 +1603,9 @@ impl WindowDelegate { } #[inline] - pub fn raw_window_handle_rwh_06(&self) -> raw_window_handle::RawWindowHandle { + pub fn raw_window_handle_raw_window_handle( + &self, + ) -> raw_window_handle::RawWindowHandle { let window_handle = raw_window_handle::AppKitWindowHandle::new({ let ptr = Retained::as_ptr(&self.view()) as *mut _; std::ptr::NonNull::new(ptr).expect("Retained should never be null") diff --git a/rio-window/src/platform_impl/mod.rs b/rio-window/src/platform_impl/mod.rs index c9369913..c03f95fb 100644 --- a/rio-window/src/platform_impl/mod.rs +++ b/rio-window/src/platform_impl/mod.rs @@ -12,9 +12,6 @@ mod platform; #[cfg(macos_platform)] #[path = "macos/mod.rs"] mod platform; -#[cfg(ios_platform)] -#[path = "ios/mod.rs"] -mod platform; #[cfg(web_platform)] #[path = "web/mod.rs"] mod platform; @@ -59,7 +56,6 @@ impl From for RootFullscreen { } #[cfg(all( - not(ios_platform), not(windows_platform), not(macos_platform), not(x11_platform), diff --git a/rio-window/src/platform_impl/orbital/event_loop.rs b/rio-window/src/platform_impl/orbital/event_loop.rs index dc3fa6fd..8b3c8fa1 100644 --- a/rio-window/src/platform_impl/orbital/event_loop.rs +++ b/rio-window/src/platform_impl/orbital/event_loop.rs @@ -850,7 +850,7 @@ impl ActiveEventLoop { pub fn listen_device_events(&self, _allowed: DeviceEvents) {} #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::Orbital( @@ -884,7 +884,7 @@ pub(crate) struct OwnedDisplayHandle; impl OwnedDisplayHandle { #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::OrbitalDisplayHandle::new().into()) diff --git a/rio-window/src/platform_impl/orbital/window.rs b/rio-window/src/platform_impl/orbital/window.rs index a940082a..9234f917 100644 --- a/rio-window/src/platform_impl/orbital/window.rs +++ b/rio-window/src/platform_impl/orbital/window.rs @@ -469,7 +469,7 @@ impl Window { } #[inline] - pub fn raw_window_handle_rwh_06( + pub fn raw_window_handle_raw_window_handle( &self, ) -> Result { let handle = raw_window_handle::OrbitalWindowHandle::new({ @@ -480,7 +480,7 @@ impl Window { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::Orbital( diff --git a/rio-window/src/platform_impl/web/event_loop/window_target.rs b/rio-window/src/platform_impl/web/event_loop/window_target.rs index af0a188f..e27288cb 100644 --- a/rio-window/src/platform_impl/web/event_loop/window_target.rs +++ b/rio-window/src/platform_impl/web/event_loop/window_target.rs @@ -693,7 +693,7 @@ impl ActiveEventLoop { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::Web( @@ -743,7 +743,7 @@ pub(crate) struct OwnedDisplayHandle; impl OwnedDisplayHandle { #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::WebDisplayHandle::new().into()) diff --git a/rio-window/src/platform_impl/web/window.rs b/rio-window/src/platform_impl/web/window.rs index eafdb873..78aa0cce 100644 --- a/rio-window/src/platform_impl/web/window.rs +++ b/rio-window/src/platform_impl/web/window.rs @@ -101,7 +101,7 @@ impl Window { } #[inline] - pub fn raw_window_handle_rwh_06( + pub fn raw_window_handle_raw_window_handle( &self, ) -> Result { self.inner @@ -119,7 +119,7 @@ impl Window { } #[inline] - pub(crate) fn raw_display_handle_rwh_06( + pub(crate) fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::Web( diff --git a/rio-window/src/platform_impl/windows/event_loop.rs b/rio-window/src/platform_impl/windows/event_loop.rs index 91766b2f..cb9e04d1 100644 --- a/rio-window/src/platform_impl/windows/event_loop.rs +++ b/rio-window/src/platform_impl/windows/event_loop.rs @@ -557,7 +557,7 @@ impl ActiveEventLoop { Some(monitor) } - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::Windows( @@ -606,7 +606,7 @@ pub(crate) struct OwnedDisplayHandle; impl OwnedDisplayHandle { #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::WindowsDisplayHandle::new().into()) diff --git a/rio-window/src/platform_impl/windows/window.rs b/rio-window/src/platform_impl/windows/window.rs index 4b58ef67..814d3b82 100644 --- a/rio-window/src/platform_impl/windows/window.rs +++ b/rio-window/src/platform_impl/windows/window.rs @@ -377,7 +377,7 @@ impl Window { } #[inline] - pub fn raw_window_handle_rwh_06( + pub fn raw_window_handle_raw_window_handle( &self, ) -> Result { // TODO: Write a test once integration framework is ready to ensure that it holds. @@ -392,7 +392,7 @@ impl Window { } #[inline] - pub fn raw_display_handle_rwh_06( + pub fn raw_display_handle_raw_window_handle( &self, ) -> Result { Ok(raw_window_handle::RawDisplayHandle::Windows( diff --git a/rio-window/src/window.rs b/rio-window/src/window.rs index 6cef0ac9..f32dbbf2 100644 --- a/rio-window/src/window.rs +++ b/rio-window/src/window.rs @@ -1681,7 +1681,7 @@ impl raw_window_handle::HasWindowHandle for Window { fn window_handle( &self, ) -> Result, raw_window_handle::HandleError> { - let raw = self.window.raw_window_handle_rwh_06()?; + let raw = self.window.raw_window_handle_raw_window_handle()?; // SAFETY: The window handle will never be deallocated while the window is alive, // and the main thread safety requirements are upheld internally by each platform. @@ -1694,7 +1694,7 @@ impl raw_window_handle::HasDisplayHandle for Window { &self, ) -> Result, raw_window_handle::HandleError> { - let raw = self.window.raw_display_handle_rwh_06()?; + let raw = self.window.raw_display_handle_raw_window_handle()?; // SAFETY: The window handle will never be deallocated while the window is alive, // and the main thread safety requirements are upheld internally by each platform.