Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 3 additions & 60 deletions app/elements/browsix-terminal/browsix-terminal.html
Original file line number Diff line number Diff line change
@@ -1,63 +1,6 @@
<link rel="import" href="../../bower_components/polymer-ts/polymer-ts.html">
<link rel="stylesheet" href="../../xterm/xterm.css" />

<dom-module id="browsix-terminal">
<template>
<style>

#output, #input_container, #input {
background: #171a1b;
font-size: 24px;
font-family: 'Hack', 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
color: #eeeeec;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
word-break: break-all;
}

#output, #input_container {
padding-left: 24px;
padding-right: 24px;
}

#output {
padding-top: 24px;
}

#output:empty {
display: none;
}

#input_container {
padding-bottom: 24px;
}

#output:empty + #input_container {
padding-top: 24px;
}

#input {
display: inline;
width: 90%;
background: transparent;
border: 0;
outline: 0;
color: #eeeeec;
font-size: 24px;
font-family: 'Hack', 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

</style>

<div id="output"></div>

<div id="input_container">
$
<input id="input" autofocus/>
</div>

</template>
</dom-module>
<div id="browsix-terminal"></div>

<script type="text/javascript" src="../../xterm/xterm.js"></script>
<script type="text/javascript" src="browsix-terminal.js"></script>
141 changes: 70 additions & 71 deletions app/elements/browsix-terminal/browsix-terminal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/// <reference path="../../../bower_components/polymer-ts/polymer-ts.d.ts"/>
/// <reference path="../../../node_modules/xterm/typings/xterm.d.ts"/>

//import { Terminal } from 'xterm';

interface ExitCallback {
(pid: number, code: number): void;
Expand All @@ -14,98 +16,95 @@ interface Kernel {
kill(pid: number): void;
}

namespace Terminal {
namespace BrowsixTerminal {
'use strict';

const ERROR = 'FLAGRANT SYSTEM ERROR';

@component('browsix-terminal')
class Terminal extends polymer.Base {
@property({type: Object})
kernel: any;

@property({type: String})
ps1: string = '$ ';
class BrowsixTerminal {
kernel: Kernel;
stdin: any;
terminal: Terminal;
line: string = "";
lineidx: number = 0;

constructor(element: HTMLElement) {
this.terminal = new Terminal({
// According to xterm.js docs, unnecessary with pty
"convertEol": true
});
this.terminal.open(element);
this.terminal.on('key', (key: string, ev: KeyboardEvent) => this.keyCallback(key, ev));

constructor() {
super();
(<any>window).Boot(
'XmlHttpRequest',
['index.json', 'fs', true],
(err: any, k: Kernel) => {
if (err) {
console.log(err);
this.$.output.innerHTML = ERROR;
this.terminal.clear();
this.terminal.writeln(ERROR);
throw new Error(err);
}
this.kernel = k;
},
{readOnly: false});
}

attached(): void {
this.$.input.addEventListener('keypress', this.onInput.bind(this));
(<any>document).body.addEventListener('click', this.focus.bind(this));
}

onInput(ev: any): void {
// If key pressed is not Return/Enter, skip
if (ev.keyCode !== 13) return;

let cmd = this.$.input.value;
this.$.output.innerHTML += this.ps1 + cmd + '<br>';
if (cmd === '') {
this.scrollBottom();
return;
}
this.setEditable(false);
let bg = cmd[cmd.length - 1] === '&';
if (bg) {
cmd = cmd.slice(0, -1).trim();
setTimeout(() => { this.setEditable(true); }, 0);
}
let completed = (pid: number, code: number) => {
this.stdin = null;
this.terminal.writeln("'sh' exited with status " + code);
};

let completed = (pid: number, code: number) => {
this.setEditable(true);
this.$.input.value = '';
this.focus();
this.scrollBottom();
};
let onInput = (pid: number, out: string ) => {
this.terminal.write(out);
};

let onInput = (pid: number, out: string) => {
// Replace all LF with HTML breaks
out = out.split('\n').join('<br>');
this.$.output.innerHTML += out;
this.scrollBottom();
};
let onHaveStdin = (stdin: any) => {
this.stdin = stdin;
}

this.kernel.system(cmd, completed, onInput, onInput);
this.kernel.system("sh", completed, onInput, onInput, onHaveStdin);
},
{readOnly: false});
}

@observe('kernel')
kernelChanged(_: Kernel, oldKernel: Kernel): void {
// we expect this to be called once, after
// we've booted the kernel.
if (oldKernel) {
console.log('unexpected kernel change');
return;
keyCallback(key: string, ev: KeyboardEvent): void {
// Newline
if (ev.keyCode == 13) {
this.terminal.writeln("");
this.line += '\n'
if (this.stdin !== null) {
this.stdin.write(new Buffer(this.line), -1, (error: any) => {});
}
this.line = '';
this.lineidx = 0;
// Backspace
} else if (ev.keyCode == 8) {
const previous = this.line.slice(0, this.lineidx - 1);
const rest = this.line.slice(this.lineidx);
this.terminal.write('\b' + rest + ' ' + '\b'.repeat(rest.length + 1));
this.line = previous + rest;
this.lineidx--;
// Up and down arrows
} else if (ev.keyCode == 38 || ev.keyCode == 40) {
// Left arrow
} else if (ev.keyCode == 37) {
this.terminal.write(key);
this.lineidx--;
if (this.lineidx == -1) {
this.lineidx = 0;
}
// Right arrow
} else if (ev.keyCode == 39) {
if (this.lineidx < this.line.length) {
this.lineidx++;
this.terminal.write(key);
}
} else {
this.terminal.write(key);
this.line += key;
this.lineidx++;
}
}

focus(): void {
this.$.input.focus();
}

setEditable(editable: boolean): void {
// Hide input if not editable
this.$.input_container.style.visibility = (editable) ? '' : 'hidden';
}

scrollBottom(): void {
(<any>window).scrollTo(0, document.documentElement.scrollHeight
|| document.body.scrollHeight);
}
}

Terminal.register();
var terminal = new BrowsixTerminal(document.getElementById('browsix-terminal'));
}
6 changes: 5 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,10 @@ function gulp_app_tasks (app_path) {
var fs = gulp.src(['fs/**/*'])
.pipe(gulp.dest('dist/fs'));

return merge(app, bower, elements, vulcanized, swBootstrap, swToolbox, fs)
var xterm = gulp.src(['node_modules/xterm/dist/**/*'])
.pipe(gulp.dest('dist/xterm'));

return merge(app, bower, elements, vulcanized, swBootstrap, swToolbox, fs, xterm)
.pipe($.size({title: 'copy'}));
});

Expand Down Expand Up @@ -523,6 +526,7 @@ gulp.task('serve', ['app:build', 'app:styles', 'app:elements', 'app:images'], fu
'/bower_components': 'bower_components',
'/fs': 'fs',
'/benchfs': 'benchfs',
'/xterm': 'node_modules/xterm/dist',
},
middleware: [],
}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"mocha": "^3.4.2",
"through2": "^2.0.3",
"tslint": "^5.5.0",
"typescript": "^2.4.1",
"typescript": "^3.4.3",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
},
Expand All @@ -67,7 +67,7 @@
"@types/mocha": "^2.2.41",
"child_process": "^1.0.2",
"node-binary-marshal": "^0.4.2",
"term.js": "github:bpowers/term.js"
"xterm": "^3.12.2"
},
"engines": {
"node": ">=4.3.0"
Expand Down
9 changes: 6 additions & 3 deletions src/kernel/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { now } from './ipc';
import { Pipe, PipeFile, isPipe } from './pipe';
import { SocketFile, isSocket } from './socket';
import { DirFile, RegularFile, NullFile, resolve } from './file';
import { ExitCallback, OutputCallback, SyscallContext, SyscallResult,
import { ExitCallback, OutputCallback, StdinCallback, SyscallContext, SyscallResult,
Syscall, ConnectCallback, IKernel, ITask, IFile, Environment } from './types';

import { HTTPParser } from './http_parser';
Expand Down Expand Up @@ -1569,7 +1569,7 @@ export class Kernel implements IKernel {
this.portWaiters[port] = cb;
}

system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback): void {
system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback, onHaveStdin: StdinCallback): void {
let splitParts: string[] = cmd.split(' ');
let parts: string[];
// only check for an = in the first part of a command,
Expand Down Expand Up @@ -1609,9 +1609,12 @@ export class Kernel implements IKernel {
let t = this.tasks[pid];
t.onExit = onExit;

let stdin = <PipeFile>t.files[0];
let stdout = <PipeFile>t.files[1];
let stderr = <PipeFile>t.files[2];

onHaveStdin(stdin);

stdout.addEventListener('write', onStdout);
stderr.addEventListener('write', onStderr);
});
Expand Down Expand Up @@ -1859,7 +1862,7 @@ export class Kernel implements IKernel {
files[i].ref();
}
} else {
files[0] = new NullFile();
files[0] = new PipeFile();
files[1] = new PipeFile();
files[2] = new PipeFile();
}
Expand Down
6 changes: 5 additions & 1 deletion src/kernel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export interface RWCallback {
(err: number, len?: number): void;
}

export interface StdinCallback {
(stdin: IFile): void;
}

export interface SyscallResult {
id: number;
name: string;
Expand All @@ -33,7 +37,7 @@ export interface IKernel {
nCPUs: number;
debug: boolean;

system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback): void;
system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback, onHaveStdin: StdinCallback): void;
exit(task: ITask, code: number): void;
wait(pid: number): void;
doSyscall(syscall: Syscall): void;
Expand Down