apple: the iOS 6 build compiles clean, and CI holds it there

RetroArch_iOS6.xcodeproj builds for armv7 at a 6.0 deployment target
against the iOS 9.3 SDK, and everything newer than iOS 6 that it
called directly is now behind a runtime check:

- CADisplayLink's rate goes through cocoa_display_link_set_rate() and
  cocoa_display_link_get_rate(): the frame-rate range from iOS 15,
  preferredFramesPerSecond from iOS 10, frameInterval before that.
  maximumFramesPerSecond, nativeBounds and safeAreaInsets are selector
  sends behind their versions.
- The document picker, size transitions and openRetroArchURL: are
  annotated API_AVAILABLE and reached through iOS 8 checks; the
  library reply opens through openURL:options:completionHandler: from
  iOS 10 and openURL: before it.
- UISelectionFeedbackGenerator, the alternate icons, layout anchors,
  the smart-text traits and base64 encoding go by selector and class
  lookup; containsString: gives way to rangeOfString:.
- MetricKit, scenes, pointer interactions and CoreHaptics declare types
  the 9.3 SDK does not have, so those stay behind SDK gates.

apple_runtime.h supplies API_AVAILABLE on SDKs older than iOS 11 as
clang's availability attribute, so annotated methods are checked there
too.  verbosity.c includes os/log.h on the same condition as its call,
gl2.c's macOS FBO extension path no longer takes iOS SDKs that also
define the macOS version macro, mem_stats.c reads
limit_bytes_remaining only where task_vm_info has it, and PRI_SIZET is
"lu" on Apple.

OpenAL is deprecated from macOS 10.15 / iOS 13 but present on every
release; openal.c defines the headers' marker empty, as the GL headers
take GL_SILENCE_DEPRECATION.  features_cpu.c declares the affinity
array only where it is filled, and the tvOS display server no longer
keeps a screen it does not use.

The new "CI iOS 6 (armv7)" workflow fetches only the 9.3 SDK's headers
and runs .github/workflows/scripts/ios-legacy-check.sh, which compiles
griffin_objc.m (ARC) and griffin.c with the iOS 6 project's own
defines at a 6.0 floor and treats every warning as an error, so an
unguarded newer API fails the job.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-07 07:41:37 +02:00
1 parent 76d678fc4c
commit 49846862ba
17 files changed
+453 -142

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
name: CI iOS 6 (armv7)
# The iOS 6 build - RetroArch_iOS6.xcodeproj's defines, ARC, armv7,
# a 6.0 deployment target - compiled against the iOS 9.3 SDK, the last
# that takes a 6.0 floor. No runner Xcode can build armv7 or anything
# below iOS 12 any more, so this runs clang on Linux against the SDK's
# headers, as the PowerPC leg does for old macOS. Every warning is an
# error: anything newer than iOS 6 has to be behind a runtime check,
# and -Wunguarded-availability is what says so.
#
# The checking is .github/workflows/scripts/ios-legacy-check.sh, which
# runs by hand the same way; this job fetches the SDK headers for it.
#
# SDK: theos/sdks, pinned to a commit; only the headers are checked out.
on:
push:
branches: [master]
pull_request:
repository_dispatch:
types: [run_build]
permissions:
contents: read
env:
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
SDKS_COMMIT: 0222fd5413cf4b9af096f37b4621afa2688572f7
SDK: iPhoneOS9.3.sdk
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
build:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- name: Install clang
run: |
sudo apt-get update -q
sudo apt-get install -y -q clang-18
clang-18 --version | head -n 1
- name: Fetch the iOS SDK headers
run: |
set -e
git init -q "$RUNNER_TEMP/sdks"
cd "$RUNNER_TEMP/sdks"
git remote add origin https://github.com/theos/sdks.git
git config core.sparseCheckout true
printf '%s\n' \
"/$SDK/usr/include/" \
"/$SDK/System/Library/Frameworks/*/Headers/" \
"/$SDK/System/Library/Frameworks/*/Frameworks/*.framework/Headers/" \
> .git/info/sparse-checkout
git fetch -q --depth 1 --filter=blob:none origin "$SDKS_COMMIT"
git checkout -q FETCH_HEAD
du -sh "$SDK"
- name: iOS 6.0
run: CC=clang-18 .github/workflows/scripts/ios-legacy-check.sh "$RUNNER_TEMP/sdks/$SDK" 6.0
+67
View File
@@ -0,0 +1,67 @@
#!/bin/sh
# The iOS 6 build, compiled for armv7 the way RetroArch_iOS6.xcodeproj
# builds it: its own defines (read from the project, so this follows
# it), ARC, gnu99, against an iOS SDK with a 6.0 deployment target.
#
# Everything newer than iOS 6 has to sit behind a runtime check -
# apple_runtime_available() and a selector send, or an availability-
# annotated method - and clang's -Wunguarded-availability is what
# enforces that, so every warning is an error here. An API the SDK does
# not declare at all is an error too: the iOS 6 build reaches newer
# APIs by selector, not by needing a newer SDK.
#
# The SDK is the iOS 9.3 one - Xcode 7's, the last to take a 6.0
# deployment target - and only its headers are needed: this compiles,
# it does not link.
#
# usage: ios-legacy-check.sh <path to iPhoneOS9.3.sdk> [floor]
# floor defaults to 6.0
#
# Needs clang (any version that targets armv7-apple-ios).
SDK=$1
FLOOR=${2:-6.0}
CC=${CC:-clang}
PROJECT=pkg/apple/RetroArch_iOS6.xcodeproj/project.pbxproj
if [ -z "$SDK" ] || [ ! -d "$SDK/System/Library/Frameworks/UIKit.framework" ]; then
echo "usage: $0 <path to iPhoneOS9.3.sdk> [floor]" >&2
exit 2
fi
if [ ! -f "$PROJECT" ]; then
echo "run from the top of the RetroArch tree" >&2
exit 2
fi
# The project's OTHER_CFLAGS defines, each once
DEFS=$(grep -o '"-D[A-Za-z_0-9=]*"' "$PROJECT" | tr -d '"' | sort -u | tr '\n' ' ')
# Xcode finds the project's own headers by bare name through its
# header map; every directory under pkg/apple and ui/drivers that
# holds one stands in for it.
HMAP=$(find pkg/apple ui/drivers -name '*.h' -exec dirname {} \; | sort -u | sed 's/^/-I/' | tr '\n' ' ')
INC="-Ideps -Ideps/7zip -Ideps/rcheevos/include -Ideps/stb -I. -Igfx/include -Ilibretro-common/include"
FLAGS="-fsyntax-only -target armv7-apple-ios$FLOOR -isysroot $SDK -std=gnu99 -fblocks
-Wunguarded-availability -Wunguarded-availability-new -Wdeprecated-declarations
-Wno-nullability-completeness -Werror -ferror-limit=0"
fail=0
check() {
name=$1; lang=$2; tu=$3; shift 3
# shellcheck disable=SC2086
if out=$($CC -x "$lang" $FLAGS "$@" $DEFS $INC $HMAP "$tu" 2>&1); then
echo "ok $name"
else
echo "FAIL $name"
printf '%s\n' "$out" | sed 's/^/ /'
fail=1
fi
}
echo "== iOS $FLOOR, armv7, $(basename "$SDK") =="
check "griffin_objc.m (ARC)" objective-c griffin/griffin_objc.m -fobjc-arc
check "griffin.c" c griffin/griffin.c
exit $fail
+29
View File
@@ -35,6 +35,35 @@
#include <sys/sysctl.h>
#endif
/* API_AVAILABLE / API_UNAVAILABLE came with the iOS 11 / macOS 10.13
* SDKs. Against an older SDK, clang gets the same availability
* attribute they expand to - so code annotated as newer than the floor
* still compiles clean there - and other compilers get nothing. Up to
* four platforms per annotation, as the SDK's own macro takes. */
#ifndef API_AVAILABLE
#if defined(__clang__)
#define APPLE_RT_AV_macos(v) __attribute__((availability(macos,introduced=v)))
#define APPLE_RT_AV_macosx(v) __attribute__((availability(macos,introduced=v)))
#define APPLE_RT_AV_ios(v) __attribute__((availability(ios,introduced=v)))
#define APPLE_RT_AV_tvos(v) __attribute__((availability(tvos,introduced=v)))
#define APPLE_RT_AV_watchos(v) __attribute__((availability(watchos,introduced=v)))
#define APPLE_RT_AV_macCatalyst(v) __attribute__((availability(macCatalyst,introduced=v)))
#define APPLE_RT_AV1(a) APPLE_RT_AV_##a
#define APPLE_RT_AV2(a, b) APPLE_RT_AV1(a) APPLE_RT_AV1(b)
#define APPLE_RT_AV3(a, b, c) APPLE_RT_AV1(a) APPLE_RT_AV2(b, c)
#define APPLE_RT_AV4(a, b, c, d) APPLE_RT_AV1(a) APPLE_RT_AV3(b, c, d)
#define APPLE_RT_AV_PICK(_1, _2, _3, _4, name, ...) name
#define API_AVAILABLE(...) \
APPLE_RT_AV_PICK(__VA_ARGS__, APPLE_RT_AV4, APPLE_RT_AV3, \
APPLE_RT_AV2, APPLE_RT_AV1, _unused)(__VA_ARGS__)
#else
#define API_AVAILABLE(...)
#endif
#endif
#ifndef API_UNAVAILABLE
#define API_UNAVAILABLE(...)
#endif
/* Encodes a version as major * 10000 + minor * 100 + patch */
#define APPLE_RUNTIME_VER(maj, min, pat) ((maj) * 10000 + (min) * 100 + (pat))
+6
View File
@@ -20,6 +20,12 @@
#include <time.h>
#ifdef __APPLE__
/* Apple marks all of OpenAL deprecated from macOS 10.15 / iOS 13 but
* still ships it on every release; the headers take an empty marker,
* as the GL ones take GL_SILENCE_DEPRECATION. */
#ifndef OPENAL_DEPRECATED
#define OPENAL_DEPRECATED
#endif
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
+8 -19
View File
@@ -318,10 +318,7 @@ static bool apple_display_server_set_resolution(void *data,
/* iOS: Only refresh rate changes */
RARCH_DBG("[Video] Setting refresh rate to %.3f Hz\n", hz);
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
COCOA_DISPLAY_LINK_SET_RATE(view.displayLink, hz);
else
view.displayLink.preferredFramesPerSecond = hz;
cocoa_display_link_set_rate(view.displayLink, hz);
return true;
}
#endif
@@ -491,22 +488,17 @@ static void *apple_display_server_get_resolution_list(
unsigned dims;
NSMutableSet *rates = [NSMutableSet set];
/* Use nativeBounds to get physical screen resolution
* (works correctly in multitasking/Split View modes) */
UIScreen *mainScreen = [UIScreen mainScreen];
CGRect nativeBounds = mainScreen.nativeBounds;
dims = VIDEO_SCALE_PACK((unsigned)nativeBounds.size.width,
(unsigned)nativeBounds.size.height);
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
currentRate = COCOA_DISPLAY_LINK_PREFERRED_RATE([CocoaView get].displayLink);
else
currentRate = [CocoaView get].displayLink.preferredFramesPerSecond;
/* Physical screen resolution (nativeBounds from iOS 8, which works
* in multitasking/Split View; bounds * scale before it) */
cocoa_get_video_output_size(&dims, NULL, 0);
currentRate = cocoa_display_link_get_rate([CocoaView get].displayLink);
/* Detect ProMotion displays and available refresh rates */
#if !TARGET_OS_TV
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), 0))
{
NSInteger maxFPS = mainScreen.maximumFramesPerSecond;
long maxFPS = apple_rt_get_long([UIScreen mainScreen],
sel_registerName("maximumFramesPerSecond"));
/* ProMotion displays (120Hz) */
if (maxFPS >= 120)
@@ -642,10 +634,7 @@ static void *apple_display_server_init(void)
if (view && view.displayLink)
{
RARCH_DBG("[Video] Setting initial refresh rate to %.3f Hz\n", hz);
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
COCOA_DISPLAY_LINK_SET_RATE(view.displayLink, hz);
else
view.displayLink.preferredFramesPerSecond = hz;
cocoa_display_link_set_rate(view.displayLink, hz);
}
#elif TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000
float hz = settings->floats.video_refresh_rate;
+15 -5
View File
@@ -117,11 +117,21 @@ static bool gl2_core_context_is_mains(gl2_t *gl);
#define GL2_DEFAULT_SHADER_TYPE RARCH_SHADER_NONE
#endif
/* Desktop OpenGL against a macOS SDK before 10.12, whose framebuffer
* names carry the EXT suffix. An iOS SDK defines the macOS version
* macro as well - below 10.12 up to the iOS 10 SDKs - so GLES builds
* are ruled out by name. */
#if defined(__MACH__) && !defined(HAVE_OPENGLES) \
&& defined(MAC_OS_X_VERSION_MAX_ALLOWED) \
&& (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
#define GL2_MAC_EXT_FBO 1
#endif
#if defined(HAVE_PSGL)
#define RARCH_GL_FRAMEBUFFER GL_FRAMEBUFFER_OES
#define RARCH_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES
#define RARCH_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
#elif (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
#elif defined(GL2_MAC_EXT_FBO)
#define RARCH_GL_FRAMEBUFFER GL_FRAMEBUFFER_EXT
#define RARCH_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_EXT
#define RARCH_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
@@ -140,7 +150,7 @@ static bool gl2_core_context_is_mains(gl2_t *gl);
#endif
#define RARCH_GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT
#define RARCH_GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT
#elif (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
#elif defined(GL2_MAC_EXT_FBO)
#define RARCH_GL_RENDERBUFFER GL_RENDERBUFFER_EXT
#define RARCH_GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_EXT
#define RARCH_GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_EXT
@@ -157,7 +167,7 @@ static bool gl2_core_context_is_mains(gl2_t *gl);
#define RARCH_GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT
#endif
#if (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
#if defined(GL2_MAC_EXT_FBO)
#define RARCH_GL_MAX_RENDERBUFFER_SIZE GL_MAX_RENDERBUFFER_SIZE_EXT
#elif defined(HAVE_PSGL)
#define RARCH_GL_MAX_RENDERBUFFER_SIZE GL_MAX_RENDERBUFFER_SIZE_OES
@@ -293,7 +303,7 @@ static bool gl2_init_pbo_readback(gl2_t *gl);
#define gl2_rb_storage glRenderbufferStorageOES
#define gl2_delete_rb glDeleteRenderbuffersOES
#elif (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
#elif defined(GL2_MAC_EXT_FBO)
#define gl2_fb_texture_2d(a, b, c, d, e) glFramebufferTexture2DEXT(a, b, c, d, e)
#define gl2_check_fb_status(target) glCheckFramebufferStatusEXT(target)
#define gl2_gen_fb(n, ids) glGenFramebuffersEXT(n, ids)
@@ -2336,7 +2346,7 @@ static bool gl2_renderchain_init_hw_render(
if (stencil)
{
#if defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES1) || (defined(__MACH__) && defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < 101200))
#if defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES1) || defined(GL2_MAC_EXT_FBO)
/* GLES2 is a bit weird, as always.
* There's no GL_DEPTH_STENCIL_ATTACHMENT like in desktop GL. */
gl2_fb_rb(RARCH_GL_FRAMEBUFFER,
+41 -31
View File
@@ -47,7 +47,11 @@ static CMMotionManager *motionManager;
#ifdef HAVE_MFI
#import <GameController/GameController.h>
#endif
#if TARGET_OS_IOS
/* CoreHaptics needs an iOS 13 SDK. Built against an older one there is
* no RAKeypressHaptics class, its lookup answers nil, and keypresses
* take the feedback generator instead. */
#if TARGET_OS_IOS && defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0)
#define RARCH_SDK_COREHAPTICS 1
#import <CoreHaptics/CoreHaptics.h>
#endif
@@ -81,11 +85,14 @@ float cocoa_screen_get_backing_scale_factor(void);
static bool small_keyboard_active = false;
static icade_map_t icade_maps[MAX_ICADE_PROFILES][MAX_ICADE_KEYS];
#if TARGET_OS_IOS
/* Fallback for iOS 10-13: a UISelectionFeedbackGenerator (iOS 10),
* made by class name and driven by selector */
static id feedbackGenerator;
#ifdef RARCH_SDK_COREHAPTICS
#define KEYPRESS_HAPTIC_AVAIL API_AVAILABLE(ios(14.0))
static CHHapticEngine *keypressHapticEngine KEYPRESS_HAPTIC_AVAIL;
static id<CHHapticPatternPlayer> keypressHapticPlayer KEYPRESS_HAPTIC_AVAIL;
/* Fallback for iOS 10-13 */
static UISelectionFeedbackGenerator *feedbackGenerator;
/* The keypress haptics are iOS 14 CoreHaptics. They live in a class
* carrying that availability, so its methods use the API directly, and
@@ -97,12 +104,18 @@ KEYPRESS_HAPTIC_AVAIL
+ (void)vibrate;
+ (void)stopEngine;
@end
#endif
/* nil when this build has no CoreHaptics; looked up once either way */
static id cocoa_keypress_haptics(void)
{
static id cls;
if (!cls)
cls = apple_rt_class("RAKeypressHaptics");
static id cls;
static int looked_up;
if (!looked_up)
{
cls = apple_rt_class("RAKeypressHaptics");
looked_up = 1;
}
return cls;
}
#endif
@@ -431,17 +444,14 @@ static void *cocoa_input_init(const char *joypad_driver)
#endif
#if TARGET_OS_IOS
if (apple_runtime_available(0, APPLE_RUNTIME_VER(14, 0, 0), 0))
apple_rt_send_void(cocoa_keypress_haptics(), @selector(startEngine));
else
if ( apple_runtime_available(0, APPLE_RUNTIME_VER(14, 0, 0), 0)
&& cocoa_keypress_haptics())
apple_rt_send_void(cocoa_keypress_haptics(), sel_registerName("startEngine"));
else if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), 0))
{
/* Fallback for iOS 10-13 */
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), 0))
{
if (!feedbackGenerator)
feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init];
[feedbackGenerator prepare];
}
if (!feedbackGenerator)
feedbackGenerator = [[apple_rt_class("UISelectionFeedbackGenerator") alloc] init];
apple_rt_send_void(feedbackGenerator, sel_registerName("prepare"));
}
#endif
@@ -735,10 +745,14 @@ static void cocoa_input_free(void *data)
return;
#if TARGET_OS_IOS
if (apple_runtime_available(0, APPLE_RUNTIME_VER(14, 0, 0), 0))
apple_rt_send_void(cocoa_keypress_haptics(), @selector(stopEngine));
else if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), 0))
if ( apple_runtime_available(0, APPLE_RUNTIME_VER(14, 0, 0), 0)
&& cocoa_keypress_haptics())
apple_rt_send_void(cocoa_keypress_haptics(), sel_registerName("stopEngine"));
else if (feedbackGenerator)
{
RARCH_RELEASE(feedbackGenerator);
feedbackGenerator = nil;
}
#endif
memset(apple_key_state, 0, sizeof(apple_key_state));
@@ -969,6 +983,7 @@ static float cocoa_input_get_sensor_input(void *data, unsigned port, unsigned id
}
#if TARGET_OS_IOS
#ifdef RARCH_SDK_COREHAPTICS
@implementation RAKeypressHaptics
+ (void)startEngine
@@ -1080,22 +1095,17 @@ static float cocoa_input_get_sensor_input(void *data, unsigned port, unsigned id
}
@end
#endif
static void cocoa_input_keypress_vibrate(void)
{
if (apple_runtime_available(0, APPLE_RUNTIME_VER(14, 0, 0), 0))
apple_rt_send_void(cocoa_keypress_haptics(), @selector(vibrate));
else
if ( apple_runtime_available(0, APPLE_RUNTIME_VER(14, 0, 0), 0)
&& cocoa_keypress_haptics())
apple_rt_send_void(cocoa_keypress_haptics(), sel_registerName("vibrate"));
else if (feedbackGenerator)
{
/* Fallback for iOS 10-13 */
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), 0))
{
if (feedbackGenerator)
{
[feedbackGenerator selectionChanged];
[feedbackGenerator prepare];
}
}
apple_rt_send_void(feedbackGenerator, sel_registerName("selectionChanged"));
apple_rt_send_void(feedbackGenerator, sel_registerName("prepare"));
}
}
#endif
+3
View File
@@ -289,8 +289,11 @@ typedef enum replay_checkpoint_behavior_ replay_checkpoint_behavior;
* @param userdata User data which will be passed to subsequent callbacks.
* @param line the line of input, which can be NULL.
**/
#ifndef INPUT_KEYBOARD_LINE_COMPLETE_T_DEFINED
#define INPUT_KEYBOARD_LINE_COMPLETE_T_DEFINED
typedef void (*input_keyboard_line_complete_t)(void *userdata,
const char *line);
#endif
struct input_keyboard_line
{
+2
View File
@@ -929,7 +929,9 @@ static size_t cpu_features_processor_order_masked(
size_t cpu_features_get_processor_order(unsigned *s, size_t len)
{
#if defined(__linux__) || (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__))
unsigned char allowed[CPU_CLASS_MAX_IDS];
#endif
const unsigned char *mask = NULL;
#if defined(__linux__)
{
@@ -525,6 +525,10 @@ typedef struct
# define PRI_SIZET "u"
#elif defined(__EMSCRIPTEN__)
# define PRI_SIZET "zu"
#elif defined(__APPLE__)
/* Darwin's size_t is unsigned long on every architecture, 32-bit
* (armv7, i386, PowerPC) included, where SIZE_MAX alone says "u". */
# define PRI_SIZET "lu"
#else
# if (SIZE_MAX == 0xFFFF)
# define PRI_SIZET "hu"
+29 -13
View File
@@ -394,11 +394,25 @@ uint64_t mem_stats_total(void)
}
#else /* the iOS family */
{
uint64_t size = 0;
int mib[2] = { CTL_HW, HW_MEMSIZE };
size_t len = sizeof(size);
#ifdef TASK_VM_INFO_REV4_COUNT
/* What this process may use: its footprint plus the headroom left
* before jetsam. limit_bytes_remaining is revision 4 of the
* structure (iOS 13): read where the SDK declares it and the
* running kernel filled it in, which the count it hands back
* says. */
task_vm_info_data_t vm_info;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
if (task_info(mach_task_self(), TASK_VM_INFO,
(task_info_t)&vm_info, &count) == KERN_SUCCESS)
if ( task_info(mach_task_self(), TASK_VM_INFO,
(task_info_t)&vm_info, &count) == KERN_SUCCESS
&& count >= TASK_VM_INFO_REV4_COUNT)
return vm_info.phys_footprint + vm_info.limit_bytes_remaining;
#endif
/* Before that, the device's memory */
if (sysctl(mib, 2, &size, &len, NULL, 0) >= 0)
return size;
return 0;
}
#endif
@@ -551,7 +565,19 @@ uint64_t mem_stats_free(void)
return cached;
}
#elif defined(MEM_STATS_APPLE)
#if !TARGET_OS_IPHONE
#if TARGET_OS_IPHONE && defined(TASK_VM_INFO_REV4_COUNT)
/* iOS 13 on: the headroom left before jetsam, revision 4 of the
* structure - read only when the running kernel filled it in. Older
* systems take the page count below, as macOS does. */
{
task_vm_info_data_t vm_info;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
if ( task_info(mach_task_self(), TASK_VM_INFO,
(task_info_t)&vm_info, &count) == KERN_SUCCESS
&& count >= TASK_VM_INFO_REV4_COUNT)
return vm_info.limit_bytes_remaining;
}
#endif
/* Free plus reclaimable (inactive) pages, through the 32-bit
* host_statistics interface: it exists back to 10.0 and runs on 10.5
* kernels, unlike host_statistics64 (10.6+) or a task_vm_info
@@ -574,16 +600,6 @@ uint64_t mem_stats_free(void)
mach_port_deallocate(mach_task_self(), host);
return avail;
}
#else /* the iOS family */
{
task_vm_info_data_t vm_info;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
if (task_info(mach_task_self(), TASK_VM_INFO,
(task_info_t)&vm_info, &count) == KERN_SUCCESS)
return vm_info.limit_bytes_remaining;
return 0;
}
#endif
#elif defined(MEM_STATS_PROC)
{
uint64_t avail = 0;
+8 -1
View File
@@ -6,6 +6,7 @@
//
#import "RetroArchPlaylistManager.h"
#include "../../../apple_runtime.h"
#include "../../../playlist.h"
#include "../../../paths.h"
#include "../../../retroarch.h"
@@ -263,7 +264,13 @@ typedef void (^PlaylistEntryBlock)(const struct playlist_entry *entry, playlist_
/* URL-safe base64 (base64url) without padding, matching the encoding other
* front-ends use for their library callbacks. */
NSString *encoded = [json base64EncodedStringWithOptions:0];
/* -base64EncodedStringWithOptions: is iOS 7 / macOS 10.9; before it
* the same encoding is -base64Encoding (deprecated since). */
NSString *encoded = apple_runtime_available(APPLE_RUNTIME_VER(10, 9, 0),
APPLE_RUNTIME_VER(7, 0, 0), APPLE_RUNTIME_VER(9, 0, 0))
? ((id (*)(id, SEL, unsigned long))objc_msgSend)(json,
sel_registerName("base64EncodedStringWithOptions:"), 0UL)
: apple_rt_get_id(json, sel_registerName("base64Encoding"));
encoded = [encoded stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
encoded = [encoded stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
encoded = [encoded stringByReplacingOccurrencesOfString:@"=" withString:@""];
+5 -1
View File
@@ -19,8 +19,12 @@ extern bool ios_running_on_ipad(void);
#endif
#if TARGET_OS_IPHONE
/* iOS native keyboard support */
/* iOS native keyboard support; the typedef is input_driver.h's, made
* once whichever header comes first */
#ifndef INPUT_KEYBOARD_LINE_COMPLETE_T_DEFINED
#define INPUT_KEYBOARD_LINE_COMPLETE_T_DEFINED
typedef void (*input_keyboard_line_complete_t)(void *userdata, const char *line);
#endif
extern bool ios_keyboard_start(char **buffer_ptr, size_t *size_ptr, size_t *ptr_ptr,
const char *label,
input_keyboard_line_complete_t callback, void *userdata);
+7
View File
@@ -107,6 +107,13 @@
@end
void get_ios_version(int *major, int *minor);
/* A display link's refresh rate, set and read the way the running OS
* provides it: a frame-rate range from iOS/tvOS 15,
* preferredFramesPerSecond from 10, and before that frameInterval, the
* screen refreshes per callback on a 60 Hz panel. */
void cocoa_display_link_set_rate(id link, float hz);
float cocoa_display_link_get_rate(id link);
#else
#define RAScreen NSScreen
+64 -42
View File
@@ -325,10 +325,7 @@ retro_time_t cocoa_last_vblank_time(void)
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), APPLE_RUNTIME_VER(10, 2, 0)))
hz = (float)apple_rt_get_long([UIScreen mainScreen],
sel_registerName("maximumFramesPerSecond"));
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
COCOA_DISPLAY_LINK_SET_RATE(view.displayLink, hz);
else
view.displayLink.preferredFramesPerSecond = hz;
cocoa_display_link_set_rate(view.displayLink, hz);
}
[view.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
#elif TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000
@@ -599,7 +596,9 @@ retro_time_t cocoa_last_vblank_time(void)
#pragma mark UIDocumentPickerViewController
-(void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
/* The document picker is iOS 8: UIKit calls these only there, and
* ios_show_file_sheet() checks the OS before it asks for one. */
-(void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url API_AVAILABLE(ios(8.0))
{
NSFileManager *manager = [NSFileManager defaultManager];
NSString *filename = (NSString*)url.path.lastPathComponent;
@@ -611,7 +610,7 @@ retro_time_t cocoa_last_vblank_time(void)
NSString *documentsDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
/* Copy file to documents directory if it's not already
* inside Documents directory */
if (![[url path] containsString:documentsDir])
if ([[url path] rangeOfString:documentsDir].location == NSNotFound)
if (![manager fileExistsAtPath:destination])
[manager copyItemAtPath:[url path] toPath:destination error:&error];
if (filebrowser_get_type() == FILEBROWSER_SCAN_FILE)
@@ -622,11 +621,11 @@ retro_time_t cocoa_last_vblank_time(void)
}
}
-(void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller
-(void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller API_AVAILABLE(ios(8.0))
{
}
-(void)showDocumentPicker
-(void)showDocumentPicker API_AVAILABLE(ios(8.0))
{
/* -initWithDocumentTypes:inMode: and the kUTType names are
* deprecated (iOS 14 / 15) and still what reaches back to iOS 8;
@@ -732,7 +731,8 @@ retro_time_t cocoa_last_vblank_time(void)
}
-(BOOL)prefersHomeIndicatorAutoHidden { return YES; }
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
/* UIKit calls this from iOS 8 on, so the call up to super is as old */
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0))
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
if (apple_runtime_available(0, APPLE_RUNTIME_VER(11, 0, 0), 0))
@@ -787,7 +787,10 @@ retro_time_t cocoa_last_vblank_time(void)
if (!window)
return;
UIEdgeInsets inset = window.safeAreaInsets;
/* -[UIView safeAreaInsets] (iOS 11); the struct is returned in
* memory everywhere but arm64 */
UIEdgeInsets inset = apple_rt_get_large_struct(UIEdgeInsets, window,
sel_registerName("safeAreaInsets"));
/* UIWindowScene.effectiveGeometry is an iOS 16 API that older
* SDKs do not declare, so it is resolved entirely at runtime via
* objc_msgSend - same cost as a compiled property access.
@@ -961,7 +964,8 @@ retro_time_t cocoa_last_vblank_time(void)
{
#if TARGET_OS_IOS
if (apple_runtime_available(0, APPLE_RUNTIME_VER(11, 0, 0), 0))
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
apple_rt_send_void(self,
sel_registerName("setNeedsUpdateOfHomeIndicatorAutoHidden"));
#endif
}
@@ -1045,7 +1049,8 @@ retro_time_t cocoa_last_vblank_time(void)
#if TARGET_OS_IOS
void ios_show_file_sheet(void)
{
[[CocoaView get] showDocumentPicker];
if (apple_runtime_available(0, APPLE_RUNTIME_VER(8, 0, 0), 0))
apple_rt_send_void([CocoaView get], @selector(showDocumentPicker));
}
#endif
@@ -1406,6 +1411,40 @@ static float cocoa_display_mode_refresh_rate(CGDirectDisplayID id)
}
#endif
#if TARGET_OS_IPHONE
void cocoa_display_link_set_rate(id link, float hz)
{
if (!link || hz <= 0.0f)
return;
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
COCOA_DISPLAY_LINK_SET_RATE(link, hz);
else if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), APPLE_RUNTIME_VER(10, 0, 0)))
apple_rt_send_long(link, sel_registerName("setPreferredFramesPerSecond:"),
(long)(hz + 0.5f));
else
{
/* Deprecated in iOS 10 and the only control before it */
long interval = (long)(60.0f / hz + 0.5f);
apple_rt_send_long(link, sel_registerName("setFrameInterval:"),
interval > 0 ? interval : 1);
}
}
float cocoa_display_link_get_rate(id link)
{
long interval;
if (!link)
return 0.0f;
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
return COCOA_DISPLAY_LINK_PREFERRED_RATE(link);
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), APPLE_RUNTIME_VER(10, 0, 0)))
return (float)apple_rt_get_long(link,
sel_registerName("preferredFramesPerSecond"));
interval = apple_rt_get_long(link, sel_registerName("frameInterval"));
return 60.0f / (float)(interval > 0 ? interval : 1);
}
#endif
float cocoa_get_refresh_rate(void)
{
#if TARGET_OS_OSX
@@ -1434,41 +1473,25 @@ float cocoa_get_refresh_rate(void)
* below stays reachable and unchanged: 10.0 - 10.2 still gets
* preferredFramesPerSecond, pre-10.0 still gets frameInterval,
* and a 0 answer here still falls through to them. */
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100300 || __TV_OS_VERSION_MAX_ALLOWED >= 100200
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), APPLE_RUNTIME_VER(10, 2, 0)))
{
NSInteger max_fps = [[UIScreen mainScreen] maximumFramesPerSecond];
long max_fps = apple_rt_get_long([UIScreen mainScreen],
sel_registerName("maximumFramesPerSecond"));
if (max_fps > 0)
return (float)max_fps;
}
#endif
{
CADisplayLink *dl = [CocoaView get].displayLink;
if (dl)
{
if (apple_runtime_available(0, APPLE_RUNTIME_VER(15, 0, 0), APPLE_RUNTIME_VER(15, 0, 0)))
return COCOA_DISPLAY_LINK_PREFERRED_RATE(dl);
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 || __TV_OS_VERSION_MAX_ALLOWED >= 100000
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), APPLE_RUNTIME_VER(10, 0, 0)))
return dl.preferredFramesPerSecond;
#endif
/* iOS 6 - 9 / tvOS < 10: only frameInterval exists. It is
* the number of screen refreshes between callbacks, so
* convert to Hz assuming a 60 Hz panel (accurate for every
* pre-iOS-10 device - ProMotion is iPad Pro 2017+). */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
{
NSInteger fi = dl.frameInterval;
return 60.0f / (float)(fi > 0 ? fi : 1);
}
#pragma clang diagnostic pop
/* Before iOS 10 this is frameInterval on a 60 Hz panel, which
* is every pre-iOS-10 device - ProMotion is iPad Pro 2017+. */
return cocoa_display_link_get_rate(dl);
}
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100300 || __TV_OS_VERSION_MAX_ALLOWED >= 100200
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), APPLE_RUNTIME_VER(10, 2, 0)))
return [UIScreen mainScreen].maximumFramesPerSecond;
#endif
return (float)apple_rt_get_long([UIScreen mainScreen],
sel_registerName("maximumFramesPerSecond"));
return 60.0f;
#endif
}
@@ -1512,14 +1535,13 @@ float cocoa_get_window_refresh_rate(void)
if (!screen)
return 0.0f;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100300 || __TV_OS_VERSION_MAX_ALLOWED >= 100200
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), APPLE_RUNTIME_VER(10, 2, 0)))
{
NSInteger max_fps = [screen maximumFramesPerSecond];
long max_fps = apple_rt_get_long(screen,
sel_registerName("maximumFramesPerSecond"));
if (max_fps > 0)
return (float)max_fps;
}
#endif
return 0.0f;
#endif
}
@@ -1569,16 +1591,16 @@ void cocoa_get_video_output_size(unsigned *dims,
{
#if TARGET_OS_IPHONE
UIScreen *screen = [UIScreen mainScreen];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || __TV_OS_VERSION_MAX_ALLOWED >= 90000
if (apple_runtime_available(0, APPLE_RUNTIME_VER(8, 0, 0), APPLE_RUNTIME_VER(9, 0, 0)))
{
/* nativeBounds is physical pixels, orientation-independent. */
CGRect b = screen.nativeBounds;
/* nativeBounds (iOS 8) is physical pixels, orientation-independent;
* a CGRect is returned in memory everywhere but arm64. */
CGRect b = apple_rt_get_large_struct(CGRect, screen,
sel_registerName("nativeBounds"));
*dims = VIDEO_SCALE_PACK((unsigned)b.size.width,
(unsigned)b.size.height);
}
else
#endif
{
/* iOS 6/7: no nativeBounds. UIScreen.bounds is in points and
* fixed to portrait orientation pre-iOS-8. Every iOS 6/7-era
+94 -29
View File
@@ -77,8 +77,20 @@
#import <AVFoundation/AVFoundation.h>
#import <CoreFoundation/CoreFoundation.h>
/* MetricKit (iOS 13) and pointer interactions (iOS 13.4) need an SDK
* that has them; built against an older one, the app has neither and
* does not register for them. */
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) \
|| (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 130000)
#define RARCH_SDK_METRICKIT 1
#define RARCH_SDK_SCENES 1
#import <MetricKit/MetricKit.h>
#import <MetricKit/MXMetricManager.h>
#endif
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400) \
|| (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 130400)
#define RARCH_SDK_POINTER 1
#endif
#import "../../pkg/apple/WebServer/WebServer.h"
@@ -155,7 +167,10 @@ static void ui_companion_cocoatouch_set_app_icon(const char *iconName)
NSString *str = nil;
if (!string_is_equal(iconName, "Default"))
str = [NSString stringWithCString:iconName encoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] setAlternateIconName:str completionHandler:nil];
/* Alternate icons are iOS 10.3 / tvOS 10.2 */
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), APPLE_RUNTIME_VER(10, 2, 0)))
((void (*)(id, SEL, id, id))objc_msgSend)([UIApplication sharedApplication],
sel_registerName("setAlternateIconName:completionHandler:"), str, nil);
}
/* Main thread only: the sole caller is materialui's icon draw, which
@@ -616,6 +631,9 @@ enum
@end
#endif
/* Scenes need an iOS 13 SDK; an app built against an older one is run
* by UIKit without them, through the app delegate. */
#ifdef RARCH_SDK_SCENES
API_AVAILABLE(ios(13.0), tvos(13.0))
@interface RetroArchSceneDelegate : UIResponder <UIWindowSceneDelegate>
@end
@@ -651,9 +669,14 @@ API_AVAILABLE(ios(13.0), tvos(13.0))
}
@end
#endif
#if TARGET_OS_IOS
@interface RetroArch_iOS () <MXMetricManagerSubscriber, UIPointerInteractionDelegate>
#if TARGET_OS_IOS && defined(RARCH_SDK_METRICKIT)
@interface RetroArch_iOS () <MXMetricManagerSubscriber>
@end
#endif
#if TARGET_OS_IOS && defined(RARCH_SDK_POINTER)
@interface RetroArch_iOS () <UIPointerInteractionDelegate>
@end
#endif
@@ -740,7 +763,7 @@ API_AVAILABLE(ios(13.0), tvos(13.0))
UIView *rootView = [CocoaView get].view;
[rootView addSubview:_renderView];
#if TARGET_OS_IOS
#if TARGET_OS_IOS && defined(RARCH_SDK_POINTER)
if (apple_runtime_available(0, APPLE_RUNTIME_VER(13, 4, 0), 0))
{
/* +[UIPointerInteraction alloc] initWithDelegate: returns +1.
@@ -754,23 +777,32 @@ API_AVAILABLE(ios(13.0), tvos(13.0))
apple_rt_get_id(apple_rt_class("UIPointerInteraction"), @selector(alloc)),
sel_registerName("initWithDelegate:"), self);
RARCH_AUTORELEASE(interaction);
[_renderView addInteraction:interaction];
apple_rt_send_id(_renderView, sel_registerName("addInteraction:"),
interaction);
_renderView.userInteractionEnabled = YES;
}
#endif
/* Layout anchors are iOS 9; the view is asked whether it has them
* and pinned to the container's edges either way. */
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __TV_OS_VERSION_MAX_ALLOWED >= 90000
if ([_renderView respondsToSelector:@selector(topAnchor)])
* and pinned to the container's edges either way. The anchors, the
* constraint between them and its activation (iOS 8) go by selector,
* so any SDK builds this. */
if ([_renderView respondsToSelector:sel_registerName("topAnchor")])
{
static const char *const edges[4] = {
"topAnchor", "bottomAnchor", "leadingAnchor", "trailingAnchor" };
SEL set_active = sel_registerName("setActive:");
SEL equal_to = sel_registerName("constraintEqualToAnchor:");
int i;
_renderView.translatesAutoresizingMaskIntoConstraints = NO;
[[_renderView.topAnchor constraintEqualToAnchor:rootView.topAnchor] setActive:YES];
[[_renderView.bottomAnchor constraintEqualToAnchor:rootView.bottomAnchor] setActive:YES];
[[_renderView.leadingAnchor constraintEqualToAnchor:rootView.leadingAnchor] setActive:YES];
[[_renderView.trailingAnchor constraintEqualToAnchor:rootView.trailingAnchor] setActive:YES];
for (i = 0; i < 4; i++)
{
SEL edge = sel_registerName(edges[i]);
id c = apple_rt_get_id_arg(apple_rt_get_id(_renderView, edge),
equal_to, apple_rt_get_id(rootView, edge));
apple_rt_send_bool(c, set_active, YES);
}
}
else
#endif
{
_renderView.frame = rootView.bounds;
_renderView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
@@ -830,8 +862,11 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
{
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
/* AVAudioSessionCategoryOptionAllowBluetoothA2DP (0x20) is iOS /
* tvOS 10; an older system is not handed a bit it does not know. */
AVAudioSessionCategoryOptions options =
AVAudioSessionCategoryOptionAllowBluetoothA2DP;
apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), APPLE_RUNTIME_VER(10, 0, 0))
? (AVAudioSessionCategoryOptions)0x20 : (AVAudioSessionCategoryOptions)0;
#if TARGET_OS_IOS
/* PlayAndRecord routes output to the receiver on iPhone unless
@@ -1138,7 +1173,11 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
const char *icon_name;
appicon_setting->default_value.string = icons->elems[0].data;
icon_name = [[application alternateIconName] cStringUsingEncoding:NSUTF8StringEncoding]; /* need to ask uico_st for this */
/* need to ask uico_st for this; alternate icons are iOS 10.3 / tvOS 10.2 */
icon_name = apple_runtime_available(0, APPLE_RUNTIME_VER(10, 3, 0), APPLE_RUNTIME_VER(10, 2, 0))
? [(NSString *)apple_rt_get_id(application, sel_registerName("alternateIconName"))
cStringUsingEncoding:NSUTF8StringEncoding]
: NULL;
for (i = 0; i < (int)icons->size; i++)
{
_len += strlen(icons->elems[i].data) + 1;
@@ -1162,7 +1201,7 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
sel_registerName("updateAppShortcuts"));
#endif
#if TARGET_OS_IOS
#if TARGET_OS_IOS && defined(RARCH_SDK_METRICKIT)
if (apple_runtime_available(0, APPLE_RUNTIME_VER(13, 0, 0), 0))
apple_rt_send_id(apple_rt_get_id(apple_rt_class("MXMetricManager"),
sel_registerName("sharedManager")),
@@ -1322,7 +1361,9 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
#endif
}
-(BOOL)openRetroArchURL:(NSURL *)url
/* NSURLComponents' query items are iOS 8; the one caller checks the
* OS before it hands a URL here. */
-(BOOL)openRetroArchURL:(NSURL *)url API_AVAILABLE(ios(8.0), tvos(9.0))
{
RARCH_LOG("RetroArch URL received: %s\n", [[url absoluteString] UTF8String]);
@@ -1413,7 +1454,14 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
}
RARCH_LOG("Returning game library to '%s'\n", [caller_scheme UTF8String]);
[[UIApplication sharedApplication] openURL:replyURL options:@{} completionHandler:nil];
/* -openURL:options:completionHandler: is iOS 10; -openURL: before it */
if (apple_runtime_available(0, APPLE_RUNTIME_VER(10, 0, 0), APPLE_RUNTIME_VER(10, 0, 0)))
((void (*)(id, SEL, id, id, id))objc_msgSend)([UIApplication sharedApplication],
sel_registerName("openURL:options:completionHandler:"),
replyURL, [NSDictionary dictionary], nil);
else
((BOOL (*)(id, SEL, id))objc_msgSend)([UIApplication sharedApplication],
sel_registerName("openURL:"), replyURL);
return YES;
}
#endif
@@ -1422,9 +1470,12 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
return NO;
}
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
if ([[url scheme] isEqualToString:@"retroarch"])
return [self openRetroArchURL:url];
return apple_runtime_available(0, APPLE_RUNTIME_VER(8, 0, 0), APPLE_RUNTIME_VER(9, 0, 0))
? ((BOOL (*)(id, SEL, id))objc_msgSend)(self,
@selector(openRetroArchURL:), url)
: NO;
NSFileManager *manager = [NSFileManager defaultManager];
NSString *filename = (NSString*)url.path.lastPathComponent;
@@ -1435,12 +1486,19 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
NSString *destination = [NSString stringWithUTF8String:fullpath];
/* Copy file to documents directory if it's not already
* inside Documents directory */
if ([url startAccessingSecurityScopedResource])
/* Security-scoped access is iOS 8; before it the file is plain */
{
if (![[url path] containsString: self.documentsDirectory])
if (![manager fileExistsAtPath:destination])
[manager copyItemAtPath:[url path] toPath:destination error:&error];
[url stopAccessingSecurityScopedResource];
bool scoped = apple_runtime_available(0, APPLE_RUNTIME_VER(8, 0, 0), APPLE_RUNTIME_VER(9, 0, 0));
if (!scoped || apple_rt_get_bool(url,
sel_registerName("startAccessingSecurityScopedResource")))
{
if ([[url path] rangeOfString:self.documentsDirectory].location == NSNotFound)
if (![manager fileExistsAtPath:destination])
[manager copyItemAtPath:[url path] toPath:destination error:&error];
if (scoped)
apple_rt_send_void(url,
sel_registerName("stopAccessingSecurityScopedResource"));
}
}
task_push_dbscan(fullpath, NULL);
return true;
@@ -1479,9 +1537,13 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
self.keyboardTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.keyboardTextField.autocorrectionType = UITextAutocorrectionTypeNo;
self.keyboardTextField.spellCheckingType = UITextSpellCheckingTypeNo;
self.keyboardTextField.smartQuotesType = UITextSmartQuotesTypeNo;
self.keyboardTextField.smartDashesType = UITextSmartDashesTypeNo;
self.keyboardTextField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
/* The smart-text traits are iOS 11; 1 is each type's ...No */
if (apple_runtime_available(0, APPLE_RUNTIME_VER(11, 0, 0), APPLE_RUNTIME_VER(11, 0, 0)))
{
apple_rt_send_long(self.keyboardTextField, sel_registerName("setSmartQuotesType:"), 1);
apple_rt_send_long(self.keyboardTextField, sel_registerName("setSmartDashesType:"), 1);
apple_rt_send_long(self.keyboardTextField, sel_registerName("setSmartInsertDeleteType:"), 1);
}
self.keyboardTextField.returnKeyType = UIReturnKeyDone;
[[CocoaView get].view addSubview:self.keyboardTextField];
}
@@ -1493,7 +1555,7 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
- (void)supportOtherAudioSessions { }
#if TARGET_OS_IOS
#if TARGET_OS_IOS && defined(RARCH_SDK_METRICKIT)
- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload *> *)payloads API_AVAILABLE(ios(13.0))
{
for (MXMetricPayload *payload in payloads)
@@ -1520,6 +1582,9 @@ bool cocoa_audio_session_begin_record(unsigned preferred_rate,
}
}
#endif
#if TARGET_OS_IOS && defined(RARCH_SDK_POINTER)
- (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region API_AVAILABLE(ios(13.4))
{
if (cocoa_input_mouse_grabbed())
+5 -1
View File
@@ -28,7 +28,11 @@
#if TARGET_IPHONE_SIMULATOR
#include <stdio.h>
#else
#if __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_10_0 || __TV_OS_VERSION_MIN_REQUIRED > __TVOS_10_0
/* The same test as the os_log call site below: a 10.0 floor logs through
* os_log, anything lower (or an SDK without os/log.h) through asl. */
#if (defined(__IPHONE_10_0) && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0)) \
|| (defined(__TV_OS_VERSION_MIN_REQUIRED) && defined(__TVOS_10_0) \
&& (__TV_OS_VERSION_MIN_REQUIRED >= __TVOS_10_0))
#include <os/log.h>
#else
#include <asl.h>