From 335b5aef1fb7215087bc5624c5f856e00a3d1d73 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 11 Feb 2019 19:22:18 -0800 Subject: [PATCH] Add rumble support --- Limelight/Input/Controller.h | 2 + Limelight/Input/ControllerSupport.h | 2 + Limelight/Input/ControllerSupport.m | 59 +++++++++++++++++++ Limelight/Stream/Connection.h | 1 + Limelight/Stream/Connection.m | 6 ++ .../StreamFrameViewController.m | 6 ++ Moonlight.xcodeproj/project.pbxproj | 3 +- moonlight-common/moonlight-common-c | 2 +- 8 files changed, 79 insertions(+), 2 deletions(-) diff --git a/Limelight/Input/Controller.h b/Limelight/Input/Controller.h index b1a41de2..73458e99 100644 --- a/Limelight/Input/Controller.h +++ b/Limelight/Input/Controller.h @@ -20,5 +20,7 @@ @property (nonatomic) short lastLeftStickY; @property (nonatomic) short lastRightStickX; @property (nonatomic) short lastRightStickY; +@property (nonatomic) unsigned short lowFreqMotor; +@property (nonatomic) unsigned short highFreqMotor; @end diff --git a/Limelight/Input/ControllerSupport.h b/Limelight/Input/ControllerSupport.h index e8d02958..c175246c 100644 --- a/Limelight/Input/ControllerSupport.h +++ b/Limelight/Input/ControllerSupport.h @@ -42,6 +42,8 @@ -(void) updateFinished:(Controller*)controller; +-(void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor; + +(int) getConnectedGamepadMask:(StreamConfiguration*)streamConfig; @property (nonatomic, strong) id connectObserver; diff --git a/Limelight/Input/ControllerSupport.m b/Limelight/Input/ControllerSupport.m index 5e75d2d9..cd330cd1 100644 --- a/Limelight/Input/ControllerSupport.m +++ b/Limelight/Input/ControllerSupport.m @@ -19,10 +19,12 @@ #include "Limelight.h" @import GameController; +@import AudioToolbox; @implementation ControllerSupport { NSLock *_controllerStreamLock; NSMutableDictionary *_controllers; + NSTimer *_rumbleTimer; OnScreenControls *_osc; @@ -42,6 +44,38 @@ #define UPDATE_BUTTON_FLAG(controller, x, y) \ ((y) ? [self setButtonFlag:controller flags:x] : [self clearButtonFlag:controller flags:x]) +-(void) rumbleController: (Controller*)controller +{ + // Only vibrate if the amplitude is large enough + if (controller.lowFreqMotor > 0x5000 || controller.highFreqMotor > 0x5000) { + // If the gamepad is nil (on-screen controls) or it's attached to the device, + // then vibrate the device itself + if (controller.gamepad == nil || [controller.gamepad isAttachedToDevice]) { + AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); + } + } +} + +-(void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor +{ + Controller* controller = [_controllers objectForKey:[NSNumber numberWithInteger:controllerNumber]]; + if (controller == nil && controllerNumber == 0 && _oscEnabled) { + // No physical controller, but we have on-screen controls + controller = _player0osc; + } + if (controller == nil) { + // No connected controller for this player + return; + } + + // Update the motor levels for the rumble timer to grab next iteration + controller.lowFreqMotor = lowFreqMotor; + controller.highFreqMotor = highFreqMotor; + + // Rumble now to ensure short vibrations aren't missed + [self rumbleController:controller]; +} + -(void) updateLeftStick:(Controller*)controller x:(short)x y:(short)y { @synchronized(controller) { @@ -412,6 +446,23 @@ return mask; } +-(void) rumbleTimer +{ + for (int i = 0; i < 4; i++) { + Controller* controller = [_controllers objectForKey:[NSNumber numberWithInteger:i]]; + if (controller == nil && i == 0 && _oscEnabled) { + // No physical controller, but we have on-screen controls + controller = _player0osc; + } + if (controller == nil) { + // No connected controller for this player + continue; + } + + [self rumbleController:controller]; + } +} + -(id) initWithConfig:(StreamConfiguration*)streamConfig { self = [super init]; @@ -433,6 +484,12 @@ Gamepad_detectDevices(); #endif + _rumbleTimer = [NSTimer scheduledTimerWithTimeInterval:0.20 + target:self + selector:@selector(rumbleTimer) + userInfo:nil + repeats:YES]; + Log(LOG_I, @"Number of supported controllers connected: %d", [ControllerSupport getGamepadCount]); Log(LOG_I, @"Multi-controller: %d", _multiController); @@ -492,6 +549,8 @@ -(void) cleanup { + [_rumbleTimer invalidate]; + _rumbleTimer = nil; [[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver]; [[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver]; [_controllers removeAllObjects]; diff --git a/Limelight/Stream/Connection.h b/Limelight/Stream/Connection.h index 8713e730..b62a3a22 100644 --- a/Limelight/Stream/Connection.h +++ b/Limelight/Stream/Connection.h @@ -19,6 +19,7 @@ - (void) launchFailed:(NSString*)message; - (void) displayMessage:(const char*)message; - (void) displayTransientMessage:(const char*)message; +- (void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor; @end diff --git a/Limelight/Stream/Connection.m b/Limelight/Stream/Connection.m index 2e6e5c38..df16755e 100644 --- a/Limelight/Stream/Connection.m +++ b/Limelight/Stream/Connection.m @@ -243,6 +243,11 @@ void ClLogMessage(const char* format, ...) va_end(va); } +void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) +{ + [_callbacks rumble:controllerNumber lowFreqMotor:lowFreqMotor highFreqMotor:highFreqMotor]; +} + -(void) terminate { // Interrupt any action blocking LiStartConnection(). This is @@ -375,6 +380,7 @@ void ClLogMessage(const char* format, ...) _clCallbacks.displayMessage = ClDisplayMessage; _clCallbacks.displayTransientMessage = ClDisplayTransientMessage; _clCallbacks.logMessage = ClLogMessage; + _clCallbacks.rumble = ClRumble; return self; } diff --git a/Limelight/ViewControllers/StreamFrameViewController.m b/Limelight/ViewControllers/StreamFrameViewController.m index 546541ac..24ee97ff 100644 --- a/Limelight/ViewControllers/StreamFrameViewController.m +++ b/Limelight/ViewControllers/StreamFrameViewController.m @@ -263,6 +263,12 @@ Log(LOG_I, @"Display transient message: %s", message); } +- (void)rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor { + Log(LOG_I, @"Rumble on gamepad %d: %04x %04x", controllerNumber, lowFreqMotor, highFreqMotor); + + [_controllerSupport rumble:controllerNumber lowFreqMotor:lowFreqMotor highFreqMotor:highFreqMotor]; +} + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; diff --git a/Moonlight.xcodeproj/project.pbxproj b/Moonlight.xcodeproj/project.pbxproj index 1d45eb85..184d10b7 100644 --- a/Moonlight.xcodeproj/project.pbxproj +++ b/Moonlight.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 9865DC3E21332D660005B9B9 /* MainFrameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462519F646E200339C8A /* MainFrameViewController.m */; }; 9890CF6B203B7EE1006C4B06 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9890CF6A203B7EE1006C4B06 /* libxml2.tbd */; }; 9897B6A1221260EF00966419 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 9897B6A0221260EF00966419 /* Controller.m */; }; + 9897B6A62212732C00966419 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 9897B6A0221260EF00966419 /* Controller.m */; }; 98CFB82F1CAD481B0048EF74 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98AB2E841CAD46840089BB98 /* libmoonlight-common.a */; }; 98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */; }; 98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */; }; @@ -1008,6 +1009,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9897B6A62212732C00966419 /* Controller.m in Sources */, 9865DC3E21332D660005B9B9 /* MainFrameViewController.m in Sources */, 9865DC36213287F30005B9B9 /* AppDelegate.m in Sources */, FB1A6819213284FB00507771 /* UIComputerView.m in Sources */, @@ -1022,7 +1024,6 @@ FB1A67E02132460A00507771 /* Logger.m in Sources */, FB1A67D52132460400507771 /* ControllerSupport.m in Sources */, FB1A67D82132460400507771 /* StreamView.m in Sources */, - 9897B6A2221260EF00966419 /* Controller.m in Sources */, FB1A67DA2132460400507771 /* OnScreenControls.m in Sources */, FB1A67DC2132460400507771 /* KeyboardSupport.m in Sources */, FB1A67CD213245F800507771 /* DataManager.m in Sources */, diff --git a/moonlight-common/moonlight-common-c b/moonlight-common/moonlight-common-c index 610c00bf..a016474a 160000 --- a/moonlight-common/moonlight-common-c +++ b/moonlight-common/moonlight-common-c @@ -1 +1 @@ -Subproject commit 610c00bf2d54698a13eb1b84d27b7e927a157a40 +Subproject commit a016474accd382994105a525e955b73df8d8ffe7