Mercurial > hg > rlgwebd
view shterm.js @ 174:dc12ba30d559
Fix further crashes when following dgamelaunch games.
The crashes apparently resulted from reading a ttyrec header and
then trying to read the data chunk before dgamelaunch produced it.
When the data chunk did become available, it would be read by the
header function.
The simplest solution was to store the position for reading the
ttyrec file in the DGLSession, and to leave it unchanged if anything
unexpected occurs when reading.
author | John "Elwin" Edwards |
---|---|
date | Mon, 12 Jan 2015 17:10:35 +0000 |
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; }