Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions Limelight/Database/DataManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
enableHdr:(BOOL)enableHdr
btMouseSupport:(BOOL)btMouseSupport
absoluteTouchMode:(BOOL)absoluteTouchMode
passthroughTouchMode:(BOOL)passthroughTouchMode
statsOverlay:(BOOL)statsOverlay;

- (NSArray*) getHosts;
Expand Down
2 changes: 2 additions & 0 deletions Limelight/Database/DataManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ - (void) saveSettingsWithBitrate:(NSInteger)bitrate
enableHdr:(BOOL)enableHdr
btMouseSupport:(BOOL)btMouseSupport
absoluteTouchMode:(BOOL)absoluteTouchMode
passthroughTouchMode:(BOOL)passthroughTouchMode
statsOverlay:(BOOL)statsOverlay {

[_managedObjectContext performBlockAndWait:^{
Expand All @@ -86,6 +87,7 @@ - (void) saveSettingsWithBitrate:(NSInteger)bitrate
settingsToSave.enableHdr = enableHdr;
settingsToSave.btMouseSupport = btMouseSupport;
settingsToSave.absoluteTouchMode = absoluteTouchMode;
settingsToSave.passthroughTouchMode = passthroughTouchMode;
settingsToSave.statsOverlay = statsOverlay;

[self saveData];
Expand Down
1 change: 1 addition & 0 deletions Limelight/Database/TemporarySettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
@property (nonatomic) BOOL enableHdr;
@property (nonatomic) BOOL btMouseSupport;
@property (nonatomic) BOOL absoluteTouchMode;
@property (nonatomic) BOOL passthroughTouchMode;
@property (nonatomic) BOOL statsOverlay;

- (id) initFromSettings:(Settings*)settings;
Expand Down
1 change: 1 addition & 0 deletions Limelight/Database/TemporarySettings.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ - (id) initFromSettings:(Settings*)settings {
self.onscreenControls = settings.onscreenControls;
self.btMouseSupport = settings.btMouseSupport;
self.absoluteTouchMode = settings.absoluteTouchMode;
self.passthroughTouchMode = settings.passthroughTouchMode;
self.statsOverlay = settings.statsOverlay;
#endif
self.uniqueId = settings.uniqueId;
Expand Down
19 changes: 19 additions & 0 deletions Limelight/Input/PassthroughTouchHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// PassthroughTouchHandler.h
// Moonlight
//
// Created by ZigZagT on 5/20/2024.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//

#import "StreamView.h"

NS_ASSUME_NONNULL_BEGIN

@interface PassthroughTouchHandler : UIResponder

-(id)initWithView:(StreamView*)view;

@end

NS_ASSUME_NONNULL_END
64 changes: 64 additions & 0 deletions Limelight/Input/PassthroughTouchHandler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// PassthroughTouchHandler.m
// Moonlight
//
// Created by ZigZagT on 5/20/2024.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//

#import "PassthroughTouchHandler.h"

#include <Limelight.h>

@implementation PassthroughTouchHandler {
StreamView* view;
}

- (id)initWithView:(StreamView*)view {
self = [self init];
self->view = view;
return self;
}

- (int)sendTouchEvent:(UITouch*)touch withType:(uint8_t)eventType {
CGPoint location = [self->view adjustCoordinatesForVideoArea:[touch locationInView:self->view]];
CGSize videoSize = [self->view getVideoAreaSize];
return LiSendTouchEvent(
eventType,
(uint32_t) (uintptr_t) touch,
location.x / videoSize.width,
location.y / videoSize.height,
0,
0,
0,
LI_ROT_UNKNOWN
);
}



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch* touch in touches) {
[self sendTouchEvent:touch withType:LI_TOUCH_EVENT_DOWN];
}
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch* touch in touches) {
[self sendTouchEvent:touch withType:LI_TOUCH_EVENT_MOVE];
}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch* touch in touches) {
[self sendTouchEvent:touch withType:LI_TOUCH_EVENT_UP];
}
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch* touch in touches) {
[self sendTouchEvent:touch withType:LI_TOUCH_EVENT_CANCEL];
}
}

@end
4 changes: 4 additions & 0 deletions Limelight/Input/StreamView.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@
- (void) updateCursorLocation:(CGPoint)location isMouse:(BOOL)isMouse;
#endif

- (CGSize) getVideoAreaSize;
- (CGPoint) adjustCoordinatesForVideoArea:(CGPoint)point;
- (void) cancelAllTouchEvents;

@end
30 changes: 28 additions & 2 deletions Limelight/Input/StreamView.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
#import "KeyboardSupport.h"
#import "RelativeTouchHandler.h"
#import "AbsoluteTouchHandler.h"
#import "PassthroughTouchHandler.h"
#import "KeyboardInputField.h"

static const double X1_MOUSE_SPEED_DIVISOR = 2.5;

@implementation StreamView {
OnScreenControls* onScreenControls;

int keyboardGestureNFingers;
KeyboardInputField* keyInputField;
BOOL isInputingText;
NSMutableSet* keysDown;
Expand Down Expand Up @@ -51,6 +53,7 @@ - (void) setupStreamView:(ControllerSupport*)controllerSupport
config:(StreamConfiguration*)streamConfig {
self->interactionDelegate = interactionDelegate;
self->streamAspectRatio = (float)streamConfig.width / (float)streamConfig.height;
self->keyboardGestureNFingers = 3;

TemporarySettings* settings = [[[DataManager alloc] init] getSettings];

Expand All @@ -68,7 +71,15 @@ - (void) setupStreamView:(ControllerSupport*)controllerSupport
#else
// iOS uses RelativeTouchHandler or AbsoluteTouchHandler depending on user preference
if (settings.absoluteTouchMode) {
self->touchHandler = [[AbsoluteTouchHandler alloc] initWithView:self];
if (settings.passthroughTouchMode) {
self->touchHandler = [[PassthroughTouchHandler alloc] initWithView:self];
// "Touch gestures for Windows" uses up to four fingers according to Microsoft support articles.
// Register keyboard gesture with 5 fingers can avoid conflict.
// Also the popup keyboarde became less useful since Windows has a built in on-screen keyboard.
self->keyboardGestureNFingers = 5;
} else {
self->touchHandler = [[AbsoluteTouchHandler alloc] initWithView:self];
}
}
else {
self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self];
Expand Down Expand Up @@ -318,6 +329,21 @@ - (void)sendStylusHoverEvent:(UIHoverGestureRecognizer*)gesture API_AVAILABLE(io

#endif

- (void)cancelAllTouchEvents {
// Use this to cancel touch events when the intention is meant for local device.
// i.e. open notification center, end streaming by swiping on edge
LiSendTouchEvent(
LI_TOUCH_EVENT_CANCEL_ALL,
0,
0,
0,
0,
0,
0,
LI_ROT_UNKNOWN
);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self handleMouseButtonEvent:BUTTON_ACTION_PRESS
forTouches:touches
Expand Down Expand Up @@ -351,7 +377,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// is triggered.
[touchHandler touchesBegan:touches withEvent:event];

if ([[event allTouches] count] == 3) {
if ([[event allTouches] count] == self->keyboardGestureNFingers) {
if (isInputingText) {
Log(LOG_D, @"Closing the keyboard");
[keyInputField resignFirstResponder];
Expand Down
2 changes: 2 additions & 0 deletions Limelight/Limelight-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
</array>
<key>GCSupportsControllerUserInteraction</key>
<true/>
<key>LSSupportsGameMode</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22225" systemVersion="23A344" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22757" systemVersion="23E224" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="hidden" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
Expand Down Expand Up @@ -32,6 +32,7 @@
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="passthroughTouchMode" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="preferredCodec" attributeType="Integer 32" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="statsOverlay" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
Expand Down
1 change: 1 addition & 0 deletions Limelight/ViewControllers/SettingsViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
@property (strong, nonatomic) IBOutlet UISegmentedControl *resolutionSelector;
@property (strong, nonatomic) IBOutlet UIView *resolutionDisplayView;
@property (strong, nonatomic) IBOutlet UISegmentedControl *touchModeSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *passthroughTouchModeSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *onscreenControlSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *optimizeSettingsSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *multiControllerSelector;
Expand Down
5 changes: 5 additions & 0 deletions Limelight/ViewControllers/SettingsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ - (void)viewDidLoad {

[self.touchModeSelector setSelectedSegmentIndex:currentSettings.absoluteTouchMode ? 1 : 0];
[self.touchModeSelector addTarget:self action:@selector(touchModeChanged) forControlEvents:UIControlEventValueChanged];
[self.passthroughTouchModeSelector setSelectedSegmentIndex:currentSettings.passthroughTouchMode ? 0 : 1];
[self.passthroughTouchModeSelector setEnabled:currentSettings.absoluteTouchMode];
[self.statsOverlaySelector setSelectedSegmentIndex:currentSettings.statsOverlay ? 1 : 0];
[self.btMouseSelector setSelectedSegmentIndex:currentSettings.btMouseSupport ? 1 : 0];
[self.optimizeSettingsSelector setSelectedSegmentIndex:currentSettings.optimizeGames ? 1 : 0];
Expand All @@ -268,6 +270,7 @@ - (void)viewDidLoad {
- (void) touchModeChanged {
// Disable on-screen controls in absolute touch mode
[self.onscreenControlSelector setEnabled:[self.touchModeSelector selectedSegmentIndex] == 0];
[self.passthroughTouchModeSelector setEnabled:[self.touchModeSelector selectedSegmentIndex] == 1];
}

- (void) updateBitrate {
Expand Down Expand Up @@ -536,6 +539,7 @@ - (void) saveSettings {
BOOL btMouseSupport = [self.btMouseSelector selectedSegmentIndex] == 1;
BOOL useFramePacing = [self.framePacingSelector selectedSegmentIndex] == 1;
BOOL absoluteTouchMode = [self.touchModeSelector selectedSegmentIndex] == 1;
BOOL passthroughTouchMode = [self.passthroughTouchModeSelector selectedSegmentIndex] == 0;
BOOL statsOverlay = [self.statsOverlaySelector selectedSegmentIndex] == 1;
BOOL enableHdr = [self.hdrSelector selectedSegmentIndex] == 1;
[dataMan saveSettingsWithBitrate:_bitrate
Expand All @@ -553,6 +557,7 @@ - (void) saveSettings {
enableHdr:enableHdr
btMouseSupport:btMouseSupport
absoluteTouchMode:absoluteTouchMode
passthroughTouchMode:passthroughTouchMode
statsOverlay:statsOverlay];
}

Expand Down
24 changes: 17 additions & 7 deletions Limelight/ViewControllers/StreamFrameViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ - (void)viewDidLoad
[self.view addGestureRecognizer:_playPauseTapGestureRecognizer];

#else
_exitSwipeRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
_exitSwipeRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped:)];
_exitSwipeRecognizer.edges = UIRectEdgeLeft;
_exitSwipeRecognizer.delaysTouchesBegan = NO;
_exitSwipeRecognizer.delaysTouchesEnded = NO;
Expand Down Expand Up @@ -186,11 +186,15 @@ - (void)viewDidLoad
object: nil];
#endif

// Only enable scroll and zoom in absolute touch mode
if (_settings.absoluteTouchMode) {
// Only enable scroll and zoom in absolute touch mode with exception of passthrough touch mode
// Windows has its own pinch / pan / scroll handling which conflicts with UIScrollView gestures.
if (_settings.absoluteTouchMode && !_settings.passthroughTouchMode) {
_scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
_scrollView.delaysContentTouches = NO;
#if !TARGET_OS_TV
[_scrollView.panGestureRecognizer setMinimumNumberOfTouches:2];
[_scrollView.panGestureRecognizer setDelaysTouchesBegan: NO];
[_scrollView.panGestureRecognizer setDelaysTouchesEnded: NO];
#endif
[_scrollView setShowsHorizontalScrollIndicator:NO];
[_scrollView setShowsVerticalScrollIndicator:NO];
Expand Down Expand Up @@ -306,6 +310,7 @@ - (void)updateOverlayText:(NSString*)text {
}

- (void) returnToMainFrame {
[self->_streamView cancelAllTouchEvents];
// Reset display mode back to default
[self updatePreferredDisplayMode:NO];

Expand All @@ -317,6 +322,7 @@ - (void) returnToMainFrame {

// This will fire if the user opens control center or gets a low battery message
- (void)applicationWillResignActive:(NSNotification *)notification {
[self->_streamView cancelAllTouchEvents];
if (_inactivityTimer != nil) {
[_inactivityTimer invalidate];
}
Expand Down Expand Up @@ -361,10 +367,14 @@ - (void)applicationDidEnterBackground:(UIApplication *)application {
[self returnToMainFrame];
}

- (void)edgeSwiped {
Log(LOG_I, @"User swiped to end stream");

[self returnToMainFrame];
- (void)edgeSwiped:(UIScreenEdgePanGestureRecognizer*)gestureRecognizer {
CGPoint touch = [gestureRecognizer locationInView:self.view];
double distancePct = touch.x / self.view.bounds.size.width;
Log(LOG_I, @"User swiped distance = %f%%", distancePct * 100);
if (distancePct > 0.5) {
Log(LOG_I, @"User swiped to end stream");
[self returnToMainFrame];
}
}

- (void) connectionStarted {
Expand Down
2 changes: 2 additions & 0 deletions Moonlight TV/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
<array>
<string>arm64</string>
</array>
<key>LSSupportsGameMode</key>
<true/>
<key>UIUserInterfaceStyle</key>
<string>Dark</string>
</dict>
Expand Down
6 changes: 6 additions & 0 deletions Moonlight.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
FBD349621A0089F6002D2A60 /* DataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD349611A0089F6002D2A60 /* DataManager.m */; };
FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86DF19F7A837001C18A8 /* UIComputerView.m */; };
FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E519F82297001C18A8 /* UIAppView.m */; };
FD10B0BF2BF1729A009AC503 /* PassthroughTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = FD10B0BE2BF1729A009AC503 /* PassthroughTouchHandler.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -320,6 +321,8 @@
FBDE86DF19F7A837001C18A8 /* UIComputerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIComputerView.m; sourceTree = "<group>"; };
FBDE86E419F82297001C18A8 /* UIAppView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAppView.h; sourceTree = "<group>"; };
FBDE86E519F82297001C18A8 /* UIAppView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAppView.m; sourceTree = "<group>"; };
FD10B0BD2BF1729A009AC503 /* PassthroughTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PassthroughTouchHandler.h; sourceTree = "<group>"; };
FD10B0BE2BF1729A009AC503 /* PassthroughTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PassthroughTouchHandler.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -501,6 +504,8 @@
FB89460919F646E200339C8A /* Input */ = {
isa = PBXGroup;
children = (
FD10B0BD2BF1729A009AC503 /* PassthroughTouchHandler.h */,
FD10B0BE2BF1729A009AC503 /* PassthroughTouchHandler.m */,
FB89460A19F646E200339C8A /* ControllerSupport.h */,
FB89460B19F646E200339C8A /* ControllerSupport.m */,
FB89460C19F646E200339C8A /* StreamView.h */,
Expand Down Expand Up @@ -905,6 +910,7 @@
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */,
FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */,
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */,
FD10B0BF2BF1729A009AC503 /* PassthroughTouchHandler.m in Sources */,
FB89462C19F646E200339C8A /* HttpManager.m in Sources */,
FB89462D19F646E200339C8A /* MDNSManager.m in Sources */,
FB89462B19F646E200339C8A /* StreamView.m in Sources */,
Expand Down
Loading