Adding keyboard support

This commit is contained in:
Diego Waxemberg
2018-08-25 15:22:32 -07:00
parent 8afcdc92d4
commit 76ab786e94
7 changed files with 289 additions and 15 deletions

View File

@@ -0,0 +1,21 @@
//
// KeyboardSupport.h
// Moonlight
//
// Created by Diego Waxemberg on 8/25/18.
// Copyright © 2018 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface KeyboardSupport : NSObject
struct KeyEvent {
u_short keycode;
u_short modifierKeycode;
u_char modifier;
};
+ (struct KeyEvent) translateKeyEvent:(unichar) inputChar;
@end

View File

@@ -0,0 +1,166 @@
//
// KeyboardSupport.m
// Moonlight
//
// Created by Diego Waxemberg on 8/25/18.
// Copyright © 2018 Moonlight Game Streaming Project. All rights reserved.
//
#import "KeyboardSupport.h"
#include <Limelight.h>
@implementation KeyboardSupport
+ (struct KeyEvent)translateKeyEvent:(unichar)inputChar {
struct KeyEvent event;
event.keycode = 0;
event.modifier = 0;
event.modifierKeycode = 0;
if (inputChar >= 0x30 && inputChar <= 0x39) {
// Numbers 0-9
event.keycode = inputChar;
} else if (inputChar >= 0x41 && inputChar <= 0x5A) {
// Capital letters
event.keycode = inputChar;
[KeyboardSupport addShiftModifier:&event];
} else if (inputChar >= 0x61 && inputChar <= 0x7A) {
// Lower case letters
event.keycode = inputChar - (0x61 - 0x41);
} switch (inputChar) {
case ' ': // Spacebar
event.keycode = 0x20;
break;
case '-': // Hyphen '-'
event.keycode = 0xBD;
break;
case '/': // Forward slash '/'
event.keycode = 0xBF;
break;
case ':': // Colon ':'
event.keycode = 0xBA;
[KeyboardSupport addShiftModifier:&event];
break;
case ';': // Semi-colon ';'
event.keycode = 0xBA;
break;
case '(': // Open parenthesis '('
event.keycode = 0x39; // '9'
[KeyboardSupport addShiftModifier:&event];
break;
case ')': // Close parenthesis ')'
event.keycode = 0x30; // '0'
[KeyboardSupport addShiftModifier:&event];
break;
case '$': // Dollar sign '$'
event.keycode = 0x34; // '4'
[KeyboardSupport addShiftModifier:&event];
break;
case '&': // Ampresand '&'
event.keycode = 0x37; // '7'
[KeyboardSupport addShiftModifier:&event];
break;
case '@': // At-sign '@'
event.keycode = 0x32; // '2'
[KeyboardSupport addShiftModifier:&event];
break;
case '"':
event.keycode = 0xDE;
[KeyboardSupport addShiftModifier:&event];
break;
case '\'':
event.keycode = 0xDE;
break;
case '!':
event.keycode = 0x31; // '1'
[KeyboardSupport addShiftModifier:&event];
break;
case '?':
event.keycode = 0xBF; // '/'
[KeyboardSupport addShiftModifier:&event];
break;
case ',':
event.keycode = 0xBC;
break;
case '<':
event.keycode = 0xBC;
[KeyboardSupport addShiftModifier:&event];
break;
case '.':
event.keycode = 0xBE;
break;
case '>':
event.keycode = 0xBE;
[KeyboardSupport addShiftModifier:&event];
break;
case '[':
event.keycode = 0xDB;
break;
case ']':
event.keycode = 0xDD;
break;
case '{':
event.keycode = 0xDB;
[KeyboardSupport addShiftModifier:&event];
break;
case '}':
event.keycode = 0xDD;
[KeyboardSupport addShiftModifier:&event];
break;
case '#':
event.keycode = 0x33; // '3'
[KeyboardSupport addShiftModifier:&event];
break;
case '%':
event.keycode = 0x35; // '5'
[KeyboardSupport addShiftModifier:&event];
break;
case '^':
event.keycode = 0x36; // '6'
[KeyboardSupport addShiftModifier:&event];
break;
case '*':
event.keycode = 0x38; // '8'
[KeyboardSupport addShiftModifier:&event];
break;
case '+':
event.keycode = 0xBB;
[KeyboardSupport addShiftModifier:&event];
break;
case '=':
event.keycode = 0xBB;
break;
case '_':
event.keycode = 0xBD;
[KeyboardSupport addShiftModifier:&event];
break;
case '\\':
event.keycode = 0xDC;
break;
case '|':
event.keycode = 0xDC;
[KeyboardSupport addShiftModifier:&event];
break;
case '~':
event.keycode = 0xC0;
[KeyboardSupport addShiftModifier:&event];
break;
case '`':
event.keycode = 0xC0;
break;
case '\t':
event.keycode = 0x09;
break;
default:
break;
}
return event;
}
+ (void) addShiftModifier:(struct KeyEvent*)event {
event->modifier = MODIFIER_SHIFT;
event->modifierKeycode = 0x10;
}
@end

View File

@@ -14,7 +14,9 @@
@end
@interface StreamView : OSView
@interface StreamView : OSView <UITextFieldDelegate>
@property (nonatomic, retain) IBOutlet UITextField* keyInputField;
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate;
- (void) setMouseDeltaFactors:(float)x y:(float)y;

View File

@@ -11,12 +11,14 @@
#import "OnScreenControls.h"
#import "DataManager.h"
#import "ControllerSupport.h"
#import "KeyboardSupport.h"
@implementation StreamView {
CGPoint touchLocation, originalLocation;
BOOL touchMoved;
OnScreenControls* onScreenControls;
BOOL isInputingText;
BOOL isDragging;
NSTimer* dragTimer;
@@ -129,9 +131,22 @@
if (isDragging) {
isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
}
else if (!touchMoved) {
if ([[event allTouches] count] == 2) {
} else if (!touchMoved) {
if ([[event allTouches] count] == 3) {
if (isInputingText) {
Log(LOG_D, @"Closing the keyboard");
[_keyInputField resignFirstResponder];
isInputingText = false;
} else {
Log(LOG_D, @"Opening the keyboard");
// Prepare the textbox used to capture keyboard events.
_keyInputField.delegate = self;
_keyInputField.text = @"0";
[_keyInputField becomeFirstResponder];
[_keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
isInputingText = true;
}
} else if ([[event allTouches] count] == 2) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
Log(LOG_D, @"Sending right mouse button press");
@@ -142,7 +157,7 @@
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
});
} else {
} else if ([[event allTouches] count] == 1) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (!self->isDragging){
Log(LOG_D, @"Sending left mouse button press");
@@ -177,5 +192,45 @@
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// This method is called when the "Return" key is pressed.
LiSendKeyboardEvent(0x0d, KEY_ACTION_DOWN, 0);
usleep(50 * 1000);
LiSendKeyboardEvent(0x0d, KEY_ACTION_UP, 0);
return NO;
}
- (void)onKeyboardPressed:(UITextField *)textField {
NSString* inputText = textField.text;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// If the text became empty, we know the user pressed the backspace key.
if ([inputText isEqual:@""]) {
LiSendKeyboardEvent(0x08, KEY_ACTION_DOWN, 0);
usleep(50 * 1000);
LiSendKeyboardEvent(0x08, KEY_ACTION_UP, 0);
} else {
struct KeyEvent event = [KeyboardSupport translateKeyEvent:[inputText characterAtIndex:1]];
if (event.keycode == 0) {
// If we don't know the code, don't send anything.
Log(LOG_W, @"Unknown key code: [%c]", [inputText characterAtIndex:1]);
return;
}
// When we want to send a modified key (like uppercase letters) we need to send the
// modifier ("shift") seperately from the key itself.
if (event.modifier != 0) {
LiSendKeyboardEvent(event.modifierKeycode, KEY_ACTION_DOWN, event.modifier);
usleep(50 * 1000);
}
LiSendKeyboardEvent(event.keycode, KEY_ACTION_DOWN, event.modifier);
usleep(50 * 1000);
LiSendKeyboardEvent(event.keycode, KEY_ACTION_UP, event.modifier);
if (event.modifier != 0) {
LiSendKeyboardEvent(event.modifierKeycode, KEY_ACTION_UP, event.modifier);
}
}
});
textField.text = @"0";
}
@end

View File

@@ -22,6 +22,7 @@
D46A73AD1CBC7D090039F1EE /* ControllerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46A73AC1CBC7D090039F1EE /* ControllerUnitTests.swift */; };
D4746EEC1CBC740C006FB401 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4746EEB1CBC740C006FB401 /* Controller.swift */; };
DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1F5A06206436B20037755F /* ConnectionHelper.m */; };
FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1A674C2131E65900507771 /* KeyboardSupport.m */; };
FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */; };
FB1D599A1BBCCD7E00F482CA /* AppCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */; };
FB290CF219B2C406004C83CF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF119B2C406004C83CF /* Foundation.framework */; };
@@ -125,6 +126,8 @@
D4746EEB1CBC740C006FB401 /* Controller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = "<group>"; };
DC1F5A05206436B10037755F /* ConnectionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConnectionHelper.h; sourceTree = "<group>"; };
DC1F5A06206436B20037755F /* ConnectionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConnectionHelper.m; sourceTree = "<group>"; };
FB1A674B2131E65900507771 /* KeyboardSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyboardSupport.h; sourceTree = "<group>"; };
FB1A674C2131E65900507771 /* KeyboardSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KeyboardSupport.m; sourceTree = "<group>"; };
FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComputerScrollView.h; sourceTree = "<group>"; };
FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComputerScrollView.m; sourceTree = "<group>"; };
FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppCollectionView.h; sourceTree = "<group>"; };
@@ -478,6 +481,8 @@
FB89460D19F646E200339C8A /* StreamView.m */,
FB4678EB1A50C40900377732 /* OnScreenControls.h */,
FB4678EC1A50C40900377732 /* OnScreenControls.m */,
FB1A674B2131E65900507771 /* KeyboardSupport.h */,
FB1A674C2131E65900507771 /* KeyboardSupport.m */,
);
path = Input;
sourceTree = "<group>";
@@ -889,6 +894,7 @@
buildActionMask = 2147483647;
files = (
FB290D0719B2C406004C83CF /* Limelight.xcdatamodeld in Sources */,
FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */,
FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */,
FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */,
FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */,

View File

@@ -282,8 +282,20 @@
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" id="JkF-l4-Qyt">
<rect key="frame" x="335" y="503" width="97" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="tintColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="textColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardAppearance="alert" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
</subviews>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="keyInputField" destination="JkF-l4-Qyt" id="oel-UD-aSP"/>
</connections>
</view>
<navigationItem key="navigationItem" id="iUf-9X-GeA"/>
<nil key="simulatedStatusBarMetrics"/>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES" initialViewController="DL0-L5-LOv">
<device id="retina4_7" orientation="portrait">
<device id="retina4_7" orientation="landscape">
<adaptation id="fullscreen"/>
</device>
<dependencies>
@@ -19,7 +19,7 @@
<objects>
<viewController id="dgh-JZ-Q7z" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="Rtu-AT-Alw" customClass="AppCollectionView">
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
<rect key="frame" x="0.0" y="0.0" width="667" height="343"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="contentInset" minX="40" minY="20" maxX="40" maxY="20"/>
@@ -48,7 +48,7 @@
<navigationItem key="navigationItem" id="1jn-Sf-Xky">
<barButtonItem key="leftBarButtonItem" style="done" id="zAn-CM-7Yz">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="pZ9-ft-T24">
<rect key="frame" x="16" y="0.0" width="109" height="44"/>
<rect key="frame" x="20" y="0.0" width="109" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Roboto-Regular" family="Roboto" pointSize="16"/>
<state key="normal" title="Moonlight" image="Logo">
@@ -76,7 +76,7 @@
<objects>
<viewController id="DL0-L5-LOv" customClass="SWRevealViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Usg-e0-g4a">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
@@ -94,7 +94,7 @@
<objects>
<viewController id="rYd-e6-cQU" userLabel="Side Bar" customClass="SettingsViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="iNk-qF-gIr" customClass="UIScrollView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8zy-ri-Dqc">
@@ -249,7 +249,7 @@
<objects>
<navigationController id="ftZ-kC-fxI" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" translucent="NO" id="0ZA-Ec-QgD">
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="667" height="32"/>
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
@@ -269,7 +269,7 @@
<objects>
<viewController id="mI3-9F-XwU" customClass="StreamFrameViewController" sceneMemberID="viewController">
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="eir-e9-IPE" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2HK-Z5-4Ch">
@@ -280,11 +280,23 @@
<nil key="highlightedColor"/>
</label>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" id="0vm-Iv-K4b">
<rect key="frame" x="178" y="260" width="20" height="20"/>
<rect key="frame" x="324" y="143" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<textField opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" id="r45-6t-nxS">
<rect key="frame" x="406" y="293" width="97" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="tintColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="textColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardAppearance="alert" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
</subviews>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="keyInputField" destination="r45-6t-nxS" id="7Du-Ju-Qcm"/>
</connections>
</view>
<navigationItem key="navigationItem" id="8uX-4T-BiC"/>
<nil key="simulatedStatusBarMetrics"/>
@@ -304,11 +316,11 @@
<objects>
<viewController storyboardIdentifier="loadingFrame" modalTransitionStyle="crossDissolve" modalPresentationStyle="overCurrentContext" id="9W0-2D-0Kp" customClass="LoadingFrameViewController" sceneMemberID="viewController">
<view key="view" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="center" id="nSw-dc-mi4">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="oNu-Gu-QeM">
<rect key="frame" x="266" y="313" width="37" height="37"/>
<rect key="frame" x="266" y="168" width="37" height="37"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>