diff --git a/Limelight/Images.xcassets/AltIcon.imageset/AltIcon.png b/Limelight/Images.xcassets/AltIcon.imageset/AltIcon.png new file mode 100644 index 0000000..222f643 Binary files /dev/null and b/Limelight/Images.xcassets/AltIcon.imageset/AltIcon.png differ diff --git a/Limelight/Images.xcassets/AltIcon.imageset/Contents.json b/Limelight/Images.xcassets/AltIcon.imageset/Contents.json new file mode 100644 index 0000000..29a9ae7 --- /dev/null +++ b/Limelight/Images.xcassets/AltIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "AltIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/Contents.json b/Limelight/Images.xcassets/Contents.json index da4a164..73c0059 100644 --- a/Limelight/Images.xcassets/Contents.json +++ b/Limelight/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Limelight/Images.xcassets/ControlIcon.imageset/Contents.json b/Limelight/Images.xcassets/ControlIcon.imageset/Contents.json new file mode 100644 index 0000000..24227b3 --- /dev/null +++ b/Limelight/Images.xcassets/ControlIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ControlIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/ControlIcon.imageset/ControlIcon.png b/Limelight/Images.xcassets/ControlIcon.imageset/ControlIcon.png new file mode 100644 index 0000000..48433dd Binary files /dev/null and b/Limelight/Images.xcassets/ControlIcon.imageset/ControlIcon.png differ diff --git a/Limelight/Images.xcassets/DeleteIcon.imageset/Contents.json b/Limelight/Images.xcassets/DeleteIcon.imageset/Contents.json new file mode 100644 index 0000000..09d0484 --- /dev/null +++ b/Limelight/Images.xcassets/DeleteIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "DeleteIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/DeleteIcon.imageset/DeleteIcon.png b/Limelight/Images.xcassets/DeleteIcon.imageset/DeleteIcon.png new file mode 100644 index 0000000..3a3e323 Binary files /dev/null and b/Limelight/Images.xcassets/DeleteIcon.imageset/DeleteIcon.png differ diff --git a/Limelight/Images.xcassets/DoneIcon.imageset/Contents.json b/Limelight/Images.xcassets/DoneIcon.imageset/Contents.json new file mode 100644 index 0000000..9229c64 --- /dev/null +++ b/Limelight/Images.xcassets/DoneIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "doneIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/DoneIcon.imageset/doneIcon.png b/Limelight/Images.xcassets/DoneIcon.imageset/doneIcon.png new file mode 100644 index 0000000..f4d178f Binary files /dev/null and b/Limelight/Images.xcassets/DoneIcon.imageset/doneIcon.png differ diff --git a/Limelight/Images.xcassets/EscapeIcon.imageset/Contents.json b/Limelight/Images.xcassets/EscapeIcon.imageset/Contents.json new file mode 100644 index 0000000..5533897 --- /dev/null +++ b/Limelight/Images.xcassets/EscapeIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "EscapeIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/EscapeIcon.imageset/EscapeIcon.png b/Limelight/Images.xcassets/EscapeIcon.imageset/EscapeIcon.png new file mode 100644 index 0000000..262f024 Binary files /dev/null and b/Limelight/Images.xcassets/EscapeIcon.imageset/EscapeIcon.png differ diff --git a/Limelight/Images.xcassets/ShiftIcon.imageset/Contents.json b/Limelight/Images.xcassets/ShiftIcon.imageset/Contents.json new file mode 100644 index 0000000..12d9131 --- /dev/null +++ b/Limelight/Images.xcassets/ShiftIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ShiftIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/ShiftIcon.imageset/ShiftIcon.png b/Limelight/Images.xcassets/ShiftIcon.imageset/ShiftIcon.png new file mode 100644 index 0000000..3e33f4a Binary files /dev/null and b/Limelight/Images.xcassets/ShiftIcon.imageset/ShiftIcon.png differ diff --git a/Limelight/Images.xcassets/TabIcon.imageset/Contents.json b/Limelight/Images.xcassets/TabIcon.imageset/Contents.json new file mode 100644 index 0000000..fd7d96e --- /dev/null +++ b/Limelight/Images.xcassets/TabIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "TabIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/TabIcon.imageset/TabIcon.png b/Limelight/Images.xcassets/TabIcon.imageset/TabIcon.png new file mode 100644 index 0000000..f562b1d Binary files /dev/null and b/Limelight/Images.xcassets/TabIcon.imageset/TabIcon.png differ diff --git a/Limelight/Images.xcassets/WindowsIcon.imageset/Contents.json b/Limelight/Images.xcassets/WindowsIcon.imageset/Contents.json new file mode 100644 index 0000000..778eea3 --- /dev/null +++ b/Limelight/Images.xcassets/WindowsIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "WindowsIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Limelight/Images.xcassets/WindowsIcon.imageset/WindowsIcon.png b/Limelight/Images.xcassets/WindowsIcon.imageset/WindowsIcon.png new file mode 100644 index 0000000..60dec54 Binary files /dev/null and b/Limelight/Images.xcassets/WindowsIcon.imageset/WindowsIcon.png differ diff --git a/Limelight/Input/StreamView.m b/Limelight/Input/StreamView.m index 10c1160..10dba73 100644 --- a/Limelight/Input/StreamView.m +++ b/Limelight/Input/StreamView.m @@ -352,6 +352,23 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; // Prepare the textbox used to capture keyboard events. keyInputField.delegate = self; keyInputField.text = @"0"; +#if !TARGET_OS_TV + // Prepare the toolbar above the keyboard for more options + UIToolbar *customToolbarView = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 44)]; + + UIBarButtonItem *doneBarButton = [self createButtonWithImageNamed:@"DoneIcon.png" backgroundColor:[UIColor clearColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x00" isToggleable:NO]; + UIBarButtonItem *windowsBarButton = [self createButtonWithImageNamed:@"WindowsIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x5B" isToggleable:YES]; + UIBarButtonItem *tabBarButton = [self createButtonWithImageNamed:@"TabIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x09" isToggleable:NO]; + UIBarButtonItem *shiftBarButton = [self createButtonWithImageNamed:@"ShiftIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0xA0" isToggleable:YES]; + UIBarButtonItem *escapeBarButton = [self createButtonWithImageNamed:@"EscapeIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x1B" isToggleable:NO]; + UIBarButtonItem *controlBarButton = [self createButtonWithImageNamed:@"ControlIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0xA2" isToggleable:YES]; + UIBarButtonItem *altBarButton = [self createButtonWithImageNamed:@"AltIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0xA4" isToggleable:YES]; + UIBarButtonItem *deleteBarButton = [self createButtonWithImageNamed:@"DeleteIcon.png" backgroundColor:[UIColor blackColor] target:self action:@selector(toolbarButtonClicked:) keyCode:@"0x2E" isToggleable:NO]; + UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + + [customToolbarView setItems:[NSArray arrayWithObjects:doneBarButton, windowsBarButton, escapeBarButton, tabBarButton, shiftBarButton, controlBarButton, altBarButton, deleteBarButton, flexibleSpace, nil]]; + keyInputField.inputAccessoryView = customToolbarView; +#endif [keyInputField becomeFirstResponder]; [keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged]; @@ -364,6 +381,63 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; } } +- (UIBarButtonItem *)createButtonWithImageNamed:(NSString *)imageName backgroundColor:(UIColor *)backgroundColor target:(id)target action:(SEL)action keyCode:(NSString *)keyCode isToggleable:(BOOL)isToggleable { + UIImage *image = [UIImage imageNamed:imageName]; + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + [button setImage:image forState:UIControlStateNormal]; + button.frame = CGRectMake(0, 0, 30, 30); + button.imageView.contentMode = UIViewContentModeScaleAspectFit; + button.imageView.backgroundColor = backgroundColor; + button.imageView.layer.cornerRadius = 10.0; + button.imageEdgeInsets = UIEdgeInsetsMake(6, 6, 6, 6); + [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; + objc_setAssociatedObject(button, "keyCode", keyCode, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(button, "isToggleable", @(isToggleable), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(button, "isOn", @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button]; + return barButton; +} + +- (void)toolbarButtonClicked:(UIButton *)sender { + BOOL isToggleable = [objc_getAssociatedObject(sender, "isToggleable") boolValue]; + BOOL isOn = [objc_getAssociatedObject(sender, "isOn") boolValue]; + if (isToggleable){ + isOn = !isOn; + // Update the button's appearance based on its new state + if (isOn) { + sender.imageView.backgroundColor = [UIColor lightGrayColor]; + } else { + sender.imageView.backgroundColor = [UIColor blackColor]; + } + } + // Update the new on/off state of the button + objc_setAssociatedObject(sender, "isOn", @(isOn), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + // Get the keyCode parameter and convert to short for key press event + NSString *keyCodeString = objc_getAssociatedObject(sender, "keyCode"); + unsigned int keyCodeInteger = 0; + NSScanner *scanner = [NSScanner scannerWithString:keyCodeString]; + [scanner scanHexInt:&keyCodeInteger]; + short keyCodeShort = (short)keyCodeInteger; + // Close keyboard if done button clicked + if ([keyCodeString isEqual: @"0x00"]){ + [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil]; + } else { + // Send key press event using keyCode parameter, toggle if necessary + // FOR SOME REASON CTRL-ALT-DELETE DOESN'T WORK??? + if (isToggleable){ + if (isOn){ + LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_DOWN, 0); + } else { + LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_UP, 0); + } + } else { + LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_DOWN, 0); + usleep(50 * 1000); + LiSendKeyboardEvent(keyCodeShort, KEY_ACTION_UP, 0); + } + } +} + - (BOOL)handleMouseButtonEvent:(int)buttonAction forTouches:(NSSet *)touches withEvent:(UIEvent *)event { #if !TARGET_OS_TV if (@available(iOS 13.4, *)) {