This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-website.git
The following commit(s) were added to refs/heads/master by this push:
new 69485786 Add NuttX Online Demo
69485786 is described below
commit 69485786e52ff306670ab74f7df988cf8658ee69
Author: Lee Lup Yuen <[email protected]>
AuthorDate: Tue Jan 23 08:40:36 2024 +0000
Add NuttX Online Demo
This PR adds the NuttX Online Demo based on [TinyEMU 64-bit RISC-V
Emulator](https://github.com/fernandotcl/TinyEMU) and WebAssembly.
The [TinyEMU Port of
NuttX](https://github.com/lupyuen2/wip-pinephone-nuttx/pull/54) shall be
upstreamed later to NuttX Mainline. This will allow the Online Demo to be
refreshed for new releases of NuttX. [More about NuttX for
TinyEMU](https://lupyuen.github.io/articles/tinyemu)
Modified Files:
`index.md`: Add link to Demo Page: "Try the online demo here"
`_includes/themes/apache/default.html`: Add Demo CSS
New Files:
`demo.md`: New Demo Page with NuttX in TinyEMU Emulator
`demo/nuttx.bin`: NuttX Image compiled from the upcoming [TinyEMU Port of
NuttX](https://github.com/lupyuen2/wip-pinephone-nuttx/pull/54) (64-bit
RISC-V). Compiled with [these
steps](https://github.com/lupyuen2/wip-pinephone-nuttx/releases/tag/tinyemu4-1)
to produce these [Build
Outputs](https://github.com/lupyuen2/wip-pinephone-nuttx/releases/tag/tinyemu4-1).
`demo/*.js, *.wasm, *.cfg, *.png`: Demo Files from
[TinyEMU](https://github.com/fernandotcl/TinyEMU) project, based on [MIT
Licence](https://github.com/fernandotcl/TinyEMU/blob/master/MIT-LICENSE.txt).
`assets/themes/apache/css/demo.css`: Demo CSS from
[TinyEMU](https://github.com/fernandotcl/TinyEMU) project, based on [MIT
Licence](https://github.com/fernandotcl/TinyEMU/blob/master/MIT-LICENSE.txt).
`_includes/themes/apache/demo.html`: New Theme for Demo Page
`_layouts/demo.html`: New Layout for Demo Page
---
_includes/themes/apache/default.html | 1 +
_includes/themes/apache/demo.html | 22 +
_layouts/demo.html | 5 +
assets/themes/apache/css/demo.css | 105 +++
index.md => demo.md | 19 +-
demo/images/bg-scrollbar-thumb-y.png | Bin 0 -> 2888 bytes
demo/images/bg-scrollbar-track-y.png | Bin 0 -> 960 bytes
demo/images/bg-scrollbar-trackend-y.png | Bin 0 -> 211 bytes
demo/images/upload-icon.png | Bin 0 -> 530 bytes
demo/jslinux.js | 667 ++++++++++++++
demo/nuttx.bin | Bin 0 -> 246848 bytes
demo/riscvemu64-wasm.js | 4 +
demo/riscvemu64-wasm.wasm | Bin 0 -> 221309 bytes
demo/root-riscv64.cfg | 7 +
demo/term.js | 1460 +++++++++++++++++++++++++++++++
index.md | 5 +
16 files changed, 2280 insertions(+), 15 deletions(-)
diff --git a/_includes/themes/apache/default.html
b/_includes/themes/apache/default.html
index bfd98222..8a5356dc 100644
--- a/_includes/themes/apache/default.html
+++ b/_includes/themes/apache/default.html
@@ -18,6 +18,7 @@
<link href="{{ site.baseurl
}}/assets/themes/apache/bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="{{ site.baseurl }}/assets/themes/apache/css/style.css?body=1"
rel="stylesheet" type="text/css">
<link href="{{ site.baseurl }}/assets/themes/apache/css/syntax.css"
rel="stylesheet" type="text/css" media="screen" />
+ <link href="{{ site.baseurl }}/assets/themes/apache/css/demo.css"
rel="stylesheet" type="text/css"/>
<!-- Le fav and touch icons -->
<!-- Update these with your own images
<link rel="shortcut icon" href="images/favicon.ico">
diff --git a/_includes/themes/apache/demo.html
b/_includes/themes/apache/demo.html
new file mode 100644
index 00000000..53836b0b
--- /dev/null
+++ b/_includes/themes/apache/demo.html
@@ -0,0 +1,22 @@
+<!--<div class="hero-unit {{ page.title | lowercase }}">
+ <h1>{% if page.tagline %} <small>{{ page.tagline }}</small>{% endif %}</h1>
+</div>
+-->
+
+<div class="row">
+ <div class="col-md-12">
+ {{ content }}
+
+ <div id="term_wrap">
+ <div id="term_container">
+ </div>
+ <div id="term_bar">
+ <progress id="net_progress">
+ </progress>
+ </div>
+ </div>
+
+ </div>
+</div>
+<script type="text/javascript" src="/demo/term.js"></script>
+<script type="text/javascript" src="/demo/jslinux.js"></script>
diff --git a/_layouts/demo.html b/_layouts/demo.html
new file mode 100644
index 00000000..16ea956d
--- /dev/null
+++ b/_layouts/demo.html
@@ -0,0 +1,5 @@
+---
+layout: default
+---
+{% include JB/setup %}
+{% include themes/apache/demo.html %}
diff --git a/assets/themes/apache/css/demo.css
b/assets/themes/apache/css/demo.css
new file mode 100644
index 00000000..5010d414
--- /dev/null
+++ b/assets/themes/apache/css/demo.css
@@ -0,0 +1,105 @@
+#os_table {
+ border: 1px solid;
+ border-collapse: collapse;
+ margin: 20px;
+}
+
+#os_table td,#os_table th, #os_table tr {
+ border: 1px solid;
+ padding: 6px;
+}
+
+.os_comment {
+ font-size: 12px;
+}
+
+#copyright {
+ font-size: 10px;
+}
+
+/* for the terminal */
+#term_wrap {
+ margin: 20px;
+ resize: both;
+ overflow: hidden;
+}
+
+.term {
+ font-family: monospace,courier,fixed,swiss,sans-serif;
+ font-weight: normal;
+ font-variant-ligatures: none;
+ color: #f0f0f0;
+ background: #000000;
+ line-height: normal;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.term_content a {
+ color: inherit;
+ text-decoration: none;
+}
+
+.term_content a:hover {
+ color: inherit;
+ text-decoration: underline;
+}
+
+.term_cursor {
+ color: #000000;
+ background: #00ff00;
+}
+
+.term_char_size {
+ display: inline-block;
+ visibility: hidden;
+ position: absolute;
+ top: 0px;
+ left: -1000px;
+ padding: 0px;
+}
+
+.term_textarea {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 0px;
+ height: 0px;
+ padding: 0px;
+ border: 0px;
+ margin: 0px;
+ opacity: 0;
+ resize: none;
+}
+
+.term_scrollbar { background: transparent
url(/demo/images/bg-scrollbar-track-y.png) no-repeat 0 0; position: relative;
background-position: 0 0; float: right; height: 100%; }
+.term_track { background: transparent
url(/demo/images/bg-scrollbar-trackend-y.png) no-repeat 0 100%; height: 100%;
width:13px; position: relative; padding: 0 1px; }
+.term_thumb { background: transparent
url(/demo/images/bg-scrollbar-thumb-y.png) no-repeat 50% 100%; height: 20px;
width: 25px; cursor: pointer; overflow: hidden; position: absolute; top: 0;
left: -5px; }
+.term_thumb .term_end { background: transparent
url(/demo/images/bg-scrollbar-thumb-y.png) no-repeat 50% 0; overflow: hidden;
height: 5px; width: 25px; }
+.noSelect { user-select: none; -o-user-select: none; -moz-user-select: none;
-khtml-user-select: none; -webkit-user-select: none; }
+
+#keyboard-icon {
+ margin-left: 5px
+ margin-right: 5px;
+}
+
+/* file import */
+#files {
+ visibility: hidden;
+ width:1px;
+ height:1px;
+ padding: 0px;
+ margin: 0px;
+ border: 0px;
+}
+
+label {
+ cursor: pointer;
+ margin-left: 5px;
+ margin-right: 5px;
+}
+
+#net_progress {
+ visibility: hidden;
+ width: 80px;
+}
\ No newline at end of file
diff --git a/index.md b/demo.md
similarity index 58%
copy from index.md
copy to demo.md
index 907a41e8..55fbea78 100644
--- a/index.md
+++ b/demo.md
@@ -1,6 +1,6 @@
---
-layout: page
-title: Home
+layout: demo
+title: Demo
tagline: Apache Project !
---
@@ -25,17 +25,6 @@ limitations under the License.
{% include JB/setup %}
-## Apache NuttX
+## NuttX Online Demo
-NuttX is a real-time operating system (RTOS) with an emphasis on standards
-compliance and small footprint. Scalable from 8-bit to 64-bit microcontroller
-environments, the primary governing standards in NuttX are Posix and ANSI
-standards. Additional standard APIs from Unix and other common RTOS's (such as
-VxWorks) are adopted for functionality not available under these standards, or
-for functionality that is not appropriate for deeply-embedded environments
(such
-as fork()).
-
-
-## Documentation
-
-Extensive documentation can be found [here]({{ site.baseurl }}/docs/latest).
+Enter `help` to see the available commands.
diff --git a/demo/images/bg-scrollbar-thumb-y.png
b/demo/images/bg-scrollbar-thumb-y.png
new file mode 100644
index 00000000..2ce41f2b
Binary files /dev/null and b/demo/images/bg-scrollbar-thumb-y.png differ
diff --git a/demo/images/bg-scrollbar-track-y.png
b/demo/images/bg-scrollbar-track-y.png
new file mode 100644
index 00000000..2636c54a
Binary files /dev/null and b/demo/images/bg-scrollbar-track-y.png differ
diff --git a/demo/images/bg-scrollbar-trackend-y.png
b/demo/images/bg-scrollbar-trackend-y.png
new file mode 100644
index 00000000..16d740c2
Binary files /dev/null and b/demo/images/bg-scrollbar-trackend-y.png differ
diff --git a/demo/images/upload-icon.png b/demo/images/upload-icon.png
new file mode 100644
index 00000000..0efc325f
Binary files /dev/null and b/demo/images/upload-icon.png differ
diff --git a/demo/jslinux.js b/demo/jslinux.js
new file mode 100644
index 00000000..d800f8b4
--- /dev/null
+++ b/demo/jslinux.js
@@ -0,0 +1,667 @@
+/*
+ * JS Linux main
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+"use strict";
+
+var term, console_write1, console_resize_event;
+var graphic_display, display_key_event, display_mouse_event;
+var net_state, net_write_packet, net_set_carrier;
+var display_wheel_event;
+var fs_import_file;
+var Module = {};
+var downloading_timer_pending = false;
+var downloading_timer;
+
+function on_update_file(f)
+{
+ var f, reader;
+ reader = new FileReader();
+ reader.onload = function (ev) {
+ var buf, buf_addr, buf_len;
+ buf = new Uint8Array(reader.result);
+ buf_len = buf.length;
+ buf_addr = _malloc(buf_len);
+ HEAPU8.set(buf, buf_addr);
+ /* the buffer is freed by the function */
+ fs_import_file(f.name, buf_addr, buf_len);
+ };
+ reader.readAsArrayBuffer(f);
+}
+
+function on_update_files(files)
+{
+ var i, n;
+ n = files.length;
+ for(i = 0; i < n; i++) {
+ on_update_file(files[i]);
+ }
+}
+
+function term_handler(str)
+{
+ var i;
+ for(i = 0; i < str.length; i++) {
+ console_write1(str.charCodeAt(i));
+ }
+}
+
+function downloading_timer_cb()
+{
+ var el = document.getElementById("net_progress");
+ el.style.visibility = "hidden";
+ downloading_timer_pending = false;
+}
+
+function update_downloading(flag)
+{
+ var el;
+ if (flag) {
+ if (downloading_timer_pending) {
+ clearTimeout(downloading_timer);
+ downloading_timer_pending = false;
+ } else {
+ el = document.getElementById("net_progress");
+ el.style.visibility = "visible";
+ }
+ } else {
+ downloading_timer_pending = true;
+ downloading_timer = setTimeout(downloading_timer_cb, 500);
+ }
+}
+
+function get_params()
+{
+ var url, query_str, p, tab, i, params, tab2;
+ query_str = window.location.href;
+ p = query_str.indexOf("?");
+ if (p < 0)
+ return {};
+ query_str = query_str.substr(p + 1);
+ tab = query_str.split("&");
+ params = {};
+ for(i = 0; i < tab.length; i++) {
+ tab2 = tab[i].split("=");
+ params[decodeURIComponent(tab2[0])] = decodeURIComponent(tab2[1]);
+ }
+ return params;
+}
+
+function get_absolute_url(fname)
+{
+ var path, p;
+
+ if (fname.indexOf(":") >= 0)
+ return fname;
+ path = window.location.pathname;
+ p = path.lastIndexOf("/");
+ if (p < 0)
+ return fname;
+ return window.location.origin + path.slice(0, p + 1) + fname;
+}
+
+function GraphicDisplay(parent_el, width, height)
+{
+ this.width = width;
+ this.height = height;
+
+ this.canvas_el = document.createElement("canvas");
+ this.canvas_el.width = width; /* logical width */
+ this.canvas_el.height = height; /* logical width */
+ /* displayed size */
+ this.canvas_el.style.width = width + "px";
+ this.canvas_el.style.height = height + "px";
+ this.canvas_el.style.cursor = "none";
+
+ parent_el.appendChild(this.canvas_el);
+
+ this.ctx = this.canvas_el.getContext("2d");
+ /* clear the display */
+ this.ctx.fillStyle = "#000000";
+ this.ctx.fillRect(0, 0, width, height);
+
+ this.image = this.ctx.createImageData(width, height);
+
+ this.key_pressed = new Uint8Array(128);
+
+ document.addEventListener("keydown",
+ this.keyDownHandler.bind(this), false);
+ document.addEventListener("keyup",
+ this.keyUpHandler.bind(this), false);
+ document.addEventListener("blur",
+ this.blurHandler.bind(this), false);
+
+ this.canvas_el.onmousedown = this.mouseMoveHandler.bind(this);
+ this.canvas_el.onmouseup = this.mouseMoveHandler.bind(this);
+ this.canvas_el.onmousemove = this.mouseMoveHandler.bind(this);
+ this.canvas_el.oncontextmenu = this.onContextMenuHandler.bind(this);
+ this.canvas_el.onwheel = this.wheelHandler.bind(this);
+}
+
+GraphicDisplay.code_to_input_map = {
+ "Escape": 0x01,
+ "Digit1": 0x02,
+ "Digit2": 0x03,
+ "Digit3": 0x04,
+ "Digit4": 0x05,
+ "Digit5": 0x06,
+ "Digit6": 0x07,
+ "Digit7": 0x08,
+ "Digit8": 0x09,
+ "Digit9": 0x0a,
+ "Digit0": 0x0b,
+ "Minus": 0x0c,
+ "Equal": 0x0d,
+ "Backspace": 0x0e,
+ "Tab": 0x0f,
+ "KeyQ": 0x10,
+ "KeyW": 0x11,
+ "KeyE": 0x12,
+ "KeyR": 0x13,
+ "KeyT": 0x14,
+ "KeyY": 0x15,
+ "KeyU": 0x16,
+ "KeyI": 0x17,
+ "KeyO": 0x18,
+ "KeyP": 0x19,
+ "BracketLeft": 0x1a,
+ "BracketRight": 0x1b,
+ "Enter": 0x1c,
+ "ControlLeft": 0x1d,
+ "KeyA": 0x1e,
+ "KeyS": 0x1f,
+ "KeyD": 0x20,
+ "KeyF": 0x21,
+ "KeyG": 0x22,
+ "KeyH": 0x23,
+ "KeyJ": 0x24,
+ "KeyK": 0x25,
+ "KeyL": 0x26,
+ "Semicolon": 0x27,
+ "Quote": 0x28,
+ "Backquote": 0x29,
+ "ShiftLeft": 0x2a,
+ "Backslash": 0x2b,
+ "KeyZ": 0x2c,
+ "KeyX": 0x2d,
+ "KeyC": 0x2e,
+ "KeyV": 0x2f,
+ "KeyB": 0x30,
+ "KeyN": 0x31,
+ "KeyM": 0x32,
+ "Comma": 0x33,
+ "Period": 0x34,
+ "Slash": 0x35,
+ "ShiftRight": 0x36,
+ "NumpadMultiply": 0x37,
+ "AltLeft": 0x38,
+ "Space": 0x39,
+ "CapsLock": 0x3a,
+ "F1": 0x3b,
+ "F2": 0x3c,
+ "F3": 0x3d,
+ "F4": 0x3e,
+ "F5": 0x3f,
+ "F6": 0x40,
+ "F7": 0x41,
+ "F8": 0x42,
+ "F9": 0x43,
+ "F10": 0x44,
+ "NumLock": 0x45,
+ "ScrollLock": 0x46,
+ "Numpad7": 0x47,
+ "Numpad8": 0x48,
+ "Numpad9": 0x49,
+ "NumpadSubtract": 0x4a,
+ "Numpad4": 0x4b,
+ "Numpad5": 0x4c,
+ "Numpad6": 0x4d,
+ "NumpadAdd": 0x4e,
+ "Numpad1": 0x4f,
+ "Numpad2": 0x50,
+ "Numpad3": 0x51,
+ "Numpad0": 0x52,
+ "NumpadDecimal": 0x53,
+ "IntlBackslash": 0x56,
+ "F11": 0x57,
+ "F12": 0x58,
+
+ "NumpadEnter": 96,
+ "ControlRight": 97,
+ "NumpadDivide": 98,
+ "AltRight": 100,
+ "Home": 102,
+ "ArrowUp": 103,
+ "PageUp": 104,
+ "ArrowLeft": 105,
+ "ArrowRight": 106,
+ "End": 107,
+ "ArrowDown": 108,
+ "PageDown": 109,
+ "Insert": 110,
+ "Delete": 111,
+ "OSLeft": 125,
+ "OSRight": 126,
+ "ContextMenu": 127,
+};
+
+GraphicDisplay.key_code_to_input_map = new Uint8Array([
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x0E, 0x0F, 0, 0, 0, 0x1C, 0, 0,
+ 0x2A, 0x1D, 0x38, 0, 0x3A, 0, 0, 0, /* 0x10 */
+ 0, 0, 0, 0x01, 0, 0, 0, 0,
+ 0x39, 104, 109, 107, 102, 105, 103, 106, /* 0x20 */
+ 0x50, 0, 0, 0, 0, 0x52, 0x53, 0,
+ 0x0B, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, /* 0x30 */
+ 0x09, 0x0A, 0, 0x27, 0, 0x0D, 0, 0,
+ 0, 0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, /* 0x40 */
+ 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18,
+ 0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, /* 0x50 */
+ 0x2D, 0x15, 0x2C, 125, 126, 127, 0, 0,
+ 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* 0x60 */
+ 0x48, 0x49, 0x37, 0x4e, 0, 0x4a, 0x53, 98,
+ 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, /* 0x70 */
+ 0x43, 0x44, 0x57, 0x58, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x45, 0, 0, 0, 0, 0, 0, 0, /* 0x90 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */
+ 0, 0, 0, 0, 0, 0x0C, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */
+ 0, 0, 0x27, 0x0D, 0x33, 0x0C, 0x34, 0x35,
+ 0x29, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */
+ 0, 0, 0, 0x1A, 0x2B, 0x1B, 0x28, 0,
+ 125, 100, 0, 0, 0, 0, 0, 0, /* 0xe0 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+]);
+
+GraphicDisplay.prototype.keyHandler = function keyHandler(ev, isDown)
+{
+ var code, input_key_code;
+
+ /* At least avoid exiting the navigator if Ctrl-Q or Ctrl-W are
+ * pressed */
+ if (ev.ctrlKey) {
+ window.onbeforeunload = function() {
+ window.onbeforeunload = null;
+ return "CTRL-W or Ctrl-Q cannot be sent to the emulator.";
+ };
+ } else {
+ window.onbeforeunload = null;
+ }
+
+ if (typeof ev.code != "undefined") {
+ code = ev.code;
+ input_key_code = GraphicDisplay.code_to_input_map[code];
+ if (typeof input_key_code != "undefined") {
+// console.log("code=" + code + " isDown=" + isDown + "
input_key_code=" + input_key_code);
+ this.key_pressed[input_key_code] = isDown;
+ display_key_event(isDown, input_key_code);
+
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+ return false;
+ }
+ } else {
+ /* fallback using keyCodes. Works only with an US keyboard */
+ code = ev.keyCode;
+ if (code < 256) {
+ input_key_code = GraphicDisplay.key_code_to_input_map[code];
+// console.log("keyCode=" + code + " isDown=" + isDown + "
input_key_code=" + input_key_code);
+ if (input_key_code) {
+ this.key_pressed[input_key_code] = isDown;
+ display_key_event(isDown, input_key_code);
+
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+GraphicDisplay.prototype.keyDownHandler = function keyDownHandler(ev)
+{
+ return this.keyHandler(ev, 1);
+}
+
+GraphicDisplay.prototype.keyUpHandler = function keyUpHandler(ev)
+{
+ return this.keyHandler(ev, 0);
+}
+
+GraphicDisplay.prototype.blurHandler = function blurHandler(ev, isDown)
+{
+ var i, n, key_pressed;
+ /* allow unloading the page */
+ window.onbeforeunload = null;
+ /* release all keys */
+ key_pressed = this.key_pressed;
+ for(i = 0; i < key_pressed.length; i++) {
+ if (key_pressed[i]) {
+ display_key_event(0, i);
+ key_pressed[i] = 0;
+ }
+ }
+}
+
+GraphicDisplay.prototype.mouseMoveHandler = function (ev)
+{
+ var x, y, rect, buttons;
+ rect = this.canvas_el.getBoundingClientRect();
+ x = ev.clientX - rect.left;
+ y = ev.clientY - rect.top;
+ buttons = ev.buttons & 7;
+// console.log("mouse: x=" + x + " y=" + y + " buttons=" + buttons);
+ display_mouse_event(x, y, buttons);
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+ return false;
+}
+
+GraphicDisplay.prototype.wheelHandler = function (ev)
+{
+ if (ev.deltaY < 0) {
+ display_wheel_event(1);
+ } else if (ev.deltaY > 0) {
+ display_wheel_event(-1);
+ }
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+}
+
+/* disable contextual menu */
+GraphicDisplay.prototype.onContextMenuHandler = function (ev)
+{
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+ return false;
+}
+
+/* Network support */
+
+function Ethernet(url)
+{
+ try {
+ this.socket = new WebSocket(url);
+ } catch(err) {
+ this.socket = null;
+ console.log("Could not open websocket url=" + url);
+ return;
+ }
+ this.socket.binaryType = 'arraybuffer';
+ this.socket.onmessage = this.messageHandler.bind(this);
+ this.socket.onclose = this.closeHandler.bind(this);
+ this.socket.onopen = this.openHandler.bind(this);
+ this.socket.onerror = this.errorHandler.bind(this);
+}
+
+Ethernet.prototype.openHandler = function(e)
+{
+ net_set_carrier(1);
+}
+
+Ethernet.prototype.closeHandler = function(e)
+{
+ net_set_carrier(0);
+}
+
+Ethernet.prototype.errorHandler = function(e)
+{
+ console.log("Websocket error=" + e);
+}
+
+Ethernet.prototype.messageHandler = function(e)
+{
+ var str, buf_len, buf_addr, buf;
+ if (e.data instanceof ArrayBuffer) {
+ buf_len = e.data.byteLength;
+ buf = new Uint8Array(e.data);
+ buf_addr = _malloc(buf_len);
+ HEAPU8.set(buf, buf_addr);
+ net_write_packet(buf_addr, buf_len);
+ _free(buf_addr);
+ } else {
+ str = e.data.toString();
+ if (str.substring(0, 5) == "ping:") {
+ try {
+ this.socket.send('pong:' + str.substring(5));
+ } catch (err) {
+ }
+ }
+ }
+}
+
+Ethernet.prototype.recv_packet = function(buf)
+{
+ if (this.socket) {
+ try {
+ this.socket.send(buf);
+ } catch (err) {
+ }
+ }
+}
+
+function start_vm(user, pwd)
+{
+ var url, mem_size, cpu, params, vm_url, cmdline, cols, rows, guest_url;
+ var font_size, graphic_enable, width, height, net_url, alloc_size;
+ var drive_url, vm_file;
+
+ function loadScript(src, f) {
+ var head = document.getElementsByTagName("head")[0];
+ var script = document.createElement("script");
+ script.src = src;
+ var done = false;
+ script.onload = script.onreadystatechange = function() {
+ // attach to both events for cross browser finish detection:
+ if ( !done && (!this.readyState ||
+ this.readyState == "loaded" || this.readyState ==
"complete") ) {
+ done = true;
+ if (f) {
+ f();
+ }
+ script.onload = script.onreadystatechange = null;
+ head.removeChild(script);
+ }
+ };
+ head.appendChild(script);
+ }
+
+ function start()
+ {
+ /* C functions called from javascript */
+ console_write1 = Module.cwrap('console_queue_char', null, ['number']);
+ console_resize_event = Module.cwrap('console_resize_event', null, []);
+ fs_import_file = Module.cwrap('fs_import_file', null, ['string',
'number', 'number']);
+ display_key_event = Module.cwrap('display_key_event', null, ['number',
'number']);
+ display_mouse_event = Module.cwrap('display_mouse_event', null,
['number', 'number', 'number']);
+ display_wheel_event = Module.cwrap('display_wheel_event', null,
['number']);
+ net_write_packet = Module.cwrap('net_write_packet', null, ['number',
'number']);
+ net_set_carrier = Module.cwrap('net_set_carrier', null, ['number']);
+
+ net_state = null;
+ if (net_url != "") {
+ net_state = new Ethernet(net_url);
+ }
+
+ Module.ccall("vm_start", null, ["string", "number", "string",
"string", "number", "number", "number", "string"], [url, mem_size, cmdline,
pwd, width, height, (net_state != null) | 0, drive_url]);
+ pwd = null;
+ }
+
+ function term_wrap_onclick_handler()
+ {
+ var term_wrap_el, w, h, term_bar_el, bar_h;
+ term_wrap_el = document.getElementById("term_wrap");
+ term_bar_el = document.getElementById("term_bar");
+ w = term_wrap_el.clientWidth;
+ h = term_wrap_el.clientHeight;
+ bar_h = term_bar_el.clientHeight;
+ if (term.resizePixel(w, h - bar_h)) {
+ console_resize_event();
+ }
+ }
+
+ /* read the parameters */
+
+ params = get_params();
+ cpu = params["cpu"] || "riscv64";
+ url = params["url"];
+ if (!url) {
+ if (cpu == "x86")
+ url = "root-x86.cfg";
+ else
+ url = "root-riscv64.cfg";
+ }
+ url = get_absolute_url(url);
+ mem_size = (params["mem"] | 0) || 128; /* in mb */
+ cmdline = params["cmdline"] || "";
+ cols = (params["cols"] | 0) || 80;
+ rows = (params["rows"] | 0) || 30;
+ font_size = (params["font_size"] | 0) || 15;
+ guest_url = params["guest_url"] || "";
+ width = (params["w"] | 0) || 1024;
+ height = (params["h"] | 0) || 640;
+ graphic_enable = params["graphic"] | 0;
+ net_url = params["net_url"]; /* empty string means no network */
+ if (typeof net_url == "undefined")
+ net_url = "wss://relay.widgetry.org/";
+ drive_url = params["drive_url"] || "";
+
+ if (user) {
+ cmdline += " LOGIN_USER=" + user;
+ } else if (guest_url) {
+ cmdline += " GUEST_URL=" + guest_url;
+ }
+
+ if (graphic_enable) {
+ graphic_display = new
GraphicDisplay(document.getElementById("term_container"), width, height);
+ } else {
+ var term_wrap_el;
+ width = 0;
+ height = 0;
+
+ /* start the terminal */
+ term = new Term({ cols: cols, rows: rows, scrollback: 10000, fontSize:
font_size });
+ term.setKeyHandler(term_handler);
+ term.open(document.getElementById("term_container"),
+ document.getElementById("term_paste"));
+
+ term_wrap_el = document.getElementById("term_wrap")
+ term_wrap_el.style.width = term.term_el.style.width;
+ term_wrap_el.onclick = term_wrap_onclick_handler;
+
+ term.write("Loading...\r\n");
+ }
+
+// console.log("cpu=" + cpu + " url=" + url + " mem=" + mem_size);
+
+ switch(cpu) {
+ case "x86":
+ vm_file = "x86emu";
+ break;
+ case "riscv64":
+ case "riscv":
+ vm_file = "riscvemu64";
+ break;
+ case "riscv32":
+ vm_file = "riscvemu32";
+ break;
+ default:
+ term.writeln("Unknown cpu=" + cpu);
+ return;
+ }
+
+ if (typeof WebAssembly === "object") {
+ /* wasm support : the memory grows automatically */
+ vm_url = vm_file + "-wasm.js";
+ } else {
+ /* set the total memory */
+ alloc_size = mem_size;
+ if (cpu == "x86")
+ alloc_size += 16;
+ if (graphic_enable) {
+ /* frame buffer memory */
+ alloc_size += (width * height * 4 + 1048576 - 1) >> 20;
+ }
+ alloc_size += 32; /* extra space (XXX: reduce it ?) */
+ alloc_size = (alloc_size + 15) & -16; /* align to 16 MB */
+ Module.TOTAL_MEMORY = alloc_size << 20;
+ vm_url = vm_file + ".js";
+ }
+ Module.preRun = start;
+
+ loadScript(vm_url, null);
+}
+
+function on_login()
+{
+ var login_wrap_el = document.getElementById("wrap");
+ var term_wrap_el = document.getElementById("term_wrap");
+ var form = document.getElementById("form");
+ var status = document.getElementById("status");
+ var user = form.user.value;
+ var pwd = form.password.value;
+
+ if (user.length <= 1) {
+ status.innerHTML = "User name must be provided";
+ return false;
+ }
+
+ login_wrap_el.style.display = "none";
+ term_wrap_el.style.display = "block";
+ form.password.value = "";
+ form.user.value = "";
+
+ start_vm(user, pwd);
+
+ return false;
+}
+
+(function() {
+ var login, params;
+
+ params = get_params();
+ login = params["login"] || 0;
+ if (login) {
+ var login_wrap_el = document.getElementById("wrap");
+ login_wrap_el.style.display = "block";
+ } else {
+ var term_wrap_el = document.getElementById("term_wrap");
+ term_wrap_el.style.display = "block";
+ start_vm(null, null);
+ }
+})();
\ No newline at end of file
diff --git a/demo/nuttx.bin b/demo/nuttx.bin
new file mode 100644
index 00000000..9f232db6
Binary files /dev/null and b/demo/nuttx.bin differ
diff --git a/demo/riscvemu64-wasm.js b/demo/riscvemu64-wasm.js
new file mode 100644
index 00000000..93a39ae1
--- /dev/null
+++ b/demo/riscvemu64-wasm.js
@@ -0,0 +1,4 @@
+var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var
key;for(key in
Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=(function(status,toThrow){throw
toThrow});Module["preRun"]=[];Module["postRun"]=[];var
ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var
ENVIRONMENT_IS_NODE=false;var
ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";EN [...]
+
+
+
diff --git a/demo/riscvemu64-wasm.wasm b/demo/riscvemu64-wasm.wasm
new file mode 100644
index 00000000..34f19613
Binary files /dev/null and b/demo/riscvemu64-wasm.wasm differ
diff --git a/demo/root-riscv64.cfg b/demo/root-riscv64.cfg
new file mode 100644
index 00000000..ef711447
--- /dev/null
+++ b/demo/root-riscv64.cfg
@@ -0,0 +1,7 @@
+/* VM configuration file */
+{
+ version: 1,
+ machine: "riscv64",
+ memory_size: 256,
+ bios: "nuttx.bin",
+}
diff --git a/demo/term.js b/demo/term.js
new file mode 100644
index 00000000..052db50d
--- /dev/null
+++ b/demo/term.js
@@ -0,0 +1,1460 @@
+/*
+ * Javascript terminal
+ *
+ * Copyright (c) 2011-2020 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+"use strict";
+
+function Term(options)
+{
+ var width, height, tot_height, scrollback;
+
+ function dummy_key_handler()
+ {
+ }
+
+ width = options.cols ? options.cols : 80;
+ height = options.rows ? options.rows : 25;
+ scrollback = options.scrollback ? options.scrollback : 0;
+ this.font_size = options.fontSize ? options.fontSize : 15;
+
+ this.w = width;
+ this.h = height;
+ this.cur_h = height; /* current height of the scroll back buffer */
+ tot_height = height + scrollback;
+ this.tot_h = tot_height; /* maximum height of the scroll back buffer */
+
+ /* y_base and y_disp are index in the circular buffer lines of
+ length cur_h. They are defined modulo tot_h, i.e. they wrap
+ when cur_h = tot_h. If cur_h < tot_h, y_base is always equal to
+ cur_h - h. */
+ this.y_base = 0; /* position of the current top screen line in the
+ scroll back buffer */
+ this.y_disp = 0; /* position of the top displayed line in the
+ scroll back buffer */
+ /* cursor position */
+ this.x = 0;
+ this.y = 0;
+ this.scroll_top = 0;
+ this.scroll_bottom = this.h;
+ this.cursorstate = 0;
+ this.handler = dummy_key_handler;
+ this.state = 0;
+ this.output_queue = "";
+ this.colors = [
+ /* normal */
+ "#000000",
+ "#aa0000",
+ "#00aa00",
+ "#aa5500",
+ "#0000aa",
+ "#aa00aa",
+ "#00aaaa",
+ "#aaaaaa",
+ /* bright */
+ "#555555",
+ "#ff5555",
+ "#55ff55",
+ "#ffff55",
+ "#5555ff",
+ "#ff55ff",
+ "#55ffff",
+ "#ffffff"
+ ];
+ /* attributes bits:
+ 0-3: bg
+ 4-7: fg
+ 8: bold
+ 9: inverse
+ */
+ this.def_attr = (7 << 4) | 0;
+ this.cur_attr = this.def_attr;
+ this.is_mac = (navigator.userAgent.indexOf("Mac") >=0 ) ? true : false;
+ this.key_rep_state = 0;
+ this.key_rep_str = "";
+
+ this.utf8 = true;
+ this.utf8_state = 0;
+ this.utf8_val = 0;
+
+ this.application_cursor = false;
+ this.application_keypad = false;
+ /* if true, emulate some behaviors of the Linux console */
+ this.linux_console = true;
+
+ this.textarea_has_focus = false;
+}
+
+Term.prototype.setKeyHandler = function(handler)
+{
+ this.handler = handler;
+}
+
+/* return the size of a character in CSS pixels using the selected font */
+function term_get_char_size(parent_el, font_size)
+{
+ var el, g, ret;
+ el = document.createElement("div");
+ el.classList.add("term", "term_char_size");
+ el.style.fontSize = font_size + "px";
+ el.textContent = "W";
+ parent_el.appendChild(el);
+ g = el.getBoundingClientRect();
+ /* the character width & height may not be an integer */
+ ret = [g.width, g.height];
+ return ret;
+}
+
+Term.prototype.open = function(parent_el)
+{
+ var y, line, i, term, c, row_el, char_size_ret;
+
+ /* set initial content */
+ this.lines = new Array();
+ c = 32 | (this.def_attr << 16);
+ for(y = 0; y < this.cur_h;y++) {
+ line = new Array();
+ for(i=0;i<this.w;i++)
+ line[i] = c;
+ this.lines[y] = line;
+ }
+
+ char_size_ret = term_get_char_size(parent_el, this.font_size);
+ /* size of the character in CSS pixels */
+ this.char_width = char_size_ret[0];
+ this.char_height = char_size_ret[1];
+
+ this.scrollbar_width = 15;
+
+ /* size of term_el in CSS pixels */
+ this.term_width = Math.ceil(this.w * this.char_width) +
+ this.scrollbar_width;
+ this.term_height = Math.ceil(this.h * this.char_height);
+
+ /* create the terminal window */
+ this.term_el = document.createElement("div");
+ this.term_el.className = "term";
+ /* XXX: could compute the font metrics */
+ this.term_el.style.fontSize = this.font_size + "px";
+ this.term_el.style.width = this.term_width + "px";
+ this.term_el.style.height = this.term_height + "px";
+ /* allow the terminal to take the focus */
+ this.term_el.setAttribute("tabindex", "0");
+
+ /* scroll bar */
+ this.scrollbar_el = document.createElement("div");
+ this.scrollbar_el.className = "term_scrollbar";
+ this.scrollbar_el.style.width = this.scrollbar_width + "px";
+ this.term_el.appendChild(this.scrollbar_el);
+
+ this.track_el = document.createElement("div");
+ this.track_el.className = "term_track";
+ this.track_el.onmousedown = this.mouseMoveHandler.bind(this);
+ this.scrollbar_el.appendChild(this.track_el);
+
+ this.thumb_el = document.createElement("div");
+ this.thumb_el.className = "term_thumb";
+ this.thumb_el.onmousedown = this.mouseDownHandler.bind(this);
+ this.track_el.appendChild(this.thumb_el);
+
+ this.end_el = document.createElement("div");
+ this.end_el.className = "term_end";
+ this.thumb_el.appendChild(this.end_el);
+
+ /* current scrollbar position */
+ this.thumb_size = -1;
+ this.thumb_pos = -1;
+
+ /* terminal content */
+ this.content_el = document.createElement("div");
+ this.content_el.className = "term_content";
+ this.content_el.style.width = (this.w) + "ch";
+ this.term_el.appendChild(this.content_el);
+
+ this.rows_el = [];
+ for(y=0;y<this.h;y++) {
+ row_el = document.createElement("div");
+ this.rows_el.push(row_el);
+ this.content_el.appendChild(row_el);
+ }
+
+ /* dummy textarea to get the input events and for the virtual
+ keyboard on mobile devices */
+ this.textarea_el = document.createElement("textarea");
+ this.textarea_el.classList.add("term_textarea");
+ this.textarea_el.setAttribute("autocorrect", "off");
+ this.textarea_el.setAttribute("autocapitalize", "off");
+ this.textarea_el.setAttribute("spellcheck", "false");
+ this.textarea_el.setAttribute("tabindex", "-1");
+ this.term_el.appendChild(this.textarea_el);
+
+ this.parent_el = parent_el;
+ parent_el.appendChild(this.term_el);
+
+ this.refresh(0, this.h - 1);
+
+ /* textarea_el events */
+ // key handler
+ this.textarea_el.addEventListener("keydown",
+ this.keyDownHandler.bind(this), true);
+ this.textarea_el.addEventListener("keyup",
+ this.keyUpHandler.bind(this), true);
+ /* keypress is deprecated, so use input */
+ this.textarea_el.addEventListener("input",
+ this.inputHandler.bind(this), true);
+ this.textarea_el.addEventListener("focus",
+ this.focusHandler.bind(this), true);
+ this.textarea_el.addEventListener("blur",
+ this.blurHandler.bind(this), true);
+
+ /* term_el events */
+ this.term_el.addEventListener("keydown",
+ this.termKeyDownHandler.bind(this),
+ true);
+ this.term_el.addEventListener("paste",
+ this.pasteHandler.bind(this), true);
+ this.term_el.addEventListener("mouseup",
+ this.termMouseUpHandler.bind(this),
+ true);
+ this.term_el.addEventListener("wheel",
+ this.wheelHandler.bind(this), false);
+
+ // cursor blinking
+ term = this;
+ setInterval(function() { term.cursor_timer_cb(); }, 1000);
+
+ this.term_el.focus();
+};
+
+Term.prototype.refresh_scrollbar = function ()
+{
+ var total_size, thumb_pos, thumb_size, y, y0;
+ total_size = this.term_el.clientHeight;
+ thumb_size = Math.ceil(this.h * total_size / this.cur_h);
+ /* position of the first line of the scroll back buffer */
+ y0 = (this.y_base + this.h) % this.cur_h;
+ y = this.y_disp - y0;
+ if (y < 0)
+ y += this.cur_h;
+ thumb_pos = Math.floor(y * total_size / this.cur_h);
+ thumb_size = Math.max(thumb_size, 30);
+ thumb_size = Math.min(thumb_size, total_size);
+ thumb_pos = Math.min(thumb_pos, total_size - thumb_size);
+// console.log("pos=" + thumb_pos + " size=" + thumb_size);
+ if (thumb_pos != this.thumb_pos || thumb_size != this.thumb_size) {
+ this.thumb_pos = thumb_pos;
+ this.thumb_size = thumb_size;
+ this.thumb_el.style.top = thumb_pos + "px";
+ this.thumb_el.style.height = thumb_size + "px";
+ }
+}
+
+/* move the text area at the cursor position so that the browser shows
+ * the correct position when the virtual keyboard is used */
+Term.prototype.move_textarea = function()
+{
+ var x, y, base_x, base_y, pos;
+
+ pos = this.term_el.getBoundingClientRect();
+ base_x = pos.left + window.scrollX;
+ base_y = pos.top + window.scrollY;
+ /* position relative to the body */
+ x = Math.ceil(this.x * this.char_width + base_x);
+ y = Math.ceil(this.y * this.char_height + base_y);
+
+ this.textarea_el.style.width = Math.ceil(this.char_width) + "px";
+ this.textarea_el.style.height = Math.ceil(this.char_height) + "px";
+ this.textarea_el.style.left = x + "px";
+ this.textarea_el.style.top = y + "px";
+ this.textarea_el.style.zIndex = 1000;
+}
+
+Term.prototype.refresh = function(ymin, ymax)
+{
+ var el, y, line, outline, c, w, i, j, cx, attr, last_attr, fg, bg, y1;
+ var http_link_len, http_link_str, bold, tmp, inverse;
+
+ function is_http_link_char(c)
+ {
+ var str =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=`.";
+ return str.indexOf(String.fromCharCode(c)) >= 0;
+ }
+
+ function right_trim(str, a)
+ {
+ var i, n;
+ n = a.length;
+ i = str.length;
+ while (i >= n && str.substr(i - n, n) == a)
+ i -= n;
+ return str.substr(0, i);
+ }
+
+ for(y = ymin; y <= ymax; y++) {
+ /* convert to HTML string */
+ y1 = y + this.y_disp;
+ if (y1 >= this.cur_h)
+ y1 -= this.cur_h;
+ line = this.lines[y1];
+ outline = "";
+ w = this.w;
+ if (y == this.y && this.cursor_state &&
+ this.y_disp == this.y_base) {
+ cx = this.x;
+ } else {
+ cx = -1;
+ }
+ last_attr = this.def_attr;
+ http_link_len = 0;
+ for(i = 0; i < w; i++) {
+ c = line[i];
+ attr = c >> 16;
+ c &= 0xffff;
+ /* test for http link */
+ if (c == 0x68 && (w - i) >= 8 && http_link_len == 0) {
+ /* test http:// or https:// */
+ if ((line[i + 1] & 0xffff) == 0x74 &&
+ (line[i + 2] & 0xffff) == 0x74 &&
+ (line[i + 3] & 0xffff) == 0x70 &&
+ (((line[i + 4] & 0xffff) == 0x3a &&
+ (line[i + 5] & 0xffff) == 0x2f &&
+ (line[i + 6] & 0xffff) == 0x2f) ||
+ ((line[i + 4] & 0xffff) == 0x73 &&
+ (line[i + 5] & 0xffff) == 0x3a &&
+ (line[i + 6] & 0xffff) == 0x2f &&
+ (line[i + 7] & 0xffff) == 0x2f))) {
+ http_link_str = "";
+ j = 0;
+ while ((i + j) < w &&
+ is_http_link_char(line[i + j] & 0xffff)) {
+ http_link_str += String.fromCharCode(line[i + j] &
0xffff);
+ j++;
+ }
+ http_link_len = j;
+ if (last_attr != this.def_attr) {
+ outline += '</span>';
+ last_attr = this.def_attr;
+ }
+ outline += "<a href='" + http_link_str + "'>";
+ }
+ }
+ if (i == cx) {
+ attr = -1; /* cursor */
+ }
+ if (attr != last_attr) {
+ if (last_attr != this.def_attr)
+ outline += '</span>';
+ if (attr != this.def_attr) {
+ if (attr == -1) {
+ /* cursor */
+ outline += '<span class="term_cursor">';
+ } else {
+ outline += '<span style="';
+ fg = (attr >> 4) & 0xf;
+ bg = attr & 0xf;
+ bold = (attr >> 8) & 1;
+ inverse = (attr >> 9) & 1;
+ if (inverse) {
+ tmp = fg;
+ fg = bg;
+ bg = tmp;
+ }
+ if (bold) {
+ /* metrics are not OK for all fonts, so disabled */
+ /* outline += 'font-weight:bold;'; */
+ /* use the bright color */
+ if (fg < 8)
+ fg += 8;
+ }
+ if (fg != 7) {
+ outline += 'color:' + this.colors[fg] + ';';
+ }
+ if (bg != 0) {
+ outline += 'background-color:' +
+ this.colors[bg] + ';';
+ }
+ outline += '">';
+ }
+ }
+ }
+ switch(c) {
+ case 32:
+ outline += " ";
+ break;
+ case 38: // '&'
+ outline += "&";
+ break;
+ case 60: // '<'
+ outline += "<";
+ break;
+ case 62: // '>'
+ outline += ">";
+ break;
+ default:
+ if (c < 32) {
+ outline += " ";
+ } else {
+ outline += String.fromCharCode(c);
+ }
+ break;
+ }
+ last_attr = attr;
+ if (http_link_len != 0) {
+ http_link_len--;
+ if (http_link_len == 0) {
+ if (last_attr != this.def_attr) {
+ outline += '</span>';
+ last_attr = this.def_attr;
+ }
+ outline += "</a>";
+ }
+ }
+ }
+ if (last_attr != this.def_attr) {
+ outline += '</span>';
+ }
+
+ /* trim trailing spaces for copy/paste */
+ outline = right_trim(outline, " ");
+ if (outline == "")
+ outline = " ";
+
+ this.rows_el[y].innerHTML = outline;
+ }
+
+ this.refresh_scrollbar();
+ this.move_textarea();
+};
+
+Term.prototype.cursor_timer_cb = function()
+{
+ this.cursor_state ^= 1;
+ this.refresh(this.y, this.y);
+};
+
+Term.prototype.show_cursor = function()
+{
+ if (!this.cursor_state) {
+ this.cursor_state = 1;
+ this.refresh(this.y, this.y);
+ }
+};
+
+/* scroll down or up in the scroll back buffer by n lines */
+Term.prototype.scroll_disp = function(n)
+{
+ var i, y1;
+ /* slow but it does not really matters */
+ if (n >= 0) {
+ for(i = 0; i < n; i++) {
+ if (this.y_disp == this.y_base)
+ break;
+ if (++this.y_disp == this.cur_h)
+ this.y_disp = 0;
+ }
+ } else {
+ n = -n;
+ y1 = this.y_base + this.h;
+ if (y1 >= this.cur_h)
+ y1 -= this.cur_h;
+ for(i = 0; i < n; i++) {
+ if (this.y_disp == y1)
+ break;
+ if (--this.y_disp < 0)
+ this.y_disp = this.cur_h - 1;
+ }
+ }
+ this.refresh(0, this.h - 1);
+};
+
+Term.prototype.write = function(str)
+{
+ var s, ymin, ymax;
+
+ function update(y)
+ {
+ ymin = Math.min(ymin, y);
+ ymax = Math.max(ymax, y);
+ }
+
+ function get_erase_char()
+ {
+ var bg_mask, attr;
+ bg_mask = 0xf;
+ attr = (s.def_attr & ~bg_mask) | (s.cur_attr & bg_mask);
+ return 32 | (attr << 16);
+ }
+
+ function erase_chars(x1, x2, y) {
+ var l, i, c, y1;
+ y1 = s.y_base + y;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ l = s.lines[y1];
+ c = get_erase_char();
+ for(i = x1; i < x2; i++)
+ l[i] = c;
+ update(y);
+ }
+
+ function erase_to_eol(x, y) {
+ erase_chars(x, s.w, y);
+ }
+
+ function erase_in_line(n) {
+ switch(n) {
+ case 0:
+ erase_to_eol(s.x, s.y);
+ break;
+ case 1:
+ erase_chars(0, s.x + 1, s.y);
+ break;
+ case 2:
+ erase_chars(0, s.w, s.y);
+ break;
+ }
+ }
+
+ function erase_in_display(n) {
+ var y;
+ switch(n) {
+ case 0:
+ erase_to_eol(s.x, s.y);
+ for(y = s.y + 1; y < s.h; y++)
+ erase_to_eol(0, y);
+ break;
+ case 1:
+ erase_chars(0, s.x + 1, s.y);
+ for(y = 0; y < s.y; y++) {
+ erase_to_eol(0, y);
+ }
+ break;
+ case 2:
+ for(y = 0; y < s.h; y++) {
+ erase_to_eol(0, y);
+ }
+ break;
+ }
+ }
+
+
+ function delete_chars(n)
+ {
+ var l, i, c, y1, j;
+ y1 = s.y + s.y_base;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ l = s.lines[y1];
+ if (n < 1)
+ n = 1;
+ c = get_erase_char();
+ j = s.x + n;
+ for(i = s.x; i < s.w; i++) {
+ if (j < s.w)
+ l[i] = l[j];
+ else
+ l[i] = c;
+ j++;
+ }
+ update(s.y);
+ }
+
+ function insert_chars(n)
+ {
+ var l, i, c, y1, x1;
+ if (n < 1)
+ n = 1;
+ if (n > s.w - s.x)
+ n = s.w - s.x;
+ y1 = s.y + s.y_base;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ l = s.lines[y1];
+ x1 = s.x + n;
+ for(i = s.w - 1; i >= x1; i--)
+ l[i] = l[i - n];
+ c = get_erase_char();
+ for(i = s.x; i < x1; i++)
+ l[i] = c;
+ update(s.y);
+ }
+
+ function csi_colors(esc_params)
+ {
+ var j, n, fg, bg, mask;
+
+ if (esc_params.length == 0) {
+ s.cur_attr= s.def_attr;
+ } else {
+ for(j = 0; j < esc_params.length; j++) {
+ n = esc_params[j];
+ if (n >= 30 && n <= 37) {
+ /* foreground */
+ fg = n - 30;
+ s.cur_attr = (s.cur_attr & ~(0xf << 4)) | (fg << 4);
+ } else if (n >= 40 && n <= 47) {
+ /* background */
+ bg = n - 40;
+ s.cur_attr = (s.cur_attr & ~0xf) | bg;
+ } else if (n >= 90 && n <= 97) {
+ /* bright foreground */
+ fg = n - 90 + 8;
+ s.cur_attr = (s.cur_attr & ~(0xf << 4)) | (fg << 4);
+ } else if (n >= 100 && n <= 107) {
+ /* bright background */
+ bg = n - 100 + 8;
+ s.cur_attr = (s.cur_attr & ~0xf) | bg;
+ } else if (n == 1) {
+ /* bold + bright */
+ s.cur_attr |= (1 << 8);
+ } else if (n == 0) {
+ /* default attr */
+ s.cur_attr = s.def_attr;
+ } else if (n == 7) {
+ /* inverse */
+ s.cur_attr |= (1 << 9);
+ } else if (n == 27) {
+ /* not inverse */
+ s.cur_attr &= ~(1 << 9);
+ } else if (n == 39) {
+ /* reset fg */
+ mask = 0x0f << 4;
+ s.cur_attr = (s.cur_attr & ~mask) | (s.def_attr & mask);
+ } else if (n == 49) {
+ /* reset bg */
+ mask = 0x0f;
+ s.cur_attr = (s.cur_attr & ~mask) | (s.def_attr & mask);
+ }
+ }
+ }
+ }
+
+ function empty_line(y, use_erase_char) {
+ var line, c, y1, x;
+ if (use_erase_char)
+ c = get_erase_char();
+ else
+ c = 32 | (s.def_attr << 16);
+ line = new Array();
+ for(x=0;x<s.w;x++)
+ line[x] = c;
+ y1 = s.y_base + y;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ s.lines[y1] = line;
+ }
+
+ function scroll_down(top, bottom, use_erase_char)
+ {
+ var y, line, y1, y2;
+
+ if (top == 0 && bottom == s.h) {
+ /* increase height of buffer if possible */
+ if (s.cur_h < s.tot_h) {
+ s.cur_h++;
+ }
+ /* move down one line */
+ if (++s.y_base == s.cur_h)
+ s.y_base = 0;
+ s.y_disp = s.y_base;
+ } else {
+ /* partial scroll */
+ for(y = top; y < bottom - 1; y++) {
+ y1 = s.y_base + y;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ y2 = y1 + 1;
+ if (y2 >= s.cur_h)
+ y2 -= s.cur_h;
+ s.lines[y1] = s.lines[y2];
+ }
+ }
+ empty_line(bottom - 1, use_erase_char);
+ update(top);
+ update(bottom - 1);
+ }
+
+ function scroll_up(top, bottom, use_erase_char) {
+ var y, y1, y2;
+ /* XXX: could scroll in the history */
+ for(y = bottom - 1; y > top; y--) {
+ y1 = s.y_base + y;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ y2 = y1 - 1;
+ if (y2 >= s.cur_h)
+ y2 -= s.cur_h;
+ s.lines[y1] = s.lines[y2];
+ }
+ empty_line(top, use_erase_char);
+ update(top);
+ update(bottom - 1);
+ }
+
+ function down_with_scroll() {
+ s.y++;
+ if (s.y == s.scroll_bottom) {
+ s.y--;
+ scroll_down(s.scroll_top, s.scroll_bottom, false);
+ } else if (s.y >= s.h) {
+ s.y--;
+ scroll_down(0, s.h, false);
+ }
+ }
+
+ function up_with_scroll() {
+ if (s.y == s.scroll_top) {
+ scroll_up(s.scroll_top, s.scroll_bottom, true);
+ } else if (s.y == 0) {
+ scroll_up(0, s.h, true);
+ } else {
+ s.y--;
+ }
+ }
+
+ function insert_lines(n) {
+ var y2;
+ if (n < 1)
+ n = 1;
+ if (s.y < s.scroll_bottom)
+ y2 = s.scroll_bottom;
+ else
+ y2 = s.h;
+ while (n != 0) {
+ scroll_up(s.y, y2, true);
+ n--;
+ }
+ }
+
+ function delete_lines(n) {
+ var y2;
+ if (n < 1)
+ n = 1;
+ if (s.y < s.scroll_bottom)
+ y2 = s.scroll_bottom;
+ else
+ y2 = s.h;
+ while (n != 0) {
+ scroll_down(s.y, y2, true);
+ n--;
+ }
+ }
+
+ var TTY_STATE_NORM = 0;
+ var TTY_STATE_ESC = 1;
+ var TTY_STATE_CSI = 2;
+ var TTY_STATE_CHARSET = 3;
+
+ function handle_char(c) {
+ var i, l, n, j, y1, y2, x1;
+
+ switch(s.state) {
+ case TTY_STATE_NORM:
+ switch(c) {
+ case 10:
+ down_with_scroll();
+ break;
+ case 13:
+ s.x = 0;
+ break;
+ case 8:
+ if (s.x > 0) {
+ s.x--;
+ }
+ break;
+ case 9: /* tab */
+ n = (s.x + 8) & ~7;
+ if (n <= s.w) {
+ s.x = n;
+ }
+ break;
+ case 27:
+ s.state = TTY_STATE_ESC;
+ break;
+ default:
+ if (c >= 32) {
+ if (s.x >= s.w) {
+ s.x = 0;
+ down_with_scroll();
+ }
+ y1 = s.y + s.y_base;
+ if (y1 >= s.cur_h)
+ y1 -= s.cur_h;
+ s.lines[y1][s.x] = (c & 0xffff) |
+ (s.cur_attr << 16);
+ s.x++;
+ update(s.y);
+ }
+ break;
+ }
+ break;
+ case TTY_STATE_ESC:
+ switch(c) {
+ case 91: // '['
+ s.esc_params = new Array();
+ s.cur_param = 0;
+ s.esc_prefix = 0;
+ s.state = TTY_STATE_CSI;
+ break;
+ case 40: // '('
+ case 41: // ')'
+ s.state = TTY_STATE_CHARSET;
+ break;
+ case 61: // '='
+ s.application_keypad = true;
+ s.state = TTY_STATE_NORM;
+ break;
+ case 62: // '>'
+ s.application_keypad = false;
+ s.state = TTY_STATE_NORM;
+ break;
+ case 77: // 'M'
+ up_with_scroll();
+ s.state = TTY_STATE_NORM;
+ break;
+ default:
+ s.state = TTY_STATE_NORM;
+ break;
+ }
+ break;
+ case TTY_STATE_CSI:
+ if (c >= 48 && c <= 57) { // '0' '9'
+ /* numeric */
+ s.cur_param = s.cur_param * 10 + c - 48;
+ } else {
+ if (c == 63) { // '?'
+ s.esc_prefix = c;
+ break;
+ }
+ /* add parsed parameter */
+ s.esc_params[s.esc_params.length] = s.cur_param;
+ s.cur_param = 0;
+ if (c == 59) // ;
+ break;
+ s.state = TTY_STATE_NORM;
+
+ // console.log("term: csi=" + s.esc_params + "
cmd="+c);
+ switch(c) {
+ case 64: // '@' insert chars
+ insert_chars(s.esc_params[0]);
+ break;
+ case 65: // 'A' up
+ n = s.esc_params[0];
+ if (n < 1)
+ n = 1;
+ s.y -= n;
+ if (s.y < 0)
+ s.y = 0;
+ break;
+ case 66: // 'B' down
+ n = s.esc_params[0];
+ if (n < 1)
+ n = 1;
+ s.y += n;
+ if (s.y >= s.h)
+ s.y = s.h - 1;
+ break;
+ case 67: // 'C' right
+ n = s.esc_params[0];
+ if (n < 1)
+ n = 1;
+ s.x += n;
+ if (s.x >= s.w - 1)
+ s.x = s.w - 1;
+ break;
+ case 68: // 'D' left
+ n = s.esc_params[0];
+ if (n < 1)
+ n = 1;
+ s.x -= n;
+ if (s.x < 0)
+ s.x = 0;
+ break;
+ case 71: /* 'G' cursor character absolute */
+ x1 = s.esc_params[0] - 1;
+ if (x1 < 0)
+ x1 = 0;
+ else if (x1 >= s.w)
+ x1 = s.w - 1;
+ s.x = x1;
+ break;
+ case 72: // 'H' goto xy
+ y1 = s.esc_params[0] - 1;
+ if (s.esc_params.length >= 2)
+ x1 = s.esc_params[1] - 1;
+ else
+ x1 = 0;
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 >= s.h)
+ y1 = s.h - 1;
+ if (x1 < 0)
+ x1 = 0;
+ else if (x1 >= s.w)
+ x1 = s.w - 1;
+ s.x = x1;
+ s.y = y1;
+ break;
+ case 74: // 'J' erase in display
+ erase_in_display(s.esc_params[0]);
+ break;
+ case 75: // 'K' erase in line
+ erase_in_line(s.esc_params[0]);
+ break;
+ case 76: // 'L' insert lines
+ insert_lines(s.esc_params[0]);
+ break;
+ case 77: // 'M' insert lines
+ delete_lines(s.esc_params[0]);
+ break;
+ case 80: // 'P'
+ delete_chars(s.esc_params[0]);
+ break;
+ case 100: // 'd' line position absolute
+ {
+ y1 = s.esc_params[0] - 1;
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 >= s.h)
+ y1 = s.h - 1;
+ s.y = y1;
+ }
+ break;
+ case 104: // 'h': set mode
+ if (s.esc_prefix == 63 && s.esc_params[0] == 1) {
+ s.application_cursor = true;
+ }
+ break;
+ case 108: // 'l': reset mode
+ if (s.esc_prefix == 63 && s.esc_params[0] == 1) {
+ s.application_cursor = false;
+ }
+ break;
+ case 109: // 'm': set color
+ csi_colors(s.esc_params);
+ break;
+ case 110: // 'n' return the cursor position
+ s.queue_chars("\x1b[" + (s.y + 1) + ";" + (s.x + 1) + "R");
+ break;
+ case 114: // 'r' set scroll region
+ y1 = s.esc_params[0] - 1;
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 >= s.h)
+ y1 = s.h - 1;
+ if (s.esc_params.length >= 2)
+ y2 = s.esc_params[1];
+ else
+ y2 = s.h;
+ if (y2 >= s.h || y2 <= y1)
+ y2 = s.h;
+ s.scroll_top = y1;
+ s.scroll_bottom = y2;
+ s.x = 0;
+ s.y = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case TTY_STATE_CHARSET:
+ /* just ignore */
+ s.state = TTY_STATE_NORM;
+ break;
+ }
+ }
+
+ function handle_utf8(c) {
+ if (s.utf8_state !== 0 && (c >= 0x80 && c < 0xc0)) {
+ s.utf8_val = (s.utf8_val << 6) | (c & 0x3F);
+ s.utf8_state--;
+ if (s.utf8_state === 0) {
+ handle_char(s.utf8_val);
+ }
+ } else if (c >= 0xc0 && c < 0xf8) {
+ s.utf8_state = 1 + (c >= 0xe0) + (c >= 0xf0);
+ s.utf8_val = c & ((1 << (6 - s.utf8_state)) - 1);
+ } else {
+ s.utf8_state = 0;
+ handle_char(c);
+ }
+ }
+
+ var i, c, utf8;
+
+ /* update region is in ymin ymax */
+ s = this;
+ ymin = s.h;
+ ymax = -1;
+ update(s.y); // remove the cursor
+ /* reset top of displayed screen to top of real screen */
+ if (s.y_base != s.y_disp) {
+ s.y_disp = s.y_base;
+ /* force redraw */
+ ymin = 0;
+ ymax = s.h - 1;
+ }
+ utf8 = s.utf8;
+ for(i = 0; i < str.length; i++) {
+ c = str.charCodeAt(i);
+ if (utf8)
+ handle_utf8(c);
+ else
+ handle_char(c);
+ }
+ update(s.y); // show the cursor
+
+ if (ymax >= ymin)
+ s.refresh(ymin, ymax);
+};
+
+Term.prototype.writeln = function (str)
+{
+ this.write(str + '\r\n');
+};
+
+Term.prototype.interceptBrowserExit = function (ev)
+{
+ /* At least avoid exiting the navigator if Ctrl-Q or Ctrl-W are
+ * pressed */
+ if (ev.ctrlKey) {
+ window.onbeforeunload = function() {
+ window.onbeforeunload = null;
+ return "CTRL-W or Ctrl-Q cannot be sent to the emulator.";
+ };
+ } else {
+ window.onbeforeunload = null;
+ }
+}
+
+Term.prototype.keyDownHandler = function (ev)
+{
+ var str;
+
+ this.interceptBrowserExit(ev);
+
+ str="";
+ switch(ev.keyCode) {
+ case 8: /* backspace */
+ str = "\x7f";
+ break;
+ case 9: /* tab */
+ str = "\x09";
+ break;
+ case 13: /* enter */
+ str = "\x0d";
+ break;
+ case 27: /* escape */
+ str = "\x1b";
+ break;
+ case 37: /* left */
+ if (ev.ctrlKey) {
+ str = "\x1b[1;5D";
+ } else if (this.application_cursor) {
+ str = "\x1bOD";
+ } else {
+ str = "\x1b[D";
+ }
+ break;
+ case 39: /* right */
+ if (ev.ctrlKey) {
+ str = "\x1b[1;5C";
+ } else if (this.application_cursor) {
+ str = "\x1bOC";
+ } else {
+ str = "\x1b[C";
+ }
+ break;
+ case 38: /* up */
+ if (ev.ctrlKey) {
+ this.scroll_disp(-1);
+ } else if (this.application_cursor) {
+ str = "\x1bOA";
+ } else {
+ str = "\x1b[A";
+ }
+ break;
+ case 40: /* down */
+ if (ev.ctrlKey) {
+ this.scroll_disp(1);
+ } else if (this.application_cursor) {
+ str = "\x1bOB";
+ } else {
+ str = "\x1b[B";
+ }
+ break;
+ case 46: /* delete */
+ str = "\x1b[3~";
+ break;
+ case 45: /* insert */
+ str = "\x1b[2~";
+ break;
+ case 36: /* home */
+ if (this.linux_console)
+ str = "\x1b[1~";
+ else if (this.application_keypad)
+ str = "\x1bOH";
+ else
+ str = "\x1b[H";
+ break;
+ case 35: /* end */
+ if (this.linux_console)
+ str = "\x1b[4~";
+ else if (this.application_keypad)
+ str = "\x1bOF";
+ else
+ str = "\x1b[F";
+ break;
+ case 33: /* page up */
+ if (ev.ctrlKey) {
+ this.scroll_disp(-(this.h - 1));
+ } else {
+ str = "\x1b[5~";
+ }
+ break;
+ case 34: /* page down */
+ if (ev.ctrlKey) {
+ this.scroll_disp(this.h - 1);
+ } else {
+ str = "\x1b[6~";
+ }
+ break;
+ default:
+ if (ev.ctrlKey) {
+ /* ctrl + key */
+ if (ev.keyCode >= 65 && ev.keyCode <= 90) {
+ str = String.fromCharCode(ev.keyCode - 64);
+ } else if (ev.keyCode == 32) {
+ str = String.fromCharCode(0);
+ }
+ } else if ((!this.is_mac && ev.altKey) ||
+ (this.is_mac && ev.metaKey)) {
+ /* meta + key (Note: we only send lower case) */
+ if (ev.keyCode >= 65 && ev.keyCode <= 90) {
+ str = "\x1b" + String.fromCharCode(ev.keyCode + 32);
+ }
+ }
+ break;
+ }
+ // console.log("keydown: keycode=" + ev.keyCode + " charcode=" +
ev.charCode + " str=" + str + " ctrl=" + ev.ctrlKey + " alt=" + ev.altKey + "
meta=" + ev.metaKey);
+ if (str) {
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+
+ this.show_cursor();
+ this.key_rep_state = 1;
+ this.key_rep_str = str;
+ this.handler(str);
+ return false;
+ } else {
+ this.key_rep_state = 0;
+ return true;
+ }
+};
+
+Term.prototype.keyUpHandler = function (ev)
+{
+ this.interceptBrowserExit(ev);
+};
+
+Term.prototype.to_utf8 = function(s)
+{
+ var i, n = s.length, r, c;
+ r = "";
+ for(i = 0; i < n; i++) {
+ c = s.charCodeAt(i);
+ if (c < 0x80) {
+ r += String.fromCharCode(c);
+ } else if (c < 0x800) {
+ r += String.fromCharCode((c >> 6) | 0xc0, (c & 0x3f) | 0x80);
+ } else if (c < 0x10000) {
+ r += String.fromCharCode((c >> 12) | 0xe0,
+ ((c >> 6) & 0x3f) | 0x80,
+ (c & 0x3f) | 0x80);
+ } else {
+ r += String.fromCharCode((c >> 18) | 0xf0,
+ ((c >> 12) & 0x3f) | 0x80,
+ ((c >> 6) & 0x3f) | 0x80,
+ (c & 0x3f) | 0x80);
+ }
+ }
+ return r;
+}
+
+Term.prototype.inputHandler = function (ev)
+{
+ var str;
+ str = this.textarea_el.value;
+ if (str) {
+ this.textarea_el.value = "";
+ this.show_cursor();
+ if (this.utf8)
+ str = this.to_utf8(str);
+ this.handler(str);
+ return false;
+ } else {
+ return true;
+ }
+};
+
+Term.prototype.termKeyDownHandler = function(ev)
+{
+ this.interceptBrowserExit(ev);
+ /* give the focus back to the textarea when a key is pressed */
+ this.textarea_el.focus();
+}
+
+Term.prototype.termMouseUpHandler = function(ev)
+{
+ var sel;
+ /* if no selection, can switch back up to the textarea focus */
+ sel = window.getSelection();
+ if (!sel || sel.isCollapsed)
+ this.textarea_el.focus();
+}
+
+Term.prototype.focusHandler = function (ev)
+{
+ this.textarea_has_focus = true;
+};
+
+Term.prototype.blurHandler = function (ev)
+{
+ /* allow unloading the page */
+ window.onbeforeunload = null;
+ this.textarea_has_focus = false;
+};
+
+Term.prototype.pasteHandler = function (ev)
+{
+ var c, str;
+ if (!this.textarea_has_focus) {
+ c = ev.clipboardData;
+ if (c) {
+ str = c.getData("text/plain");
+ if (this.utf8)
+ str = this.to_utf8(str);
+ this.queue_chars(str);
+ return false;
+ }
+ }
+}
+
+Term.prototype.wheelHandler = function (ev)
+{
+ if (ev.deltaY < 0)
+ this.scroll_disp(-3);
+ else if (ev.deltaY > 0)
+ this.scroll_disp(3);
+ ev.stopPropagation();
+}
+
+Term.prototype.mouseDownHandler = function (ev)
+{
+ this.thumb_el.onmouseup = this.mouseUpHandler.bind(this);
+ document.onmousemove = this.mouseMoveHandler.bind(this);
+ document.onmouseup = this.mouseUpHandler.bind(this);
+
+ /* disable potential selection */
+ document.body.className += " noSelect";
+
+ this.mouseMoveHandler(ev);
+}
+
+Term.prototype.mouseMoveHandler = function (ev)
+{
+ var total_size, pos, new_y_disp, y, y0;
+ total_size = this.term_el.clientHeight;
+ y = ev.clientY - this.track_el.getBoundingClientRect().top;
+ pos = Math.floor((y - (this.thumb_size / 2)) * this.cur_h / total_size);
+ new_y_disp = Math.min(Math.max(pos, 0), this.cur_h - this.h);
+ /* position of the first line of the scroll back buffer */
+ y0 = (this.y_base + this.h) % this.cur_h;
+ new_y_disp += y0;
+ if (new_y_disp >= this.cur_h)
+ new_y_disp -= this.cur_h;
+ if (new_y_disp != this.y_disp) {
+ this.y_disp = new_y_disp;
+ this.refresh(0, this.h - 1);
+ }
+}
+
+Term.prototype.mouseUpHandler = function (ev)
+{
+ this.thumb_el.onmouseup = null;
+ document.onmouseup = null;
+ document.onmousemove = null;
+ document.body.className = document.body.className.replace(" noSelect", "");
+}
+
+/* output queue to send back asynchronous responses */
+Term.prototype.queue_chars = function (str)
+{
+ this.output_queue += str;
+ if (this.output_queue)
+ setTimeout(this.outputHandler.bind(this), 0);
+};
+
+Term.prototype.outputHandler = function ()
+{
+ if (this.output_queue) {
+ this.handler(this.output_queue);
+ this.output_queue = "";
+ }
+};
+
+Term.prototype.getSize = function ()
+{
+ return [this.w, this.h];
+};
+
+/* resize the terminal (size in pixels). Return true if the display
+ size was modified. */
+/* XXX: may be simpler to separate the scrollback buffer from the
+ screen buffer */
+Term.prototype.resizePixel = function (new_width, new_height)
+{
+ var new_w, new_h, y, x, line, c, row_el, d, new_cur_h, e;
+
+ if (new_width == this.term_width && new_height == this.term_height)
+ return false;
+ new_w = Math.floor((new_width - this.scrollbar_width) /
+ this.char_width);
+ new_h = Math.floor(new_height / this.char_height);
+ if (new_w <= 0 || new_h <= 0 || new_h > this.tot_h)
+ return false;
+
+ this.term_width = new_width;
+ this.term_height = new_height;
+ this.term_el.style.width = this.term_width + "px";
+ this.term_el.style.height = this.term_height + "px";
+
+ /* XXX: could keep the EOL positions */
+ if (new_w < this.w) {
+ /* reduce the line width */
+ for(y = 0; y < this.cur_h;y++) {
+ line = this.lines[y];
+ line = line.slice(0, new_w);
+ }
+ } else if (new_w > this.w) {
+ /* increase the line width */
+ c = 32 | (this.def_attr << 16);
+ for(y = 0; y < this.cur_h;y++) {
+ line = this.lines[y];
+ for(x = this.w; x < new_w; x++)
+ line[x] = c;
+ }
+ }
+
+ if (this.x >= new_w)
+ this.x = new_w - 1;
+
+ d = new_h - this.h;
+ if (d < 0) {
+ d = -d;
+ /* remove displayed lines */
+
+ /* strip the DOM terminal content */
+ for(y = new_h; y < this.h; y++) {
+ row_el = this.rows_el[y];
+ this.content_el.removeChild(row_el);
+ }
+ this.rows_el = this.rows_el.slice(0, new_h);
+
+ /* adjust cursor position if needed */
+ if (this.y >= new_h) {
+ if (d > this.y)
+ d = this.y;
+ this.y -= d;
+ this.y_base += d;
+ if (this.y_base >= this.tot_h)
+ this.y_base -= this.tot_h;
+ }
+
+ if (this.scroll_bottom > new_h)
+ this.scroll_bottom = new_h;
+ /* fail safe for scroll top */
+ if (this.scroll_top >= this.scroll_bottom)
+ this.scroll_top = 0;
+
+ } else if (d > 0) {
+ /* add displayed lines */
+
+ if (this.cur_h == this.tot_h) {
+ if (d > this.tot_h - this.h)
+ d = this.tot_h - this.h;
+ } else {
+ if (d > this.y_base)
+ d = this.y_base;
+ }
+ this.y_base -= d;
+ if (this.y_base < 0)
+ this.y_base += this.tot_h;
+ this.y += d;
+
+ if (this.scroll_bottom == this.h)
+ this.scroll_bottom = new_h;
+
+ /* extend the DOM terminal content */
+ for(y = this.h; y < new_h; y++) {
+ row_el = document.createElement("div");
+ this.rows_el.push(row_el);
+ this.content_el.appendChild(row_el);
+ }
+ }
+
+ if (this.cur_h < this.tot_h) {
+ new_cur_h = this.y_base + new_h;
+ if (new_cur_h < this.cur_h) {
+ /* remove lines in the scroll back buffer */
+ this.lines = this.lines.slice(0, new_cur_h);
+ } else if (new_cur_h > this.cur_h) {
+ /* add lines in the scroll back buffer */
+ c = 32 | (this.def_attr << 16);
+ for(y = this.cur_h; y < new_cur_h; y++) {
+ line = new Array();
+ for(x = 0; x < new_w; x++)
+ line[x] = c;
+ this.lines[y] = line;
+ }
+ }
+ this.cur_h = new_cur_h;
+ }
+
+ this.w = new_w;
+ this.h = new_h;
+
+ if (this.y >= this.h)
+ this.y = this.h - 1;
+
+ /* reset display position */
+ this.y_disp = this.y_base;
+/*
+ console.log("lines.length", this.lines.length, "cur_h", this.cur_h,
+ "y_base", this.y_base, "h", this.h,
+ "scroll_bottom", this.scroll_bottom);
+*/
+ this.refresh(0, this.h - 1);
+ return true;
+}
\ No newline at end of file
diff --git a/index.md b/index.md
index 907a41e8..dd59b4b5 100644
--- a/index.md
+++ b/index.md
@@ -39,3 +39,8 @@ as fork()).
## Documentation
Extensive documentation can be found [here]({{ site.baseurl }}/docs/latest).
+
+
+## Online Demo
+
+Try the online demo [here]({{ site.baseurl }}/demo).