mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-04-18 06:20:21 +00:00
Add rumble support
This commit is contained in:
@@ -20,5 +20,7 @@
|
|||||||
@property (nonatomic) short lastLeftStickY;
|
@property (nonatomic) short lastLeftStickY;
|
||||||
@property (nonatomic) short lastRightStickX;
|
@property (nonatomic) short lastRightStickX;
|
||||||
@property (nonatomic) short lastRightStickY;
|
@property (nonatomic) short lastRightStickY;
|
||||||
|
@property (nonatomic) unsigned short lowFreqMotor;
|
||||||
|
@property (nonatomic) unsigned short highFreqMotor;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
-(void) updateFinished:(Controller*)controller;
|
-(void) updateFinished:(Controller*)controller;
|
||||||
|
|
||||||
|
-(void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor;
|
||||||
|
|
||||||
+(int) getConnectedGamepadMask:(StreamConfiguration*)streamConfig;
|
+(int) getConnectedGamepadMask:(StreamConfiguration*)streamConfig;
|
||||||
|
|
||||||
@property (nonatomic, strong) id connectObserver;
|
@property (nonatomic, strong) id connectObserver;
|
||||||
|
|||||||
@@ -19,10 +19,12 @@
|
|||||||
#include "Limelight.h"
|
#include "Limelight.h"
|
||||||
|
|
||||||
@import GameController;
|
@import GameController;
|
||||||
|
@import AudioToolbox;
|
||||||
|
|
||||||
@implementation ControllerSupport {
|
@implementation ControllerSupport {
|
||||||
NSLock *_controllerStreamLock;
|
NSLock *_controllerStreamLock;
|
||||||
NSMutableDictionary *_controllers;
|
NSMutableDictionary *_controllers;
|
||||||
|
NSTimer *_rumbleTimer;
|
||||||
|
|
||||||
OnScreenControls *_osc;
|
OnScreenControls *_osc;
|
||||||
|
|
||||||
@@ -42,6 +44,38 @@
|
|||||||
#define UPDATE_BUTTON_FLAG(controller, x, y) \
|
#define UPDATE_BUTTON_FLAG(controller, x, y) \
|
||||||
((y) ? [self setButtonFlag:controller flags:x] : [self clearButtonFlag:controller flags:x])
|
((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
|
-(void) updateLeftStick:(Controller*)controller x:(short)x y:(short)y
|
||||||
{
|
{
|
||||||
@synchronized(controller) {
|
@synchronized(controller) {
|
||||||
@@ -412,6 +446,23 @@
|
|||||||
return mask;
|
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
|
-(id) initWithConfig:(StreamConfiguration*)streamConfig
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
@@ -433,6 +484,12 @@
|
|||||||
Gamepad_detectDevices();
|
Gamepad_detectDevices();
|
||||||
#endif
|
#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, @"Number of supported controllers connected: %d", [ControllerSupport getGamepadCount]);
|
||||||
Log(LOG_I, @"Multi-controller: %d", _multiController);
|
Log(LOG_I, @"Multi-controller: %d", _multiController);
|
||||||
|
|
||||||
@@ -492,6 +549,8 @@
|
|||||||
|
|
||||||
-(void) cleanup
|
-(void) cleanup
|
||||||
{
|
{
|
||||||
|
[_rumbleTimer invalidate];
|
||||||
|
_rumbleTimer = nil;
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver];
|
[[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver];
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver];
|
[[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver];
|
||||||
[_controllers removeAllObjects];
|
[_controllers removeAllObjects];
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
- (void) launchFailed:(NSString*)message;
|
- (void) launchFailed:(NSString*)message;
|
||||||
- (void) displayMessage:(const char*)message;
|
- (void) displayMessage:(const char*)message;
|
||||||
- (void) displayTransientMessage:(const char*)message;
|
- (void) displayTransientMessage:(const char*)message;
|
||||||
|
- (void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,11 @@ void ClLogMessage(const char* format, ...)
|
|||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor)
|
||||||
|
{
|
||||||
|
[_callbacks rumble:controllerNumber lowFreqMotor:lowFreqMotor highFreqMotor:highFreqMotor];
|
||||||
|
}
|
||||||
|
|
||||||
-(void) terminate
|
-(void) terminate
|
||||||
{
|
{
|
||||||
// Interrupt any action blocking LiStartConnection(). This is
|
// Interrupt any action blocking LiStartConnection(). This is
|
||||||
@@ -375,6 +380,7 @@ void ClLogMessage(const char* format, ...)
|
|||||||
_clCallbacks.displayMessage = ClDisplayMessage;
|
_clCallbacks.displayMessage = ClDisplayMessage;
|
||||||
_clCallbacks.displayTransientMessage = ClDisplayTransientMessage;
|
_clCallbacks.displayTransientMessage = ClDisplayTransientMessage;
|
||||||
_clCallbacks.logMessage = ClLogMessage;
|
_clCallbacks.logMessage = ClLogMessage;
|
||||||
|
_clCallbacks.rumble = ClRumble;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,6 +263,12 @@
|
|||||||
Log(LOG_I, @"Display transient message: %s", message);
|
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
|
- (void)didReceiveMemoryWarning
|
||||||
{
|
{
|
||||||
[super didReceiveMemoryWarning];
|
[super didReceiveMemoryWarning];
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
9865DC3E21332D660005B9B9 /* MainFrameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462519F646E200339C8A /* MainFrameViewController.m */; };
|
9865DC3E21332D660005B9B9 /* MainFrameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89462519F646E200339C8A /* MainFrameViewController.m */; };
|
||||||
9890CF6B203B7EE1006C4B06 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9890CF6A203B7EE1006C4B06 /* libxml2.tbd */; };
|
9890CF6B203B7EE1006C4B06 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9890CF6A203B7EE1006C4B06 /* libxml2.tbd */; };
|
||||||
9897B6A1221260EF00966419 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 9897B6A0221260EF00966419 /* Controller.m */; };
|
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 */; };
|
98CFB82F1CAD481B0048EF74 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98AB2E841CAD46840089BB98 /* libmoonlight-common.a */; };
|
||||||
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */; };
|
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */; };
|
||||||
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */; };
|
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */; };
|
||||||
@@ -1008,6 +1009,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
9897B6A62212732C00966419 /* Controller.m in Sources */,
|
||||||
9865DC3E21332D660005B9B9 /* MainFrameViewController.m in Sources */,
|
9865DC3E21332D660005B9B9 /* MainFrameViewController.m in Sources */,
|
||||||
9865DC36213287F30005B9B9 /* AppDelegate.m in Sources */,
|
9865DC36213287F30005B9B9 /* AppDelegate.m in Sources */,
|
||||||
FB1A6819213284FB00507771 /* UIComputerView.m in Sources */,
|
FB1A6819213284FB00507771 /* UIComputerView.m in Sources */,
|
||||||
@@ -1022,7 +1024,6 @@
|
|||||||
FB1A67E02132460A00507771 /* Logger.m in Sources */,
|
FB1A67E02132460A00507771 /* Logger.m in Sources */,
|
||||||
FB1A67D52132460400507771 /* ControllerSupport.m in Sources */,
|
FB1A67D52132460400507771 /* ControllerSupport.m in Sources */,
|
||||||
FB1A67D82132460400507771 /* StreamView.m in Sources */,
|
FB1A67D82132460400507771 /* StreamView.m in Sources */,
|
||||||
9897B6A2221260EF00966419 /* Controller.m in Sources */,
|
|
||||||
FB1A67DA2132460400507771 /* OnScreenControls.m in Sources */,
|
FB1A67DA2132460400507771 /* OnScreenControls.m in Sources */,
|
||||||
FB1A67DC2132460400507771 /* KeyboardSupport.m in Sources */,
|
FB1A67DC2132460400507771 /* KeyboardSupport.m in Sources */,
|
||||||
FB1A67CD213245F800507771 /* DataManager.m in Sources */,
|
FB1A67CD213245F800507771 /* DataManager.m in Sources */,
|
||||||
|
|||||||
Reference in New Issue
Block a user