Mercurial > hg > rlgwebd
view shterm.js @ 213:08665c56c6a0 default tip
Fix race condition related to watching DGL games.
It's possible for a dgamelaunch game to end and cause rlgwebd to stop watching
it before rlgwebd has started watching it.
author | John "Elwin" Edwards |
---|---|
date | Sun, 16 Aug 2020 20:56:18 -0400 |
parents | c4a32007d2dc |
children |
line wrap: on
line source
/* shterm.js: browser-side JavaScript to handle I/O for termemu.js when it * is running a shell via the webtty.js server. */ var isalive = false; // Whether the session is currently active. var conn = null; // WebSocket function writeData(hexstr) { var codenum; var codes = []; var nc; var u8wait = 0; /* Stores bits from previous bytes of multibyte sequences. */ var expect = 0; /* The number of 10------ bytes expected. */ /* UTF-8 translation. */ for (var i = 0; i < hexstr.length; i += 2) { nc = Number("0x" + hexstr.substr(i, 2)); if (nc < 0x7F) { /* 0------- */ codes.push(nc); /* Any incomplete sequence will be discarded. */ u8wait = 0; expect = 0; } else if (nc < 0xC0) { /* 10------ : part of a multibyte sequence */ if (expect > 0) { u8wait <<= 6; u8wait += (nc & 0x3F); expect--; if (expect == 0) { codes.push(u8wait); u8wait = 0; } } else { /* Assume an initial byte was missed. */ u8wait = 0; } } /* These will all discard any incomplete sequence. */ else if (nc < 0xE0) { /* 110----- : introduces 2-byte sequence */ u8wait = (nc & 0x1F); expect = 1; } else if (nc < 0xF0) { /* 1110---- : introduces 3-byte sequence */ u8wait = (nc & 0x0F); expect = 2; } else if (nc < 0xF8) { /* 11110--- : introduces 4-byte sequence */ u8wait = (nc & 0x07); expect = 3; } else if (nc < 0xFC) { /* 111110-- : introduces 5-byte sequence */ u8wait = (nc & 0x03); expect = 4; } else if (nc < 0xFE) { /* 1111110- : introduces 6-byte sequence */ u8wait = (nc & 0x01); expect = 5; } else { /* 1111111- : should never appear */ u8wait = 0; expect = 0; } /* Supporting all 31 bits is probably overkill... */ } termemu.write(codes); return; } function sendback(str) { /* For responding to terminal queries. */ if (conn) { var msgObj = {"t": "d", "d": str}; conn.send(JSON.stringify(msgObj)); } else { debug(1, "No connection: cannot send " + str); } return; } function sendkey(ev) { if (!isalive) return; var keynum = ev.keyCode; var code; if (keynum >= 65 && keynum <= 90) { /* Letters. */ if (ev.ctrlKey) keynum -= 64; else if (!ev.shiftKey) keynum += 32; code = keynum.toString(16); if (code.length < 2) code = "0" + code; } else if (keynum >= 48 && keynum <= 57) { /* The number row. */ if (ev.shiftKey) { code = numShifts[keynum - 48].toString(16); } else { code = keynum.toString(16); } } else if (keynum in keyHexCodes) { if (ev.shiftKey) code = keyHexCodes[keynum][1]; else code = keyHexCodes[keynum][0]; } else if (keynum >= 16 && keynum <= 20) { return; } else { debug(1, "Ignoring keycode " + keynum); return; } ev.preventDefault(); var msgObj = {"t": "d", "d": code}; conn.send(JSON.stringify(msgObj)); //dkey(code); return; } var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c", ';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e" } var kpkeys = { "KP1": "1b4f46", "KP2": "1b4f42", "KP3": "1b5b367e", "KP4": "1b4f44", "KP5": "1b5b45", "KP6": "1b4f43", "KP7": "1b4f48", "KP8": "1b4f41", "KP9": "1b5b357e" }; function vkey(c) { if (!isalive) return; var keystr; if (c.match(/^[a-z]$/)) { if (termemu.ctrlp()) { var n = c.charCodeAt(0) - 96; keystr = n.toString(16); if (keystr.length < 2) keystr = "0" + keystr; } else if (termemu.shiftp()) keystr = c.toUpperCase().charCodeAt(0).toString(16); else keystr = c.charCodeAt(0).toString(16); } else if (c.match(/^[0-9]$/)) { if (termemu.shiftp()) keystr = numShifts[c.charCodeAt(0) - 48].toString(16); else keystr = c.charCodeAt(0).toString(16); } else if (c == '\n') keystr = "0a"; else if (c == '\t') keystr = "09"; else if (c == '\b') keystr = "08"; else if (c == ' ') keystr = "20"; else if (c in charshifts) { if (termemu.shiftp()) keystr = charshifts[c]; else keystr = c.charCodeAt(0).toString(16); } else if (c in kpkeys) { keystr = kpkeys[c]; } else return; //writeData("Sending " + keystr); var msgObj = {"t": "d", "d": keystr}; conn.send(JSON.stringify(msgObj)); return; } function setup() { keyHexCodes.init(); termemu.init("termwrap", 24, 80); setTitle("Not connected."); return; } function toggleshift() { termemu.toggleshift(); keydiv = document.getElementById("shiftkey"); if (termemu.shiftp()) keydiv.className = "keysel"; else keydiv.className = "key"; return; } function togglectrl() { termemu.togglectrl(); keydiv = document.getElementById("ctrlkey"); if (termemu.ctrlp()) keydiv.className = "keysel"; else keydiv.className = "key"; return; } function login(h, w) { if (conn) return; if (!(window.WebSocket)) { debug(1, "Cannot connect: WebSockets not supported"); return; } var sockurl = "ws://" + window.location.host + "/sock?w=" + w + "&h=" + h; conn = new WebSocket(sockurl); conn.onopen = function (event) { isalive = true; setTitle("Logged in"); debug(1, "Logged in via WebSocket"); } conn.onmessage = function (event) { var msgObj = JSON.parse(event.data); if (msgObj.t == 'l') { termemu.resize(msgObj.h, msgObj.w); } else if (msgObj.t == 'd') { debug(0, msgObj.d); writeData(msgObj.d); } else if (msgObj.t == 'q') { debug(0, "Quit message!"); conn.close(); } } conn.onclose = function (event) { conn = null; isalive = false; debug(1, "WebSocket connection closed."); setTitle("Not connected."); } } function stop() { if (conn) { conn.close(); } else { debug(1, "Cannot stop: connection already closed"); } return; } function setTitle(tstr) { var titlespan = document.getElementById("ttitle"); var tnode = document.createTextNode(tstr); if (titlespan.childNodes.length == 0) titlespan.appendChild(tnode); else titlespan.replaceChild(tnode, titlespan.childNodes[0]); return; } function debug(level, msg) { if (level < debugSuppress) return; var msgdiv = document.createElement("div"); var msgtext = document.createTextNode(msg); msgdiv.appendChild(msgtext); document.getElementById("debug").appendChild(msgdiv); return; } /* This should be a termemu method. */ function textsize(larger) { var cssSize = document.getElementById("term").style.fontSize; if (!cssSize) return; var match = cssSize.match(/\d*/); if (!match) return; var csize = Number(match[0]); var nsize; if (larger) { if (csize >= 48) nsize = 48; else if (csize >= 20) nsize = csize + 4; else if (csize >= 12) nsize = csize + 2; else if (csize >= 8) nsize = csize + 1; else nsize = 8; } else { if (csize <= 8) nsize = 8; else if (csize <= 12) nsize = csize - 1; else if (csize <= 20) nsize = csize - 2; else if (csize <= 48) nsize = csize - 4; else nsize = 48; } document.getElementById("term").style.fontSize = nsize.toString() + "px"; termemu.fixsize(); debug(1, "Changing font size to " + nsize.toString()); return; } function bell(on) { var imgnode = document.getElementById("bell"); if (on) { imgnode.style.visibility = "visible"; window.setTimeout(bell, 1500, false); } else imgnode.style.visibility = "hidden"; return; } function dkey(codestr) { var dstr = "Keystring: "; for (var i = 0; i < codestr.length; i += 2) { code = Number("0x" + codestr.substr(i, 2)); if (code < 32 || (code >= 127 && code < 160)) dstr += "\\x" + code.toString(16); else dstr += String.fromCharCode(code); } debug(1, dstr); return; }