Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual terminal support #56

Merged
merged 8 commits into from
Dec 3, 2014
Merged
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
24 changes: 20 additions & 4 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,27 @@ <h3 id="man-pages-search-title">Search Linux Man Pages</h3>
<div id="doc-container" data-bind="html: challengeDoc"></div>
<div id="tty-pane">
<div id="tty-container">
<canvas id="tty" class="screen" width="640" height="384">Terminal</canvas>
<button data-bind="click: function () { ttyFullScreen(!ttyFullScreen()); }" class="btn btn-xs btn-default">
<span data-bind="css: ttyToggleBtnClass"></span>
</button>
<canvas id="tty0" class="screen" width="640" height="384">Terminal1</canvas>
<div class="tty-buttons">
<button data-bind="click: function () { ttyFullScreen(!ttyFullScreen()); }" class="btn btn-xs btn-default">
<span data-bind="css: ttyToggleBtnClass"></span>
</button>
<button data-bind="click: function () { isPrimaryTTY(!isPrimaryTTY()); }" class="btn btn-xs btn-default">
<span data-bind="css: ttySwitchBtnClass"></span>
</button>
</div>
</div>
<div id="tty-container-two">
<canvas id="tty1" class="screen" width="640" height="384">Terminal2</canvas>
<div class="tty-buttons">
<button data-bind="click: function () { ttyFullScreen(!ttyFullScreen()); }" class="btn btn-xs btn-default">
<span data-bind="css: ttyToggleBtnClass"></span>
</button>
<button data-bind="click: function () { isPrimaryTTY(!isPrimaryTTY()); }" class="btn btn-xs btn-default">
<span data-bind="css: ttySwitchBtnClass"></span>
</button>
</div>
</div>
</div>
</div>

Expand Down
Binary file modified app/jor1k_hd_images/hdgcc-mod.bz2
Binary file not shown.
43 changes: 34 additions & 9 deletions app/scripts/jor1k-master.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
window.Jor1kGUI = (function () {
'use strict';

function UARTDev(worker) {
function UARTDev(worker, tty) {
this.ReceiveChar = function (c) {
if (worker.lastMouseDownTarget !== worker.fbcanvas) {
worker.sendToWorker('tty0', c);
worker.sendToWorker(tty, c);
}
};
}

function Jor1kGUI(termId, imageurls, relayURL) {
function Jor1kGUI(termId, termIdTwo, imageurls, relayURL) {
this.urls = imageurls;

this.worker = new Worker('jor1k/js/worker/worker.js');
Expand All @@ -35,6 +35,7 @@ window.Jor1kGUI = (function () {
this.sendToWorker('Reset');
this.sendToWorker('LoadAndStart', this.urls);
this.term.PauseBlink(false);
this.termTwo.PauseBlink(false);
};

this.pause = function (pause) {
Expand All @@ -48,25 +49,34 @@ window.Jor1kGUI = (function () {
this.sendToWorker('execute', 0);
}
this.term.PauseBlink(pause);
this.termTwo.PauseBlink(pause);
};

this.terminalcanvas = document.getElementById(termId);
this.terminalcanvastwo = document.getElementById(termIdTwo);

this.term = new Terminal(24, 80, termId);
this.terminput = new TerminalInput(new UARTDev(this));
this.termTwo = new Terminal(24, 80, termIdTwo);
this.terminput = new TerminalInput(new UARTDev(this, termId));
this.terminputtwo = new TerminalInput(new UARTDev(this, termIdTwo));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the tty parameter to the UARTDev constructors be the literal strings 'tty0' and 'tty1' instead of termId and termIdTwo?
The uart sends the command to the worker which only understands 'tty0' and 'tty1' - it doesn't know about the HTML element ids. It just happens that the element ids are the strings 'tty0' and 'tty1' so everything works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was deliberate on my part, I prefer not to use hardcoded constants and it makes sense if you think of a terminal canvas 'attaching' to a UART for I/O.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point. However, the way I see it is that you are supposed to use the hardcoded constants. The jor1k codebase, sys-runtime.js and live-edit.js all use these string constants to refer to the ttys. These should ideally be global constants/enums or something instead of strings. If you want to use the element ids to refer to the ttys then the ids should be constants - the element ids shouldn't then be passed in as parameters to the Jor1kGUI constructor. What happens if the HTML programmer uses some different ids and passes them to the Jor1kGUI constructor? The very fact that the ids are accepted as params means that we cannot rely on them being the constants we expect them to be. If you want to get rid of the constants entirely, then you'll need to modify jor1k itself or somehow map from the term ids to the constants. This was just my opinion, what do you think?

Anyway, no changes are required, I just merged in your code.


this.ignoreKeys = function () {
//Simpler but not as general, return( document.activeElement.type==="textarea" || document.activeElement.type==='input');
return ((this.lastMouseDownTarget !== this.terminalcanvas));
return ((this.lastMouseDownTarget !== this.terminalcanvas) && (this.lastMouseDownTarget !== this.terminalcanvastwo));
};

var recordTarget = function (event) {
this.lastMouseDownTarget = event.target;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this? Based on your hosted test setup, currently what happens is when you switch to full screen on the first (left) terminal, the focus is set to the second terminal -- hence anything you type is sent to the second (right) terminal.
You need to set focus to the terminal on which the full screen button was clicked.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I forgot to fix that. I'll get around to fixing that.

Compilation is most likely because terminal one is using a different TTY device, I will look into that also

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your latest commit fixed it.

}.bind(this);

// set the focus to the terminal after toggling full screen
// TODO: implement terminal switching full screen
SysViewModel.getInstance().ttyFullScreen.subscribe(function () {
this.lastMouseDownTarget = this.terminalcanvas;
if(SysViewModel.getInstance().isPrimaryTTY()) {
this.lastMouseDownTarget = this.terminalcanvas;
} else {
this.lastMouseDownTarget = this.terminalcanvastwo;
}
}, this);

if(document.addEventListener) {
Expand All @@ -80,23 +90,35 @@ window.Jor1kGUI = (function () {
return true;
}
this.sendToWorker('keypress', {keyCode:event.keyCode, charCode:event.charCode});
return this.terminput.OnKeyPress(event);
if(this.lastMouseDownTarget === this.terminalcanvas) {
return this.terminput.OnKeyPress(event);
} else if(this.lastMouseDownTarget === this.terminalcanvastwo) {
return this.terminputtwo.OnKeyPress(event);
}
}.bind(this);

document.onkeydown = function (event) {
if(this.ignoreKeys()) {
return true;
}
this.sendToWorker('keydown', {keyCode:event.keyCode, charCode:event.charCode});
return this.terminput.OnKeyDown(event);
if(this.lastMouseDownTarget === this.terminalcanvas) {
return this.terminput.OnKeyDown(event);
} else if(this.lastMouseDownTarget === this.terminalcanvastwo) {
return this.terminputtwo.OnKeyDown(event);
}
}.bind(this);

document.onkeyup = function (event) {
if(this.ignoreKeys()) {
return true;
}
this.sendToWorker('keyup', {keyCode:event.keyCode, charCode:event.charCode});
return this.terminput.OnKeyUp(event);
if(this.lastMouseDownTarget === this.terminalcanvas) {
return this.terminput.OnKeyUp(event);
} else if(this.lastMouseDownTarget === this.terminalcanvastwo) {
return this.terminputtwo.OnKeyUp(event);
}
}.bind(this);

this.ethernet = new Ethernet(relayURL);
Expand Down Expand Up @@ -130,6 +152,9 @@ window.Jor1kGUI = (function () {
case 'tty0':
this.term.PutChar(e.data.data);
break;
case 'tty1':
this.termTwo.PutChar(e.data.data);
break;
case 'Stop':
console.log('Received stop signal');
this.stop = true;
Expand Down
2 changes: 1 addition & 1 deletion app/scripts/live-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ window.LiveEdit = (function () {
// null if cancelled
// result = { 'exitcode':gcc_exit_code, 'stats':stats,'annotations':annotations,'gcc_ouput':gcc_output}

this.runtime.sendKeys('clear\n');
this.runtime.sendKeys('tty0', 'clear\n');

var aceAnnotations = [], gccOptsErrors = [];
result.annotations.forEach(function (annotation) {
Expand Down
28 changes: 17 additions & 11 deletions app/scripts/sys-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,30 @@ window.SysRuntime = (function () {
}.bind(this);

var onBootFinished = function (completed) {
if (completed && this.bootFinished) {
this.notifyListeners('ready', true);
}
this.bootFinished = completed;
}.bind(this);

var onTTYLogin1 = function (completed) {
if (completed) {
this.notifyListeners('ready', true);
this.sendKeys('tty0', 'stty -clocal crtscts -ixoff\necho boot2ready-$?\n', 'boot2ready-0', onBootFinished);
}
}.bind(this);

var onTTYLogin = function (completed) {
var onTTYLogin2 = function (completed) {
if (completed) {
this.sendKeys('\nstty -clocal crtscts -ixoff\ngcc hello.c;echo boot2ready-$?;rm a.out\n', 'boot2ready-0', onBootFinished);
this.sendKeys('tty1', 'stty -clocal crtscts -ixoff\necho boot2ready-$?\n', 'boot2ready-0', onBootFinished);
}
}.bind(this);

// Wait for tty to be ready
document.addEventListener('jor1k_terminal_put_char', this.putCharListener, false);

this.jor1kgui = new Jor1kGUI('tty', ['../../bin/vmlinux.bin.bz2', '../../../jor1k_hd_images/hdgcc-mod.bz2'], '');
this.sendKeys('', 'root login on \'console\'', onTTYLogin);
this.jor1kgui = new Jor1kGUI('tty0', 'tty1', ['../../bin/vmlinux.bin.bz2', '../../../jor1k_hd_images/hdgcc-mod.bz2'], '');
this.sendKeys('tty0', '', 'root login on \'ttyS0\'', onTTYLogin1);
this.sendKeys('tty1', '', 'root login on \'ttyS1\'', onTTYLogin2);
return this;
}

Expand Down Expand Up @@ -96,15 +103,15 @@ window.SysRuntime = (function () {

}.bind(this);

this.sendKeys('\x03\ncd ~;rm program.c program 2>/dev/null\n');
this.sendKeys('tty0', '\x03\ncd ~;rm program.c program 2>/dev/null\n');

this.sendTextFile('program.c', code);

var cmd = 'echo \\#\\#\\#GCC_COMPILE\\#\\#\\#;clear;gcc ' + gccOptions +
' program.c -o program; echo GCC_EXIT_CODE: $?; echo \\#\\#\\#GCC_COMPILE_FINISHED\\#\\#\\#' +
this.compileTicket + '.;clear\n';

this.expecting = this.sendKeys(cmd, 'GCC_COMPILE_FINISHED###' + this.compileTicket + '.', compileCb);
this.expecting = this.sendKeys('tty0', cmd, 'GCC_COMPILE_FINISHED###' + this.compileTicket + '.', compileCb);

return this.compileTicket;
};
Expand Down Expand Up @@ -149,11 +156,11 @@ window.SysRuntime = (function () {
}
cmdargs = cmdargs.replace('\\', '\\\\').replace('\n','\\n');
// Don't \x03 ; it interrupts the clear command
this.sendKeys('\n' + filename + ' ' + cmdargs + '\n');
this.sendKeys('tty0', '\n' + filename + ' ' + cmdargs + '\n');
};

SysRuntime.prototype.sendTextFile = function(filename, contents) {
this.sendKeys('\nstty raw\ndd ibs=1 of=' + filename + ' count=' + contents.length + '\n'+ contents + '\nstty -raw\n');
this.sendKeys('tty0', '\nstty raw\ndd ibs=1 of=' + filename + ' count=' + contents.length + '\n'+ contents + '\nstty -raw\n');
};

// Used to broadcast 'putchar' and 'ready' events
Expand Down Expand Up @@ -184,9 +191,8 @@ window.SysRuntime = (function () {
}
};

SysRuntime.prototype.sendKeys = function (text, expect, success, cancel) {
SysRuntime.prototype.sendKeys = function (tty, text, expect, success, cancel) {
/* jshint bitwise: false */
var tty = false ? 'tty1' : 'tty0';
var expectResult = null;
this.jor1kgui.pause(false);

Expand Down
18 changes: 18 additions & 0 deletions app/scripts/sys-view-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,36 @@ window.SysViewModel = (function () {
self.aceFontSize = ko.observable();

self.ttyContainer = $('#tty-container');
self.ttyContainerTwo = $('#tty-container-two');
self.currentTTYContainer = self.ttyContainer;
self.ttyFullScreen = ko.observable(false);
self.ttyFullScreen.subscribe(function (newTTYFullScreenStatus) {
if (newTTYFullScreenStatus) {
self.ttyContainer.appendTo('body');
self.ttyContainerTwo.appendTo('body');
} else {
self.ttyContainer.appendTo('#tty-pane');
self.ttyContainerTwo.appendTo('#tty-pane');
}
});
self.isPrimaryTTY = ko.observable(true);
self.isPrimaryTTY.subscribe(function (newFrontTTY) {
self.currentTTYContainer.hide();
if (newFrontTTY) {
self.currentTTYContainer = self.ttyContainer;
} else {
self.currentTTYContainer = self.ttyContainerTwo;
}
self.currentTTYContainer.show();
});
self.ttyToggleBtnClass = ko.pureComputed(function () {
return 'glyphicon ' + (self.ttyFullScreen() ? 'glyphicon-resize-small' : 'glyphicon-resize-full');
});

self.ttySwitchBtnClass = ko.pureComputed(function () {
return 'glyphicon ' + (self.isPrimaryTTY() ? 'glyphicon-chevron-right' : 'glyphicon-chevron-left');
});

self.openManPageTabs = ko.observableArray();
self.currentActiveTabIndex = ko.observable(-2);
self.closeManPageTab = function (tab) {
Expand Down
22 changes: 20 additions & 2 deletions app/styles/sysplayground.scss
Original file line number Diff line number Diff line change
Expand Up @@ -140,21 +140,39 @@
z-index: 100;
}

#tty-container-two {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
z-index: 100;
display: none;
}

body {
overflow: hidden;
}

#tty {
#tty0 {
width: 100%;
height: 100%;
}

#tty-container > button {
#tty1 {
width: 100%;
height: 100%;
}

.tty-buttons {
position: absolute;
top: 5px;
right: 5px;
}

.tty-buttons > button {
float: right;
}

.man-page-tab {
width: 100%;
height: 100%;
Expand Down