mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
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:
1 parent
76d678fc4c
commit
49846862ba
17 files changed
+453
-142
No files matched your search
@@ -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
@@ -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
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:@""];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user