Skip to content

Commit

Permalink
Get xtermjs working
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Nov 1, 2017
1 parent 3b28905 commit 2d038ae
Show file tree
Hide file tree
Showing 13 changed files with 3,518 additions and 40 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ tags.log
.floo
pdfs
cross-*.txt

bundle.js
node_modules
2 changes: 1 addition & 1 deletion app/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ - (int)startThings {
if (err < 0)
return err;

char *program = "/bin/sh";
char *program = "/bin/login";
char *argv[] = {program, NULL};
char *envp[] = {NULL};
err = create_init_process(program, argv, envp);
Expand Down
27 changes: 3 additions & 24 deletions app/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13174"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<customFonts key="customFonts">
<array key="Menlo.ttc">
<string>Menlo-Regular</string>
</array>
</customFonts>
<scenes>
<!--Terminal View Controller-->
<scene sceneID="tne-QT-ifu">
Expand All @@ -21,26 +17,9 @@
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="tvA-LA-mXj">
<rect key="frame" x="0.0" y="20" width="414" height="716"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="tvA-LA-mXj" secondAttribute="bottom" id="J00-vr-pys"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="tvA-LA-mXj" secondAttribute="trailing" id="hTM-Vi-6ll"/>
<constraint firstItem="tvA-LA-mXj" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="pJr-4k-UBB"/>
<constraint firstItem="tvA-LA-mXj" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="yXe-3b-55s"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="textView" destination="tvA-LA-mXj" id="ETB-2F-1gj"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
<customObject id="EOd-v0-bwv" customClass="Terminal"/>
Expand Down
5 changes: 3 additions & 2 deletions app/Terminal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
// Created by Theodore Dubois on 10/18/17.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>

@interface Terminal : NSObject

+ (Terminal *)terminalWithType:(int)type number:(int)number;
- (size_t)write:(const void *)buf length:(size_t)len;

@property (readonly) NSString *content;
@property (readonly) WKWebView *webView;

@end

Expand Down
68 changes: 57 additions & 11 deletions app/Terminal.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,85 @@
#import "Terminal.h"
#include "fs/tty.h"

@interface Terminal ()
@interface Terminal () <WKScriptMessageHandler>

@property NSString *content;
@property WKWebView *webView;
@property struct tty *tty;
@property NSMutableData *pendingData;

@end

@implementation Terminal

static Terminal *terminal = nil;

- (instancetype)init {
if (terminal)
return terminal;
if (self = [super init]) {
self.content = @"";
self.pendingData = [NSMutableData new];
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
for (NSString *name in @[@"sendInput", @"log"]) {
[config.userContentController addScriptMessageHandler:self name:name];
}
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
[self.webView loadRequest:
[NSURLRequest requestWithURL:
[NSBundle.mainBundle URLForResource:@"term" withExtension:@"html"]]];
[self.webView addObserver:self forKeyPath:@"loading" options:0 context:NULL];
terminal = self;
}
return self;
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"log"]) {
NSLog(@"%@", message.body);
} else if ([message.name isEqualToString:@"sendInput"]) {
NSString *input = message.body;
NSData *data = [input dataUsingEncoding:NSUTF8StringEncoding];
tty_input(self.tty, data.bytes, data.length);
}
}

+ (Terminal *)terminalWithType:(int)type number:(int)number {
// there's only one terminal currently
static Terminal *terminal = nil;
if (terminal == nil)
terminal = [Terminal new];
return terminal;
return [Terminal new];
}

- (size_t)write:(const void *)buf length:(size_t)len {
NSString *str = [[NSString alloc] initWithBytes:buf length:len encoding:NSUTF8StringEncoding];
self.content = [self.content stringByAppendingString:str];
[self.pendingData appendData:[NSData dataWithBytes:buf length:len]];
[self performSelectorOnMainThread:@selector(sendPendingOutput) withObject:nil waitUntilDone:NO];
return len;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (object == self.webView && [keyPath isEqualToString:@"loading"] && !self.webView.loading) {
[self sendPendingOutput];
[self.webView removeObserver:self forKeyPath:@"loading"];
}
}

- (void)sendPendingOutput {
if (self.webView.loading)
return;
if (self.pendingData.length == 0)
return;
NSString *str = [[NSString alloc] initWithData:self.pendingData encoding:NSUTF8StringEncoding];
self.pendingData = [NSMutableData new];
NSError *err;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@[str] options:0 error:&err];
if (err != nil)
NSLog(@"%@", err);
NSString *jsToEvaluate = [NSString stringWithFormat:@"output(%@[0])", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]];
[self.webView evaluateJavaScript:jsToEvaluate completionHandler:nil];
}

@end

static int ios_tty_open(struct tty *tty) {
tty->data = (void *) CFBridgingRetain([Terminal terminalWithType:tty->type number:tty->num]);
Terminal *terminal = [Terminal terminalWithType:tty->type number:tty->num];
terminal.tty = tty;
tty->data = (void *) CFBridgingRetain(terminal);
return 0;
}

Expand Down
9 changes: 7 additions & 2 deletions app/TerminalViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
@interface TerminalViewController ()

@property Terminal *terminal;
@property (weak, nonatomic) IBOutlet UITextView *textView;

@end

Expand All @@ -21,7 +20,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
[self.textView performSelectorOnMainThread:@selector(setText:) withObject:self.terminal.content waitUntilDone:NO];
//[self.textView performSelectorOnMainThread:@selector(setText:) withObject:self.terminal.content waitUntilDone:NO];
}

- (void)viewDidLoad {
Expand All @@ -31,6 +30,12 @@ - (void)viewDidLoad {
forKeyPath:@"content"
options:NSKeyValueObservingOptionInitial
context:NULL];
[self.terminal.webView.configuration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
UIView *termView = self.terminal.webView;
termView.frame = self.view.frame;
[self.view addSubview:termView];
termView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
termView.translatesAutoresizingMaskIntoConstraints = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(ishExited:)
name:ISHExitedNotification
Expand Down
43 changes: 43 additions & 0 deletions iSH.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
/* Begin PBXBuildFile section */
BB0FC5921F980A6C00803272 /* Terminal.m in Sources */ = {isa = PBXBuildFile; fileRef = BB0FC5911F980A6B00803272 /* Terminal.m */; };
BB18B2871F97F6D00059FCD8 /* libsoftfloat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB18B2741F97F1C40059FCD8 /* libsoftfloat.a */; };
BB4A53AA1FAA496700A72ACE /* bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53A81FAA496700A72ACE /* bundle.js */; };
BB4A53AB1FAA496700A72ACE /* term.html in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53A91FAA496700A72ACE /* term.html */; };
BB4A53B01FAA787A00A72ACE /* term.css in Resources */ = {isa = PBXBuildFile; fileRef = BB4A53AF1FAA787900A72ACE /* term.css */; };
BB623CF91FA7C68800932047 /* alpine in Resources */ = {isa = PBXBuildFile; fileRef = BBF124901FA7C3100088FB50 /* alpine */; };
BB792B551F96D90D00FFB7A4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B541F96D90D00FFB7A4 /* AppDelegate.m */; };
BB792B581F96D90D00FFB7A4 /* TerminalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B571F96D90D00FFB7A4 /* TerminalViewController.m */; };
Expand Down Expand Up @@ -109,6 +112,12 @@
BB0FC5911F980A6B00803272 /* Terminal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Terminal.m; sourceTree = "<group>"; };
BB18B2741F97F1C40059FCD8 /* libsoftfloat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsoftfloat.a; sourceTree = BUILT_PRODUCTS_DIR; };
BB18B27E1F97F24D0059FCD8 /* xcode-meson.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "xcode-meson.sh"; sourceTree = "<group>"; };
BB4A539A1FAA411D00A72ACE /* package.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = package.json; sourceTree = "<group>"; };
BB4A539C1FAA490C00A72ACE /* term.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = term.js; sourceTree = "<group>"; };
BB4A539D1FAA490C00A72ACE /* webpack.config.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = webpack.config.js; sourceTree = "<group>"; };
BB4A53A81FAA496700A72ACE /* bundle.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = bundle.js; sourceTree = "<group>"; };
BB4A53A91FAA496700A72ACE /* term.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = term.html; sourceTree = "<group>"; };
BB4A53AF1FAA787900A72ACE /* term.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = term.css; sourceTree = "<group>"; };
BB792B501F96D90D00FFB7A4 /* iSH.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iSH.app; sourceTree = BUILT_PRODUCTS_DIR; };
BB792B531F96D90D00FFB7A4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
BB792B541F96D90D00FFB7A4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -254,10 +263,24 @@
name = "most of the code";
sourceTree = "<group>";
};
BB4A53991FAA40FD00A72ACE /* xtermjs */ = {
isa = PBXGroup;
children = (
BB4A539C1FAA490C00A72ACE /* term.js */,
BB4A53AF1FAA787900A72ACE /* term.css */,
BB4A53A91FAA496700A72ACE /* term.html */,
BB4A539D1FAA490C00A72ACE /* webpack.config.js */,
BB4A539A1FAA411D00A72ACE /* package.json */,
BB4A53A81FAA496700A72ACE /* bundle.js */,
);
path = xtermjs;
sourceTree = "<group>";
};
BB792B451F96D8E000FFB7A4 = {
isa = PBXGroup;
children = (
BB792B521F96D90D00FFB7A4 /* app */,
BB4A53991FAA40FD00A72ACE /* xtermjs */,
BB4A53931FAA393B00A72ACE /* most of the code */,
BB18B27F1F97F2590059FCD8 /* Scripts */,
BB792B511F96D90D00FFB7A4 /* Products */,
Expand Down Expand Up @@ -327,6 +350,7 @@
BB792B4D1F96D90D00FFB7A4 /* Frameworks */,
BBF1248B1FA7BF530088FB50 /* Download Alpine */,
BBF1248A1FA7BDBA0088FB50 /* Create Alpine Filesystem */,
BB4A53AC1FAA49CA00A72ACE /* Run Webpack */,
BB792B4E1F96D90D00FFB7A4 /* Resources */,
);
buildRules = (
Expand Down Expand Up @@ -409,7 +433,10 @@
files = (
BB623CF91FA7C68800932047 /* alpine in Resources */,
BB792B601F96D90D00FFB7A4 /* LaunchScreen.storyboard in Resources */,
BB4A53AB1FAA496700A72ACE /* term.html in Resources */,
BB4A53AA1FAA496700A72ACE /* bundle.js in Resources */,
BB792B5D1F96D90D00FFB7A4 /* Assets.xcassets in Resources */,
BB4A53B01FAA787A00A72ACE /* term.css in Resources */,
BB792B5B1F96D90D00FFB7A4 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -434,6 +461,22 @@
shellScript = "cd $TARGET_BUILD_DIR\n$SRCROOT/xcode-meson.sh\nninja subprojects/softfloat/libsoftfloat.a\ncp subprojects/softfloat/libsoftfloat.a .";
showEnvVarsInLog = 0;
};
BB4A53AC1FAA49CA00A72ACE /* Run Webpack */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"$(SRCROOT)/xtermjs",
);
name = "Run Webpack";
outputPaths = (
"$(SRCROOT)/xtermjs/bundle.js",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/sh -e";
shellScript = "cd $SRCROOT/xtermjs\nwebpack";
};
BB792B7F1F96E35800FFB7A4 /* Build using Meson */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down
14 changes: 14 additions & 0 deletions xtermjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "xtermjs-glue",
"dependencies": {
"xterm": "^2.9.2"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"css-loader": "^0.28.7",
"style-loader": "^0.19.0",
"webpack": "^3.8.1"
}
}
31 changes: 31 additions & 0 deletions xtermjs/term.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
html, body, #terminal {
position: absolute;
width: 100%;
height: 100%;
margin: 0;
}

#terminal {
margin: 0 5px;
}

.terminal {
background: transparent;
color: black;
font-family: "Menlo";
font-size: 12px;
}

.terminal .xterm-viewport {
background: transparent;
}

.terminal.focus:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor {
background-color: black;
color: #000;
}
.terminal:not(.focus) .terminal-cursor {
outline: 1px solid black;
outline-offset: -1px;
background-color: transparent;
}
3 changes: 3 additions & 0 deletions xtermjs/term.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!doctype html>
<div id="terminal"></div>
<script src="bundle.js"></script>
17 changes: 17 additions & 0 deletions xtermjs/term.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Terminal from 'xterm';
Terminal.loadAddon('fit');
import 'xterm/dist/xterm.css';
import './term.css';

const term = new Terminal();
term.open(document.getElementById('terminal'))
term.fit();

term.on('data', function(data) {
console.log(data);
window.webkit.messageHandlers.sendInput.postMessage(data);
});

window.output = function(data) {
term.write(data);
};
28 changes: 28 additions & 0 deletions xtermjs/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const path = require('path');
const webpack = require('webpack');

module.exports = {
entry: './term.js',
output: {
filename: 'bundle.js',
path: __dirname,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {presets: ['env']},
},
// for xterm
{
test: /\.css$/,
loader: 'style-loader!css-loader',
},
],
},
plugins: [
new webpack.IgnorePlugin(/.*\.js\.map$/i),
],
};
Loading

0 comments on commit 2d038ae

Please sign in to comment.