Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${CORE_MEDIA_LIBRARY}
${CORE_VIDEO_LIBRARY}
${FOUNDATION_LIBRARY}
${IO_KIT_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY})

set(APPLE_PLIST_TEMPLATE "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/build/Info.plist.in")
Expand All @@ -48,7 +49,9 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.m"
"${CMAKE_SOURCE_DIR}/src/platform/macos/display.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/input.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/hid_gamepad.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/hid_gamepad.m"
"${CMAKE_SOURCE_DIR}/src/platform/macos/input.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/microphone.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/misc.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/misc.h"
Expand Down
6 changes: 6 additions & 0 deletions cmake/dependencies/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ FIND_LIBRARY(CORE_AUDIO_LIBRARY CoreAudio)
FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia)
FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo)
FIND_LIBRARY(FOUNDATION_LIBRARY Foundation)
# IOKit is needed for IOHIDUserDevice* (virtual gamepad device — hid_gamepad.m).
# Actually creating devices at runtime requires the user to disable AMFI via
# `nvram boot-args="amfi_get_out_of_my_way=1"`, but the symbols themselves
# are unconditionally present and the host alloc_gamepad path probes
# availability before relying on them.
FIND_LIBRARY(IO_KIT_LIBRARY IOKit)
FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox)

if(SUNSHINE_ENABLE_TRAY)
Expand Down
72 changes: 72 additions & 0 deletions src/platform/macos/hid_gamepad.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @file src/platform/macos/hid_gamepad.h
* @brief Virtual HID gamepad via IOHIDUserDevice for macOS.
* @details Creates a system-wide virtual gamepad that macOS Game Controller
* framework recognizes. Requires SIP to be disabled.
*/
#pragma once

#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDUserDevice.h>

/**
* HID report sent to IOHIDUserDevice. Packed to exactly 14 bytes.
* Matches the HID report descriptor defined in hid_gamepad.m.
*/
typedef struct __attribute__((packed)) {
uint8_t reportId; // Always 0x01
uint16_t buttons; // 16 button bits
uint8_t hatSwitch; // D-pad hat switch (0-7 = directions, 8 = neutral)
uint8_t leftTrigger; // 0-255
uint8_t rightTrigger; // 0-255
int16_t leftStickX; // -32768 to 32767
int16_t leftStickY; // -32768 to 32767
int16_t rightStickX; // -32768 to 32767
int16_t rightStickY; // -32768 to 32767
} HIDGamepadReport;

@interface HIDGamepad : NSObject

@property (nonatomic, assign) int gamepadIndex;
@property (nonatomic, assign) BOOL isConnected;
@property (nonatomic, assign) IOHIDUserDeviceRef hidDevice;
@property (nonatomic, strong) dispatch_queue_t hidQueue;

/**
* Probes whether IOHIDUserDevice virtual gamepads can be created.
* Returns NO when SIP is enabled (device creation fails).
*/
+ (BOOL)isAvailable;

- (instancetype)initWithIndex:(int)index;

/**
* Creates the IOHIDUserDevice and sends an initial neutral-state report.
* @return YES on success, NO on failure.
*/
- (BOOL)createDevice;

/**
* Maps Sunshine's gamepad state to an HID report and sends it.
* @param buttons Sunshine's 32-bit buttonFlags (only lower 16 bits + HOME used)
* @param lsX Left stick X (-32768..32767)
* @param lsY Left stick Y (-32768..32767)
* @param rsX Right stick X (-32768..32767)
* @param rsY Right stick Y (-32768..32767)
* @param lt Left trigger (0..255)
* @param rt Right trigger (0..255)
*/
- (void)updateState:(uint32_t)buttons
leftStickX:(int16_t)lsX
leftStickY:(int16_t)lsY
rightStickX:(int16_t)rsX
rightStickY:(int16_t)rsY
leftTrigger:(uint8_t)lt
rightTrigger:(uint8_t)rt;

/**
* Destroys the IOHIDUserDevice and cleans up resources.
*/
- (void)disconnect;

@end
Loading