Move UIKit backend to winit-uikit

This commit is contained in:
Mads Marquart 2025-05-25 16:19:30 +02:00 committed by GitHub
parent 0adc0898f0
commit 927af44aa4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 128 additions and 110 deletions

2
.github/CODEOWNERS vendored
View file

@ -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

View file

@ -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 }}

View file

@ -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

View file

@ -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)]

View file

@ -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::*;

View file

@ -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")
}
}

View file

@ -1 +0,0 @@
../appkit/notification_center.rs

View file

@ -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;

72
winit-uikit/Cargo.toml Normal file
View file

@ -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",
] }

1
winit-uikit/README.md Symbolic link
View file

@ -0,0 +1 @@
../README.md

View file

@ -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)*) => {

View file

@ -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<EventLoop, EventLoopError> {
pub fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<EventLoop, EventLoopError> {
let mtm = MainThreadMarker::new()
.expect("On iOS, `EventLoop` must be created on the main thread");

View file

@ -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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<crate::platform_impl::Window>().unwrap();
let window = self.cast_ref::<UIKitWindow>().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::<IosMonitorHandle>().unwrap();
let monitor = self.cast_ref::<UIKitMonitorHandle>().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::<IosMonitorHandle>().unwrap();
let monitor = self.cast_ref::<UIKitMonitorHandle>().unwrap();
monitor.preferred_video_mode()
}
}

View file

@ -0,0 +1 @@
../../src/platform_impl/apple/appkit/notification_center.rs

View file

@ -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<bool>,

View file

@ -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))]