Skip to content

Commit

Permalink
Use available! macro
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 24, 2025
1 parent eb93b8e commit deb9de9
Show file tree
Hide file tree
Showing 9 changed files with 36 additions and 116 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ objc2-foundation = { version = "0.3.0", default-features = false, features = [
"NSObjCRuntime",
"NSOperation",
"NSString",
"NSProcessInfo",
"NSThread",
"NSSet",
] }
Expand Down
4 changes: 3 additions & 1 deletion src/platform_impl/apple/appkit/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::OnceLock;

use objc2::rc::Retained;
use objc2::runtime::Sel;
use objc2::{msg_send, sel, AllocAnyThread, ClassType};
use objc2::{available, msg_send, sel, AllocAnyThread, ClassType};
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
use objc2_foundation::{
ns_string, NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString,
Expand Down Expand Up @@ -204,7 +204,9 @@ pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Retained<NSCursor> {
CursorIcon::EwResize | CursorIcon::ColResize => NSCursor::resizeLeftRightCursor(),
CursorIcon::NsResize | CursorIcon::RowResize => NSCursor::resizeUpDownCursor(),
CursorIcon::Help => _helpCursor(),
CursorIcon::ZoomIn if available!(macos = 15.0) => unsafe { NSCursor::zoomInCursor() },
CursorIcon::ZoomIn => _zoomInCursor(),
CursorIcon::ZoomOut if available!(macos = 15.0) => unsafe { NSCursor::zoomOutCursor() },
CursorIcon::ZoomOut => _zoomOutCursor(),
CursorIcon::NeResize => _windowResizeNorthEastCursor(),
CursorIcon::NwResize => _windowResizeNorthWestCursor(),
Expand Down
5 changes: 3 additions & 2 deletions src/platform_impl/apple/appkit/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use core_foundation::runloop::{
};
use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::ProtocolObject;
use objc2::{msg_send, sel, ClassType};
use objc2::{available, msg_send, ClassType};
use objc2_app_kit::{
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
NSApplicationWillTerminateNotification, NSWindow,
Expand Down Expand Up @@ -130,7 +130,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
fn system_theme(&self) -> Option<Theme> {
let app = NSApplication::sharedApplication(self.mtm);

if app.respondsToSelector(sel!(effectiveAppearance)) {
// Dark appearance was introduced in macOS 10.14
if available!(macos = 10.14) {
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
} else {
Some(Theme::Light)
Expand Down
10 changes: 8 additions & 2 deletions src/platform_impl/apple/appkit/window_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use std::sync::{Arc, Mutex};
use core_graphics::display::CGDisplay;
use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{AnyObject, ProtocolObject};
use objc2::{define_class, msg_send, sel, ClassType, DefinedClass, MainThreadOnly, Message};
use objc2::{
available, define_class, msg_send, sel, ClassType, DefinedClass, MainThreadOnly, Message,
};
use objc2_app_kit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
Expand Down Expand Up @@ -1734,7 +1736,7 @@ impl WindowDelegate {
let mtm = MainThreadMarker::from(self);
let app = NSApplication::sharedApplication(mtm);

if app.respondsToSelector(sel!(effectiveAppearance)) {
if available!(macos = 10.14) {
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
} else {
Some(Theme::Light)
Expand Down Expand Up @@ -1896,6 +1898,10 @@ impl WindowExtMacOS for WindowDelegate {

#[inline]
fn select_tab_at_index(&self, index: usize) {
if !available!(macos = 10.13) {
tracing::warn!("window tab groups are only available on macOS 10.13+");
return;
}
if let Some(group) = self.window().tabGroup() {
if let Some(windows) = unsafe { self.window().tabbedWindows() } {
if index < windows.len() {
Expand Down
93 changes: 2 additions & 91 deletions src/platform_impl/apple/uikit/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::cell::{OnceCell, RefCell, RefMut};
use std::collections::HashSet;
use std::os::raw::c_void;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, OnceLock};
use std::sync::{Arc, Mutex};
use std::time::Instant;
use std::{mem, ptr};

Expand All @@ -15,11 +15,8 @@ use core_foundation::runloop::{
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
};
use objc2::rc::Retained;
use objc2::sel;
use objc2_core_foundation::{CGRect, CGSize};
use objc2_foundation::{
MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion, NSProcessInfo,
};
use objc2_foundation::MainThreadMarker;
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};

use super::super::event_handler::EventHandler;
Expand Down Expand Up @@ -614,89 +611,3 @@ impl EventLoopWaker {
}
}
}

macro_rules! os_capabilities {
(
$(
$(#[$attr:meta])*
$error_name:ident: $objc_call:literal,
$name:ident: $major:literal-$minor:literal
),*
$(,)*
) => {
#[derive(Clone, Debug)]
pub struct OSCapabilities {
$(
pub $name: bool,
)*

os_version: NSOperatingSystemVersion,
}

impl 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! {
/// <https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc>
#[allow(unused)] // error message unused
safe_area_err_msg: "-[UIView safeAreaInsets]",
safe_area: 11-0,
/// <https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc>
home_indicator_hidden_err_msg: "-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]",
home_indicator_hidden: 11-0,
/// <https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc>
defer_system_gestures_err_msg: "-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystem]",
defer_system_gestures: 11-0,
/// <https://developer.apple.com/documentation/uikit/uiscreen/2806814-maximumframespersecond?language=objc>
maximum_frames_per_second_err_msg: "-[UIScreen maximumFramesPerSecond]",
maximum_frames_per_second: 10-3,
/// <https://developer.apple.com/documentation/uikit/uitouch/1618110-force?language=objc>
#[allow(unused)] // error message unused
force_touch_err_msg: "-[UITouch force]",
force_touch: 9-0,
}

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<OSCapabilities> = OnceLock::new();
OS_CAPABILITIES.get_or_init(|| OSCapabilities::from_os_version(get_version())).clone()
}
10 changes: 5 additions & 5 deletions src/platform_impl/apple/uikit/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ use std::{fmt, hash, ptr};

use dispatch2::{run_on_main, MainThreadBound};
use objc2::rc::Retained;
use objc2::Message;
use objc2::{available, Message};
use objc2_foundation::{MainThreadMarker, NSInteger};
use objc2_ui_kit::{UIScreen, UIScreenMode};

use super::app_state;
use crate::dpi::PhysicalPosition;
use crate::monitor::VideoMode;

Expand Down Expand Up @@ -211,8 +210,7 @@ impl MonitorHandle {

fn refresh_rate_millihertz(uiscreen: &UIScreen) -> Option<NonZeroU32> {
let refresh_rate_millihertz: NSInteger = {
let os_capabilities = app_state::os_capabilities();
if os_capabilities.maximum_frames_per_second {
if available!(ios = 10.3, tvos = 10.2) {
uiscreen.maximumFramesPerSecond()
} else {
// https://developer.apple.com/library/archive/technotes/tn2460/_index.html
Expand All @@ -225,7 +223,9 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> Option<NonZeroU32> {
//
// FIXME: earlier OSs could calculate the refresh rate using
// `-[CADisplayLink duration]`.
os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps");
tracing::warn!(
"`maximumFramesPerSecond` requires iOS 10.3+ or tvOS 10.3+. Defaulting to 60 fps"
);
60
}
};
Expand Down
5 changes: 2 additions & 3 deletions src/platform_impl/apple/uikit/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::cell::{Cell, RefCell};

use objc2::rc::Retained;
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
use objc2::{define_class, msg_send, sel, DefinedClass};
use objc2::{available, define_class, msg_send, sel, DefinedClass};
use objc2_core_foundation::{CGFloat, CGPoint, CGRect};
use objc2_foundation::{MainThreadMarker, NSObject, NSSet, NSString};
use objc2_ui_kit::{
Expand Down Expand Up @@ -462,13 +462,12 @@ impl WinitView {
fn handle_touches(&self, touches: &NSSet<UITouch>) {
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 let UITouchType::Pencil = touch_type {
None
} else if os_supports_force {
} else if available!(ios = 9.0, tvos = 9.0, visionos = 1.0) {
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.
Expand Down
19 changes: 11 additions & 8 deletions src/platform_impl/apple/uikit/view_controller.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::cell::Cell;

use objc2::rc::Retained;
use objc2::{define_class, msg_send, DefinedClass};
use objc2::{available, define_class, msg_send, DefinedClass};
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;

Expand Down Expand Up @@ -78,11 +77,13 @@ impl WinitViewController {

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 {
if available!(ios = 11.0, visionos = 1.0) {
self.setNeedsUpdateOfHomeIndicatorAutoHidden();
} else {
os_capabilities.home_indicator_hidden_err_msg("ignoring")
tracing::warn!(
"`setNeedsUpdateOfHomeIndicatorAutoHidden` requires iOS 11.0+ or visionOS. \
Ignoring"
);
}
}

Expand All @@ -92,11 +93,13 @@ impl WinitViewController {
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 {
if available!(ios = 11.0, visionos = 1.0) {
self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures();
} else {
os_capabilities.defer_system_gestures_err_msg("ignoring")
tracing::warn!(
"`setNeedsUpdateOfScreenEdgesDeferringSystemGestures` requires iOS 11.0+ or \
visionOS. Ignoring"
);
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/platform_impl/apple/uikit/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::VecDeque;

use dispatch2::MainThreadBound;
use objc2::rc::Retained;
use objc2::{class, define_class, msg_send};
use objc2::{available, class, define_class, msg_send};
use objc2_core_foundation::{CGFloat, CGPoint, CGRect, CGSize};
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
use objc2_ui_kit::{
Expand Down Expand Up @@ -196,8 +196,7 @@ impl Inner {
}

pub fn safe_area(&self) -> PhysicalInsets<u32> {
// Only available on iOS 11.0
let insets = if app_state::os_capabilities().safe_area {
let insets = if available!(ios = 11.0, tvos = 11.0, visionos = 1.0) {
self.view.safeAreaInsets()
} else {
// Assume the status bar frame is the only thing that obscures the view
Expand Down

0 comments on commit deb9de9

Please sign in to comment.