diff --git a/app/Base.lproj/Main.storyboard b/app/Base.lproj/Main.storyboard
index c267a753..61f6d854 100644
--- a/app/Base.lproj/Main.storyboard
+++ b/app/Base.lproj/Main.storyboard
@@ -23,7 +23,7 @@
-
+
diff --git a/app/DelayedUITask.h b/app/DelayedUITask.h
new file mode 100644
index 00000000..fd558f5e
--- /dev/null
+++ b/app/DelayedUITask.h
@@ -0,0 +1,15 @@
+//
+// DelayedUITask.h
+// iSH
+//
+// Created by Theodore Dubois on 11/8/17.
+//
+
+#import
+
+@interface DelayedUITask : NSObject
+
+- (instancetype)initWithTarget:(id)target action:(SEL)action;
+- (void)schedule;
+
+@end
diff --git a/app/DelayedUITask.m b/app/DelayedUITask.m
new file mode 100644
index 00000000..378266ae
--- /dev/null
+++ b/app/DelayedUITask.m
@@ -0,0 +1,35 @@
+//
+// DelayedUITask.m
+// iSH
+//
+// Created by Theodore Dubois on 11/8/17.
+//
+
+#import "DelayedUITask.h"
+
+@interface DelayedUITask ()
+
+@property id target;
+@property SEL action;
+@property NSTimer *timer;
+
+@end
+
+@implementation DelayedUITask
+
+- (instancetype)initWithTarget:(id)target action:(SEL)action {
+ if (self = [super init]) {
+ self.target = target;
+ self.action = action;
+ }
+ return self;
+}
+
+- (void)schedule {
+ if (!self.timer.valid) {
+ self.timer = [NSTimer timerWithTimeInterval:1.0/60 target:self.target selector:self.action userInfo:nil repeats:NO];
+ [NSRunLoop.mainRunLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
+ }
+}
+
+@end
diff --git a/app/Info.plist b/app/Info.plist
index 9213f39c..cd7187cb 100644
--- a/app/Info.plist
+++ b/app/Info.plist
@@ -28,6 +28,8 @@
armv7
+ UIStatusBarHidden
+
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
diff --git a/app/Terminal.m b/app/Terminal.m
index 140e02a1..325a3e6e 100644
--- a/app/Terminal.m
+++ b/app/Terminal.m
@@ -6,6 +6,7 @@
//
#import "Terminal.h"
+#import "DelayedUITask.h"
#include "fs/tty.h"
@interface Terminal ()
@@ -14,6 +15,9 @@
@property struct tty *tty;
@property NSMutableData *pendingData;
+@property DelayedUITask *refreshTask;
+@property DelayedUITask *scrollToBottomTask;
+
@end
@implementation Terminal
@@ -25,6 +29,9 @@ static Terminal *terminal = nil;
return terminal;
if (self = [super init]) {
self.pendingData = [NSMutableData new];
+ self.refreshTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(refresh)];
+ self.scrollToBottomTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(scrollToBottom)];
+
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
[config.userContentController addScriptMessageHandler:self name:@"log"];
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
@@ -45,36 +52,40 @@ static Terminal *terminal = nil;
- (size_t)write:(const void *)buf length:(size_t)len {
@synchronized (self) {
[self.pendingData appendData:[NSData dataWithBytes:buf length:len]];
- [self performSelectorOnMainThread:@selector(sendPendingOutput) withObject:nil waitUntilDone:NO];
+ [self.refreshTask schedule];
}
return len;
}
- (void)sendInput:(const char *)buf length:(size_t)len {
tty_input(self.tty, buf, len);
+ [self.scrollToBottomTask schedule];
+}
+
+- (void)scrollToBottom {
+ [self.webView evaluateJavaScript:@"term.scrollToBottom()" completionHandler:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == self.webView && [keyPath isEqualToString:@"loading"] && !self.webView.loading) {
- [self sendPendingOutput];
+ [self.refreshTask schedule];
[self.webView removeObserver:self forKeyPath:@"loading"];
}
}
-- (void)sendPendingOutput {
+- (void)refresh {
+ if (self.webView.loading)
+ return;
+
NSString *str;
@synchronized (self) {
- if (self.webView.loading)
- return;
- if (self.pendingData.length == 0)
- return;
str = [[NSString alloc] initWithData:self.pendingData encoding:NSUTF8StringEncoding];
self.pendingData = [NSMutableData new];
}
- NSError *err;
+
+ NSError *err = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@[str] options:0 error:&err];
- if (err != nil)
- NSLog(@"%@", err);
+ NSAssert(err == nil, @"JSON serialization failed, wtf");
NSString *jsToEvaluate = [NSString stringWithFormat:@"term.write(%@[0])", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]];
[self.webView evaluateJavaScript:jsToEvaluate completionHandler:nil];
}
diff --git a/app/TerminalView.m b/app/TerminalView.m
index c510a14c..b9367e87 100644
--- a/app/TerminalView.m
+++ b/app/TerminalView.m
@@ -21,6 +21,8 @@
WKWebView *webView = terminal.webView;
[webView.configuration.userContentController addScriptMessageHandler:self name:@"focus"];
webView.frame = self.frame;
+ self.opaque = webView.opaque = NO;
+ webView.backgroundColor = UIColor.clearColor;
[self addSubview:webView];
webView.translatesAutoresizingMaskIntoConstraints = NO;
[webView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
diff --git a/app/TerminalViewController.m b/app/TerminalViewController.m
index b68b1380..87eadcbb 100644
--- a/app/TerminalViewController.m
+++ b/app/TerminalViewController.m
@@ -29,7 +29,7 @@
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(keyboardDidSomething:)
- name:UIKeyboardDidShowNotification
+ name:UIKeyboardWillShowNotification
object:nil];
[center addObserver:self
selector:@selector(keyboardDidSomething:)
@@ -42,11 +42,33 @@
object:nil];
}
+- (BOOL)prefersStatusBarHidden {
+ return YES;
+}
+
- (void)keyboardDidSomething:(NSNotification *)notification {
- NSValue *frame = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
- self.bottomConstraint.constant = -frame.CGRectValue.size.height;
- NSLog(@"bottom constant = %f", self.bottomConstraint.constant);
- [self.termView setNeedsUpdateConstraints];
+ if (self.termView.needsUpdateConstraints) {
+ // initial layout hasn't happened yet, so animation is going to look really bad
+ return;
+ }
+
+ CGFloat pad = 0;
+ if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
+ NSValue *frame = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
+ pad = frame.CGRectValue.size.height;
+ }
+ NSLog(@"pad = %f", pad);
+ self.bottomConstraint.constant = -pad;
+ [self.view setNeedsUpdateConstraints];
+ NSNumber *interval = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
+ NSNumber *curve = notification.userInfo[UIKeyboardAnimationCurveUserInfoKey];
+ [UIView animateWithDuration:interval.doubleValue
+ delay:0
+ options:curve.integerValue << 16
+ animations:^{
+ [self.view layoutIfNeeded];
+ }
+ completion:nil];
}
- (void)ishExited:(NSNotification *)notification {
diff --git a/xtermjs/term.css b/xtermjs/term.css
index 0b3e474f..c3615282 100644
--- a/xtermjs/term.css
+++ b/xtermjs/term.css
@@ -5,6 +5,10 @@ html, body, #terminal {
margin: 0;
}
+body {
+ background: transparent;
+}
+
#terminal {
padding: 0 5px;
}