From 98361c90ecbb82da70e72e28b3ffac8782946183 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Thu, 10 Oct 2024 12:23:04 +0800 Subject: [PATCH] Virtual keyboard & touch fix --- OpenParsec.xcodeproj/project.pbxproj | 4 - OpenParsec/CParsec.swift | 14 +- OpenParsec/KeyboardViewController.swift | 63 ------ OpenParsec/ParsecSDKBridge.swift | 248 ++++++++++++++++++++---- OpenParsec/ParsecView.swift | 22 +-- OpenParsec/ParsecViewController.swift | 205 ++++++++++++++++++-- OpenParsec/TouchHandlingView.swift | 4 - README.md | 8 + 8 files changed, 431 insertions(+), 137 deletions(-) delete mode 100644 OpenParsec/KeyboardViewController.swift diff --git a/OpenParsec.xcodeproj/project.pbxproj b/OpenParsec.xcodeproj/project.pbxproj index 1aeefe4..9f1ebac 100644 --- a/OpenParsec.xcodeproj/project.pbxproj +++ b/OpenParsec.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ 27A267352B1AEAB700F34C63 /* SettingsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A267342B1AEAB700F34C63 /* SettingsHandler.swift */; }; 27A923E029E8E53000F54BDA /* TouchHandlingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A923DF29E8E53000F54BDA /* TouchHandlingView.swift */; }; 27A923E229E8FEE900F54BDA /* UIViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A923E129E8FEE900F54BDA /* UIViewControllerWrapper.swift */; }; - 27A923E429E9035D00F54BDA /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A923E329E9035D00F54BDA /* KeyboardViewController.swift */; }; 27AC751829EA339B00E8CAF7 /* URLImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27AC751729EA339B00E8CAF7 /* URLImage.swift */; }; 27AD36602B19731800C8A607 /* ExUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27AD365F2B19731800C8A607 /* ExUI.swift */; }; 27B23A222B1B979C00B52F14 /* ParsecMetalRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B23A212B1B979C00B52F14 /* ParsecMetalRenderer.swift */; }; @@ -72,7 +71,6 @@ 27A267342B1AEAB700F34C63 /* SettingsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHandler.swift; sourceTree = ""; }; 27A923DF29E8E53000F54BDA /* TouchHandlingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchHandlingView.swift; sourceTree = ""; }; 27A923E129E8FEE900F54BDA /* UIViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerWrapper.swift; sourceTree = ""; }; - 27A923E329E9035D00F54BDA /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = ""; }; 27AC751729EA339B00E8CAF7 /* URLImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLImage.swift; sourceTree = ""; }; 27AD365F2B19731800C8A607 /* ExUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExUI.swift; sourceTree = ""; }; 27B23A212B1B979C00B52F14 /* ParsecMetalRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsecMetalRenderer.swift; sourceTree = ""; }; @@ -148,7 +146,6 @@ 84480EBD2ADC4FDA007DE5F1 /* GameController.swift */, 27E61A9F292965FD00FF6563 /* Info.plist */, 17EEB91B2BEE62BA00502A3A /* KeyBoardTest.swift */, - 27A923E329E9035D00F54BDA /* KeyboardViewController.swift */, 27E61A9C292965FD00FF6563 /* LaunchScreen.storyboard */, 27E61AA52929817700FF6563 /* LoginView.swift */, 27E61AA92929B92200FF6563 /* MainView.swift */, @@ -270,7 +267,6 @@ 17CD1E032BF07BC3003D2102 /* ViewContainerPatch.swift in Sources */, 27E61AA8292994B500FF6563 /* ActivityIndicator.swift in Sources */, 271D14FC292EAA3600D7F1D6 /* ParsecGLKRenderer.swift in Sources */, - 27A923E429E9035D00F54BDA /* KeyboardViewController.swift in Sources */, 27E61A94292965FC00FF6563 /* SceneDelegate.swift in Sources */, 27A267352B1AEAB700F34C63 /* SettingsHandler.swift in Sources */, 27AD36602B19731800C8A607 /* ExUI.swift in Sources */, diff --git a/OpenParsec/CParsec.swift b/OpenParsec/CParsec.swift index 044145d..b29b25b 100644 --- a/OpenParsec/CParsec.swift +++ b/OpenParsec/CParsec.swift @@ -63,6 +63,8 @@ protocol ParsecService { func sendMouseDelta(_ dx: Int32, _ dy: Int32) func sendMousePosition(_ x: Int32, _ y: Int32) func sendKeyboardMessage(event: KeyBoardKeyEvent) + func sendVirtualKeyboardInput(text: String) + func sendVirtualKeyboardInput(text: String, isOn: Bool) func sendGameControllerButtonMessage(controllerId: UInt32, _ button: ParsecGamepadButton, pressed: Bool) func sendGameControllerAxisMessage(controllerId: UInt32, _ button: ParsecGamepadAxis, _ value: Int16) func sendGameControllerUnplugMessage(controllerId: UInt32) @@ -128,8 +130,8 @@ class CParsec { parsecImpl.setFrame(width, height, scale) // set client resolution - ParsecResolution.resolutions[1].width = Int(width) - ParsecResolution.resolutions[1].height = Int(height) + ParsecResolution.resolutions[1].width = Int(width * scale) + ParsecResolution.resolutions[1].height = Int(height * scale) } @@ -171,6 +173,14 @@ class CParsec parsecImpl.sendKeyboardMessage(event: event) } + static func sendVirtualKeyboardInput(text: String) { + parsecImpl.sendVirtualKeyboardInput(text: text) + } + + static func sendVirtualKeyboardInput(text: String, isOn: Bool) { + parsecImpl.sendVirtualKeyboardInput(text: text, isOn: isOn) + } + static func sendGameControllerButtonMessage(controllerId:UInt32, _ button:ParsecGamepadButton, pressed:Bool) { parsecImpl.sendGameControllerButtonMessage(controllerId: controllerId, button, pressed: pressed) diff --git a/OpenParsec/KeyboardViewController.swift b/OpenParsec/KeyboardViewController.swift deleted file mode 100644 index 1f5d1fb..0000000 --- a/OpenParsec/KeyboardViewController.swift +++ /dev/null @@ -1,63 +0,0 @@ -import ParsecSDK -import UIKit - -class KeyboardViewController:UIViewController -{ - override func viewDidLoad() - { - super.viewDidLoad() - print("KeyboardViewController did load!") - } - - @objc func handleKeyCommand(sender:UIKeyCommand) - { - print("KeyboardViewController keyboard info: \(sender)") - -// CParsec.sendKeyboardMessage(sender:sender) - } - - @objc func handleModifierKeyCommand(sender:UIKeyCommand) - { - print("KeyboardViewController keyboard modifier info \(sender.modifierFlags.rawValue)") - } - - override var keyCommands:[UIKeyCommand]? - { - // Create an array to hold the key commands - var commands = [UIKeyCommand]() - - // Add a key command for each printable ASCII character - for scalar in (Unicode.Scalar(32)...Unicode.Scalar(255)).makeIterator() - { - let input = String(scalar) - let keyCommand = UIKeyCommand(action:#selector(handleKeyCommand(sender:)), input:input, modifierFlags:[], propertyList:input) - print("Key added to the Commands: \(input)") - commands.append(keyCommand) - } - - // ESC Key - let escKeyCommand = UIKeyCommand(action:#selector(handleKeyCommand(sender:)), input:UIKeyCommand.inputEscape, modifierFlags:[], propertyList:nil) - - // TAB Key - let tabKeyCommand = UIKeyCommand(action:#selector(handleKeyCommand(sender:)), input:"\t", modifierFlags:[], propertyList:nil) - - // Shift Keys - let shiftKeyCommand = UIKeyCommand(action:#selector(handleKeyCommand(sender:)), input:"", modifierFlags:.shift, propertyList:nil) - - // CTRL Key - let ctrlKeyCommand = UIKeyCommand(action:#selector(handleKeyCommand(sender:)), input:"", modifierFlags:.control, propertyList:nil) - - // CMD Key - let commandKey = UIKeyCommand(action:#selector(handleKeyCommand(sender:)), input:"", modifierFlags:.command, propertyList:nil) - - // Return the array of key commands - return commands + - [ - escKeyCommand, - tabKeyCommand, - shiftKeyCommand, - ctrlKeyCommand, - commandKey - ] - } -} diff --git a/OpenParsec/ParsecSDKBridge.swift b/OpenParsec/ParsecSDKBridge.swift index 694a347..3839408 100644 --- a/OpenParsec/ParsecSDKBridge.swift +++ b/OpenParsec/ParsecSDKBridge.swift @@ -44,7 +44,8 @@ class ParsecSDKBridge: ParsecService private var _parsec:OpaquePointer! private var _audio:OpaquePointer! private let _audioPtr:UnsafeRawPointer - + + private var isVirtualShiftOn = false public var clientWidth:Float = 1920 public var clientHeight:Float = 1080 @@ -58,28 +59,28 @@ class ParsecSDKBridge: ParsecService init() { print("Parsec SDK Version: " + String(ParsecSDKBridge.PARSEC_VER)) - + ParsecSetLogCallback( - { (level, msg, opaque) in - print("[\(level == LOG_DEBUG ? "D" : "I")] \(String(cString:msg!))") - }, nil) - + { (level, msg, opaque) in + print("[\(level == LOG_DEBUG ? "D" : "I")] \(String(cString:msg!))") + }, nil) + audio_init(&_audio) - + ParsecInit(ParsecSDKBridge.PARSEC_VER, nil, nil, &_parsec) - + self._audioPtr = UnsafeRawPointer(_audio) } deinit { - + ParsecDestroy(_parsec) audio_destroy(&_audio) } - + func connect(_ peerID:String) -> ParsecStatus { var parsecClientCfg = ParsecClientConfig() @@ -95,7 +96,7 @@ class ParsecSDKBridge: ParsecService parsecClientCfg.video.1.decoderCompatibility = false parsecClientCfg.video.1.decoderH265 = true - parsecClientCfg.mediaContainer = 0 + parsecClientCfg.mediaContainer = 0 parsecClientCfg.protocol = 1 //parsecClientCfg.secret = "" parsecClientCfg.pngCursor = false @@ -104,45 +105,45 @@ class ParsecSDKBridge: ParsecService return ParsecClientConnect(_parsec, &parsecClientCfg, NetworkHandler.clinfo?.session_id, peerID) } - + func disconnect() { - audio_clear(&_audio) + audio_clear(&_audio) ParsecClientDisconnect(_parsec) backgroundTaskRunning = false } - + func getStatus() -> ParsecStatus { return ParsecClientGetStatus(_parsec, nil) } - + func getStatusEx(_ pcs:inout ParsecClientStatus) -> ParsecStatus { self.hostHeight = Float(pcs.decoder.0.height) self.hostWidth = Float(pcs.decoder.0.width) return ParsecClientGetStatus(_parsec, &pcs) - + } func setFrame(_ width:CGFloat, _ height:CGFloat, _ scale:CGFloat) { ParsecClientSetDimensions(_parsec, UInt8(DEFAULT_STREAM), UInt32(width), UInt32(height), Float(scale)) - + clientWidth = Float(width) clientHeight = Float(height) } - + func renderGLFrame(timeout:UInt32 = 16) // timeout in ms, 16 == 60 FPS, 8 == 120 FPS, etc. { ParsecClientGLRenderFrame(_parsec, UInt8(DEFAULT_STREAM), nil, nil, timeout) } /*static func renderMetalFrame(_ queue:inout MTLCommandQueue, _ texturePtr:UnsafeMutablePointer, timeout:UInt32 = 16) // timeout in ms, 16 == 60 FPS, 8 == 120 FPS, etc. - { - ParsecClientMetalRenderFrame(_parsec, UInt8(DEFAULT_STREAM), &queue, texturePtr, nil, nil, timeout) - }*/ - + { + ParsecClientMetalRenderFrame(_parsec, UInt8(DEFAULT_STREAM), &queue, texturePtr, nil, nil, timeout) + }*/ + func pollAudio(timeout:UInt32 = 16) // timeout in ms, 16 == 60 FPS, 8 == 120 FPS, etc. { ParsecClientPollAudio(_parsec, audio_cb, timeout, _audioPtr) @@ -150,7 +151,7 @@ class ParsecSDKBridge: ParsecService var getFirstCursor = false var mousePositionRelative = false - + func pollEvent(timeout:UInt32 = 16) // timeout in ms, 16 == 60 FPS, 8 == 120 FPS, etc. { var e: ParsecClientEvent! @@ -215,7 +216,7 @@ class ParsecSDKBridge: ParsecService mouseInfo.mouseX = Int32(event.cursor.positionX) mouseInfo.mouseY = Int32(event.cursor.positionY) } - + mouseInfo.cursorHotX = Int(event.cursor.hotX) mouseInfo.cursorHotY = Int(event.cursor.hotY) @@ -223,13 +224,13 @@ class ParsecSDKBridge: ParsecService var colors = [RGBA]() let count = size << 2 for i in 0..(_ value: T, minValue: T, maxValue: T) -> T where T : Comparable { return min(max(value, minValue), maxValue) } - + func sendMousePosition(_ x:Int32, _ y:Int32) { mouseInfo.mouseX = ParsecSDKBridge.clamp(x, minValue: 0, maxValue: Int32(self.clientWidth)) @@ -332,6 +333,79 @@ class ParsecSDKBridge: ParsecService motionMessage.mouseMotion.relative = true ParsecClientSendMessage(_parsec, &motionMessage) } + + func getKeyCodeByText(text: String) -> (ParsecKeycode?, Bool) { + var keyCode : ParsecKeycode? + var useShift = false + if text.count == 1 { + let char = Character(text) + if char.isLetter || char.isNumber { + keyCode = ParsecSDKBridge.parsecKeyCodeTranslator(text.uppercased()) + if char.isUppercase { + useShift = true + } + } else if char.isNewline { + keyCode = ParsecKeycode(40) + } else if char.isWhitespace{ + keyCode = ParsecKeycode(44) + } else { + let (keycodeRaw, keyMod) = ParsecSDKBridge.getParsecKeycode(for: text) + if keycodeRaw != -1 { + keyCode = ParsecKeycode(UInt32(keycodeRaw)) + if keyMod { + useShift = true + } + } + } + } else { + keyCode = ParsecSDKBridge.parsecKeyCodeTranslator(text) + } + + return (keyCode, useShift) + } + + func sendVirtualKeyboardInput(text: String) { + let (keyCode, useShift) = getKeyCodeByText(text: text) + + guard let keyCode else { + return + } + var keyboardMessagePress = ParsecMessage() + keyboardMessagePress.type = MESSAGE_KEYBOARD + keyboardMessagePress.keyboard.pressed = true + if !isVirtualShiftOn && useShift { + keyboardMessagePress.keyboard.code = ParsecKeycode(rawValue: 225) + ParsecClientSendMessage(_parsec, &keyboardMessagePress) + } + keyboardMessagePress.keyboard.code = keyCode + ParsecClientSendMessage(_parsec, &keyboardMessagePress) + keyboardMessagePress.keyboard.pressed = false + if !isVirtualShiftOn && useShift { + keyboardMessagePress.keyboard.code = ParsecKeycode(rawValue: 225) + ParsecClientSendMessage(_parsec, &keyboardMessagePress) + keyboardMessagePress.keyboard.code = keyCode + } + ParsecClientSendMessage(_parsec, &keyboardMessagePress) + } + + func sendVirtualKeyboardInput(text: String, isOn: Bool) { + let (keyCode, useShift) = getKeyCodeByText(text: text) + + guard let keyCode else { + return + } + + if keyCode.rawValue == 225 { + isVirtualShiftOn = isOn + } + + var keyboardMessagePress = ParsecMessage() + keyboardMessagePress.type = MESSAGE_KEYBOARD + keyboardMessagePress.keyboard.pressed = isOn + keyboardMessagePress.keyboard.code = keyCode + ParsecClientSendMessage(_parsec, &keyboardMessagePress) + + } func sendKeyboardMessage(event:KeyBoardKeyEvent) { @@ -737,7 +811,7 @@ class ParsecSDKBridge: ParsecService } } - static func parsecKeyCodeTranslator(_ str:String) -> ParsecKeycode + static func parsecKeyCodeTranslator(_ str:String) -> ParsecKeycode? { switch str { @@ -863,8 +937,114 @@ class ParsecSDKBridge: ParsecService case "AUDIOMUTE": return ParsecKeycode(262) case "MEDIASELECT": return ParsecKeycode(263) - default: return ParsecKeycode(UInt32(0)) + default: return nil + } + } + + static func getParsecKeycode(for key: String) -> (ParsecKeycode: Int, keyMod: Bool) { + var keyMod = false + var parsecKeycode: Int = 0 + + switch key { + // Non-shifted characters + case "-": + parsecKeycode = 45 + case "=": + parsecKeycode = 46 + case "[": + parsecKeycode = 47 + case "]": + parsecKeycode = 48 + case "\\": + parsecKeycode = 49 + case ";": + parsecKeycode = 51 + case "’": + parsecKeycode = 52 + case "'": + parsecKeycode = 52 + case "`": + parsecKeycode = 53 + case ",": + parsecKeycode = 54 + case ".": + parsecKeycode = 55 + case "/": + parsecKeycode = 56 + + // Shifted characters + case "_": + parsecKeycode = 45 + keyMod = true + case "+": + parsecKeycode = 46 + keyMod = true + case "{": + parsecKeycode = 47 + keyMod = true + case "}": + parsecKeycode = 48 + keyMod = true + case "|": + parsecKeycode = 49 + keyMod = true + case ":": + parsecKeycode = 51 + keyMod = true + case "\"": + parsecKeycode = 52 + keyMod = true + case "”": + parsecKeycode = 52 + keyMod = true + case "~": + parsecKeycode = 53 + keyMod = true + case "<": + parsecKeycode = 54 + keyMod = true + case ">": + parsecKeycode = 55 + keyMod = true + case "?": + parsecKeycode = 56 + keyMod = true + case "!": + parsecKeycode = 30 + keyMod = true + case "@": + parsecKeycode = 31 + keyMod = true + case "#": + parsecKeycode = 32 + keyMod = true + case "$": + parsecKeycode = 33 + keyMod = true + case "%": + parsecKeycode = 34 + keyMod = true + case "^": + parsecKeycode = 35 + keyMod = true + case "&": + parsecKeycode = 36 + keyMod = true + case "*": + parsecKeycode = 37 + keyMod = true + case "(": + parsecKeycode = 38 + keyMod = true + case ")": + parsecKeycode = 39 + keyMod = true + + default: + parsecKeycode = -1 // Unknown key } + + return (parsecKeycode, keyMod) } func sendGameControllerButtonMessage(controllerId:UInt32, _ button:ParsecGamepadButton, pressed:Bool) diff --git a/OpenParsec/ParsecView.swift b/OpenParsec/ParsecView.swift index edc1036..6f02662 100644 --- a/OpenParsec/ParsecView.swift +++ b/OpenParsec/ParsecView.swift @@ -75,6 +75,7 @@ struct ParsecView:View @State var muted:Bool = false @State var preferH265:Bool = true + @State var constantFps = false @State var resolutions : [ParsecResolution] @State var bitrates : [Int] @@ -101,8 +102,7 @@ struct ParsecView:View // Input handlers // TouchHandlingView(handleTouch:onTouch, handleTap:onTap) // .zIndex(2) -//// UIViewControllerWrapper(KeyboardViewController()) -//// .zIndex(3) + // UIViewControllerWrapper(GamepadViewController()) // .zIndex(1) // @@ -203,26 +203,11 @@ struct ParsecView:View } Button(action:toggleConstantFps) { - Text("Constant FPS: \(DataManager.model.constantFps ? "ON" : "ON")") - .padding(12) - .frame(maxWidth:.infinity) - .multilineTextAlignment(.center) - } - /*Button(action:{showDisplays = true}) - { - Text("Switch Display") + Text("Constant FPS: \(constantFps ? "ON" : "OFF")") .padding(12) .frame(maxWidth:.infinity) .multilineTextAlignment(.center) } - .actionSheet(isPresented:$showDisplays, content:genDisplaySheet)*/ - /*Button(action:{inSettings = true}) - { - Text("Settings") - .padding(12) - .frame(maxWidth:.infinity) - .multilineTextAlignment(.center) - }*/ Rectangle() .fill(Color("Foreground")) .opacity(0.25) @@ -340,6 +325,7 @@ struct ParsecView:View func toggleConstantFps() { DataManager.model.constantFps.toggle() + constantFps = DataManager.model.constantFps updateHostVideoConfig() } diff --git a/OpenParsec/ParsecViewController.swift b/OpenParsec/ParsecViewController.swift index 47a32c7..996b8ed 100644 --- a/OpenParsec/ParsecViewController.swift +++ b/OpenParsec/ParsecViewController.swift @@ -7,6 +7,7 @@ import Foundation import UIKit +import ParsecSDK protocol ParsecPlayground { @@ -16,12 +17,15 @@ protocol ParsecPlayground { } -class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGestureRecognizerDelegate{ +class ParsecViewController :UIViewController { var glkView: ParsecPlayground! var gamePadController: GamepadController! var touchController: TouchController! var u:UIImageView? var lastImg: CGImage? + + var keyboardAccessoriesView : UIToolbar? + override var prefersPointerLocked: Bool { return true } @@ -33,8 +37,6 @@ class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGe self.gamePadController = GamepadController(viewController: self) self.touchController = TouchController(viewController: self) - - } required init?(coder: NSCoder) { @@ -91,8 +93,13 @@ class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGe let twoFingerTapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(handleTwoFingerTap(_:))) twoFingerTapGestureRecognizer.numberOfTouchesRequired = 2 view.addGestureRecognizer(twoFingerTapGestureRecognizer) -// view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) -// view.backgroundColor = UIColor(red: 0x66, green: 0xcc, blue: 0xff, alpha: 1.0) + // view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) + // view.backgroundColor = UIColor(red: 0x66, green: 0xcc, blue: 0xff, alpha: 1.0) + + let threeFingerTapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(handleThreeFinderTap(_:))) + threeFingerTapGestureRecognizer.numberOfTouchesRequired = 3 + view.addGestureRecognizer(threeFingerTapGestureRecognizer) + } @@ -118,7 +125,7 @@ class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGe for press in presses { CParsec.sendKeyboardMessage(event:KeyBoardKeyEvent(input: press.key, isPressBegin: true) ) } - + } override func pressesEnded (_ presses: Set, with event: UIPressesEvent?) { @@ -126,11 +133,16 @@ class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGe for press in presses { CParsec.sendKeyboardMessage(event:KeyBoardKeyEvent(input: press.key, isPressBegin: false) ) } - + } +} + +extension ParsecViewController : UIGestureRecognizerDelegate { + @objc func handlePanGesture(_ gestureRecognizer:UIPanGestureRecognizer) { + // print("number = \(gestureRecognizer.numberOfTouches) status = \(gestureRecognizer.state.rawValue)") if gestureRecognizer.numberOfTouches == 2 { let translation = gestureRecognizer.translation(in: gestureRecognizer.view) @@ -141,24 +153,45 @@ class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGe } let location = gestureRecognizer.location(in:gestureRecognizer.view) touchController.onTouch(typeOfTap: 1, location: location, state: gestureRecognizer.state) + } else if gestureRecognizer.numberOfTouches == 1 { + let position = gestureRecognizer.location(in: gestureRecognizer.view) + CParsec.sendMousePosition(Int32(position.x), Int32(position.y)) + + if gestureRecognizer.state == .began { + let button = ParsecMouseButton.init(rawValue: 1) + CParsec.sendMouseClickMessage(button, true) + } + + } else if gestureRecognizer.numberOfTouches == 0 { + if gestureRecognizer.state == .ended || gestureRecognizer.state == .cancelled { + let button = ParsecMouseButton.init(rawValue: 1) + CParsec.sendMouseClickMessage(button, false) + } } - + } - + @objc func handleSingleFingerTap(_ gestureRecognizer:UITapGestureRecognizer) { let location = gestureRecognizer.location(in:gestureRecognizer.view) touchController.onTap(typeOfTap: 1, location: location) - + } - + @objc func handleTwoFingerTap(_ gestureRecognizer:UITapGestureRecognizer) { let location = gestureRecognizer.location(in: gestureRecognizer.view) touchController.onTap(typeOfTap: 3, location: location) } + @objc func handleThreeFinderTap(_ gestureRecognizer:UITapGestureRecognizer) { + showKeyboard() + } + +} + +extension ParsecViewController : UIPointerInteractionDelegate { func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? { return UIPointerStyle.hidden() } @@ -174,8 +207,156 @@ class ParsecViewController :UIViewController, UIPointerInteractionDelegate, UIGe return nil } - override func viewDidDisappear(_ animated: Bool) { +} + +class KeyBoardButton : UIButton { + let keyText : String + let isToggleable : Bool + var isOn = false + + required init(keyText: String, isToggleable: Bool) { + self.keyText = keyText + self.isToggleable = isToggleable + super.init(frame: .zero) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Virtual Keyboard +extension ParsecViewController : UIKeyInput { + var hasText: Bool { + return true + } + + override var canBecomeFirstResponder: Bool { + return true + } + + func insertText(_ text: String) { + CParsec.sendVirtualKeyboardInput(text: text) + } + + func deleteBackward() { + CParsec.sendVirtualKeyboardInput(text: "BACKSPACE") + } + + // copied from moonlight https://github.com/moonlight-stream/moonlight-ios/blob/022352c1667788d8626b659d984a290aa5c25e17/Limelight/Input/StreamView.m#L393 + override var inputAccessoryView: UIView? { + + if let keyboardAccessoriesView { + return keyboardAccessoriesView + } + + let customToolbarView = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 44)) + + let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped)) + let windowsBarButton = createKeyboardButton(displayText: "⌘", keyText: "LGUI", isToggleable: true) + let tabBarButton = createKeyboardButton(displayText: "⇥", keyText: "TAB", isToggleable: false) + let shiftBarButton = createKeyboardButton(displayText: "⇧", keyText: "SHIFT", isToggleable: true) + let escapeBarButton = createKeyboardButton(displayText: "⎋", keyText: "UIKeyInputEscape", isToggleable: false) + let controlBarButton = createKeyboardButton(displayText: "⌃", keyText: "CONTROL", isToggleable: true) + let altBarButton = createKeyboardButton(displayText: "⌥", keyText: "LALT", isToggleable: true) + let deleteBarButton = createKeyboardButton(displayText: "Del", keyText: "DELETE", isToggleable: false) + let f1Button = createKeyboardButton(displayText: "F1", keyText: "F1", isToggleable: false) + let f2Button = createKeyboardButton(displayText: "F2", keyText: "F2", isToggleable: false) + let f3Button = createKeyboardButton(displayText: "F3", keyText: "F3", isToggleable: false) + let f4Button = createKeyboardButton(displayText: "F4", keyText: "F4", isToggleable: false) + let f5Button = createKeyboardButton(displayText: "F5", keyText: "F5", isToggleable: false) + let f6Button = createKeyboardButton(displayText: "F6", keyText: "F6", isToggleable: false) + let f7Button = createKeyboardButton(displayText: "F7", keyText: "F7", isToggleable: false) + let f8Button = createKeyboardButton(displayText: "F8", keyText: "F8", isToggleable: false) + let f9Button = createKeyboardButton(displayText: "F9", keyText: "F9", isToggleable: false) + let f10Button = createKeyboardButton(displayText: "F10", keyText: "F10", isToggleable: false) + let f11Button = createKeyboardButton(displayText: "F11", keyText: "F11", isToggleable: false) + let f12Button = createKeyboardButton(displayText: "F12", keyText: "F11", isToggleable: false) + let upButton = createKeyboardButton(displayText: "↑", keyText: "UP", isToggleable: false) + let downButton = createKeyboardButton(displayText: "↓", keyText: "DOWN", isToggleable: false) + let leftButton = createKeyboardButton(displayText: "←", keyText: "LEFT", isToggleable: false) + let rightButton = createKeyboardButton(displayText: "→", keyText: "RIGHT", isToggleable: false) + + + let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + + customToolbarView.setItems([windowsBarButton, escapeBarButton, tabBarButton, shiftBarButton, controlBarButton, altBarButton, deleteBarButton, + f1Button, f2Button, f3Button, f4Button, f5Button, f6Button, f7Button, f8Button, f9Button, f10Button, f11Button, f12Button, + upButton, downButton, leftButton, rightButton, + flexibleSpace, doneButton + ], animated: false) + keyboardAccessoriesView = customToolbarView + return customToolbarView + } + + func createKeyboardButton(displayText: String, keyText: String, isToggleable: Bool) -> UIBarButtonItem { + let button = KeyBoardButton(keyText: keyText, isToggleable: isToggleable) + + // Set the image and button properties + button.setTitle(displayText, for: .normal) + button.titleLabel?.font = UIFont(name: "System", size: 10.0) + button.frame = CGRect(x: 0, y: 0, width: 40, height: 40) + button.titleLabel?.frame = CGRect(x: 0, y: 0, width: 30, height: 30) + if let label = button.titleLabel { + NSLayoutConstraint.activate([ + label.heightAnchor.constraint(greaterThanOrEqualToConstant: 30), + label.widthAnchor.constraint(greaterThanOrEqualToConstant: 30) + ]) + label.layer.cornerRadius = 3.0 + label.backgroundColor = .black + label.textAlignment = .center + } + NSLayoutConstraint.activate([ + button.heightAnchor.constraint(greaterThanOrEqualToConstant: 40), + button.widthAnchor.constraint(greaterThanOrEqualToConstant: 40) + ]) + button.titleLabel?.contentMode = .scaleAspectFit + + // Set target and action for button + button.addTarget(target, action: #selector(toolbarButtonClicked(_:)), for: .touchUpInside) + // Create a UIBarButtonItem with the custom button + let barButton = UIBarButtonItem(customView: button) + + return barButton + } + + @objc func toolbarButtonClicked(_ sender: KeyBoardButton) { + let isToggleable = sender.isToggleable + var isOn = sender.isOn + + if isToggleable { + isOn.toggle() + if isOn { + sender.titleLabel?.backgroundColor = .lightGray + } else { + sender.titleLabel?.backgroundColor = .black + } + } + + sender.isOn = isOn + let keyText = sender.keyText + + + if isToggleable { + if isOn { + CParsec.sendVirtualKeyboardInput(text: keyText, isOn: true) + } else { + CParsec.sendVirtualKeyboardInput(text: keyText, isOn: false) + } + } else { + CParsec.sendVirtualKeyboardInput(text: keyText) + } + + } + + @objc func doneTapped() { + // Resign first responder to dismiss the keyboard + resignFirstResponder() + } + + @objc func showKeyboard() { + becomeFirstResponder() } } diff --git a/OpenParsec/TouchHandlingView.swift b/OpenParsec/TouchHandlingView.swift index 556dba9..11bf4da 100644 --- a/OpenParsec/TouchHandlingView.swift +++ b/OpenParsec/TouchHandlingView.swift @@ -44,10 +44,6 @@ class TouchController func onTap(typeOfTap:Int, location:CGPoint) { - // Log the touch location - print("Touch location: \(location)") - print("Touch type: \(typeOfTap)") - let x = Int32(location.x) let y = Int32(location.y) diff --git a/README.md b/README.md index 6606fb6..fe47dde 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,11 @@ OpenParsec is a simple, open-source Parsec client for iOS/iPadOS written in Swif This project is still a major WIP, so apologies for the currently lackluster documentation. I'm also very new to both Swift and SwiftUI so I'm sure there are many places for improvement. Before building, make sure you have the Parsec SDK framework symlinked or copied to the `Frameworks` folder. Builds were tested on Xcode Version 12.5. + +## On-Screen Keyboard +When streaming, you can tap with 3 fingers to bring up on-screen keyboard. + +## Lag / Low Bitrate Issue +If you encounter lags from nowhere or your bitrate hardly goes over 10 Mbps, download Steam Link and do a network test. If you see constant lag spike in the graph, then it's a problem with Apple and there's little we can do to solve this problem. See [here](https://github.com/moonlight-stream/moonlight-ios/issues/627) for more disscussion. + +If you can't change your wireless router's channel to 149 like me, my personal experience is that you can try to power off the device you are using to streaming as well as any nearby Apple devices, especially Mac, then only power on the device you are using to streaming and do the aforementioned network test again. You can turn on other devices if the lag spike is gone and it may sustain for couple hours or days. \ No newline at end of file