From 5a8954b84a9a2dfdbb5b5a4203d3b1c0b9876f46 Mon Sep 17 00:00:00 2001 From: Aaron Riekenberg Date: Wed, 3 Jul 2024 19:17:14 -0500 Subject: [PATCH] Update wasm files. --- wasm/tcell.js | 348 +++++++++++++++++++++++++++------------------ wasm/termstyle.css | 131 +++++++++++------ 2 files changed, 297 insertions(+), 182 deletions(-) diff --git a/wasm/tcell.js b/wasm/tcell.js index b309248..2f14405 100644 --- a/wasm/tcell.js +++ b/wasm/tcell.js @@ -1,4 +1,4 @@ -// Copyright 2023 The TCell Authors +// Copyright 2024 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -12,202 +12,272 @@ // See the License for the specific language governing permissions and // limitations under the License. -const wasmFilePath = "tetris.wasm" -const term = document.getElementById("terminal") -var width = 80; var height = 24 +const wasmFilePath = "tetris.wasm"; +const term = document.getElementById("terminal"); +var width = 80; +var height = 24; const beepAudio = new Audio("beep.wav"); -var cx = -1; var cy = -1 -var cursorClass = "cursor-blinking-block" +var cx = -1; +var cy = -1; +var cursorClass = "cursor-blinking-block"; +var cursorColor = ""; -var content // {data: row[height], dirty: bool} +var content; // {data: row[height], dirty: bool} // row = {data: element[width], previous: span} // dirty/[previous being null] indicates if previous (or entire terminal) needs to be recalculated. // dirty is true/null if terminal/previous need to be re-calculated/shown function initialize() { - resize(width, height) // initialize content - show() // then show the screen + resize(width, height); // initialize content + show(); // then show the screen } function resize(w, h) { - - width = w - height = h - content = {data: new Array(height), dirty: true} - for (let i = 0; i < height; i++) { - content.data[i] = {data: new Array(width), previous: null} - } - - clearScreen() + width = w; + height = h; + content = { data: new Array(height), dirty: true }; + for (let i = 0; i < height; i++) { + content.data[i] = { data: new Array(width), previous: null }; + } + + clearScreen(); } function clearScreen(fg, bg) { - if (fg) { term.style.color = intToHex(fg) } - if (bg) { term.style.backgroundColor = intToHex(bg) } - - content.dirty = true - for (let i = 0; i < height; i++) { - content.data[i].previous = null // we set the row to be recalculated later - for (let j = 0; j < width; j++) { - content.data[i].data[j] = document.createTextNode(" ") // set the entire row to spaces. - } + if (fg) { + term.style.color = intToHex(fg); + } + if (bg) { + term.style.backgroundColor = intToHex(bg); + } + + content.dirty = true; + for (let i = 0; i < height; i++) { + content.data[i].previous = null; // we set the row to be recalculated later + for (let j = 0; j < width; j++) { + content.data[i].data[j] = document.createTextNode(" "); // set the entire row to spaces. } + } } -function drawCell(x, y, mainc, combc, fg, bg, attrs) { - var combString = String.fromCharCode(mainc) - combc.forEach(char => {combString += String.fromCharCode(char)}); - - var span = document.createElement("span") - var use = false - - if ((attrs & (1<<2)) != 0) { // reverse video - var temp = bg - bg = fg - fg = temp - use = true +function drawCell(x, y, s, fg, bg, attrs, us, uc) { + var span = document.createElement("span"); + var use = false; + + if ((attrs & (1 << 2)) != 0) { + // reverse video + var temp = bg; + bg = fg; + fg = temp; + use = true; + } + if (fg != -1) { + span.style.color = intToHex(fg); + use = true; + } + if (bg != -1) { + span.style.backgroundColor = intToHex(bg); + use = true; + } + + // NB: these has to be updated if Attrs.go changes + if (attrs != 0) { + use = true; + if ((attrs & 1) != 0) { + span.classList.add("bold"); } - if (fg != -1) { span.style.color = intToHex(fg); use = true } - if (bg != -1) { span.style.backgroundColor = intToHex(bg); use = true } - - if (attrs != 0) { - use = true - if ((attrs & 1) != 0) { span.classList.add("bold") } - if ((attrs & (1<<1)) != 0) { span.classList.add("blink") } - if ((attrs & (1<<3)) != 0) { span.classList.add("underline") } - if ((attrs & (1<<4)) != 0) { span.classList.add("dim") } - if ((attrs & (1<<5)) != 0) { span.classList.add("italic") } - if ((attrs & (1<<6)) != 0) { span.classList.add("strikethrough") } + if ((attrs & (1 << 4)) != 0) { + span.classList.add("dim"); } - - var textnode = document.createTextNode(combString) - span.appendChild(textnode) - - content.dirty = true // invalidate terminal- new cell - content.data[y].previous = null // invalidate row- new row - content.data[y].data[x] = use ? span : textnode + if ((attrs & (1 << 5)) != 0) { + span.classList.add("italic"); + } + if ((attrs & (1 << 6)) != 0) { + span.classList.add("strikethrough"); + } + } + if (us != 0) { + use = true; + if (us == 1) { + span.classList.add("underline"); + } else if (us == 2) { + span.classList.add("double_underline"); + } else if (us == 3) { + span.classList.add("curly_underline"); + } else if (us == 4) { + span.classList.add("dotted_underline"); + } else if (us == 5) { + span.classList.add("dashed_underline"); + } + if (uc != -1) { + span.style.textDecorationColor = intToHex(uc); + } + } + + if ((attrs & (1 << 1)) != 0) { + var blink = document.createElement("span"); + blink.classList.add("blink"); + var textnode = document.createTextNode(s); + blink.appendChild(textnode); + span.appendChild(blink); + } else { + var textnode = document.createTextNode(s); + span.appendChild(textnode); + } + + content.dirty = true; // invalidate terminal- new cell + content.data[y].previous = null; // invalidate row- new row + content.data[y].data[x] = use ? span : textnode; } function show() { - if (!content.dirty) { - return // no new draws; no need to update + if (!content.dirty) { + return; // no new draws; no need to update + } + + displayCursor(); + + term.innerHTML = ""; + content.data.forEach((row) => { + if (row.previous == null) { + row.previous = document.createElement("span"); + row.data.forEach((c) => { + row.previous.appendChild(c); + }); + row.previous.appendChild(document.createTextNode("\n")); } + term.appendChild(row.previous); + }); - displayCursor() - - term.innerHTML = "" - content.data.forEach(row => { - if (row.previous == null) { - row.previous = document.createElement("span") - row.data.forEach(c => { - row.previous.appendChild(c) - }) - row.previous.appendChild(document.createTextNode("\n")) - } - term.appendChild(row.previous) - }) - - content.dirty = false + content.dirty = false; } function showCursor(x, y) { - content.dirty = true + content.dirty = true; - if (!(cx < 0 || cy < 0)) { // if original position is a valid cursor position - content.data[cy].previous = null; - if (content.data[cy].data[cx].classList) { - content.data[cy].data[cx].classList.remove(cursorClass) - } + if (!(cx < 0 || cy < 0)) { + // if original position is a valid cursor position + content.data[cy].previous = null; + if (content.data[cy].data[cx].classList) { + content.data[cy].data[cx].classList.remove(cursorClass); } + } - cx = x - cy = y + cx = x; + cy = y; } function displayCursor() { - content.dirty = true + content.dirty = true; - if (!(cx < 0 || cy < 0)) { // if new position is a valid cursor position - content.data[cy].previous = null; + if (!(cx < 0 || cy < 0)) { + // if new position is a valid cursor position + content.data[cy].previous = null; - if (!content.data[cy].data[cx].classList) { - var span = document.createElement("span") - span.appendChild(content.data[cy].data[cx]) - content.data[cy].data[cx] = span - } - - content.data[cy].data[cx].classList.add(cursorClass) + if (!content.data[cy].data[cx].classList) { + var span = document.createElement("span"); + span.appendChild(content.data[cy].data[cx]); + content.data[cy].data[cx] = span; } -} -function setCursorStyle(newClass) { - if (newClass == cursorClass) { - return + if (cursorColor != "") { + term.style.setProperty("--cursor-color", cursorColor); + } else { + term.style.setProperty("--cursor-color", "lightgrey"); } - if (!(cx < 0 || cy < 0)) { - // mark cursor row as dirty; new class has been applied to (cx, cy) - content.dirty = true - content.data[cy].previous = null + content.data[cy].data[cx].classList.add(cursorClass); + } +} - if (content.data[cy].data[cx].classList) { - content.data[cy].data[cx].classList.remove(cursorClass) - } +function setCursorStyle(newClass, newColor) { + if (newClass == cursorClass && newColor == cursorColor) { + return; + } - // adding the new class will be dealt with when displayCursor() is called + if (!(cx < 0 || cy < 0)) { + // mark cursor row as dirty; new class has been applied to (cx, cy) + content.dirty = true; + content.data[cy].previous = null; + + if (content.data[cy].data[cx].classList) { + content.data[cy].data[cx].classList.remove(cursorClass); } - cursorClass = newClass + // adding the new class will be dealt with when displayCursor() is called + } + + cursorClass = newClass; + cursorColor = newColor; } function beep() { - beepAudio.currentTime = 0; - beepAudio.play(); + beepAudio.currentTime = 0; + beepAudio.play(); } -function intToHex(n) { - return "#" + n.toString(16).padStart(6, '0') +function setTitle(title) { + document.title = title; } -initialize() - -let fontwidth = term.clientWidth / width -let fontheight = term.clientHeight / height +function intToHex(n) { + return "#" + n.toString(16).padStart(6, "0"); +} -document.addEventListener("keydown", e => { - onKeyEvent(e.key, e.shiftKey, e.altKey, e.ctrlKey, e.metaKey) -}) +initialize(); -term.addEventListener("click", e => { - onMouseClick(Math.min((e.offsetX / fontwidth) | 0, width-1), Math.min((e.offsetY / fontheight) | 0, height-1), e.which, e.shiftKey, e.altKey, e.ctrlKey) -}) +let fontwidth = term.clientWidth / width; +let fontheight = term.clientHeight / height; -term.addEventListener("mousemove", e => { - onMouseMove(Math.min((e.offsetX / fontwidth) | 0, width-1), Math.min((e.offsetY / fontheight) | 0, height-1), e.which, e.shiftKey, e.altKey, e.ctrlKey) -}) +document.addEventListener("keydown", (e) => { + onKeyEvent(e.key, e.shiftKey, e.altKey, e.ctrlKey, e.metaKey); +}); -term.addEventListener("focus", e => { - onFocus(true) -}) +term.addEventListener("click", (e) => { + onMouseClick( + Math.min((e.offsetX / fontwidth) | 0, width - 1), + Math.min((e.offsetY / fontheight) | 0, height - 1), + e.which, + e.shiftKey, + e.altKey, + e.ctrlKey + ); +}); -term.addEventListener("blur", e => { - onFocus(false) -}) -term.tabIndex = 0 +term.addEventListener("mousemove", (e) => { + onMouseMove( + Math.min((e.offsetX / fontwidth) | 0, width - 1), + Math.min((e.offsetY / fontheight) | 0, height - 1), + e.which, + e.shiftKey, + e.altKey, + e.ctrlKey + ); +}); +term.addEventListener("focus", (e) => { + onFocus(true); +}); -document.addEventListener("paste", e => { - e.preventDefault(); - var text = (e.originalEvent || e).clipboardData.getData('text/plain'); - onPaste(true) - for (let i = 0; i < text.length; i++) { - onKeyEvent(text.charAt(i), false, false, false, false) - } - onPaste(false) +term.addEventListener("blur", (e) => { + onFocus(false); +}); +term.tabIndex = 0; + +document.addEventListener("paste", (e) => { + e.preventDefault(); + var text = (e.originalEvent || e).clipboardData.getData("text/plain"); + onPaste(true); + for (let i = 0; i < text.length; i++) { + onKeyEvent(text.charAt(i), false, false, false, false); + } + onPaste(false); }); const go = new Go(); -WebAssembly.instantiateStreaming(fetch(wasmFilePath), go.importObject).then((result) => { +WebAssembly.instantiateStreaming(fetch(wasmFilePath), go.importObject).then( + (result) => { go.run(result.instance); -}); + } +); diff --git a/wasm/termstyle.css b/wasm/termstyle.css index 0cb47fc..f4f4e06 100644 --- a/wasm/termstyle.css +++ b/wasm/termstyle.css @@ -1,72 +1,117 @@ * { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-family: "Menlo", "Andale Mono", "Courier New", Monospace; + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-family: "Menlo", "Andale Mono", "Courier New", Monospace; } -#terminal-container { - text-align: center; +#terminal { + background-color: black; + color: green; + display: inline-block; + + /* Copy paste! */ + user-select: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + --cursor-color: lightgrey; } -#terminal { - border: 1px solid var(--fg); +/* Style attributes */ - background-color: white; - color: green; - display: inline-block; +.bold { + font-weight: bold; +} - /* Copy paste! */ - user-select: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; +.blink { + animation: blinker 1s step-start infinite; } -/* Style attributes */ +.underline { + text-decoration: underline; +} -.bold { font-weight: bold; } +.dim { + filter: brightness(50); +} -.blink { animation: blinker 1s step-start infinite; } +.italic { + font-style: italic; +} -.underline { text-decoration: underline; } +.strikethrough { + text-decoration: line-through; +} + +.double_underline { + text-decoration: underline double; +} -.dim { filter: brightness(50) } +.curly_underline { + text-decoration: underline wavy; +} -.italic { font-style: italic; } +.dotted_underline { + text-decoration: underline dotted; +} -.strikethrough { text-decoration: line-through; } +.dashed_underline { + text-decoration: underline dashed; +} /* Cursor styles */ -.cursor-steady-block { background-color: lightgrey !important; } -.cursor-blinking-block { animation: blinking-block 1s step-start infinite !important; } -@keyframes blinking-block { 50% { background-color: lightgrey; } } +.cursor-steady-block { + background-color: var(--cursor-color) !important; +} +.cursor-blinking-block { + animation: blinking-block 1s step-start infinite !important; +} +@keyframes blinking-block { + 50% { + background-color: var(--cursor-color); + } +} -.cursor-steady-underline { text-decoration: underline lightgrey !important; } -.cursor-blinking-underline { animation: blinking-underline 1s step-start infinite !important; } -@keyframes blinking-underline { 50% { text-decoration: underline lightgrey; } } +.cursor-steady-underline { + text-decoration: underline var(--cursor-color) !important; +} +.cursor-blinking-underline { + animation: blinking-underline 1s step-start infinite !important; +} +@keyframes blinking-underline { + 50% { + text-decoration: underline var(--cursor-color); + } +} -.cursor-steady-bar { margin-left: -2px; } +.cursor-steady-bar { + margin-left: -2px; +} .cursor-steady-bar:before { - content: ' '; - width: 2px; - background-color: lightgrey !important; - display: inline-block; + content: " "; + width: 2px; + background-color: var(--cursor-color) !important; + display: inline-block; +} +.cursor-blinking-bar { + margin-left: -2px; } -.cursor-blinking-bar { margin-left: -2px; } .cursor-blinking-bar:before { - content: ' '; - width: 2px; - background-color: lightgrey !important; - display: inline-block; - animation: blinker 1s step-start infinite; + content: " "; + width: 2px; + background-color: var(--cursor-color) !important; + display: inline-block; + animation: blinker 1s step-start infinite; } /* General animations */ @keyframes blinker { - 50% { opacity: 0; } + 50% { + opacity: 0; + } }