diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index afc6e1149..54400e613 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,7 +2,7 @@ /winit-android @MarijnS95 # Apple (AppKit + UIKit) -/src/platform/ios.rs @madsmtm +/winit-uikit @madsmtm /src/platform/macos.rs @madsmtm /src/platform_impl/apple @madsmtm /winit-common/src/core_foundation @madsmtm diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df500d9dd..7d81a68ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -201,6 +201,11 @@ jobs: if: contains(matrix.platform.target, 'redox') run: cargo test -p winit-orbital + - name: Test winit UIKit + if: contains(matrix.platform.target, 'ios') + # TODO: Run on Simulator + run: cargo $CMD test -p winit-uikit --target=${{ matrix.platform.target }} --no-run + - name: Test winit Win32 if: contains(matrix.platform.target, 'windows') run: cargo $CMD test -p winit-win32 --target=${{ matrix.platform.target }} diff --git a/Cargo.toml b/Cargo.toml index 9412f84aa..33a9be1d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ winit-android = { version = "0.0.0", path = "winit-android" } winit-common = { version = "0.0.0", path = "winit-common" } winit-core = { version = "0.0.0", path = "winit-core" } winit-orbital = { version = "0.0.0", path = "winit-orbital" } +winit-uikit = { version = "0.0.0", path = "winit-uikit" } winit-win32 = { version = "0.0.0", path = "winit-win32" } # Core dependencies. @@ -164,6 +165,7 @@ serde = [ "dpi/serde", "bitflags/serde", "winit-core/serde", + "winit-uikit/serde", ] wayland = [ "wayland-client", @@ -296,52 +298,8 @@ objc2-foundation = { workspace = true, features = [ "NSValue", ] } -# UIKit [target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies] -objc2-core-foundation = { workspace = true, features = [ - "std", - "CFCGTypes", - "CFBase", - "CFRunLoop", - "CFString", -] } -objc2-foundation = { workspace = true, features = [ - "std", - "block2", - "objc2-core-foundation", - "NSArray", - "NSEnumerator", - "NSGeometry", - "NSObjCRuntime", - "NSOperation", - "NSString", - "NSThread", - "NSSet", -] } -objc2-ui-kit = { workspace = true, features = [ - "std", - "objc2-core-foundation", - "UIApplication", - "UIDevice", - "UIEvent", - "UIGeometry", - "UIGestureRecognizer", - "UITextInput", - "UITextInputTraits", - "UIOrientation", - "UIPanGestureRecognizer", - "UIPinchGestureRecognizer", - "UIResponder", - "UIRotationGestureRecognizer", - "UIScreen", - "UIScreenMode", - "UITapGestureRecognizer", - "UITouch", - "UITraitCollection", - "UIView", - "UIViewController", - "UIWindow", -] } +winit-uikit.workspace = true [target.'cfg(target_os = "windows")'.dependencies] winit-win32.workspace = true diff --git a/src/platform/mod.rs b/src/platform/mod.rs index aba1ddc64..0d403e2c4 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -5,7 +5,7 @@ #[cfg(android_platform)] pub use winit_android as android; #[cfg(ios_platform)] -pub mod ios; +pub use winit_uikit as ios; #[cfg(macos_platform)] pub mod macos; #[cfg(orbital_platform)] diff --git a/src/platform_impl/apple/mod.rs b/src/platform_impl/apple/mod.rs index 5c2bb639a..d500720ab 100644 --- a/src/platform_impl/apple/mod.rs +++ b/src/platform_impl/apple/mod.rs @@ -1,11 +1,6 @@ #[cfg(target_os = "macos")] mod appkit; -#[cfg(not(target_os = "macos"))] -mod uikit; #[allow(unused_imports)] #[cfg(target_os = "macos")] pub use self::appkit::*; -#[allow(unused_imports)] -#[cfg(not(target_os = "macos"))] -pub use self::uikit::*; diff --git a/src/platform_impl/apple/uikit/mod.rs b/src/platform_impl/apple/uikit/mod.rs deleted file mode 100644 index b27f8853b..000000000 --- a/src/platform_impl/apple/uikit/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![allow(clippy::let_unit_value)] - -mod app_state; -mod event_loop; -mod monitor; -mod notification_center; -mod view; -mod view_controller; -mod window; - -use std::fmt; - -pub(crate) use self::event_loop::{ - ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes, -}; -pub(crate) use self::monitor::MonitorHandle; -pub(crate) use self::window::Window; - -#[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/src/platform_impl/apple/uikit/notification_center.rs b/src/platform_impl/apple/uikit/notification_center.rs deleted file mode 120000 index bc249d18f..000000000 --- a/src/platform_impl/apple/uikit/notification_center.rs +++ /dev/null @@ -1 +0,0 @@ -../appkit/notification_center.rs \ No newline at end of file diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index bbb83acb0..a81249774 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -1,17 +1,19 @@ #[cfg(android_platform)] pub(crate) use winit_android as platform; -#[cfg(target_vendor = "apple")] +#[cfg(macos_platform)] mod apple; #[cfg(any(x11_platform, wayland_platform))] mod linux; #[cfg(orbital_platform)] pub(crate) use winit_orbital as platform; +#[cfg(ios_platform)] +pub(crate) use winit_uikit as platform; #[cfg(web_platform)] mod web; #[cfg(windows_platform)] pub(crate) use winit_win32 as platform; -#[cfg(target_vendor = "apple")] +#[cfg(macos_platform)] use self::apple as platform; #[cfg(any(x11_platform, wayland_platform))] use self::linux as platform; diff --git a/winit-uikit/Cargo.toml b/winit-uikit/Cargo.toml new file mode 100644 index 000000000..a2b7d2665 --- /dev/null +++ b/winit-uikit/Cargo.toml @@ -0,0 +1,72 @@ +[package] +description = "Winit's UIKit (iOS/tvOS/visionOS) backend" +documentation = "https://docs.rs/winit-uikit" +edition.workspace = true +license.workspace = true +name = "winit-uikit" +repository.workspace = true +rust-version.workspace = true +version = "0.0.0" + +[features] +serde = ["dep:serde", "bitflags/serde", "smol_str/serde", "dpi/serde"] + +[dependencies] +bitflags.workspace = true +dpi.workspace = true +rwh_06.workspace = true +serde = { workspace = true, optional = true } +smol_str.workspace = true +tracing.workspace = true +winit-common = { workspace = true, features = ["core-foundation", "event-handler"] } +winit-core.workspace = true + +# Platform-specific +[target.'cfg(target_vendor = "apple")'.dependencies] +block2.workspace = true +dispatch2.workspace = true +objc2.workspace = true +objc2-core-foundation = { workspace = true, features = [ + "std", + "CFCGTypes", + "CFBase", + "CFRunLoop", + "CFString", +] } +objc2-foundation = { workspace = true, features = [ + "std", + "block2", + "objc2-core-foundation", + "NSArray", + "NSEnumerator", + "NSGeometry", + "NSObjCRuntime", + "NSOperation", + "NSString", + "NSThread", + "NSSet", +] } +objc2-ui-kit = { workspace = true, features = [ + "std", + "objc2-core-foundation", + "UIApplication", + "UIDevice", + "UIEvent", + "UIGeometry", + "UIGestureRecognizer", + "UITextInput", + "UITextInputTraits", + "UIOrientation", + "UIPanGestureRecognizer", + "UIPinchGestureRecognizer", + "UIResponder", + "UIRotationGestureRecognizer", + "UIScreen", + "UIScreenMode", + "UITapGestureRecognizer", + "UITouch", + "UITraitCollection", + "UIView", + "UIViewController", + "UIWindow", +] } diff --git a/winit-uikit/README.md b/winit-uikit/README.md new file mode 120000 index 000000000..32d46ee88 --- /dev/null +++ b/winit-uikit/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/src/platform_impl/apple/uikit/app_state.rs b/winit-uikit/src/app_state.rs similarity index 99% rename from src/platform_impl/apple/uikit/app_state.rs rename to winit-uikit/src/app_state.rs index bc6f96d68..acffa1514 100644 --- a/src/platform_impl/apple/uikit/app_state.rs +++ b/winit-uikit/src/app_state.rs @@ -23,8 +23,8 @@ use winit_core::event::{StartCause, SurfaceSizeWriter, WindowEvent}; use winit_core::event_loop::ControlFlow; use winit_core::window::WindowId; -use super::window::WinitUIWindow; -use super::ActiveEventLoop; +use crate::event_loop::ActiveEventLoop; +use crate::window::WinitUIWindow; macro_rules! bug { ($($msg:tt)*) => { diff --git a/src/platform_impl/apple/uikit/event_loop.rs b/winit-uikit/src/event_loop.rs similarity index 97% rename from src/platform_impl/apple/uikit/event_loop.rs rename to winit-uikit/src/event_loop.rs index 91a73fa27..990c0556f 100644 --- a/src/platform_impl/apple/uikit/event_loop.rs +++ b/winit-uikit/src/event_loop.rs @@ -28,11 +28,12 @@ use winit_core::window::{Theme, Window as CoreWindow}; use super::app_state::{send_occluded_event_for_all_windows, AppState}; use super::notification_center::create_observer; -use super::{app_state, monitor, MonitorHandle}; -use crate::platform_impl::Window; +use crate::monitor::MonitorHandle; +use crate::window::Window; +use crate::{app_state, monitor}; #[derive(Debug)] -pub(crate) struct ActiveEventLoop { +pub struct ActiveEventLoop { pub(super) mtm: MainThreadMarker, } @@ -138,12 +139,10 @@ pub struct EventLoop { } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct PlatformSpecificEventLoopAttributes {} +pub struct PlatformSpecificEventLoopAttributes {} impl EventLoop { - pub(crate) fn new( - _: &PlatformSpecificEventLoopAttributes, - ) -> Result { + pub fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { let mtm = MainThreadMarker::new() .expect("On iOS, `EventLoop` must be created on the main thread"); diff --git a/src/platform/ios.rs b/winit-uikit/src/lib.rs similarity index 93% rename from src/platform/ios.rs rename to winit-uikit/src/lib.rs index 1051a30b6..7ae806f2e 100644 --- a/src/platform/ios.rs +++ b/winit-uikit/src/lib.rs @@ -1,4 +1,4 @@ -//! # iOS / UIKit +//! # Winit's UIKit (iOS/tvOS/visionOS) backend //! //! Winit has [the same iOS version requirements as `rustc`][rustc-ios-version], although it's //! frequently only tested on newer iOS versions. @@ -98,16 +98,26 @@ //! register an application delegate, so you can set up a custom one in a nib file instead. //! //! [app-delegate]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate?language=objc +#![cfg(target_vendor = "apple")] // TODO: Remove once `objc2` allows compiling on all platforms + +mod app_state; +mod event_loop; +mod monitor; +mod notification_center; +mod view; +mod view_controller; +mod window; use std::os::raw::c_void; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use winit_core::window::PlatformWindowAttributes; +use winit_core::monitor::{MonitorHandle, VideoMode}; +use winit_core::window::{PlatformWindowAttributes, Window}; -use crate::monitor::{MonitorHandle, VideoMode}; -use crate::platform_impl::MonitorHandle as IosMonitorHandle; -use crate::window::Window; +pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; +use self::monitor::MonitorHandle as UIKitMonitorHandle; +use self::window::Window as UIKitWindow; /// Additional methods on [`Window`] that are specific to iOS. pub trait WindowExtIOS { @@ -212,25 +222,25 @@ pub trait WindowExtIOS { impl WindowExtIOS for dyn Window + '_ { #[inline] fn set_scale_factor(&self, scale_factor: f64) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_scale_factor(scale_factor)); } #[inline] fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_valid_orientations(valid_orientations)); } #[inline] fn set_prefers_home_indicator_hidden(&self, hidden: bool) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden)); } #[inline] fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| { w.set_preferred_screen_edges_deferring_system_gestures(edges) }); @@ -238,19 +248,19 @@ impl WindowExtIOS for dyn Window + '_ { #[inline] fn set_prefers_status_bar_hidden(&self, hidden: bool) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_prefers_status_bar_hidden(hidden)); } #[inline] fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style)) } #[inline] fn recognize_pinch_gesture(&self, should_recognize: bool) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); } @@ -261,7 +271,7 @@ impl WindowExtIOS for dyn Window + '_ { minimum_number_of_touches: u8, maximum_number_of_touches: u8, ) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| { w.recognize_pan_gesture( should_recognize, @@ -273,13 +283,13 @@ impl WindowExtIOS for dyn Window + '_ { #[inline] fn recognize_doubletap_gesture(&self, should_recognize: bool) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.recognize_doubletap_gesture(should_recognize)); } #[inline] fn recognize_rotation_gesture(&self, should_recognize: bool) { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.recognize_rotation_gesture(should_recognize)); } } @@ -395,13 +405,13 @@ impl MonitorHandleExtIOS for MonitorHandle { fn ui_screen(&self) -> *mut c_void { // SAFETY: The marker is only used to get the pointer of the screen let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() }; - let monitor = self.cast_ref::().unwrap(); + let monitor = self.cast_ref::().unwrap(); objc2::rc::Retained::as_ptr(monitor.ui_screen(mtm)) as *mut c_void } #[inline] fn preferred_video_mode(&self) -> VideoMode { - let monitor = self.cast_ref::().unwrap(); + let monitor = self.cast_ref::().unwrap(); monitor.preferred_video_mode() } } diff --git a/src/platform_impl/apple/uikit/monitor.rs b/winit-uikit/src/monitor.rs similarity index 100% rename from src/platform_impl/apple/uikit/monitor.rs rename to winit-uikit/src/monitor.rs diff --git a/winit-uikit/src/notification_center.rs b/winit-uikit/src/notification_center.rs new file mode 120000 index 000000000..46ece48db --- /dev/null +++ b/winit-uikit/src/notification_center.rs @@ -0,0 +1 @@ +../../src/platform_impl/apple/appkit/notification_center.rs \ No newline at end of file diff --git a/src/platform_impl/apple/uikit/view.rs b/winit-uikit/src/view.rs similarity index 100% rename from src/platform_impl/apple/uikit/view.rs rename to winit-uikit/src/view.rs diff --git a/src/platform_impl/apple/uikit/view_controller.rs b/winit-uikit/src/view_controller.rs similarity index 98% rename from src/platform_impl/apple/uikit/view_controller.rs rename to winit-uikit/src/view_controller.rs index e3a3ddaa5..56d7d53a0 100644 --- a/src/platform_impl/apple/uikit/view_controller.rs +++ b/winit-uikit/src/view_controller.rs @@ -8,7 +8,7 @@ use objc2_ui_kit::{ UIUserInterfaceIdiom, UIView, UIViewController, }; -use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos}; +use crate::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos}; pub struct ViewControllerState { prefers_status_bar_hidden: Cell, diff --git a/src/platform_impl/apple/uikit/window.rs b/winit-uikit/src/window.rs similarity index 99% rename from src/platform_impl/apple/uikit/window.rs rename to winit-uikit/src/window.rs index aba2b1efb..5b9d7bcdf 100644 --- a/src/platform_impl/apple/uikit/window.rs +++ b/winit-uikit/src/window.rs @@ -30,8 +30,10 @@ use winit_core::window::{ use super::app_state::EventWrapper; use super::view::WinitView; use super::view_controller::WinitViewController; -use super::{app_state, monitor, ActiveEventLoop, MonitorHandle}; -use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos}; +use super::{app_state, monitor}; +use crate::event_loop::ActiveEventLoop; +use crate::monitor::MonitorHandle; +use crate::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos}; define_class!( #[unsafe(super(UIWindow, UIResponder, NSObject))]