mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
@available has two problems for RetroArch's range of targets: 1. It needs a clang new enough to know the syntax, so the ancient toolchains building the legacy iOS/macOS targets cannot compile any file that uses it. 2. It lowers to a call to __isPlatformVersionAtLeast() every time it is evaluated, including in per-frame and per-input paths. Introduce apple_runtime.h with apple_runtime_available(mac, ios, tvos), a drop-in replacement that resolves the running OS version exactly once per process and caches it in a static int; after warm-up every check is a single load and integer compare, cheaper than @available. Platforms the original check did not list are passed as 0, preserving @available's semantics of returning true on unlisted platforms. The version is read from UIDevice systemVersion on iOS/tvOS (never shimmed, works on any version), and on macOS from the kern.osproductversion sysctl - which reports the real OS version even for binaries linked against pre-11.0 SDKs, where the NSProcessInfo APIs (and @available's own plist source) are subject to the 10.16 compatibility shim - with an operatingSystemVersionString fallback for pre-10.13.4 systems. This also fixes the broken iOS 9 / iOS 6 CI builds: cocoa_common.m used UIWindowScene.effectiveGeometry, an iOS 16 API that older SDKs (e.g. iPhoneOS 14.2) do not declare at all, so @available could not save compilation. That call chain is now resolved entirely at runtime via objc_msgSend (identical cost to a compiled property access), with the capability probe cached in a static. All 84 live @available sites across 13 files are converted. Behavior on every OS version is unchanged: the same paths are taken at the same version thresholds. Swift #available sites are untouched; Swift enforces availability at compile time and those files only build on modern toolchains. Existing compile-time SDK gates (e.g. around NSWorkspace isVoiceOverEnabled and the Metal HDR blocks) are kept, since they guard symbol declarations, not runtime dispatch; comments updated accordingly.
110 lines
3.8 KiB
Objective-C
110 lines
3.8 KiB
Objective-C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2011-2026 - Daniel De Matteis
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef __APPLE_RUNTIME_H
|
|
#define __APPLE_RUNTIME_H
|
|
|
|
/* Runtime replacement for @available().
|
|
*
|
|
* @available needs a clang new enough to know the syntax (Xcode 9+)
|
|
* and calls __isPlatformVersionAtLeast() every time it is evaluated.
|
|
* The helpers here compile on any toolchain down to the ancient ones
|
|
* building the legacy iOS/macOS targets, work on any OS version at
|
|
* runtime, and resolve the OS version exactly once per process -
|
|
* afterwards every check is a cached integer compare.
|
|
*/
|
|
|
|
#include <TargetConditionals.h>
|
|
#import <Foundation/Foundation.h>
|
|
#if TARGET_OS_IPHONE
|
|
#import <UIKit/UIKit.h>
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
/* Encodes a version as major * 10000 + minor * 100 + patch */
|
|
#define APPLE_RUNTIME_VER(maj, min, pat) ((maj) * 10000 + (min) * 100 + (pat))
|
|
|
|
/* Returns the running OS version, APPLE_RUNTIME_VER-encoded.
|
|
* Resolved once; the benign race on the static is idempotent. */
|
|
static int apple_runtime_os_version(void)
|
|
{
|
|
static int ver = -1;
|
|
if (ver == -1)
|
|
{
|
|
int part[3] = {0, 0, 0};
|
|
int i = 0;
|
|
const char *s = NULL;
|
|
#if TARGET_OS_IPHONE
|
|
/* Never shimmed, works on any iOS/tvOS version */
|
|
s = [[[UIDevice currentDevice] systemVersion] UTF8String];
|
|
#else
|
|
/* kern.osproductversion (10.13.4+) reports the real OS version;
|
|
* the NSProcessInfo APIs are subject to the 10.16 compatibility
|
|
* shim for binaries linked against pre-11.0 SDKs. */
|
|
char buf[64];
|
|
size_t len = sizeof(buf);
|
|
if (sysctlbyname("kern.osproductversion", buf, &len, NULL, 0) == 0)
|
|
s = buf;
|
|
else
|
|
{
|
|
/* Pre-10.13.4 fallback: "Version 10.9.5 (Build 13F34)" -
|
|
* skip ahead to the first digit */
|
|
s = [[[NSProcessInfo processInfo] operatingSystemVersionString]
|
|
UTF8String];
|
|
while (s && *s && (*s < '0' || *s > '9'))
|
|
s++;
|
|
}
|
|
#endif
|
|
if (s)
|
|
{
|
|
for (; *s && i < 3; s++)
|
|
{
|
|
if (*s >= '0' && *s <= '9')
|
|
part[i] = part[i] * 10 + (*s - '0');
|
|
else if (*s == '.')
|
|
i++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
ver = APPLE_RUNTIME_VER(part[0], part[1], part[2]);
|
|
#if !TARGET_OS_IPHONE
|
|
/* Belt and braces: if the shim still got to us, 10.16 == 11.0 */
|
|
if (ver == APPLE_RUNTIME_VER(10, 16, 0))
|
|
ver = APPLE_RUNTIME_VER(11, 0, 0);
|
|
#endif
|
|
}
|
|
return ver;
|
|
}
|
|
|
|
/* Drop-in replacement for if (@available(macOS a, iOS b, tvOS c, *)).
|
|
* Pass 0 for platforms the original check did not list: @available
|
|
* returns true on platforms not named (that is the '*'), and any
|
|
* real OS version is >= 0. */
|
|
#if defined(TARGET_OS_TV) && TARGET_OS_TV
|
|
#define apple_runtime_available(macos_v, ios_v, tvos_v) \
|
|
(apple_runtime_os_version() >= (tvos_v))
|
|
#elif TARGET_OS_IPHONE
|
|
#define apple_runtime_available(macos_v, ios_v, tvos_v) \
|
|
(apple_runtime_os_version() >= (ios_v))
|
|
#else
|
|
#define apple_runtime_available(macos_v, ios_v, tvos_v) \
|
|
(apple_runtime_os_version() >= (macos_v))
|
|
#endif
|
|
|
|
#endif /* __APPLE_RUNTIME_H */
|