From 75ab28a2c3634fe1975ab5f96559da6097f34c09 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 25 Mar 2020 18:24:04 -0700 Subject: [PATCH] Enhance physical keyboard support on iOS 13.4 Fixes #394 --- .travis.yml | 12 +- Limelight/Input/KeyboardSupport.h | 1 + Limelight/Input/KeyboardSupport.m | 212 +++++++++++++++++++++++++++++- Limelight/Input/StreamView.m | 39 ++++++ 4 files changed, 257 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5515c3..3923048 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,20 +6,20 @@ git: matrix: include: - os: osx - osx_image: xcode11 + osx_image: xcode11.4 env: - SCHEME="Moonlight" - - TARGET="platform=iOS Simulator,OS=13.0,name=iPhone 11 Pro" + - TARGET="platform=iOS Simulator,OS=13.4,name=iPhone 11 Pro" - os: osx - osx_image: xcode11 + osx_image: xcode11.4 env: - SCHEME="Moonlight" - - TARGET="platform=iOS Simulator,OS=13.0,name=iPad Pro (9.7-inch)" + - TARGET="platform=iOS Simulator,OS=13.4,name=iPad Pro (9.7-inch)" - os: osx - osx_image: xcode11 + osx_image: xcode11.4 env: - SCHEME="Moonlight TV" - - TARGET="platform=tvOS Simulator,OS=13.0,name=Apple TV 4K" + - TARGET="platform=tvOS Simulator,OS=13.4,name=Apple TV 4K" script: - set -o pipefail && xcodebuild -project Moonlight.xcodeproj -scheme "$SCHEME" -destination "$TARGET" build | xcpretty \ No newline at end of file diff --git a/Limelight/Input/KeyboardSupport.h b/Limelight/Input/KeyboardSupport.h index 770de8a..1388162 100644 --- a/Limelight/Input/KeyboardSupport.h +++ b/Limelight/Input/KeyboardSupport.h @@ -16,6 +16,7 @@ struct KeyEvent { u_char modifier; }; ++ (BOOL)sendKeyEvent:(UIKey*)key down:(BOOL)down API_AVAILABLE(ios(13.4)); + (struct KeyEvent) translateKeyEvent:(unichar) inputChar withModifierFlags:(UIKeyModifierFlags)modifierFlags; @end diff --git a/Limelight/Input/KeyboardSupport.m b/Limelight/Input/KeyboardSupport.m index 642dda9..5624d2f 100644 --- a/Limelight/Input/KeyboardSupport.m +++ b/Limelight/Input/KeyboardSupport.m @@ -11,6 +11,216 @@ @implementation KeyboardSupport ++ (BOOL)sendKeyEvent:(UIKey*)key down:(BOOL)down API_AVAILABLE(ios(13.4)){ + char modifierFlags = 0; + short keyCode = 0; + + if (key.modifierFlags & UIKeyModifierShift) { + modifierFlags |= MODIFIER_SHIFT; + } + if (key.modifierFlags & UIKeyModifierAlternate) { + modifierFlags |= MODIFIER_ALT; + } + if (key.modifierFlags & UIKeyModifierControl) { + modifierFlags |= MODIFIER_CTRL; + } + + // This converts UIKeyboardHIDUsage values to Win32 VK_* values + // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + if (key.keyCode >= UIKeyboardHIDUsageKeyboardA && + key.keyCode <= UIKeyboardHIDUsageKeyboardZ) { + keyCode = (key.keyCode - UIKeyboardHIDUsageKeyboardA) + 0x41; + } + else if (key.keyCode == UIKeyboardHIDUsageKeyboard0) { + // This key is at the beginning of the VK_ range but the end + // of the UIKeyboardHIDUsageKeyboard range. + keyCode = 0x30; + } + else if (key.keyCode >= UIKeyboardHIDUsageKeyboard1 && + key.keyCode <= UIKeyboardHIDUsageKeyboard9) { + keyCode = (key.keyCode - UIKeyboardHIDUsageKeyboard1) + 0x31; + } + else if (key.keyCode == UIKeyboardHIDUsageKeypad0) { + // This key is at the beginning of the VK_ range but the end + // of the UIKeyboardHIDUsageKeypad range. + keyCode = 0x60; + } + else if (key.keyCode >= UIKeyboardHIDUsageKeypad1 && + key.keyCode <= UIKeyboardHIDUsageKeypad9) { + keyCode = (key.keyCode - UIKeyboardHIDUsageKeypad1) + 0x61; + } + else if (key.keyCode >= UIKeyboardHIDUsageKeyboardF1 && + key.keyCode <= UIKeyboardHIDUsageKeyboardF12) { + keyCode = (key.keyCode - UIKeyboardHIDUsageKeyboardF1) + 0x70; + } + else if (key.keyCode >= UIKeyboardHIDUsageKeyboardF13 && + key.keyCode <= UIKeyboardHIDUsageKeyboardF24) { + keyCode = (key.keyCode - UIKeyboardHIDUsageKeyboardF13) + 0x7C; + } + else { + switch (key.keyCode) { + case UIKeyboardHIDUsageKeyboardReturnOrEnter: + keyCode = 0x0D; + break; + case UIKeyboardHIDUsageKeyboardEscape: + keyCode = 0x1B; + break; + case UIKeyboardHIDUsageKeyboardDeleteOrBackspace: + keyCode = 0x08; + break; + case UIKeyboardHIDUsageKeyboardTab: + keyCode = 0x09; + break; + case UIKeyboardHIDUsageKeyboardSpacebar: + keyCode = 0x20; + break; + case UIKeyboardHIDUsageKeyboardHyphen: + keyCode = 0xBD; + break; + case UIKeyboardHIDUsageKeyboardEqualSign: + keyCode = 0xBB; + break; + case UIKeyboardHIDUsageKeyboardOpenBracket: + keyCode = 0xDB; + break; + case UIKeyboardHIDUsageKeyboardCloseBracket: + keyCode = 0xDD; + break; + case UIKeyboardHIDUsageKeyboardBackslash: + keyCode = 0xDC; + break; + case UIKeyboardHIDUsageKeyboardSemicolon: + keyCode = 0xBA; + break; + case UIKeyboardHIDUsageKeyboardQuote: + keyCode = 0xDE; + break; + case UIKeyboardHIDUsageKeyboardGraveAccentAndTilde: + keyCode = 0xC0; + break; + case UIKeyboardHIDUsageKeyboardComma: + keyCode = 0xBC; + break; + case UIKeyboardHIDUsageKeyboardPeriod: + keyCode = 0xBE; + break; + case UIKeyboardHIDUsageKeyboardSlash: + keyCode = 0xBF; + break; + case UIKeyboardHIDUsageKeyboardCapsLock: + keyCode = 0x14; + break; + case UIKeyboardHIDUsageKeyboardPrintScreen: + keyCode = 0x2A; + break; + case UIKeyboardHIDUsageKeyboardScrollLock: + keyCode = 0x91; + break; + case UIKeyboardHIDUsageKeyboardPause: + keyCode = 0x13; + break; + case UIKeyboardHIDUsageKeyboardInsert: + keyCode = 0x2D; + break; + case UIKeyboardHIDUsageKeyboardHome: + keyCode = 0x24; + break; + case UIKeyboardHIDUsageKeyboardPageUp: + keyCode = 0x21; + break; + case UIKeyboardHIDUsageKeyboardDeleteForward: + keyCode = 0x2E; + break; + case UIKeyboardHIDUsageKeyboardEnd: + keyCode = 0x23; + break; + case UIKeyboardHIDUsageKeyboardPageDown: + keyCode = 0x22; + break; + case UIKeyboardHIDUsageKeyboardRightArrow: + keyCode = 0x27; + break; + case UIKeyboardHIDUsageKeyboardLeftArrow: + keyCode = 0x25; + break; + case UIKeyboardHIDUsageKeyboardDownArrow: + keyCode = 0x28; + break; + case UIKeyboardHIDUsageKeyboardUpArrow: + keyCode = 0x26; + break; + case UIKeyboardHIDUsageKeypadNumLock: + keyCode = 0x90; + break; + case UIKeyboardHIDUsageKeypadSlash: + keyCode = 0x6F; + break; + case UIKeyboardHIDUsageKeypadAsterisk: + keyCode = 0x6A; + break; + case UIKeyboardHIDUsageKeypadHyphen: + keyCode = 0x6D; + break; + case UIKeyboardHIDUsageKeypadPlus: + keyCode = 0x6B; + break; + case UIKeyboardHIDUsageKeypadEnter: + keyCode = 0x0D; // FIXME: Is this correct? + break; + case UIKeyboardHIDUsageKeypadPeriod: + keyCode = 0x6E; + break; + case UIKeyboardHIDUsageKeyboardNonUSBackslash: + keyCode = 0xE2; + break; + case UIKeyboardHIDUsageKeypadComma: + keyCode = 0x6C; + break; + case UIKeyboardHIDUsageKeyboardCancel: + keyCode = 0x03; + break; + case UIKeyboardHIDUsageKeyboardClear: + keyCode = 0x0C; + break; + case UIKeyboardHIDUsageKeyboardCrSelOrProps: + keyCode = 0xF7; + break; + case UIKeyboardHIDUsageKeyboardExSel: + keyCode = 0xF8; + break; + case UIKeyboardHIDUsageKeyboardLeftGUI: + case UIKeyboardHIDUsageKeyboardLeftControl: + keyCode = 0xA2; + break; + case UIKeyboardHIDUsageKeyboardLeftShift: + keyCode = 0xA0; + break; + case UIKeyboardHIDUsageKeyboardLeftAlt: + keyCode = 0xA4; + break; + case UIKeyboardHIDUsageKeyboardRightGUI: + case UIKeyboardHIDUsageKeyboardRightControl: + keyCode = 0xA3; + break; + case UIKeyboardHIDUsageKeyboardRightShift: + keyCode = 0xA1; + break; + case UIKeyboardHIDUsageKeyboardRightAlt: + keyCode = 0xA5; + break; + default: + NSLog(@"Unhandled HID usage: %lu", (unsigned long)key.keyCode); + assert(0); + return false; + } + } + + LiSendKeyboardEvent(keyCode, + down ? KEY_ACTION_DOWN : KEY_ACTION_UP, + modifierFlags); + return true; +} + + (struct KeyEvent)translateKeyEvent:(unichar)inputChar withModifierFlags:(UIKeyModifierFlags)modifierFlags { struct KeyEvent event; event.keycode = 0; @@ -22,13 +232,13 @@ case UIKeyModifierShift: [KeyboardSupport addShiftModifier:&event]; break; + case UIKeyModifierCommand: case UIKeyModifierControl: [KeyboardSupport addControlModifier:&event]; break; case UIKeyModifierAlternate: [KeyboardSupport addAltModifier:&event]; break; - case UIKeyModifierCommand: case UIKeyModifierNumericPad: break; } diff --git a/Limelight/Input/StreamView.m b/Limelight/Input/StreamView.m index a1cb01a..bed2d1d 100644 --- a/Limelight/Input/StreamView.m +++ b/Limelight/Input/StreamView.m @@ -10,6 +10,7 @@ #include #import "DataManager.h" #import "ControllerSupport.h" +#import "KeyboardSupport.h" #import "TextFieldKeyboardDelegate.h" @implementation StreamView { @@ -211,6 +212,44 @@ } +- (void)pressesBegan:(NSSet *)presses withEvent:(UIPressesEvent *)event { + BOOL handled = NO; + + if (@available(iOS 13.4, tvOS 13.4, *)) { + for (UIPress* press in presses) { + // For now, we'll treated it as handled if we handle at least one of the + // UIPress events inside the set. + if (press.key != nil && [KeyboardSupport sendKeyEvent:press.key down:YES]) { + // This will prevent the legacy UITextField from receiving the event + handled = YES; + } + } + } + + if (!handled) { + [super pressesBegan:presses withEvent:event]; + } +} + +- (void)pressesEnded:(NSSet *)presses withEvent:(UIPressesEvent *)event { + BOOL handled = NO; + + if (@available(iOS 13.4, tvOS 13.4, *)) { + for (UIPress* press in presses) { + // For now, we'll treated it as handled if we handle at least one of the + // UIPress events inside the set. + if (press.key != nil && [KeyboardSupport sendKeyEvent:press.key down:NO]) { + // This will prevent the legacy UITextField from receiving the event + handled = YES; + } + } + } + + if (!handled) { + [super pressesEnded:presses withEvent:event]; + } +} + - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { Log(LOG_D, @"Touch up");