Files
Toby Fox f555de201f feature: iOS Support (#116)
* build: add an iOS arm64 target

Selects iOS before macOS, since APPLE is true for both and the macOS
branch would otherwise claim an iOS configure and pick a memory backend
that cannot compile there.

Third-party libraries are pinned static on Apple targets. aurora picks
shared ones whenever BUILD_SHARED_LIBS is merely undefined, and its
extern/ tree unsets the cache entry while working around xxhash, so
passing it on the command line only survives one configure. On iOS that
was fatal rather than untidy: libpng linked as a dylib with an @rpath
into the build tree, which dyld cannot resolve inside a bundle.

User state lives in the container's Documents rather than a name-scoped
Application Support directory, so Config.toml sits beside the game data
a relative dvd_root resolves against, and both are reachable over file
sharing.

(cherry picked from commit ad7231fca3d55c5d7d1807c5accb6874d702111f)
(cherry picked from commit 76cc9594e02d5a858c0848b4b4e4e5a585151cc0)
(cherry picked from commit 80a6b53abd699942178fde2f16010d54dba9952b)

* ios: resolve the flat guest base at runtime

No fixed base works on every device. Probing an iPhone 17 Pro and an
iPad Pro M5 with the same 24-candidate sweep gave disjoint sets of free
4 GiB windows - 448 GiB only, against 12, 16, 20, 24, 32 and 48 GiB -
and the extended-virtual-addressing entitlement changed neither map. So
iOS takes whatever the kernel offers and publishes it, while every other
target keeps its compile-time constant.

The backend is the macOS one adapted twice over: <mach/mach_vm.h> is not
in the iOS SDK, so the vm_* calls are used instead, and the backing store
is an anonymous mapping rather than a file in /tmp, which the sandbox
denies.

g_requiresCheckedAccess is set from the real page size. Apple Silicon's
16 KiB host pages are coarser than the 4 KiB guest page, so page
protection alone cannot be relied on.

(cherry picked from commit 0677b83ed45e8b59d779d37cad172c737daf08eb)
(cherry picked from commit 9a8a80ffd4c3f87652e37208fc88afd0a94e6cf8)
(cherry picked from commit 2b7707e6226ee20a6c0564c0f9bbcdd06b511061)

* ios: pick a display explicitly and survive a second scene

A bare SDL_WINDOWPOS_CENTERED does not say which display it means. With
an external screen attached that left the window sized for one display
and the surface for another, which played audio over a black screen.
The primary display is now named outright and its bounds used.

SDL calls SDL_main from scene:willConnectToSession:, once per connecting
scene, and iOS creates a second scene when a display is attached. That
re-entered main while the first call was still inside the game loop: the
data sections reloaded, no static constructors ran the second time, and
aurora then failed to create a window. Declaring
UIApplicationSupportsMultipleScenes=false does not prevent the extra
scene, so the guard belongs here.

(cherry picked from commit d8b984cfbb8521f06776ae765131452c4380e473)
(cherry picked from commit d20895a98739c11f97de1222834859e2a55e3516)
(cherry picked from commit 492f57ef5122305b0ab63258a5a4efe960f5abc9)

* ios: add on-screen touch controls

A touch device has no pad and no F10, so without these it boots to a
screen nothing can drive.

The overlay polls SDL's finger list rather than using ImGui widgets,
whose SDL backend collapses touch to one emulated mouse - steering,
accelerating and drifting all happen at once. Input merges into
PAD__Read_HLE only when port 0 has no real pad, so a controller always
wins, and the overlay hides itself while one is attached.

Positions are held as a distance from a screen edge in units of screen
height. A fraction of width lands somewhere different on a 1.45:1 iPad
and a 2.17:1 iPhone. The d-pad is one cross with the direction taken
from the dominant axis, and the stick keeps the finger that grabbed it
until that finger lifts, so sliding past the gate does not drop steering
mid-corner.

Buttons use Zacksly's GameCube icons, CC BY 3.0, with attribution beside
them in the bundle. The dark backing is each glyph's own silhouette
tinted black, so the d-pad and the triggers are not given a disc they do
not have. They are decoded up front: doing it lazily made the frame the
controls appear on pay for every decode at once.

Tapping the FPS readout opens the settings bar, which is otherwise
unreachable - and the readout's tap target is cleared before the early
return when FPS display is off, or a stale rectangle keeps swallowing
taps meant for the fallback menu button.

PADIsInputBlocked comes back to aurora; it went with the WUP-028 revert
and the overlay needs it to avoid driving the guest while the bar is up.

(cherry picked from commit 79d3de3c0b9c883ff2ec34a5964498a475e91240)
(cherry picked from commit cf7a8edb3405d4f7d7fd189d19cdb2f746b190f9)
(cherry picked from commit 360261208c95616ec81d8f8c3d69efc3c776c54b)

* ios: build an unsigned ipa

CMake bundles iOS targets with its own default Info.plist, whose
CFBundleIdentifier is empty and which carries none of the iOS keys, so
what came out of the build could not be installed. It now gets a real
plist and is packaged as an .ipa.

Unsigned on purpose: AltStore, SideStore and LiveContainer sign on the
device with the user's own Apple ID, so anything applied here would only
be replaced. WiiCompiled.entitlements is a template for signing by hand,
which those tools ignore.

Packaging runs after the asset copies rather than before, or the archive
gets the binary and nothing else. The touch directory is cleared before
it is copied, since copy_directory merges and a removed icon would
otherwise be shipped.

(cherry picked from commit 5ce43949dbf27abff6782bc41e3be0ede3ac135b)
(cherry picked from commit 633e3e0aa2f7ea116741f27ec46e9c743be9cf9a)
(cherry picked from commit a4ae6f2e8bdf11596a89dca9607aba92713184be)

* docs: document the iOS build

Covers building the .ipa, sideloading it, the one entitlement the app
needs, and where the game data goes.

The configure line is the one that works: without CMAKE_SYSTEM_PROCESSOR
the Dawn package URL comes out as dawn-ios-.tar.gz and the build fails
on a 404.

(cherry picked from commit 08eefad3fd1cb5e8f79a144ac119bc65852e33e6)
(cherry picked from commit b7cd38e07eefc8af4c1c60ca411c009eec4e212a)
(cherry picked from commit 5a1650ef194f9da4ae8a68f1a9779b16ee118ca8)

* ios: stub out Discord presence

It connects over a Unix socket to a local Discord client, which iOS does
not have, so it can only ever fail and back off. Stubbed rather than left
retrying.

(cherry picked from commit 69aa5e5c297abe65188d391748b8fb11c57337d2)

* build: keep the macOS host tests off iOS and give the audit target libpng

The macOS test executables were gated on MKW_PLATFORM_MACOS, which iOS also sets, so a full iOS build tried to compile guest_flat_memory_macos.cpp against an SDK with no <mach/mach_vm.h>. The products-off audit compile picked up the touch overlay sources without the PNG include path.

* ios: tidy the touch decode and pad merge after review

The row-pointer buffer now lives in PngReader so a libpng longjmp cannot skip its destructor. PAD__Read_HLE no longer uses the port 0 error code to decide whether touch applies; keyboard bindings report PAD_ERR_NONE with no controller attached, and TouchPad::Read already checks for a physical pad.

* docs: tag the iOS build fence as sh

* ios: pin the CPU baseline instead of tuning for the build host

-mcpu=native tuned the phone binary for whichever Mac compiled it, and upstream clang rejects the flag outright when cross-compiling. iOS 17 implies the A12 and later.

* build: cross-compile the iOS products from Linux

* use online sdk instead of mac sdk to build on linux

* build: ios platform identifier

* build: build the ios ipa on windows too

* fix: strap cover ends too early on slower devices

* ci: compile the ios runtime on a linux runner

* docs: note the entitlement needed on devices with less than 4gb ram
2026-09-05 10:12:53 +02:00

302 lines
8.0 KiB
C

#ifndef DOLPHIN_PAD_H
#define DOLPHIN_PAD_H
#include <dolphin/types.h>
#define PAD_SPEC_0 0
#define PAD_SPEC_1 1
#define PAD_SPEC_2 2
#define PAD_SPEC_3 3
#define PAD_SPEC_4 4
#define PAD_SPEC_5 5
#define PAD_CHAN0 0
#define PAD_CHAN1 1
#define PAD_CHAN2 2
#define PAD_CHAN3 3
#define PAD_CHANMAX 4
#define PAD_MOTOR_STOP 0
#define PAD_MOTOR_RUMBLE 1
#define PAD_MOTOR_STOP_HARD 2
#define PAD_ERR_NONE 0
#define PAD_ERR_NO_CONTROLLER -1
#define PAD_ERR_NOT_READY -2
#define PAD_ERR_TRANSFER -3
#define PAD_MAX_CONTROLLERS 4
#define PAD_BUTTON_LEFT 0x0001
#define PAD_BUTTON_RIGHT 0x0002
#define PAD_BUTTON_DOWN 0x0004
#define PAD_BUTTON_UP 0x0008
#define PAD_TRIGGER_Z 0x0010
#define PAD_TRIGGER_R 0x0020
#define PAD_TRIGGER_L 0x0040
#define PAD_BUTTON_A 0x0100
#define PAD_BUTTON_B 0x0200
#define PAD_BUTTON_X 0x0400
#define PAD_BUTTON_Y 0x0800
#define PAD_BUTTON_MENU 0x1000
#define PAD_BUTTON_START 0x1000
#ifdef TARGET_PC
#define PAD_BUTTON_BACK 0x0002000
#define PAD_BUTTON_GUIDE 0x0004000
#define PAD_BUTTON_MISC1 0x0008000
#define PAD_BUTTON_MISC2 0x0010000
#define PAD_BUTTON_MISC3 0x0020000
#define PAD_BUTTON_MISC4 0x0040000
#define PAD_BUTTON_MISC5 0x0080000
#define PAD_BUTTON_MISC6 0x0100000
#define PAD_BUTTON_RIGHT_PADDLE1 0x0200000
#define PAD_BUTTON_LEFT_PADDLE1 0x0400000
#define PAD_BUTTON_RIGHT_PADDLE2 0x0800000
#define PAD_BUTTON_LEFT_PADDLE2 0x1000000
#define PAD_BUTTON_RIGHT_STICK 0x2000000
#define PAD_BUTTON_LEFT_STICK 0x4000000
#define PAD_BUTTON_TOUCHPAD 0x8000000
#define PAD_EXT_BUTTON_COUNT 15
#endif
#define PAD_BUTTON_COUNT 12
// added by Aurora, not present in original SDK
#define PAD_AXIS_LEFT_X_POS 0
#define PAD_AXIS_LEFT_X_NEG 1
#define PAD_AXIS_LEFT_Y_POS 2
#define PAD_AXIS_LEFT_Y_NEG 3
#define PAD_AXIS_RIGHT_X_POS 4
#define PAD_AXIS_RIGHT_X_NEG 5
#define PAD_AXIS_RIGHT_Y_POS 6
#define PAD_AXIS_RIGHT_Y_NEG 7
#define PAD_AXIS_TRIGGER_L 8
#define PAD_AXIS_TRIGGER_R 9
#define PAD_AXIS_COUNT 10
#define PAD_CHAN0_BIT 0x80000000
#define PAD_CHAN1_BIT 0x40000000
#define PAD_CHAN2_BIT 0x20000000
#define PAD_CHAN3_BIT 0x10000000
#define PADButtonDown(buttonLast, button) (((buttonLast) ^ (button)) & (button))
#define PADButtonUp(buttonLast, button) (((buttonLast) ^ (button)) & (buttonLast))
#ifdef __cplusplus
extern "C" {
#endif
#define PAD_NATIVE_BUTTON_INVALID 0xFFFFFFFF
typedef struct PADStatus {
u16 button;
s8 stickX;
s8 stickY;
s8 substickX;
s8 substickY;
union {
u8 triggerLeft;
u8 triggerL;
};
union {
u8 triggerRight;
u8 triggerR;
};
u8 analogA;
u8 analogB;
s8 err;
#ifdef TARGET_PC
u32 extButton;
#endif
} PADStatus;
typedef enum PADAxisSign {
AXIS_SIGN_NEGATIVE = -1,
AXIS_SIGN_POSITIVE = 1,
} PADAxisSign;
typedef struct PADSignedNativeAxis {
int32_t nativeAxis;
PADAxisSign sign;
} PADSignedNativeAxis;
struct SDL_Gamepad;
BOOL PADInit();
u32 PADRead(PADStatus* status);
BOOL PADReset(u32 mask);
BOOL PADRecalibrate(u32 mask);
void PADClamp(PADStatus* status);
void PADClampCircle(PADStatus* status);
void PADControlMotor(u32 chan, u32 cmd);
void PADSetSpec(u32 spec);
void PADControlAllMotors(const u32* cmdArr);
void PADSetAnalogMode(u32 mode);
#ifdef TARGET_PC
#define PAD_KEY_INVALID (-1)
#define PAD_KEY_MOUSE_LEFT (-2)
#define PAD_KEY_MOUSE_MIDDLE (-3)
#define PAD_KEY_MOUSE_RIGHT (-4)
#define PAD_KEY_MOUSE_X1 (-5)
#define PAD_KEY_MOUSE_X2 (-6)
typedef u16 PADButton;
typedef u32 PADExtButton;
typedef u16 PADAxis;
typedef struct PADKeyButtonBinding {
s32 scancode;
PADButton padButton;
} PADKeyButtonBinding;
typedef struct PADKeyAxisBinding {
s32 scancode;
PADAxis padAxis;
s16 influence; // normalized percentage between 0 and 1
} PADKeyAxisBinding;
/* New API to facilitate controller interactions */
typedef struct PADDeadZones {
bool emulateTriggers;
bool useDeadzones;
u16 stickDeadZone;
u16 substickDeadZone;
u16 leftTriggerActivationZone;
u16 rightTriggerActivationZone;
} PADDeadZones;
typedef struct PADButtonMapping {
u32 nativeButton;
PADButton padButton;
} PADButtonMapping;
typedef struct PADAxisMapping {
PADSignedNativeAxis nativeAxis;
s32 nativeButton;
PADAxis padAxis;
} PADAxisMapping;
typedef struct PADDefaultMapping {
PADButtonMapping buttons[PAD_BUTTON_COUNT];
PADAxisMapping axes[PAD_AXIS_COUNT];
} PADDefaultMapping;
/* Returns the total number of controllers */
u32 PADCount();
/* Returns the controller name for the given index into the controller map */
const char* PADGetNameForControllerIndex(u32 idx);
void PADSetPortForIndex(u32 index, u32 port);
s32 PADGetIndexForPort(u32 port);
void PADGetVidPid(u32 port, u32* vid, u32* pid);
void PADClearPort(u32 port);
const char* PADGetName(u32 port);
void PADSetButtonMapping(u32 port, PADButtonMapping mapping);
void PADSetAllButtonMappings(u32 port, PADButtonMapping buttons[PAD_BUTTON_COUNT]);
PADButtonMapping* PADGetButtonMappings(u32 port, u32* buttonCount);
void PADSetAltButtonMapping(u32 port, PADButtonMapping mapping);
PADButtonMapping* PADGetAltButtonMappings(u32 port, u32* buttonCount);
void PADSetAxisMapping(u32 port, PADAxisMapping mapping);
void PADSetAllAxisMappings(u32 port, PADAxisMapping axes[PAD_AXIS_COUNT]);
PADAxisMapping* PADGetAxisMappings(u32 port, u32* axisCount);
void PADSerializeMappings();
BOOL PADSetKeyButtonBinding(u32 port, PADKeyButtonBinding binding);
BOOL PADSetKeyButtonBindings(u32 port, PADKeyButtonBinding bindings[PAD_BUTTON_COUNT]);
PADKeyButtonBinding* PADGetKeyButtonBindings(u32 port, u32* buttonCount);
BOOL PADSetKeyAxisBinding(u32 port, PADKeyAxisBinding binding);
BOOL PADSetKeyAxisBindings(u32 port, PADKeyAxisBinding bindings[PAD_BUTTON_COUNT]);
PADKeyAxisBinding* PADGetKeyAxisBindings(u32 port, u32* axisCount);
void PADClearKeyBindings(u32 port);
void PADSetKeyboardActive(u32 port, BOOL active);
PADDeadZones* PADGetDeadZones(u32 port);
const char* PADGetButtonName(PADButton);
const char* PADGetNativeButtonName(u32 button);
const char* PADGetAxisName(PADAxis);
const char* PADGetAxisDirectionLabel(PADAxis);
const char* PADGetNativeAxisName(PADSignedNativeAxis axis);
BOOL PADIsGCAdapter(u32 port);
/**
* Returns the SDL gamepad for the index into the controller map.
*/
struct SDL_Gamepad* PADGetSDLGamepadForIndex(u32 index);
/* Returns the first native button which is currently pressed */
s32 PADGetNativeButtonPressed(u32 port);
/* Returns the first native axis which is currently pulled halfway or more */
PADSignedNativeAxis PADGetNativeAxisPulled(u32 port);
void PADRestoreDefaultMapping(u32 port);
void PADBlockInput(bool block);
bool PADIsInputBlocked(void);
/**
* Set the default controller mapping used.
*
* Must be called before PADInit.
*/
typedef enum {
PAD_TYPE_UNKNOWN = 0,
PAD_TYPE_STANDARD,
PAD_TYPE_XBOX360,
PAD_TYPE_XBOXONE,
PAD_TYPE_PS3,
PAD_TYPE_PS4,
PAD_TYPE_PS5,
PAD_TYPE_SWITCH_PROCON,
PAD_TYPE_JOYCON_LEFT,
PAD_TYPE_JOYCON_RIGHT,
PAD_TYPE_JOYCON_PAIR,
PAD_TYPE_GAMECUBE,
PAD_TYPE_NSO_GAMECUBE,
} PADControllerType;
void PADSetDefaultMapping(const PADDefaultMapping* mapping, PADControllerType type);
BOOL PADSetColor(u32 port, u8 red, u8 green, u8 blue);
BOOL PADGetColor(u32 port, u8* red, u8* green, u8* blue);
typedef enum {
PAD_SENSOR_INVALID = -1,
PAD_SENSOR_UNKNOWN,
PAD_SENSOR_ACCEL,
PAD_SENSOR_GYRO,
PAD_SENSOR_ACCEL_LEFT,
PAD_SENSOR_GYRO_LEFT,
PAD_SENSOR_ACCEL_RIGHT,
PAD_SENSOR_GYRO_RIGHT,
} PADSensorType;
BOOL PADSetSensorEnabled(u32 port, PADSensorType sensor, BOOL enabled);
BOOL PADHasSensor(u32 port, PADSensorType sensor);
BOOL PADGetSensorData(u32 port, PADSensorType sensor, f32* data, int nValues);
BOOL PADSetRumbleIntensity(u32 port, u16 low, u16 high);
BOOL PADGetRumbleIntensity(u32 port, u16* low, u16* high);
BOOL PADSupportsRumbleIntensity(u32 port);
typedef enum PADBatteryState {
PAD_BATTERYSTATE_ERROR = -1,
PAD_BATTERYSTATE_UNKNOWN,
PAD_BATTERYSTATE_ON_BATTERY,
PAD_BATTERYSTATE_NO_BATTERY,
PAD_BATTERYSTATE_CHARGING,
PAD_BATTERYSTATE_CHARGED,
} PADBatteryState;
PADBatteryState PADGetBatteryState(u32 port, f32* perc);
PADControllerType PADGetControllerType(u32 port);
PADControllerType PADGetControllerTypeForIndex(u32 index);
#endif
#ifdef __cplusplus
}
#endif
#endif