mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-04-18 06:20:21 +00:00
Rewrote a bunch of controller support. Added auto on-screen controls layouts (GCGamepad not finished). Added LB+Start and RB+Start shortcuts for select and special (same as Android).
This commit is contained in:
@@ -7,19 +7,166 @@
|
||||
//
|
||||
|
||||
#import "ControllerSupport.h"
|
||||
#import "OnScreenControls.h"
|
||||
#include "Limelight.h"
|
||||
|
||||
@import GameController;
|
||||
|
||||
@implementation ControllerSupport
|
||||
@implementation ControllerSupport {
|
||||
NSLock *_controllerValueLock;
|
||||
NSLock *_controllerStreamLock;
|
||||
|
||||
OnScreenControls *_osc;
|
||||
|
||||
int _lastButtonFlags;
|
||||
char _lastLeftTrigger, _lastRightTrigger;
|
||||
short _lastLeftStickX, _lastLeftStickY;
|
||||
short _lastRightStickX, _lastRightStickY;
|
||||
|
||||
#define EMULATING_SELECT 0x1
|
||||
#define EMULATING_SPECIAL 0x2
|
||||
int _emulatingButtonFlags;
|
||||
}
|
||||
|
||||
// UPDATE_BUTTON(flag, pressed)
|
||||
#define UPDATE_BUTTON(x, y) (buttonFlags = \
|
||||
(y) ? (buttonFlags | (x)) : (buttonFlags & ~(x)))
|
||||
|
||||
static NSLock *controllerStreamLock;
|
||||
-(void) updateLeftStick:(short)x y:(short)y
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastLeftStickX = x;
|
||||
_lastLeftStickY = y;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
+(void) registerControllerCallbacks
|
||||
-(void) updateRightStick:(short)x y:(short)y
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastRightStickX = x;
|
||||
_lastRightStickY = y;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateLeftTrigger:(char)left
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastLeftTrigger = left;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateRightTrigger:(char)right
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastRightTrigger = right;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateTriggers:(char)left right:(char)right
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastLeftTrigger = left;
|
||||
_lastRightTrigger = right;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) handleSpecialCombosReleased:(int)releasedButtons
|
||||
{
|
||||
if (releasedButtons & PLAY_FLAG) {
|
||||
if ((_emulatingButtonFlags & EMULATING_SELECT) &&
|
||||
(releasedButtons & LB_FLAG)) {
|
||||
_lastButtonFlags &= ~BACK_FLAG;
|
||||
_emulatingButtonFlags &= ~EMULATING_SELECT;
|
||||
}
|
||||
if ((_emulatingButtonFlags & EMULATING_SPECIAL) &&
|
||||
(releasedButtons & RB_FLAG)) {
|
||||
_lastButtonFlags &= ~SPECIAL_FLAG;
|
||||
_emulatingButtonFlags &= ~EMULATING_SPECIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) handleSpecialCombosPressed
|
||||
{
|
||||
// Special button combos for select and special
|
||||
if (_lastButtonFlags & PLAY_FLAG) {
|
||||
// If LB and start are down, trigger select
|
||||
if (_lastButtonFlags & LB_FLAG) {
|
||||
_lastButtonFlags |= BACK_FLAG;
|
||||
_lastButtonFlags &= ~(PLAY_FLAG | LB_FLAG);
|
||||
_emulatingButtonFlags |= EMULATING_SELECT;
|
||||
}
|
||||
// If RB and start are down, trigger special
|
||||
else if (_lastButtonFlags & RB_FLAG) {
|
||||
_lastButtonFlags |= SPECIAL_FLAG;
|
||||
_lastButtonFlags &= ~(PLAY_FLAG | RB_FLAG);
|
||||
_emulatingButtonFlags |= EMULATING_SPECIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) updateButtonFlags:(int)flags
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
int releasedButtons = (_lastButtonFlags ^ flags) & ~flags;
|
||||
|
||||
_lastButtonFlags = flags;
|
||||
|
||||
// This must be called before handleSpecialCombosPressed
|
||||
// because we clear the original button flags there
|
||||
[self handleSpecialCombosReleased: releasedButtons];
|
||||
|
||||
[self handleSpecialCombosPressed];
|
||||
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) setButtonFlag:(int)flags
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastButtonFlags |= flags;
|
||||
[self handleSpecialCombosPressed];
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) clearButtonFlag:(int)flags
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastButtonFlags &= ~flags;
|
||||
[self handleSpecialCombosReleased: flags];
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateFinished
|
||||
{
|
||||
[_controllerStreamLock lock];
|
||||
[_controllerValueLock lock];
|
||||
|
||||
LiSendControllerEvent(_lastButtonFlags, _lastLeftTrigger, _lastRightTrigger, _lastLeftStickX, _lastLeftStickY, _lastRightStickX, _lastRightStickY);
|
||||
|
||||
[_controllerValueLock unlock];
|
||||
[_controllerStreamLock unlock];
|
||||
}
|
||||
|
||||
-(void) unregisterControllerCallbacks
|
||||
{
|
||||
for (int i = 0; i < [[GCController controllers] count]; i++) {
|
||||
GCController *controller = [GCController controllers][i];
|
||||
|
||||
if (controller != NULL) {
|
||||
controller.controllerPausedHandler = NULL;
|
||||
|
||||
if (controller.extendedGamepad != NULL) {
|
||||
controller.extendedGamepad.valueChangedHandler = NULL;
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
controller.gamepad.valueChangedHandler = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) registerControllerCallbacks
|
||||
{
|
||||
for (int i = 0; i < [[GCController controllers] count]; i++) {
|
||||
GCController *controller = [GCController controllers][i];
|
||||
@@ -27,16 +174,14 @@ static NSLock *controllerStreamLock;
|
||||
if (controller != NULL) {
|
||||
NSLog(@"Controller connected!");
|
||||
controller.controllerPausedHandler = ^(GCController *controller) {
|
||||
// We call LiSendControllerEvent while holding a lock to prevent
|
||||
// multiple simultaneous calls since this function isn't thread safe.
|
||||
[controllerStreamLock lock];
|
||||
LiSendControllerEvent(PLAY_FLAG, 0, 0, 0, 0, 0, 0);
|
||||
[self setButtonFlag:PLAY_FLAG];
|
||||
[self updateFinished];
|
||||
|
||||
// Pause for 100 ms
|
||||
usleep(100 * 1000);
|
||||
|
||||
LiSendControllerEvent(0, 0, 0, 0, 0, 0, 0);
|
||||
[controllerStreamLock unlock];
|
||||
[self clearButtonFlag:PLAY_FLAG];
|
||||
[self updateFinished];
|
||||
};
|
||||
|
||||
if (controller.extendedGamepad != NULL) {
|
||||
@@ -68,12 +213,11 @@ static NSLock *controllerStreamLock;
|
||||
leftTrigger = gamepad.leftTrigger.value * 0xFF;
|
||||
rightTrigger = gamepad.rightTrigger.value * 0xFF;
|
||||
|
||||
// We call LiSendControllerEvent while holding a lock to prevent
|
||||
// multiple simultaneous calls since this function isn't thread safe.
|
||||
[controllerStreamLock lock];
|
||||
LiSendControllerEvent(buttonFlags, leftTrigger, rightTrigger,
|
||||
leftStickX, leftStickY, rightStickX, rightStickY);
|
||||
[controllerStreamLock unlock];
|
||||
[self updateButtonFlags:buttonFlags];
|
||||
[self updateLeftStick:leftStickX y:leftStickY];
|
||||
[self updateRightStick:rightStickX y:rightStickY];
|
||||
[self updateTriggers:leftTrigger right:rightTrigger];
|
||||
[self updateFinished];
|
||||
};
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
@@ -93,11 +237,8 @@ static NSLock *controllerStreamLock;
|
||||
UPDATE_BUTTON(LB_FLAG, gamepad.leftShoulder.pressed);
|
||||
UPDATE_BUTTON(RB_FLAG, gamepad.rightShoulder.pressed);
|
||||
|
||||
// We call LiSendControllerEvent while holding a lock to prevent
|
||||
// multiple simultaneous calls since this function isn't thread safe.
|
||||
[controllerStreamLock lock];
|
||||
LiSendControllerEvent(buttonFlags, 0, 0, 0, 0, 0, 0);
|
||||
[controllerStreamLock unlock];
|
||||
[self updateButtonFlags:buttonFlags];
|
||||
[self updateFinished];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -105,22 +246,72 @@ static NSLock *controllerStreamLock;
|
||||
|
||||
}
|
||||
|
||||
-(void) updateAutoOnScreenControlMode
|
||||
{
|
||||
// Auto on-screen control support may not be enabled
|
||||
if (_osc == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnScreenControlsLevel level = OnScreenControlsLevelFull;
|
||||
|
||||
// We currently stop after the first controller we find.
|
||||
// Maybe we'll want to change that logic later.
|
||||
for (int i = 0; i < [[GCController controllers] count]; i++) {
|
||||
GCController *controller = [GCController controllers][i];
|
||||
|
||||
if (controller != NULL) {
|
||||
if (controller.extendedGamepad != NULL) {
|
||||
level = OnScreenControlsLevelAutoGCExtendedGamepad;
|
||||
break;
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
level = OnScreenControlsLevelAutoGCGamepad;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[_osc setLevel:level];
|
||||
}
|
||||
|
||||
-(void) initAutoOnScreenControlMode:(OnScreenControls*)osc
|
||||
{
|
||||
_osc = osc;
|
||||
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}
|
||||
|
||||
-(id) init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (controllerStreamLock == NULL) {
|
||||
controllerStreamLock = [[NSLock alloc] init];
|
||||
}
|
||||
_controllerStreamLock = [[NSLock alloc] init];
|
||||
_controllerValueLock = [[NSLock alloc] init];
|
||||
|
||||
self.connectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[ControllerSupport registerControllerCallbacks];
|
||||
// Register callbacks on the new controller
|
||||
[self registerControllerCallbacks];
|
||||
|
||||
// Re-evaluate the on-screen control mode
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}];
|
||||
self.disconnectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
NSLog(@"Controller disconnected!");
|
||||
|
||||
// Reset all controller state to be safe
|
||||
[self updateButtonFlags:0];
|
||||
[self updateLeftStick:0 y:0];
|
||||
[self updateRightStick:0 y:0];
|
||||
[self updateTriggers:0 right:0];
|
||||
[self updateFinished];
|
||||
|
||||
// Re-evaluate the on-screen control mode
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}];
|
||||
|
||||
[ControllerSupport registerControllerCallbacks];
|
||||
// Register for controller callbacks on any existing controllers
|
||||
[self registerControllerCallbacks];
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -129,6 +320,8 @@ static NSLock *controllerStreamLock;
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver];
|
||||
|
||||
[self unregisterControllerCallbacks];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user