Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8028f14387 | ||
|
|
daba5340f7 | ||
|
|
a82e14961c | ||
|
|
6c9ae7e126 | ||
|
|
a3c84f9a8f | ||
|
|
7aa3cd7f75 | ||
|
|
58ed6098c4 | ||
|
|
16a36a3d26 | ||
|
|
9d1cefeaab | ||
|
|
357600de8f | ||
|
|
fe699c7918 | ||
|
|
4edaa0687d | ||
|
|
f8ebae044a | ||
|
|
9e9539472c | ||
|
|
b57e0ee75b | ||
|
|
65e34a1634 |
+4
-3
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.7)
|
||||
# Do not attempt to build it with cmake, use make instead. #
|
||||
###################################################################
|
||||
|
||||
project(esp_vt100_firmware)
|
||||
project(ESPTerm)
|
||||
|
||||
set(CMAKE_CXX_STANDARD GNU99)
|
||||
|
||||
@@ -141,7 +141,7 @@ set(SOURCE_FILES
|
||||
user/apars_osc.c
|
||||
user/apars_osc.h
|
||||
user/apars_dcs.c
|
||||
user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h)
|
||||
user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h user/jstring.c user/jstring.h)
|
||||
|
||||
include_directories(include)
|
||||
include_directories(user)
|
||||
@@ -163,6 +163,7 @@ add_definitions(
|
||||
-DADMIN_PASSWORD="asdf"
|
||||
-DGIT_HASH="blabla"
|
||||
-DDEBUG_HEAP=1
|
||||
-DDEBUG_MALLOC=1
|
||||
-DESPFS_HEATSHRINK)
|
||||
|
||||
add_executable(esp_vt100_firmware ${SOURCE_FILES})
|
||||
add_executable(ESPTerm ${SOURCE_FILES})
|
||||
|
||||
+4
-1
@@ -50,5 +50,8 @@ GLOBAL_CFLAGS = \
|
||||
-DDEBUG_ANSI=1 \
|
||||
-DDEBUG_ANSI_NOIMPL=1 \
|
||||
-DHTTPD_MAX_BACKLOG_SIZE=8192 \
|
||||
-DHTTPD_MAX_HEAD_LEN=1024 \
|
||||
-DHTTPD_MAX_POST_LEN=512 \
|
||||
-DDEBUG_INPUT=0 \
|
||||
-DDEBUG_HEAP=1
|
||||
-DDEBUG_HEAP=1 \
|
||||
-DDEBUG_MALLOC=0
|
||||
|
||||
@@ -14,6 +14,8 @@ function process_html($s) {
|
||||
return $s;
|
||||
}
|
||||
|
||||
$no_tpl_files = ['help', 'cfg_wifi_conn'];
|
||||
|
||||
ob_start();
|
||||
foreach($_pages as $_k => $p) {
|
||||
if ($p->bodyclass == 'api') continue;
|
||||
@@ -30,7 +32,7 @@ foreach($_pages as $_k => $p) {
|
||||
// $s = process_html($s);
|
||||
|
||||
ob_clean(); // clean up
|
||||
$of = __DIR__ . '/../html/' . $_k . '.tpl';
|
||||
$of = __DIR__ . '/../html/' . $_k . (in_array($_k, $no_tpl_files) ? '.html' : '.tpl');
|
||||
file_put_contents($of, $s); // write to a file
|
||||
}
|
||||
|
||||
|
||||
+60
-17
@@ -416,8 +416,12 @@ a:hover {
|
||||
cursor: pointer; }
|
||||
|
||||
ul > * {
|
||||
padding-top: .2em;
|
||||
padding-bottom: .2em; }
|
||||
padding-top: .3em;
|
||||
padding-bottom: .3em; }
|
||||
|
||||
ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0; }
|
||||
|
||||
/* Main outer container */
|
||||
#outer {
|
||||
@@ -629,7 +633,36 @@ ul > * {
|
||||
.Box.mobopen .Row.explain {
|
||||
margin-top: 18px; } }
|
||||
|
||||
.Box.fold h2 {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 2px 1.3rem 2px 5px;
|
||||
margin: 0 -5px 0 -5px; }
|
||||
.Box.fold h2::after {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
content: '▸';
|
||||
top: 50%;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
transform: translate(0, -50%) rotate(90deg); }
|
||||
.Box.fold.expanded h2::after {
|
||||
transform: translate(-25%, -50%) rotate(-90deg);
|
||||
margin-bottom: 1rem; }
|
||||
.Box.fold .Row {
|
||||
display: none; }
|
||||
.Box.fold.expanded .Row {
|
||||
display: flex; }
|
||||
.Box.fold.expanded .Row.v {
|
||||
display: block; }
|
||||
|
||||
@media screen and (max-width: 544px) {
|
||||
.Box.fold h2, .Box.mobcol h2 {
|
||||
padding: 2px 1.3rem 2px 5px;
|
||||
margin: 0 -5px 0 -5px; }
|
||||
.Box.fold.expanded h2::after, .Box.mobcol.expanded h2::after {
|
||||
margin-bottom: 1rem; }
|
||||
|
||||
.Box.mobcol h2 {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
@@ -648,10 +681,12 @@ ul > * {
|
||||
margin-bottom: 1rem; }
|
||||
.Box.mobcol .Row {
|
||||
display: none; }
|
||||
.Box.mobcol #ap-box {
|
||||
display: none; }
|
||||
.Box.mobcol.expanded .Row {
|
||||
display: flex; }
|
||||
.Box.mobcol.expanded .Row.v {
|
||||
display: block; }
|
||||
.Box.mobcol #ap-box {
|
||||
display: none; }
|
||||
.Box.mobcol.expanded #ap-box {
|
||||
display: block; } }
|
||||
.Modal {
|
||||
@@ -875,6 +910,16 @@ form {
|
||||
margin-top: 0; }
|
||||
.Row:last-child {
|
||||
margin-bottom: 0; }
|
||||
.Row.v {
|
||||
display: block; }
|
||||
.Row .aside {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 5px; }
|
||||
@media screen and (max-width: 544px) {
|
||||
.Row .aside {
|
||||
margin: 0;
|
||||
float: none; } }
|
||||
.Row .spacer {
|
||||
width: 160px; }
|
||||
@media screen and (max-width: 544px) {
|
||||
@@ -1107,7 +1152,7 @@ body.term #screen {
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
font-family: monospace;
|
||||
font-size: 16pt;
|
||||
font-size: 20px;
|
||||
white-space: nowrap;
|
||||
background: #111213;
|
||||
padding: 6px;
|
||||
@@ -1115,12 +1160,11 @@ body.term #screen {
|
||||
border: 2px solid #3983CD; }
|
||||
body.term #screen span {
|
||||
white-space: pre;
|
||||
cursor: pointer; }
|
||||
body.term #screen span:hover {
|
||||
outline: 1px solid rgba(255, 255, 255, 0.4); }
|
||||
@media screen and (max-width: 544px) {
|
||||
body.term #screen span:hover {
|
||||
outline: 0 none; } }
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
line-height: 1.15em;
|
||||
width: 0.6em;
|
||||
overflow: visible; }
|
||||
body.term #buttons {
|
||||
margin-top: 10px;
|
||||
white-space: nowrap; }
|
||||
@@ -1605,20 +1649,19 @@ body.term #botnav {
|
||||
padding: 0.38198rem 0;
|
||||
text-align: center; }
|
||||
|
||||
.ansiref, .ansiref td, .ansiref th {
|
||||
.Row table, .Row table td, .Row table th {
|
||||
border: 1px solid #666; }
|
||||
.ansiref th, .ansiref td {
|
||||
.Row table th, .Row table td {
|
||||
white-space: normal; }
|
||||
.ansiref th {
|
||||
.Row table th {
|
||||
background-color: rgba(255, 255, 255, 0.1); }
|
||||
|
||||
.ansiref td:nth-child(1) {
|
||||
font-family: monospace; }
|
||||
.ansiref.w100 {
|
||||
width: 100%; }
|
||||
.ansiref.w100 td:nth-child(1) {
|
||||
width: 9em; }
|
||||
.ansiref.w100 td:nth-child(2) {
|
||||
width: 8em; }
|
||||
width: 6em; }
|
||||
|
||||
@media screen and (min-width: 545px) {
|
||||
.mq-phone {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
+178
-29
@@ -770,6 +770,14 @@
|
||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
||||
};
|
||||
|
||||
function isModifierPressed(mod) {
|
||||
if (mod=='control'||mod=='ctrl') return _mods[17];
|
||||
if (mod=='shift') return _mods[16];
|
||||
if (mod=='meta') return _mods[91];
|
||||
if (mod=='alt') return _mods[18];
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) {
|
||||
var key, handler, k, i, modifiersMatch, scope;
|
||||
@@ -995,6 +1003,7 @@
|
||||
global.key.deleteScope = deleteScope;
|
||||
global.key.filter = filter;
|
||||
global.key.isPressed = isPressed;
|
||||
global.key.isModifier = isModifierPressed;
|
||||
global.key.getPressedKeyCodes = getPressedKeyCodes;
|
||||
global.key.noConflict = noConflict;
|
||||
global.key.unbind = unbindKey;
|
||||
@@ -1216,7 +1225,7 @@ $.ready(function () {
|
||||
});
|
||||
|
||||
// Expanding boxes on mobile
|
||||
$('.Box.mobcol').forEach(function(x) {
|
||||
$('.Box.mobcol,.Box.fold').forEach(function(x) {
|
||||
var h = x.querySelector('h2');
|
||||
|
||||
var hdl = function() {
|
||||
@@ -1555,6 +1564,41 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
|
||||
w.init = wifiInit;
|
||||
w.startScanning = startScanning;
|
||||
})(window.WiFi = {});
|
||||
/** Decode two-byte number */
|
||||
function parse2B(s, i) {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
/** Decode three-byte number */
|
||||
function parse3B(s, i) {
|
||||
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127;
|
||||
}
|
||||
|
||||
function Chr(n) {
|
||||
return String.fromCharCode(n);
|
||||
}
|
||||
|
||||
function encode2B(n) {
|
||||
var lsb, msb;
|
||||
lsb = (n % 127);
|
||||
n = ((n - lsb) / 127);
|
||||
lsb += 1;
|
||||
msb = (n + 1);
|
||||
return Chr(lsb) + Chr(msb);
|
||||
}
|
||||
|
||||
function encode3B(n) {
|
||||
var lsb, msb, xsb;
|
||||
lsb = (n % 127);
|
||||
n = (n - lsb) / 127;
|
||||
lsb += 1;
|
||||
msb = (n % 127);
|
||||
n = (n - msb) / 127;
|
||||
msb += 1;
|
||||
xsb = (n + 1);
|
||||
return Chr(lsb) + Chr(msb) + Chr(xsb);
|
||||
}
|
||||
|
||||
var Screen = (function () {
|
||||
var W = 0, H = 0; // dimensions
|
||||
var inited = false;
|
||||
@@ -1567,12 +1611,14 @@ var Screen = (function () {
|
||||
bg: 0,
|
||||
attrs: 0,
|
||||
suppress: false, // do not turn on in blink interval (for safe moving)
|
||||
forceOn: false,
|
||||
hidden: false, // do not show
|
||||
hanging: false, // xenl
|
||||
};
|
||||
|
||||
var screen = [];
|
||||
var blinkIval;
|
||||
var cursorFlashStartIval;
|
||||
|
||||
var frakturExceptions = {
|
||||
'C': '\u212d',
|
||||
@@ -1679,8 +1725,21 @@ var Screen = (function () {
|
||||
(function() {
|
||||
var x = i % W;
|
||||
var y = Math.floor(i / W);
|
||||
e.addEventListener('click', function () {
|
||||
Input.onTap(y, x);
|
||||
e.addEventListener('mouseenter', function (evt) {
|
||||
Input.onMouseMove(x, y);
|
||||
});
|
||||
e.addEventListener('mousedown', function (evt) {
|
||||
Input.onMouseDown(x, y, evt.button+1);
|
||||
});
|
||||
e.addEventListener('mouseup', function (evt) {
|
||||
Input.onMouseUp(x, y, evt.button+1);
|
||||
});
|
||||
e.addEventListener('contextmenu', function (evt) {
|
||||
evt.preventDefault();
|
||||
});
|
||||
e.addEventListener('mousewheel', function (evt) {
|
||||
Input.onMouseWheel(x, y, evt.deltaY>0?1:-1);
|
||||
return false;
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -1717,14 +1776,14 @@ var Screen = (function () {
|
||||
}
|
||||
|
||||
if (!cursor.suppress) {
|
||||
_draw(_curCell(), cursor.a);
|
||||
_draw(_curCell(), cursor.forceOn || cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// blink attribute
|
||||
setInterval(function () {
|
||||
$('#screen').removeClass('blink-hide');
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
$('#screen').addClass('blink-hide');
|
||||
}, 800); // 200 ms ON
|
||||
}, 1000);
|
||||
@@ -1732,16 +1791,6 @@ var Screen = (function () {
|
||||
inited = true;
|
||||
}
|
||||
|
||||
/** Decode two-byte number */
|
||||
function parse2B(s, i) {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
/** Decode three-byte number */
|
||||
function parse3B(s, i) {
|
||||
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127;
|
||||
}
|
||||
|
||||
var SEQ_SET_COLOR_ATTR = 1;
|
||||
var SEQ_REPEAT = 2;
|
||||
var SEQ_SET_COLOR = 3;
|
||||
@@ -1752,6 +1801,8 @@ var Screen = (function () {
|
||||
|
||||
if (!inited) _init();
|
||||
|
||||
var cursorMoved;
|
||||
|
||||
// Set size
|
||||
num = parse2B(str, i); i += 2; // height
|
||||
num2 = parse2B(str, i); i += 2; // width
|
||||
@@ -1763,6 +1814,7 @@ var Screen = (function () {
|
||||
// Cursor position
|
||||
num = parse2B(str, i); i += 2; // row
|
||||
num2 = parse2B(str, i); i += 2; // col
|
||||
cursorMoved = (cursor.x != num2 || cursor.y != num);
|
||||
cursorSet(num, num2);
|
||||
// console.log("Cursor at ",num, num2);
|
||||
|
||||
@@ -1826,9 +1878,17 @@ var Screen = (function () {
|
||||
|
||||
_drawAll();
|
||||
|
||||
if (!cursor.hidden || cursor.hanging) {
|
||||
// hide cursor asap
|
||||
_draw(_curCell(), false);
|
||||
// if (!cursor.hidden || cursor.hanging || !cursor.suppress) {
|
||||
// // hide cursor asap
|
||||
// _draw(_curCell(), false);
|
||||
// }
|
||||
|
||||
if (cursorMoved) {
|
||||
cursor.forceOn = true;
|
||||
cursorFlashStartIval = setTimeout(function() {
|
||||
cursor.forceOn = false;
|
||||
}, 1200);
|
||||
_draw(_curCell(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1897,6 +1957,8 @@ var Screen = (function () {
|
||||
/** Handle connections */
|
||||
var Conn = (function() {
|
||||
var ws;
|
||||
var heartbeatTout;
|
||||
var pingIv;
|
||||
|
||||
function onOpen(evt) {
|
||||
console.log("CONNECTED");
|
||||
@@ -1913,9 +1975,13 @@ var Conn = (function() {
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
//console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
// . = heartbeat
|
||||
if (evt.data != '.') {
|
||||
//console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
}
|
||||
heartbeat();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -1936,6 +2002,8 @@ var Conn = (function() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
heartbeat();
|
||||
|
||||
ws = new WebSocket("ws://"+_root+"/term/update.ws");
|
||||
ws.onopen = onOpen;
|
||||
ws.onclose = onClose;
|
||||
@@ -1948,11 +2016,33 @@ var Conn = (function() {
|
||||
if (status !== 200) location.reload(true);
|
||||
console.log("Data received!");
|
||||
Screen.load(resp);
|
||||
heartbeat();
|
||||
|
||||
showPage();
|
||||
});
|
||||
}
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(heartbeatTout);
|
||||
heartbeatTout = setTimeout(heartbeatFail, 2000);
|
||||
}
|
||||
|
||||
function heartbeatFail() {
|
||||
console.error("Heartbeat lost, probing server...");
|
||||
pingIv = setInterval(function() {
|
||||
console.log("> ping");
|
||||
$.get('http://'+_root+'/system/ping', function(resp, status) {
|
||||
if (status == 200) {
|
||||
clearInterval(pingIv);
|
||||
console.info("Server ready, reloading page...");
|
||||
location.reload();
|
||||
}
|
||||
}, {
|
||||
timeout: 100,
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return {
|
||||
ws: null,
|
||||
init: init,
|
||||
@@ -1960,7 +2050,22 @@ var Conn = (function() {
|
||||
};
|
||||
})();
|
||||
|
||||
/** User input */
|
||||
/**
|
||||
* User input
|
||||
*
|
||||
* --- Rx messages: ---
|
||||
* S - screen content (binary encoding of the entire screen with simple compression)
|
||||
* T - text labels - Title and buttons, \0x01-separated
|
||||
* B - beep
|
||||
* . - heartbeat
|
||||
*
|
||||
* --- Tx messages ---
|
||||
* s - string
|
||||
* b - action button
|
||||
* p - mb press
|
||||
* r - mb release
|
||||
* m - mouse move
|
||||
*/
|
||||
var Input = (function() {
|
||||
var opts = {
|
||||
np_alt: false,
|
||||
@@ -1969,15 +2074,11 @@ var Input = (function() {
|
||||
};
|
||||
|
||||
function sendStrMsg(str) {
|
||||
Conn.send("STR:"+str);
|
||||
}
|
||||
|
||||
function sendPosMsg(y, x) {
|
||||
Conn.send("TAP:"+y+','+x);
|
||||
Conn.send("s"+str);
|
||||
}
|
||||
|
||||
function sendBtnMsg(n) {
|
||||
Conn.send("BTN:"+n);
|
||||
Conn.send("b"+Chr(n));
|
||||
}
|
||||
|
||||
function fa(alt, normal) {
|
||||
@@ -2098,6 +2199,10 @@ var Input = (function() {
|
||||
_bindFnKeys();
|
||||
}
|
||||
|
||||
var mb1 = 0;
|
||||
var mb2 = 0;
|
||||
var mb3 = 0;
|
||||
|
||||
function init() {
|
||||
_initKeys();
|
||||
|
||||
@@ -2107,11 +2212,30 @@ var Input = (function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('mousedown', function(evt) {
|
||||
if (evt.button == 0) mb1 = 1;
|
||||
if (evt.button == 1) mb2 = 1;
|
||||
if (evt.button == 2) mb3 = 1;
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', function(evt) {
|
||||
if (evt.button == 0) mb1 = 0;
|
||||
if (evt.button == 1) mb2 = 0;
|
||||
if (evt.button == 2) mb3 = 0;
|
||||
});
|
||||
}
|
||||
|
||||
function packModifiersForMouse() {
|
||||
return (key.isModifier('ctrl')?1:0) |
|
||||
(key.isModifier('shift')?2:0) |
|
||||
(key.isModifier('alt')?4:0) |
|
||||
(key.isModifier('meta')?8:0);
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onTap: sendPosMsg,
|
||||
// onTap: sendPosMsg,
|
||||
sendString: sendStrMsg,
|
||||
setAlts: function(cu, np, fn) {
|
||||
if (opts.cu_alt != cu || opts.np_alt != np || opts.fn_alt != fn) {
|
||||
@@ -2123,6 +2247,31 @@ var Input = (function() {
|
||||
_bindFnKeys();
|
||||
}
|
||||
},
|
||||
onMouseMove: function (x, y) {
|
||||
var b = mb1 ? 1 : mb2 ? 2 : mb3 ? 3 : 0;
|
||||
var m = packModifiersForMouse();
|
||||
Conn.send("m" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
},
|
||||
onMouseDown: function (x, y, b) {
|
||||
if (b > 3 || b < 1) return;
|
||||
var m = packModifiersForMouse();
|
||||
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
console.log("B ",b," M ",m);
|
||||
},
|
||||
onMouseUp: function (x, y, b) {
|
||||
if (b > 3 || b < 1) return;
|
||||
var m = packModifiersForMouse();
|
||||
Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
console.log("B ",b," M ",m);
|
||||
},
|
||||
onMouseWheel: function (x, y, dir) {
|
||||
// -1 ... btn 4 (away from user)
|
||||
// +1 ... btn 5 (towards user)
|
||||
var m = packModifiersForMouse();
|
||||
var b = (dir < 0 ? 4 : 5);
|
||||
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
console.log("B ",b," M ",m);
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ $.ready(function () {
|
||||
});
|
||||
|
||||
// Expanding boxes on mobile
|
||||
$('.Box.mobcol').forEach(function(x) {
|
||||
$('.Box.mobcol,.Box.fold').forEach(function(x) {
|
||||
var h = x.querySelector('h2');
|
||||
|
||||
var hdl = function() {
|
||||
|
||||
@@ -67,6 +67,14 @@
|
||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
||||
};
|
||||
|
||||
function isModifierPressed(mod) {
|
||||
if (mod=='control'||mod=='ctrl') return _mods[17];
|
||||
if (mod=='shift') return _mods[16];
|
||||
if (mod=='meta') return _mods[91];
|
||||
if (mod=='alt') return _mods[18];
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) {
|
||||
var key, handler, k, i, modifiersMatch, scope;
|
||||
@@ -292,6 +300,7 @@
|
||||
global.key.deleteScope = deleteScope;
|
||||
global.key.filter = filter;
|
||||
global.key.isPressed = isPressed;
|
||||
global.key.isModifier = isModifierPressed;
|
||||
global.key.getPressedKeyCodes = getPressedKeyCodes;
|
||||
global.key.noConflict = noConflict;
|
||||
global.key.unbind = unbindKey;
|
||||
|
||||
+168
-28
@@ -1,3 +1,38 @@
|
||||
/** Decode two-byte number */
|
||||
function parse2B(s, i) {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
/** Decode three-byte number */
|
||||
function parse3B(s, i) {
|
||||
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127;
|
||||
}
|
||||
|
||||
function Chr(n) {
|
||||
return String.fromCharCode(n);
|
||||
}
|
||||
|
||||
function encode2B(n) {
|
||||
var lsb, msb;
|
||||
lsb = (n % 127);
|
||||
n = ((n - lsb) / 127);
|
||||
lsb += 1;
|
||||
msb = (n + 1);
|
||||
return Chr(lsb) + Chr(msb);
|
||||
}
|
||||
|
||||
function encode3B(n) {
|
||||
var lsb, msb, xsb;
|
||||
lsb = (n % 127);
|
||||
n = (n - lsb) / 127;
|
||||
lsb += 1;
|
||||
msb = (n % 127);
|
||||
n = (n - msb) / 127;
|
||||
msb += 1;
|
||||
xsb = (n + 1);
|
||||
return Chr(lsb) + Chr(msb) + Chr(xsb);
|
||||
}
|
||||
|
||||
var Screen = (function () {
|
||||
var W = 0, H = 0; // dimensions
|
||||
var inited = false;
|
||||
@@ -10,12 +45,14 @@ var Screen = (function () {
|
||||
bg: 0,
|
||||
attrs: 0,
|
||||
suppress: false, // do not turn on in blink interval (for safe moving)
|
||||
forceOn: false,
|
||||
hidden: false, // do not show
|
||||
hanging: false, // xenl
|
||||
};
|
||||
|
||||
var screen = [];
|
||||
var blinkIval;
|
||||
var cursorFlashStartIval;
|
||||
|
||||
var frakturExceptions = {
|
||||
'C': '\u212d',
|
||||
@@ -122,8 +159,21 @@ var Screen = (function () {
|
||||
(function() {
|
||||
var x = i % W;
|
||||
var y = Math.floor(i / W);
|
||||
e.addEventListener('click', function () {
|
||||
Input.onTap(y, x);
|
||||
e.addEventListener('mouseenter', function (evt) {
|
||||
Input.onMouseMove(x, y);
|
||||
});
|
||||
e.addEventListener('mousedown', function (evt) {
|
||||
Input.onMouseDown(x, y, evt.button+1);
|
||||
});
|
||||
e.addEventListener('mouseup', function (evt) {
|
||||
Input.onMouseUp(x, y, evt.button+1);
|
||||
});
|
||||
e.addEventListener('contextmenu', function (evt) {
|
||||
evt.preventDefault();
|
||||
});
|
||||
e.addEventListener('mousewheel', function (evt) {
|
||||
Input.onMouseWheel(x, y, evt.deltaY>0?1:-1);
|
||||
return false;
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -160,14 +210,14 @@ var Screen = (function () {
|
||||
}
|
||||
|
||||
if (!cursor.suppress) {
|
||||
_draw(_curCell(), cursor.a);
|
||||
_draw(_curCell(), cursor.forceOn || cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// blink attribute
|
||||
setInterval(function () {
|
||||
$('#screen').removeClass('blink-hide');
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
$('#screen').addClass('blink-hide');
|
||||
}, 800); // 200 ms ON
|
||||
}, 1000);
|
||||
@@ -175,16 +225,6 @@ var Screen = (function () {
|
||||
inited = true;
|
||||
}
|
||||
|
||||
/** Decode two-byte number */
|
||||
function parse2B(s, i) {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
/** Decode three-byte number */
|
||||
function parse3B(s, i) {
|
||||
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127;
|
||||
}
|
||||
|
||||
var SEQ_SET_COLOR_ATTR = 1;
|
||||
var SEQ_REPEAT = 2;
|
||||
var SEQ_SET_COLOR = 3;
|
||||
@@ -195,6 +235,8 @@ var Screen = (function () {
|
||||
|
||||
if (!inited) _init();
|
||||
|
||||
var cursorMoved;
|
||||
|
||||
// Set size
|
||||
num = parse2B(str, i); i += 2; // height
|
||||
num2 = parse2B(str, i); i += 2; // width
|
||||
@@ -206,6 +248,7 @@ var Screen = (function () {
|
||||
// Cursor position
|
||||
num = parse2B(str, i); i += 2; // row
|
||||
num2 = parse2B(str, i); i += 2; // col
|
||||
cursorMoved = (cursor.x != num2 || cursor.y != num);
|
||||
cursorSet(num, num2);
|
||||
// console.log("Cursor at ",num, num2);
|
||||
|
||||
@@ -269,9 +312,17 @@ var Screen = (function () {
|
||||
|
||||
_drawAll();
|
||||
|
||||
if (!cursor.hidden || cursor.hanging) {
|
||||
// hide cursor asap
|
||||
_draw(_curCell(), false);
|
||||
// if (!cursor.hidden || cursor.hanging || !cursor.suppress) {
|
||||
// // hide cursor asap
|
||||
// _draw(_curCell(), false);
|
||||
// }
|
||||
|
||||
if (cursorMoved) {
|
||||
cursor.forceOn = true;
|
||||
cursorFlashStartIval = setTimeout(function() {
|
||||
cursor.forceOn = false;
|
||||
}, 1200);
|
||||
_draw(_curCell(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +391,8 @@ var Screen = (function () {
|
||||
/** Handle connections */
|
||||
var Conn = (function() {
|
||||
var ws;
|
||||
var heartbeatTout;
|
||||
var pingIv;
|
||||
|
||||
function onOpen(evt) {
|
||||
console.log("CONNECTED");
|
||||
@@ -356,9 +409,13 @@ var Conn = (function() {
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
//console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
// . = heartbeat
|
||||
if (evt.data != '.') {
|
||||
//console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
}
|
||||
heartbeat();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -379,6 +436,8 @@ var Conn = (function() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
heartbeat();
|
||||
|
||||
ws = new WebSocket("ws://"+_root+"/term/update.ws");
|
||||
ws.onopen = onOpen;
|
||||
ws.onclose = onClose;
|
||||
@@ -391,11 +450,33 @@ var Conn = (function() {
|
||||
if (status !== 200) location.reload(true);
|
||||
console.log("Data received!");
|
||||
Screen.load(resp);
|
||||
heartbeat();
|
||||
|
||||
showPage();
|
||||
});
|
||||
}
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(heartbeatTout);
|
||||
heartbeatTout = setTimeout(heartbeatFail, 2000);
|
||||
}
|
||||
|
||||
function heartbeatFail() {
|
||||
console.error("Heartbeat lost, probing server...");
|
||||
pingIv = setInterval(function() {
|
||||
console.log("> ping");
|
||||
$.get('http://'+_root+'/system/ping', function(resp, status) {
|
||||
if (status == 200) {
|
||||
clearInterval(pingIv);
|
||||
console.info("Server ready, reloading page...");
|
||||
location.reload();
|
||||
}
|
||||
}, {
|
||||
timeout: 100,
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return {
|
||||
ws: null,
|
||||
init: init,
|
||||
@@ -403,7 +484,22 @@ var Conn = (function() {
|
||||
};
|
||||
})();
|
||||
|
||||
/** User input */
|
||||
/**
|
||||
* User input
|
||||
*
|
||||
* --- Rx messages: ---
|
||||
* S - screen content (binary encoding of the entire screen with simple compression)
|
||||
* T - text labels - Title and buttons, \0x01-separated
|
||||
* B - beep
|
||||
* . - heartbeat
|
||||
*
|
||||
* --- Tx messages ---
|
||||
* s - string
|
||||
* b - action button
|
||||
* p - mb press
|
||||
* r - mb release
|
||||
* m - mouse move
|
||||
*/
|
||||
var Input = (function() {
|
||||
var opts = {
|
||||
np_alt: false,
|
||||
@@ -412,15 +508,11 @@ var Input = (function() {
|
||||
};
|
||||
|
||||
function sendStrMsg(str) {
|
||||
Conn.send("STR:"+str);
|
||||
}
|
||||
|
||||
function sendPosMsg(y, x) {
|
||||
Conn.send("TAP:"+y+','+x);
|
||||
Conn.send("s"+str);
|
||||
}
|
||||
|
||||
function sendBtnMsg(n) {
|
||||
Conn.send("BTN:"+n);
|
||||
Conn.send("b"+Chr(n));
|
||||
}
|
||||
|
||||
function fa(alt, normal) {
|
||||
@@ -541,6 +633,10 @@ var Input = (function() {
|
||||
_bindFnKeys();
|
||||
}
|
||||
|
||||
var mb1 = 0;
|
||||
var mb2 = 0;
|
||||
var mb3 = 0;
|
||||
|
||||
function init() {
|
||||
_initKeys();
|
||||
|
||||
@@ -550,11 +646,30 @@ var Input = (function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('mousedown', function(evt) {
|
||||
if (evt.button == 0) mb1 = 1;
|
||||
if (evt.button == 1) mb2 = 1;
|
||||
if (evt.button == 2) mb3 = 1;
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', function(evt) {
|
||||
if (evt.button == 0) mb1 = 0;
|
||||
if (evt.button == 1) mb2 = 0;
|
||||
if (evt.button == 2) mb3 = 0;
|
||||
});
|
||||
}
|
||||
|
||||
function packModifiersForMouse() {
|
||||
return (key.isModifier('ctrl')?1:0) |
|
||||
(key.isModifier('shift')?2:0) |
|
||||
(key.isModifier('alt')?4:0) |
|
||||
(key.isModifier('meta')?8:0);
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onTap: sendPosMsg,
|
||||
// onTap: sendPosMsg,
|
||||
sendString: sendStrMsg,
|
||||
setAlts: function(cu, np, fn) {
|
||||
if (opts.cu_alt != cu || opts.np_alt != np || opts.fn_alt != fn) {
|
||||
@@ -566,6 +681,31 @@ var Input = (function() {
|
||||
_bindFnKeys();
|
||||
}
|
||||
},
|
||||
onMouseMove: function (x, y) {
|
||||
var b = mb1 ? 1 : mb2 ? 2 : mb3 ? 3 : 0;
|
||||
var m = packModifiersForMouse();
|
||||
Conn.send("m" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
},
|
||||
onMouseDown: function (x, y, b) {
|
||||
if (b > 3 || b < 1) return;
|
||||
var m = packModifiersForMouse();
|
||||
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
console.log("B ",b," M ",m);
|
||||
},
|
||||
onMouseUp: function (x, y, b) {
|
||||
if (b > 3 || b < 1) return;
|
||||
var m = packModifiersForMouse();
|
||||
Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
console.log("B ",b," M ",m);
|
||||
},
|
||||
onMouseWheel: function (x, y, dir) {
|
||||
// -1 ... btn 4 (away from user)
|
||||
// +1 ... btn 5 (towards user)
|
||||
var m = packModifiersForMouse();
|
||||
var b = (dir < 0 ? 4 : 5);
|
||||
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
|
||||
console.log("B ",b," M ",m);
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
+563
-324
@@ -1,361 +1,600 @@
|
||||
<div class="Box">
|
||||
<h2>Troubleshooting</h2>
|
||||
<div class="Box fold">
|
||||
<h2>Tips & Troubleshooting</h2>
|
||||
|
||||
<ul>
|
||||
<li>ESPTerm communicates with external µC via UART on pins <b>Rx</b> and <b>Tx</b>.
|
||||
It can be configured in <a href="<?= url('cfg_system') ?>">System Settings</a>.
|
||||
Make sure you're using the correct baud rate and other settings on the other side.
|
||||
<div class="Row v">
|
||||
<img src="/img/adapter.jpg" class="aside" alt="ESPTerm v2">
|
||||
<ul>
|
||||
<li><b>Communication UART (Rx, Tx)</b> can be configured in the <a href="<?= url('cfg_system') ?>">System Settings</a>.
|
||||
|
||||
<li>Boot log and debug messages are available on pin <b>GPIO2</b> (P2) at 115200\,bps, 1 stop bit, no parity.
|
||||
<li><b>Boot log and debug messages</b> are available on pin <b>GPIO2</b> (P2) at 115200\,baud, 1 stop bit, no parity.
|
||||
Those messages may be disabled through compile flags.
|
||||
|
||||
<li>ESPTerm uses 3.3\,V logic. For communication with 5\,V logic (eg. Aurdino), 470\,R
|
||||
protection resistors are recommended. The ESPTerm breakout board already includes those resistors.
|
||||
<li><b>Loopback test</b>: Connect the Rx and Tx pins with a piece of wire. Anything you type in the browser should
|
||||
appear on the screen. Set Parser Timeout = 0 in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>
|
||||
to be able to manually enter escape sequences.
|
||||
|
||||
<li>If the Low Voltage Detector ("LVD") LED on the ESPTerm breakout lights up, your power connections have too high
|
||||
resistance or the power supply is insufficient. ESP8266 can consume in short spikes up to 500\,A and
|
||||
the supply voltage on the module must not drop below about 2.7\,V. Avoid needless breadboard jumpers and too thin wires.
|
||||
<li><b>For best performance</b>, use the module in Client mode (connected to external network) and minimize the number
|
||||
of simultaneous connections. Enabling AP consumes extra RAM and strains the processor.
|
||||
|
||||
<li>Connect Rx and Tx with a piece of wire to verify that the terminal works correctly; you should then immediately
|
||||
see what you type in the browser. <i>This won't work if your ESP8266 board has a built-in USB-serial
|
||||
converter connected to those pins.</i>
|
||||
<li>In AP mode, <b>check that the WiFi channel used is clear</b>; interference may cause flaky connection.
|
||||
A good mobile app to use for this is
|
||||
<a href="https://play.google.com/store/apps/details?id=com.farproc.wifi.analyzer">WiFi Analyzer (Google Play)</a>.
|
||||
Adjust the hotspot strength and range using the Tx Power setting.
|
||||
|
||||
<li>For best performance, use the module in Client mode (connected to external network). Enabling AP mode consumes
|
||||
extra RAM and cycles, since the captive portal DNS server and DHCP server are be loaded.
|
||||
|
||||
<li>In AP mode, check that the channel used by ESPTerm is clear; interference may cause flaky connection. A good mobile app to use for this is <a href="https://play.google.com/store/apps/details?id=com.farproc.wifi.analyzer">WiFi Analyzer (Google Play)</a>
|
||||
|
||||
<li>The terminal browser display needs WebSockets for the real-time updating to work.
|
||||
It will not work with some old browsers (Android < 4.4, IE < 10, Opera Mini)
|
||||
- see the <a href="https://caniuse.com/websockets/embed/">compatibility chart</a>.
|
||||
|
||||
<li>At most 4 clients can be connected to the AP at the same time. This is a limitation of the ESP8266 SDK.
|
||||
</ul>
|
||||
<li>Hold the BOOT button (GPIO0 to GND) for ~1 second to force enable AP. Hold it for ~6 seconds to restore default settings.
|
||||
(This is indicated by the blue LED rapidly flashing). Default settings can be overwritten in the
|
||||
<a href="<?= url('cfg_system') ?>">System Settings</a>.
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box">
|
||||
<h2>Screen</h2>
|
||||
<div class="Box fold">
|
||||
<h2>Escape Sequences Intro</h2>
|
||||
|
||||
<ul>
|
||||
<li>Most ANSI escape sequences are supported.
|
||||
<div class="Row v">
|
||||
<img src="/img/vt100.jpg" class="aside" alt="VT102">
|
||||
|
||||
<li>The initial screen size and other terminal options can be configured in
|
||||
<a href="<?= url('cfg_term') ?>">Terminal Settings</a>
|
||||
<p>ESPTerm emulates VT102 (pictured) with some additions from later VT models and <code>xterm</code>.
|
||||
All commonly used attributes and commands are supported.
|
||||
ESPTerm is capable of displaying ncurses applications such as <i>Midnight Commander</i> using <i>agetty</i>.</p>
|
||||
|
||||
<li>Display updates are sent to the browser after 20 ms of inactivity on the communication UART.
|
||||
This is to avoid hogging the server with tiny updates during large screen repaints.
|
||||
</ul>
|
||||
<p>
|
||||
ESPTerm accepts UTF-8 characters received on the communication UART and displays them on the screen,
|
||||
interpreting some codes as Control Characters. Those are e.g. Carriage Return (13), Line Feed (10), Tab (9), Backspace (8) and Bell (7).
|
||||
Escape sequences start with the control character ESC (27), followed by any number of ASCII characters
|
||||
forming the body of the command. On this page, we'll use <code>\e</code> to indicate ESC.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Escape sequences can be divided based on their first character and structure. Most common types are:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>CSI commands: <code>\e[</code> followed by 1 optional leading character, multiple numbers divided by
|
||||
semicolons, and one or two trailing characters. Those control the cursor, set attributes and manipulate screen content. E.g. <code>\e[?7;10h</code>, <code>\e[2J</code></li>
|
||||
<li>SGR commands: CSI terminated by <code>m</code>. Those set text attributes and colors. E.g. <code>\e[</code></li>
|
||||
<li>OSC commands: <code>\e]</code> followed by any number of UTF-8 characters (ESPTerm supports up to 64)
|
||||
terminated by <code>\e\\</code> (ESC and backslash) or Bell (7). Those are used to exchange text strings.</li>
|
||||
<li>There are a few other commands that don't follow this pattern, such as <code>\e7</code> or
|
||||
<code>\e#8</code>, mostly for historical reasons.</li>
|
||||
</ul>
|
||||
|
||||
<p>A list of the most important escape sequences is presented in the following sections.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box theme-0">
|
||||
<h2>Color Reference</h2>
|
||||
<div class="Box fold">
|
||||
<h2>Screen Behavior</h2>
|
||||
|
||||
<p>Color is set using <code>\e[<c>m</code> where <code><c></code> are numbers separated by semicolons
|
||||
(and <code>\e</code> is ESC, code 27). The actual color representation depends on a color theme which
|
||||
can be selected in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.</p>
|
||||
<div class="Row v">
|
||||
<p>The initial screen size, title text and button labels can be configured in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.</p>
|
||||
|
||||
<b>Foreground</b>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg7 fg0">30</span>
|
||||
<span class="bg0 fg1">31</span>
|
||||
<span class="bg0 fg2">32</span>
|
||||
<span class="bg0 fg3">33</span>
|
||||
<span class="bg0 fg4">34</span>
|
||||
<span class="bg0 fg5">35</span>
|
||||
<span class="bg0 fg6">36</span>
|
||||
<span class="bg0 fg7">37</span>
|
||||
<p>
|
||||
Screen updates are sent to the browser through a WebSocket after some time of inactivity on the communication UART
|
||||
(called "Redraw Delay"). After an update is sent, at least a time of "Redraw Cooldown" must elapse before the next
|
||||
update can be sent. Those delays are used is to avoid burdening the server with tiny updates during a large screen
|
||||
repaint. If you experience issues (broken image due to dropped bytes), try adjusting those config options. It may also
|
||||
be useful to try different baud rates.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg0 fg8">90</span>
|
||||
<span class="bg0 fg9">91</span>
|
||||
<span class="bg0 fg10">92</span>
|
||||
<span class="bg0 fg11">93</span>
|
||||
<span class="bg0 fg12">94</span>
|
||||
<span class="bg0 fg13">95</span>
|
||||
<span class="bg0 fg14">96</span>
|
||||
<span class="bg0 fg15">97</span>
|
||||
</div>
|
||||
|
||||
<b>Background</b>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg0 fg15">40</span>
|
||||
<span class="bg1 fg15">41</span>
|
||||
<span class="bg2 fg15">42</span>
|
||||
<span class="bg3 fg0">43</span>
|
||||
<span class="bg4 fg15">44</span>
|
||||
<span class="bg5 fg15">45</span>
|
||||
<span class="bg6 fg15">46</span>
|
||||
<span class="bg7 fg0">47</span>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg8 fg15">100</span>
|
||||
<span class="bg9 fg0">101</span>
|
||||
<span class="bg10 fg0">102</span>
|
||||
<span class="bg11 fg0">103</span>
|
||||
<span class="bg12 fg0">104</span>
|
||||
<span class="bg13 fg0">105</span>
|
||||
<span class="bg14 fg0">106</span>
|
||||
<span class="bg15 fg0">107</span>
|
||||
</div>
|
||||
|
||||
<p>To make the text bold, use the "bold" attribute (eg. <code>\e[31;40;1m</code> for
|
||||
<span style="padding: 2px;" class="bg0 fg1 bold">bold red on black</span>).</p>
|
||||
|
||||
<p>The colors are reset to default using <code>\e[0m</code>. For more info, see the tables below.</p>
|
||||
</div>
|
||||
|
||||
<div class="Box">
|
||||
<div class="Box fold">
|
||||
<h2>Text Attributes</h2>
|
||||
|
||||
<div class="Row v">
|
||||
<p>
|
||||
All text attributes are set using SGR commands like <code>\e[10;20;30m</code>, with up to 10 numbers separated by semicolons.
|
||||
To restore all attributes to their default states, use SGR 0: <code>\e[0m</code> or <code>\e[m</code>.
|
||||
</p>
|
||||
|
||||
<p>Those are the supported text attributes SGR codes:</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Style</th><th>Enable</th><th>Disable</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><b>Bold</b></td><td>1</td><td>21, 22</td></tr>
|
||||
<tr><td style="opacity:.6">Faint</td><td>2</td><td>22</td></tr>
|
||||
<tr><td><i>Italic</i></td><td>3</td><td>23</td></tr>
|
||||
<tr><td><u>Underlined</u></td><td>4</td><td>24</td></tr>
|
||||
<tr><td>Blink</td><td>5</td><td>25</td></tr>
|
||||
<tr><td><span style="color:black;background:#ccc;">Inverse</span></td><td>7</td><td>27</td></tr>
|
||||
<tr><td><s>Striked</s></td><td>9</td><td>29</td></tr>
|
||||
<tr><td>𝔉𝔯𝔞𝔨𝔱𝔲𝔯</td><td>20</td><td>23</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box fold theme-0">
|
||||
<h2>Colors</h2>
|
||||
|
||||
<div class="Row v">
|
||||
<p>Colors are set using SGR commands (like <code>\e[10;20;30m</code>). The following tables list the SGR codes to use.
|
||||
Selected colors are used for any new text entered, as well as for empty space when using line and screen clearing commands.
|
||||
The configured default colors can be restored using SGR 39 for foreground and SGR 49 for background.</p>
|
||||
|
||||
<p>The actual color representation depends on a color theme which
|
||||
can be selected in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.</p>
|
||||
|
||||
<h3>Foreground colors</h3>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg7 fg0">30</span>
|
||||
<span class="bg0 fg1">31</span>
|
||||
<span class="bg0 fg2">32</span>
|
||||
<span class="bg0 fg3">33</span>
|
||||
<span class="bg0 fg4">34</span>
|
||||
<span class="bg0 fg5">35</span>
|
||||
<span class="bg0 fg6">36</span>
|
||||
<span class="bg0 fg7">37</span>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg0 fg8">90</span>
|
||||
<span class="bg0 fg9">91</span>
|
||||
<span class="bg0 fg10">92</span>
|
||||
<span class="bg0 fg11">93</span>
|
||||
<span class="bg0 fg12">94</span>
|
||||
<span class="bg0 fg13">95</span>
|
||||
<span class="bg0 fg14">96</span>
|
||||
<span class="bg0 fg15">97</span>
|
||||
</div>
|
||||
|
||||
<h3>Background colors</h3>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg0 fg15">40</span>
|
||||
<span class="bg1 fg15">41</span>
|
||||
<span class="bg2 fg15">42</span>
|
||||
<span class="bg3 fg0">43</span>
|
||||
<span class="bg4 fg15">44</span>
|
||||
<span class="bg5 fg15">45</span>
|
||||
<span class="bg6 fg15">46</span>
|
||||
<span class="bg7 fg0">47</span>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span class="bg8 fg15">100</span>
|
||||
<span class="bg9 fg0">101</span>
|
||||
<span class="bg10 fg0">102</span>
|
||||
<span class="bg11 fg0">103</span>
|
||||
<span class="bg12 fg0">104</span>
|
||||
<span class="bg13 fg0">105</span>
|
||||
<span class="bg14 fg0">106</span>
|
||||
<span class="bg15 fg0">107</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box fold">
|
||||
<h2>User Input</h2>
|
||||
|
||||
<p>
|
||||
All the user types on their keyboard is sent as-is to the UART, including ESC, ANSI sequences for arrow keys and CR-LF for Enter.
|
||||
</p>
|
||||
<div class="Row v">
|
||||
<h3>Keyboard</h3>
|
||||
|
||||
<p>The buttons under the screen send ASCII codes 1, 2, 3, 4, 5. This choice was made to make their parsing as simple as possible.</p>
|
||||
<p>
|
||||
The user can input text using their keyboard, or on Android, using the on-screen keyboard which is open using
|
||||
a button beneath the screen. Supported are all printable characters, as well as many control keys, such as arrows, Ctrl+letters
|
||||
and function keys. Sequences sent by function keys are based on VT102 and xterm.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mouse input (click/tap) is sent as <code>\e[<y>;<x>M</code>. You can use this for virtual on-screen buttons.
|
||||
</p>
|
||||
<p>
|
||||
The codes sent by Home, End, F1-F4 and cursor keys are affected by various keyboard modes (Application Cursor Keys,
|
||||
Application Numpad Mode, SS3 Fn Keys Mode).
|
||||
Some can be set in the <a href="<?= url('cfg_term') ?>">Terminal Settings</a>, others via commands.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Here are some examples of control key codes:
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Key</th><th>Code</th><th>Key</th><th>Code</th></tr></thead>
|
||||
<tr>
|
||||
<td>🡑</td>
|
||||
<td><code>\e[A</code></td>
|
||||
<td>F1</td>
|
||||
<td><code>\eOP</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🡓</td>
|
||||
<td><code>\e[B</code></td>
|
||||
<td>F2</td>
|
||||
<td><code>\eOQ</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🡒</td>
|
||||
<td><code>\e[C</code></td>
|
||||
<td>F3</td>
|
||||
<td><code>\eOR</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🡐</td>
|
||||
<td><code>\e[D</code></td>
|
||||
<td>F4</td>
|
||||
<td><code>\eOS</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Home</td>
|
||||
<td><code>\eOH</code></td>
|
||||
<td>F5</td>
|
||||
<td><code>\e[15~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>End</td>
|
||||
<td><code>\eOF</code></td>
|
||||
<td>F6</td>
|
||||
<td><code>\e[17~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Insert</td>
|
||||
<td><code>\e[2~</code></td>
|
||||
<td>F7</td>
|
||||
<td><code>\e[18~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Delete</td>
|
||||
<td><code>\e[3~</code></td>
|
||||
<td>F8</td>
|
||||
<td><code>\e[19~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Page Up</td>
|
||||
<td><code>\e[5~</code></td>
|
||||
<td>F9</td>
|
||||
<td><code>\e[20~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Page Down</td>
|
||||
<td><code>\e[6~</code></td>
|
||||
<td>F10</td>
|
||||
<td><code>\e[21~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Enter</td>
|
||||
<td><code>CR (13)</code></td>
|
||||
<td>F11</td>
|
||||
<td><code>\e[23~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ctrl+Enter</td>
|
||||
<td><code>LF (10)</code></td>
|
||||
<td>F12</td>
|
||||
<td><code>\e[24~</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tab</td>
|
||||
<td><code>TAB (9)</code></td>
|
||||
<td>ESC</td>
|
||||
<td><code>ESC (27)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Backspace</td>
|
||||
<td><code>BS (8)</code></td>
|
||||
<td>Ctrl+A..Z</td>
|
||||
<td><code>ASCII 1-26</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>Action buttons</h3>
|
||||
|
||||
<p>
|
||||
The blue buttons under the screen send ASCII codes 1, 2, 3, 4, 5, which incidentally correspond to Ctrl+A,B,C,D,E.
|
||||
This choice was made to make button press parsing as simple as possible.
|
||||
</p>
|
||||
|
||||
<h3>Mouse</h3>
|
||||
|
||||
<p>
|
||||
ESPTerm in the current version does not implement standard mouse input. Instead, clicks/taps produce CSI sequences
|
||||
<code>\e[<i>r</i>;<i>c</i>M</code> (row, column). You can use this for virtual on-screen buttons.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box">
|
||||
<h2>Supported ANSI Escape Sequences</h2>
|
||||
<div class="Box fold">
|
||||
<h2>Cursor Commands</h2>
|
||||
|
||||
<p>Sequences are started by ASCII code 27 (ESC, <code>\e</code>, <code>\x1b</code>, <code>\033</code>)</p>
|
||||
<div class="Row v">
|
||||
<p>
|
||||
The coordinates are 1-based, origin is top left. The cursor can move within the entire screen,
|
||||
or in the active Scrolling Region if Origin Mode is enabled.
|
||||
</p>
|
||||
|
||||
<p>Instead of <code>\a</code> (BELL, ASCII 7) in the commands, you can use `\e\` (ESC + backslash). It's the Text Separator code.</p>
|
||||
<p>After writing a character, the cursor advances to the right. If it has reached the end of the row,
|
||||
it stays on the same line, but writing the next character makes it jump to the start of the next
|
||||
line first, scrolling up if needed. If Auto-wrap mode is disabled, the cursor never wraps or scrolls
|
||||
the screen.
|
||||
</p>
|
||||
|
||||
<h3>Text Attributes</h3>
|
||||
<p>
|
||||
<b>Legend:</b>
|
||||
Italic letters such as <i>n</i> are ASCII numbers that serve as arguments, separated with a semicolon.
|
||||
If an argument is left out, it's treated as 0 or 1, depending on what makes sense for the command.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
All text attributes are set using <code>\e[...m</code> where the dots are numbers separated by semicolons.
|
||||
You can combine up to 3 attributes in a single command.
|
||||
</p>
|
||||
<h3>Movement</h3>
|
||||
|
||||
<table class="ansiref">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>Reset text attributes to default</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Bold</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>21 or 22</td>
|
||||
<td>Bold off</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>Inverse (fg/bg swap when printing)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>27</td>
|
||||
<td>Inverse OFF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>30-37, 90-97</td>
|
||||
<td>Foreground color, normal and bright</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>40-47, 100-107</td>
|
||||
<td>Background color, normal and bright</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>A<br>
|
||||
\e[<i>n</i>B<br>
|
||||
\e[<i>n</i>C<br>
|
||||
\e[<i>n</i>D
|
||||
</td>
|
||||
<td>Move cursor up (A), down (B), right (C), left (D)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>F<br>
|
||||
\e[<i>n</i>E
|
||||
</td>
|
||||
<td>Go <i>n</i> lines up (F) or down (E), start of line</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>r</i>d<br>
|
||||
\e[<i>c</i>G<br>
|
||||
\e[<i>r</i>;<i>c</i>H
|
||||
</td>
|
||||
<td>
|
||||
Go to absolute position - row (d), column (G), or both (H). Use <code>\e[H</code> to go to 1,1.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[6n</td>
|
||||
<td>Query cursor position. Sent back as <code>\e[<i>r</i>;<i>c</i>R</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Cursor</h3>
|
||||
<h3>Save / restore</h3>
|
||||
|
||||
<p>Movement commands scroll the screen if needed. The coordinates are 1-based, origin top left.</p>
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e[s<br>\e[u</td>
|
||||
<td>Save (s) or restore (u) cursor position</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e7<br>\e8</td>
|
||||
<td>Save (7) or restore (8) cursor position and attributes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Params</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e[<n>A</td>
|
||||
<td>[count]</td>
|
||||
<td>Move cursor up</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>B</td>
|
||||
<td>[count]</td>
|
||||
<td>Move cursor down</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>C</td>
|
||||
<td>[count]</td>
|
||||
<td>Move cursor forward (right)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>D</td>
|
||||
<td>[count]</td>
|
||||
<td>Move cursor backward (left)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>E</td>
|
||||
<td>[count]</td>
|
||||
<td>Go N lines down, start of line</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>F</td>
|
||||
<td>[count]</td>
|
||||
<td>Go N lines up, start of line</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>G</td>
|
||||
<td>column</td>
|
||||
<td>Go to column</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<y>;<x>G</td>
|
||||
<td>[row=1];[col=1]</td>
|
||||
<td>Go to row and column</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[6n</td>
|
||||
<td>--</td>
|
||||
<td>Query cursor position. Position is sent back as <code>\e[?;?R</code> with row;column.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[s</td>
|
||||
<td>--</td>
|
||||
<td>Store position</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[u</td>
|
||||
<td>--</td>
|
||||
<td>Restore position</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e7</td>
|
||||
<td>--</td>
|
||||
<td>Store position & attributes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e8</td>
|
||||
<td>--</td>
|
||||
<td>Restore position & attributes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[?25l</td>
|
||||
<td>--</td>
|
||||
<td>Hide cursor</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[?25h</td>
|
||||
<td>--</td>
|
||||
<td>Show cursor</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[?7l</td>
|
||||
<td>--</td>
|
||||
<td>Disable cursor auto-wrap & auto-scroll at end of screen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[?7h</td>
|
||||
<td>--</td>
|
||||
<td>Enable cursor auto-wrap & auto-scroll at end of screen</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Scrolling Region</h3>
|
||||
|
||||
<h3>Screen</h3>
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e[<i>a</i>;<i>b</i>r</td>
|
||||
<td>Set scrolling region to rows <i>a</i> through <i>b</i> and go to 1,1. By default, the
|
||||
scrolling region spans the entire screen height. The cursor can leave the region using
|
||||
absolute position commands, unless Origin Mode (see below) is active.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[?6h<br>\e[?6l</td>
|
||||
<td>Enable (h) or disable (l) Origin Mode and go to 1,1. In Origin Mode, all coordinates
|
||||
are relative to the Scrolling Region and the cursor can't leave the region.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>S<br>
|
||||
\e[<i>n</i>T
|
||||
</td>
|
||||
<td>Move contents of the Scrolling Region up (S) or down (T), pad with empty lines of the current background color.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Params</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e[<m>J</td>
|
||||
<td>[mode=0]</td>
|
||||
<td>Clear screen. Mode: 0 - from cursor, 1 - to cursor, 2 - all</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<m>K</td>
|
||||
<td>[mode=0]</td>
|
||||
<td>Erase line. Mode: 0 - from cursor, 1 - to cursor, 2 - all</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>S</td>
|
||||
<td>[lines]</td>
|
||||
<td>Scroll screen content up, add empty line at the bottom</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>T</td>
|
||||
<td>[lines]</td>
|
||||
<td>Scroll screen content down, add empty line at the top</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e]W<r>;<c>\a</td>
|
||||
<td>rows;cols</td>
|
||||
<td>Set screen size (max ~ 80x30). This also clears the screen. This is a custom ESPTerm OSC command.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Tab stops</h3>
|
||||
|
||||
<h3>System Commands</h3>
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\eH</td>
|
||||
<td>Set tab stop at the current column. There are, by default, tabs every 8 columns.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>I<br>
|
||||
\e[<i>n</i>Z
|
||||
</td>
|
||||
<td>Advance (I) or go back (Z) <i>n</i> tab stops or end/start of line. ASCII TAB (9) is equivalent to <code>\e[1I</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[0g<br>
|
||||
\e[3g<br>
|
||||
</td>
|
||||
<td>Clear tab stop at the current column (0), or all columns (3).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Params</th>
|
||||
<th>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\ec</td>
|
||||
<td>--</td>
|
||||
<td>
|
||||
"Device Reset" - clear screen, reset attributes, show cursor & move it to 1,1.
|
||||
The screen size and WiFi settings stay unchanged.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[5n</td>
|
||||
<td>--</td>
|
||||
<td>Query device status, replies with <code>\e[0n</code> "device is OK". Can be used to check if the UART works.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e]TITLE=…\a</td>
|
||||
<td>Title text</td>
|
||||
<td>Set terminal title. This is a custom ESPTerm OSC command.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e]BTNn=…\a</td>
|
||||
<td>1-5, label</td>
|
||||
<td>Set button label. This is a custom ESPTerm OSC command.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ASCII 24 (18h)</td>
|
||||
<td></td>
|
||||
<td>This symbol is sent by ESPTerm when it becomes ready to receive commands. It can be used to wait before it becomes
|
||||
ready on power-up, or to detect a spontaneous ESPTerm restart - that could happen due to RAM exhaustion due
|
||||
to a memory leak, or perhaps a power outage.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Other options</h3>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e[?7h<br>\e[?7l</td>
|
||||
<td>Enable (h) or disable (l) cursor auto-wrap and screen auto-scroll</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[?25h<br>\e[?25l</td>
|
||||
<td>Show (h) or hide (l) the cursor</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box fold">
|
||||
<h2>Screen Content Manipulation</h2>
|
||||
|
||||
<div class="Row v">
|
||||
<p>
|
||||
<b>Legend:</b>
|
||||
Italic letters such as <i>n</i> are ASCII numbers that serve as arguments, separated with a semicolon.
|
||||
If an argument is left out, it's treated as 0 or 1, depending on what makes sense for the command.
|
||||
</p>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e[<i>m</i>J</td>
|
||||
<td>Clear part of screen. <i>m</i>: 0 - from cursor, 1 - to cursor, 2 - all</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<i>m</i>K</td>
|
||||
<td>Erase part of line. <i>m</i>: 0 - from cursor, 1 - to cursor, 2 - all</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>X
|
||||
</td>
|
||||
<td>Erase <i>n</i> characters in line.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>L<br>
|
||||
\e[<i>n</i>M
|
||||
</td>
|
||||
<td>Insert (L) or delete (M) <i>n</i> lines. Following lines are pulled up or pushed down.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e[<i>n</i>@<br>
|
||||
\e[<i>nP
|
||||
</td>
|
||||
<td>Insert (@) or delete (P) <i>n</i> characters. The rest of the line is pulled left or pushed right.
|
||||
Characters going past the end of line are lost.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box fold">
|
||||
<h2>Alternate Character Sets</h2>
|
||||
|
||||
<div class="Row v">
|
||||
<p>
|
||||
ESPTerm implements Alternate Character Sets as a way to print box drawing characters
|
||||
and special symbols. A character set can change what each received ASCII character
|
||||
is printed as on the screen (eg. "{" is "π" in codepage 0). The implementation is based
|
||||
on the original VT devices.
|
||||
Since ESPTerm also fully supports UTF-8, you can probably ignore this feature and use
|
||||
Unicode directly. It's added for compatibility with some programs that use this.
|
||||
</p>
|
||||
|
||||
<p>The following codepages are implemented:</p>
|
||||
|
||||
<ul>
|
||||
<li>B - US ASCII (default)</li>
|
||||
<li>A - UK ASCII: # replaced with £</li>
|
||||
<li>0 - Symbols and basic line drawing (standard DEC alternate character set)</li>
|
||||
<li>1 - Symbols and advanced line drawing (based on DOS codepage 437)</li>
|
||||
</ul>
|
||||
|
||||
<p>To see what character maps to which symbol, look in the source code or try it. All codepages use codes 32-127, 32 being space.</p>
|
||||
|
||||
<p>
|
||||
There are two character set slots, G0 and G1.
|
||||
Those slots are selected as active using ASCII codes Shift In and Shift Out (those originally served for shifting
|
||||
a red-black typewriter tape). Each slot (G0 and G1) can have a different codepage assigned. G0 and G1 and the active slot number are
|
||||
saved and restored with the cursor and cleared with a screen reset (<code>\ec</code>).
|
||||
</p>
|
||||
|
||||
<p>The following commands are used:</p>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\e(<i>x</i></td>
|
||||
<td>Set G0 = codepage <i>x</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e)<i>x</i></td>
|
||||
<td>Set G1 = codepage <i>x</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SO (14)</td>
|
||||
<td>Activate G0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SI (15)</td>
|
||||
<td>Activate G1</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Box fold">
|
||||
<h2>System Commands</h2>
|
||||
|
||||
<div class="Row v">
|
||||
<p>
|
||||
It's possible to dynamically change the screen title text and action button labels.
|
||||
Setting an empty label to a button makes it look disabled. The buttons send ASCII 1-5 when clicked.
|
||||
Those changes are not retained after restart.
|
||||
</p>
|
||||
|
||||
<table class="ansiref w100">
|
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>\ec</td>
|
||||
<td>
|
||||
Clear screen, reset attributes and cursor.
|
||||
The screen size, title and button labels remain unchanged.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[5n</td>
|
||||
<td>
|
||||
Query device status, ESPTerm replies with <code>\e[0n</code> "device is OK".
|
||||
Can be used to check if the terminal has booted up and is ready to receive commands.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CAN (24)</td>
|
||||
<td>
|
||||
This ASCII code is not a command, but is sent by ESPTerm when it becomes ready to receive commands.
|
||||
When this code is received on the UART, it means ESPTerm has restarted and is ready. Use this to detect
|
||||
spontaneous restarts which require a full screen repaint.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e]0;<i>title</i>\a</td>
|
||||
<td>Set screen title (this is a standard OSC command)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
\e]<i>81</i>;<i>btn1</i>\a<br>
|
||||
\e]<i>82</i>;<i>btn2</i>\a<br>
|
||||
\e]<i>83</i>;<i>btn3</i>\a<br>
|
||||
\e]<i>84</i>;<i>btn4</i>\a<br>
|
||||
\e]<i>85</i>;<i>btn5</i>\a<br>
|
||||
</td>
|
||||
<td>Set button 1-5 label - eg. <code>\e]81;Yes\a</code>
|
||||
sets the first button text to "Yes".</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[8;<i>r</i>;<i>c</i>t</td>
|
||||
<td>Set screen size (this is a command borrowed from xterm)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,6 +29,20 @@ form { @include naked(); }
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.v {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.aside {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
@include media($phone) {
|
||||
margin: 0; float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: $form-label-w;
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@ a:hover {
|
||||
}
|
||||
|
||||
ul > * {
|
||||
padding-top: .2em;
|
||||
padding-bottom: .2em;
|
||||
padding-top: .3em;
|
||||
padding-bottom: .3em;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -103,44 +103,69 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mobcol-base {
|
||||
h2 {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 2px 1.3rem 2px 5px;
|
||||
margin: 0 -5px 0 -5px;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
content: '▸';
|
||||
|
||||
top:50%;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
transform: translate(0,-50%) rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded h2::after {
|
||||
transform: translate(-25%,-50%) rotate(-90deg);
|
||||
margin-bottom: dist(0);
|
||||
}
|
||||
|
||||
.Row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
.Row {
|
||||
display: flex;
|
||||
|
||||
&.v {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Box.fold {
|
||||
@include mobcol-base;
|
||||
}
|
||||
|
||||
@include media($phone) {
|
||||
.Box.mobcol {
|
||||
.Box.fold, .Box.mobcol {
|
||||
h2 {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 2px 1.3rem 2px 5px;
|
||||
margin: 0 -5px 0 -5px;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
content: '▸';
|
||||
|
||||
top:50%;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
transform: translate(0,-50%) rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded h2::after {
|
||||
transform: translate(-25%,-50%) rotate(-90deg);
|
||||
margin-bottom: dist(0);
|
||||
}
|
||||
}
|
||||
|
||||
.Row {
|
||||
display: none;
|
||||
}
|
||||
.Box.mobcol {
|
||||
@include mobcol-base;
|
||||
|
||||
#ap-box {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
.Row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#ap-box {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ansiref {
|
||||
.Row table {
|
||||
&,td,th{
|
||||
border: 1px solid #666;
|
||||
}
|
||||
@@ -45,7 +45,9 @@
|
||||
th {
|
||||
background-color: rgba(255,255,255,.1);
|
||||
}
|
||||
}
|
||||
|
||||
.ansiref {
|
||||
td:nth-child(1) {
|
||||
font-family: monospace;
|
||||
}
|
||||
@@ -54,11 +56,7 @@
|
||||
width: 100%;
|
||||
|
||||
td:nth-child(1) {
|
||||
width: 9em;
|
||||
}
|
||||
|
||||
td:nth-child(2) {
|
||||
width: 8em;
|
||||
width: 6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ body.term {
|
||||
#screen {
|
||||
@include noselect();
|
||||
font-family: monospace;
|
||||
font-size: 16pt;
|
||||
font-size: 20px;//16pt; - 16pt causes vertical breaks in line drawing characters??
|
||||
white-space: nowrap;
|
||||
background: #111213;
|
||||
padding: 6px;
|
||||
@@ -27,12 +27,11 @@ body.term {
|
||||
span {
|
||||
white-space: pre;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
outline: 1px solid rgba(#ffffff, 0.4);
|
||||
@include media($phone) {
|
||||
outline: 0 none;
|
||||
}
|
||||
}
|
||||
|
||||
display: inline-block;
|
||||
line-height: 1.15em; // this adjusts the spacing
|
||||
width: 0.6em; // horizontal spacing, estimated to be about right
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule libesphttpd updated: 566e95af0c...13fa224963
+14
-6
@@ -451,16 +451,24 @@ static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts)
|
||||
// - discard repeated keypress events between keydown and keyup.
|
||||
ansi_noimpl("Auto-repeat toggle");
|
||||
}
|
||||
else if (n == 9 || (n >= 1000 && n <= 1006)) {
|
||||
// TODO mouse
|
||||
// 1000 - C11 mouse - Send Mouse X & Y on button press and release.
|
||||
// 1001 - Hilite mouse tracking
|
||||
else if (n == 9 || (n >= 1000 && n <= 1006) || n == 1015) {
|
||||
// 9 - X10 tracking
|
||||
// 1000 - X11 mouse - Send Mouse X & Y on button press and release.
|
||||
// 1001 - Hilite mouse tracking - not impl
|
||||
// 1002 - Cell Motion Mouse Tracking
|
||||
// 1003 - All Motion Mouse Tracking
|
||||
// 1004 - Send FocusIn/FocusOut events
|
||||
// 1005 - Enable UTF-8 Mouse Mode
|
||||
// 1005 - Enable UTF-8 Mouse Mode - we implement this as an alias to X10 mode
|
||||
// 1006 - SGR mouse mode
|
||||
ansi_noimpl("Mouse tracking");
|
||||
|
||||
if (n == 9) mouse_tracking.mode = yn ? MTM_X10 : MTM_NONE;
|
||||
else if (n == 1000) mouse_tracking.mode = yn ? MTM_NORMAL : MTM_NONE;
|
||||
else if (n == 1002) mouse_tracking.mode = yn ? MTM_BUTTON_MOTION : MTM_NONE;
|
||||
else if (n == 1003) mouse_tracking.mode = yn ? MTM_ANY_MOTION : MTM_NONE;
|
||||
else if (n == 1004) mouse_tracking.focus_tracking = yn;
|
||||
else if (n == 1005) mouse_tracking.encoding = yn ? MTE_UTF8 : MTE_SIMPLE;
|
||||
else if (n == 1006) mouse_tracking.encoding = yn ? MTE_SGR : MTE_SIMPLE;
|
||||
else if (n == 1015) mouse_tracking.encoding = yn ? MTE_URXVT : MTE_SIMPLE;
|
||||
}
|
||||
else if (n == 12) {
|
||||
// TODO Cursor blink on/off
|
||||
|
||||
+1
-13
@@ -15,18 +15,6 @@
|
||||
#include "screen.h"
|
||||
#include "ansi_parser.h"
|
||||
|
||||
/**
|
||||
* Helper function to set terminal button label
|
||||
* @param num - button number 1-5
|
||||
* @param str - button text
|
||||
*/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
set_button_text(int num, const char *str)
|
||||
{
|
||||
strncpy(termconf_scratch.btn[num-1], str, TERM_BTN_LEN);
|
||||
screen_notifyChange(CHANGE_LABELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to parse incoming OSC (Operating System Control)
|
||||
* @param buffer - the OSC body (after OSC and before ST)
|
||||
@@ -52,7 +40,7 @@ apars_handle_osc(const char *buffer)
|
||||
screen_set_title(buffer);
|
||||
}
|
||||
else if (n >= 81 && n <= 85) { // numbers chosen to not collide with any xterm supported codes
|
||||
set_button_text(n - 80, buffer);
|
||||
screen_set_button_text(n - 80, buffer);
|
||||
}
|
||||
else {
|
||||
ansi_noimpl("OSC %d ; %s ST", n, buffer);
|
||||
|
||||
+114
-46
@@ -7,11 +7,13 @@
|
||||
#include "screen.h"
|
||||
#include "uart_buffer.h"
|
||||
#include "ansi_parser.h"
|
||||
#include "jstring.h"
|
||||
|
||||
#define LOOPBACK 0
|
||||
|
||||
#define SOCK_BUF_LEN 1024
|
||||
static char sock_buff[SOCK_BUF_LEN];
|
||||
#define HB_TIME 1000
|
||||
|
||||
#define SOCK_BUF_LEN 2000
|
||||
|
||||
volatile bool notify_available = true;
|
||||
volatile bool notify_cooldown = false;
|
||||
@@ -19,6 +21,7 @@ volatile bool notify_cooldown = false;
|
||||
static ETSTimer notifyContentTim;
|
||||
static ETSTimer notifyLabelsTim;
|
||||
static ETSTimer notifyCooldownTim;
|
||||
static ETSTimer heartbeatTim;
|
||||
|
||||
// we're trying to do a kind of mutex here, without the actual primitives
|
||||
// this might glitch, very rarely.
|
||||
@@ -39,6 +42,8 @@ notifyContentTimCb(void *arg)
|
||||
{
|
||||
void *data = NULL;
|
||||
int max_bl, total_bl;
|
||||
char sock_buff[SOCK_BUF_LEN];
|
||||
|
||||
cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl);
|
||||
|
||||
if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
|
||||
@@ -69,6 +74,8 @@ notifyContentTimCb(void *arg)
|
||||
static void ICACHE_FLASH_ATTR
|
||||
notifyLabelsTimCb(void *arg)
|
||||
{
|
||||
char sock_buff[SOCK_BUF_LEN];
|
||||
|
||||
if (!notify_available || notify_cooldown) {
|
||||
// postpone a little
|
||||
TIMER_START(¬ifyLabelsTim, notifyLabelsTimCb, 1, 0);
|
||||
@@ -117,69 +124,128 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic)
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods)
|
||||
{
|
||||
// one-based
|
||||
x++;
|
||||
y++;
|
||||
|
||||
bool ctrl = (mods & 1) > 0;
|
||||
bool shift = (mods & 2) > 0;
|
||||
bool alt = (mods & 4) > 0;
|
||||
bool meta = (mods & 8) > 0;
|
||||
enum MTM mtm = mouse_tracking.mode;
|
||||
enum MTE mte = mouse_tracking.encoding;
|
||||
|
||||
// No message on release in X10 mode
|
||||
if (mtm == MTM_X10 && (button == 0 || evt == 'r')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt == 'm' && mtm != MTM_BUTTON_MOTION && mtm != MTM_ANY_MOTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt == 'm' && mtm == MTM_BUTTON_MOTION && button == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int eventcode = 0;
|
||||
|
||||
if (mtm == MTM_X10) {
|
||||
eventcode = button-1;
|
||||
}
|
||||
else {
|
||||
if (button == 0 || (evt == 'r' && mte != MTE_SGR)) eventcode = 3; // release
|
||||
else if (button == 1) eventcode = 0;
|
||||
else if (button == 2) eventcode = 1;
|
||||
else if (button == 3) eventcode = 2;
|
||||
else if (button == 4) eventcode = 64;
|
||||
else if (button == 5) eventcode = 65;
|
||||
|
||||
if (shift) eventcode |= 4;
|
||||
if (alt || meta) eventcode |= 8;
|
||||
if (ctrl) eventcode |= 16;
|
||||
|
||||
if (mtm == MTM_BUTTON_MOTION || mtm == MTM_ANY_MOTION) {
|
||||
if (evt == 'm') {
|
||||
eventcode |= 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode
|
||||
char buf[20];
|
||||
buf[0] = 0;
|
||||
if (mte == MTE_SIMPLE || mte == MTE_UTF8) {
|
||||
// strictly, for UTF8 this will break if any coord is over 127,
|
||||
// but that is unlikely due to screen size limitations in ESPTerm
|
||||
sprintf(buf, "\x1b[M%c%c%c", (u8)(32+eventcode), (u8)(32+x), (u8)(32+y));
|
||||
}
|
||||
else if (mte == MTE_SGR) {
|
||||
sprintf(buf, "\x1b[<%d;%d;%d%c", eventcode, x, y, evt == 'p' ? 'M' : 'm');
|
||||
}
|
||||
else if (mte == MTE_URXVT) {
|
||||
sprintf(buf, "\x1b[%d;%d;%dM", (u8)(32+eventcode), (u8)(32+x), (u8)(32+y));
|
||||
}
|
||||
|
||||
UART_SendAsync(buf, -1);
|
||||
}
|
||||
|
||||
/** Socket received a message */
|
||||
void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
{
|
||||
char buf[20];
|
||||
// Add terminator if missing (seems to randomly happen)
|
||||
data[len] = 0;
|
||||
|
||||
// TODO re-implement those, use single byte markers and B2 encoding
|
||||
|
||||
ws_dbg("Sock RX str: %s, len %d", data, len);
|
||||
|
||||
if (strstarts(data, "STR:")) {
|
||||
int y, x, m, b;
|
||||
u8 btnNum;
|
||||
|
||||
char c = data[0];
|
||||
switch (c) {
|
||||
case 's':
|
||||
// pass string verbatim
|
||||
#if LOOPBACK
|
||||
for(int i=4;i<strlen(data); i++) {
|
||||
ansi_parser(data[i]);
|
||||
}
|
||||
#else
|
||||
UART_SendAsync(data+4, -1);
|
||||
UART_SendAsync(data+1, -1);
|
||||
#endif
|
||||
}
|
||||
else if (strstarts(data, "BTN:")) {
|
||||
// send button as low ASCII value 1-9
|
||||
u8 btnNum = (u8) (data[4] - '0');
|
||||
if (btnNum > 0 && btnNum < 10) {
|
||||
UART_SendAsync((const char *) &btnNum, 1);
|
||||
}
|
||||
}
|
||||
else if (strstarts(data, "TAP:")) {
|
||||
// this comes in as 0-based
|
||||
|
||||
int y=0, x=0;
|
||||
|
||||
char *pc=data+4;
|
||||
char c;
|
||||
int phase=0;
|
||||
|
||||
while((c=*pc++) != '\0') {
|
||||
if (c==','||c==';') {
|
||||
phase++;
|
||||
break;
|
||||
case 'b':
|
||||
// action button press
|
||||
btnNum = (u8) (data[1]);
|
||||
if (btnNum > 0 && btnNum < 10) {
|
||||
UART_SendAsync((const char *) &btnNum, 1); // TODO this is where we use user-configured codes
|
||||
}
|
||||
else if (c>='0' && c<='9') {
|
||||
if (phase==0) {
|
||||
y=y*10+(c-'0');
|
||||
} else {
|
||||
x=x*10+(c-'0');
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
case 'p':
|
||||
case 'r':
|
||||
if (mouse_tracking.mode == MTM_NONE) break; // no need to parse, not enabled
|
||||
|
||||
if (!screen_isCoordValid(y, x)) {
|
||||
ws_warn("Mouse input at invalid coordinates");
|
||||
return;
|
||||
}
|
||||
// mouse move
|
||||
y = parse2B(data+1); // row, 0-based
|
||||
x = parse2B(data+3); // column, 0-based
|
||||
b = parse2B(data+5); // mouse button, 0 = none, 1-5 = button number
|
||||
m = parse2B(data+7); // modifier keys held
|
||||
|
||||
ws_dbg("Screen clicked at row %d, col %d", y+1, x+1);
|
||||
|
||||
// Send as 1-based to user
|
||||
sprintf(buf, "\033[%d;%dM", y+1, x+1);
|
||||
UART_SendAsync(buf, -1);
|
||||
sendMouseAction(c,y,x,b,m);
|
||||
break;
|
||||
default:
|
||||
ws_warn("Bad command.");
|
||||
}
|
||||
else {
|
||||
ws_warn("Bad command.");
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
|
||||
{
|
||||
if (notify_available) {
|
||||
// Heartbeat packet - indicate we're still connected
|
||||
// JS reloads the page if heartbeat is lost for a couple seconds
|
||||
cgiWebsockBroadcast(URL_WS_UPDATE, ".", 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,4 +254,6 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
|
||||
{
|
||||
ws_info("Socket connected to "URL_WS_UPDATE);
|
||||
ws->recvCb = updateSockRx;
|
||||
|
||||
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/09/04.
|
||||
//
|
||||
|
||||
#include "jstring.h"
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
encode2B(u16 number, WordB2 *stru)
|
||||
{
|
||||
stru->lsb = (u8) (number % 127);
|
||||
number = (u16) ((number - stru->lsb) / 127);
|
||||
stru->lsb += 1;
|
||||
|
||||
stru->msb = (u8) (number + 1);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
encode3B(u32 number, WordB3 *stru)
|
||||
{
|
||||
stru->lsb = (u8) (number % 127);
|
||||
number = (number - stru->lsb) / 127;
|
||||
stru->lsb += 1;
|
||||
|
||||
stru->msb = (u8) (number % 127);
|
||||
number = (number - stru->msb) / 127;
|
||||
stru->msb += 1;
|
||||
|
||||
stru->xsb = (u8) (number + 1);
|
||||
}
|
||||
|
||||
u16 ICACHE_FLASH_ATTR
|
||||
parse2B(const char *str)
|
||||
{
|
||||
return (u16) ((str[0] - 1) + (str[1] - 1) * 127);
|
||||
}
|
||||
|
||||
u32 ICACHE_FLASH_ATTR
|
||||
parse3B(const char *str)
|
||||
{
|
||||
return (u32) ((str[0] - 1) + (str[1] - 1) * 127 + (str[2] - 1) * 127 * 127);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/09/04.
|
||||
//
|
||||
|
||||
#ifndef ESPTERM_JSTRING_H
|
||||
#define ESPTERM_JSTRING_H
|
||||
|
||||
#include <esp8266.h>
|
||||
|
||||
typedef struct {
|
||||
u8 lsb;
|
||||
u8 msb;
|
||||
} WordB2;
|
||||
|
||||
typedef struct {
|
||||
u8 lsb;
|
||||
u8 msb;
|
||||
u8 xsb;
|
||||
} WordB3;
|
||||
|
||||
void encode2B(u16 number, WordB2 *stru);
|
||||
|
||||
void encode3B(u32 number, WordB3 *stru);
|
||||
|
||||
u16 parse2B(const char *str);
|
||||
|
||||
u32 parse3B(const char *str);
|
||||
|
||||
#endif //ESPTERM_JSTRING_H
|
||||
+2
-2
@@ -29,7 +29,7 @@ HttpdBuiltInUrl routes[] = {
|
||||
// --- Web pages ---
|
||||
ROUTE_TPL_FILE("/", tplScreen, "/term.tpl"),
|
||||
ROUTE_TPL_FILE("/about/?", tplAbout, "/about.tpl"),
|
||||
ROUTE_FILE("/help/?", "/help.tpl"),
|
||||
ROUTE_FILE("/help/?", "/help.html"),
|
||||
|
||||
// --- Sockets ---
|
||||
ROUTE_CGI("/term/init", cgiTermInitialImage),
|
||||
@@ -46,7 +46,7 @@ HttpdBuiltInUrl routes[] = {
|
||||
ROUTE_REDIRECT("/cfg/?", "/cfg/wifi"),
|
||||
|
||||
ROUTE_TPL_FILE("/cfg/wifi/?", tplWlan, "/cfg_wifi.tpl"),
|
||||
ROUTE_FILE("/cfg/wifi/connecting/?", "/cfg_wifi_conn.tpl"),
|
||||
ROUTE_FILE("/cfg/wifi/connecting/?", "/cfg_wifi_conn.html"),
|
||||
ROUTE_CGI("/cfg/wifi/scan", cgiWiFiScan),
|
||||
ROUTE_CGI("/cfg/wifi/connstatus", cgiWiFiConnStatus),
|
||||
ROUTE_CGI("/cfg/wifi/set", cgiWiFiSetParams),
|
||||
|
||||
+29
-32
@@ -5,10 +5,13 @@
|
||||
#include "sgr.h"
|
||||
#include "ascii.h"
|
||||
#include "apars_logging.h"
|
||||
#include "jstring.h"
|
||||
|
||||
TerminalConfigBundle * const termconf = &persist.current.termconf;
|
||||
TerminalConfigBundle termconf_scratch;
|
||||
|
||||
MouseTrackingConfig mouse_tracking;
|
||||
|
||||
// forward declare
|
||||
static void utf8_remap(char* out, char g, char charset);
|
||||
|
||||
@@ -198,6 +201,8 @@ terminal_apply_settings_noclear(void)
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_init(void)
|
||||
{
|
||||
dbg("Screen buffer size = %d bytes", sizeof(screen));
|
||||
|
||||
NOTIFY_LOCK();
|
||||
screen_reset();
|
||||
NOTIFY_DONE();
|
||||
@@ -255,6 +260,10 @@ screen_reset(void)
|
||||
scr.vm0 = 0;
|
||||
scr.vm1 = H-1;
|
||||
|
||||
mouse_tracking.encoding = MTE_SIMPLE;
|
||||
mouse_tracking.focus_tracking = false;
|
||||
mouse_tracking.mode = MTM_NONE;
|
||||
|
||||
// size is left unchanged
|
||||
screen_clear(CLEAR_ALL);
|
||||
|
||||
@@ -627,6 +636,18 @@ screen_set_title(const char *title)
|
||||
screen_notifyChange(CHANGE_LABELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set terminal button label
|
||||
* @param num - button number 1-5
|
||||
* @param str - button text
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_button_text(int num, const char *text)
|
||||
{
|
||||
strncpy(termconf_scratch.btn[num-1], text, TERM_BTN_LEN);
|
||||
screen_notifyChange(CHANGE_LABELS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift screen upwards
|
||||
*/
|
||||
@@ -1146,14 +1167,14 @@ static const u16 codepage_1[] =
|
||||
0x2663, // 37 % ♣ (5)
|
||||
0x2660, // 38 & ♠ (6)
|
||||
0x2022, // 39 ' • (7) - inverse dot and circle left out, can be done with SGR
|
||||
0x231B, // 40 ( ⌛ - hourglass (timer icon)
|
||||
0x0,//0x231B, // 40 ( ⌛ - hourglass (timer icon)
|
||||
0x25CB, // 41 ) ○ (9)
|
||||
0x21AF, // 42 * ↯ - electricity (lightning monitor...)
|
||||
0x266A, // 43 + ♪ (13)
|
||||
0x266B, // 44 , ♫ (14)
|
||||
0x263C, // 45 - ☼ (15)
|
||||
0x2302, // 46 . ⌂ (127)
|
||||
0x2622, // 47 / ☢ - radioactivity (geiger counter...)
|
||||
0x0,//0x2622, // 47 / ☢ - radioactivity (geiger counter...)
|
||||
0x2591, // 48 0 ░ (176) - this block is kept aligned and ordered from DOS, moved -128
|
||||
0x2592, // 49 1 ▒ (177)
|
||||
0x2593, // 50 2 ▓ (178)
|
||||
@@ -1212,10 +1233,10 @@ static const u16 codepage_1[] =
|
||||
0x25BC, // 103 g ▼ (31)
|
||||
0x25BA, // 104 h ► (16)
|
||||
0x25C4, // 105 i ◄ (17)
|
||||
0x25E2, // 106 j ◢ - added for slanted corners
|
||||
0x25E3, // 107 k ◣
|
||||
0x25E4, // 108 l ◤
|
||||
0x25E5, // 109 m ◥
|
||||
0x0,//0x25E2, // 106 j ◢ - added for slanted corners
|
||||
0x0,//0x25E3, // 107 k ◣
|
||||
0x0,//0x25E4, // 108 l ◤
|
||||
0x0,//0x25E5, // 109 m ◥
|
||||
0x256D, // 110 n ╭ - rounded corners
|
||||
0x256E, // 111 o ╮
|
||||
0x256F, // 112 p ╯
|
||||
@@ -1231,8 +1252,8 @@ static const u16 codepage_1[] =
|
||||
0x0, // 122 z
|
||||
0x0, // 123 {
|
||||
0x0, // 124 |
|
||||
0x2714, // 125 } ✔ - checkboxes or checklist items
|
||||
0x2718, // 126 ~ ✘
|
||||
0x0,//0x2714, // 125 } ✔ - checkboxes or checklist items
|
||||
0x0,//0x2718, // 126 ~ ✘
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1307,30 +1328,6 @@ struct ScreenSerializeState {
|
||||
int index;
|
||||
};
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
encode2B(u16 number, WordB2 *stru)
|
||||
{
|
||||
stru->lsb = (u8) (number % 127);
|
||||
number = (u16) ((number - stru->lsb) / 127);
|
||||
stru->lsb += 1;
|
||||
|
||||
stru->msb = (u8) (number + 1);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
encode3B(u32 number, WordB3 *stru)
|
||||
{
|
||||
stru->lsb = (u8) (number % 127);
|
||||
number = (number - stru->lsb) / 127;
|
||||
stru->lsb += 1;
|
||||
|
||||
stru->msb = (u8) (number % 127);
|
||||
number = (number - stru->msb) / 127;
|
||||
stru->msb += 1;
|
||||
|
||||
stru->xsb = (u8) (number + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* buffer should be at least 64+5*10+6 long (title + buttons + 6), ie. 120
|
||||
* @param buffer
|
||||
|
||||
+24
-15
@@ -50,7 +50,7 @@
|
||||
#define SCR_DEF_TITLE "ESPTerm"
|
||||
|
||||
/** Maximum screen size (determines size of the static data array) */
|
||||
#define MAX_SCREEN_SIZE (80*26)
|
||||
#define MAX_SCREEN_SIZE (80*25)
|
||||
|
||||
#define TERMCONF_VERSION 1
|
||||
|
||||
@@ -80,6 +80,29 @@ extern TerminalConfigBundle * const termconf;
|
||||
*/
|
||||
extern TerminalConfigBundle termconf_scratch;
|
||||
|
||||
enum MTM {
|
||||
MTM_NONE = 0,
|
||||
MTM_X10 = 1,
|
||||
MTM_NORMAL = 2,
|
||||
MTM_BUTTON_MOTION = 3,
|
||||
MTM_ANY_MOTION = 4,
|
||||
};
|
||||
|
||||
enum MTE {
|
||||
MTE_SIMPLE = 0,
|
||||
MTE_UTF8 = 1,
|
||||
MTE_SGR = 2,
|
||||
MTE_URXVT = 3,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum MTM mode;
|
||||
bool focus_tracking;
|
||||
enum MTE encoding;
|
||||
} MouseTrackingConfig;
|
||||
|
||||
extern MouseTrackingConfig mouse_tracking;
|
||||
|
||||
/** Restore default settings to termconf. Does not apply or copy to scratch. */
|
||||
void terminal_restore_defaults(void);
|
||||
/** Apply settings, redraw (clears the screen) */
|
||||
@@ -97,17 +120,6 @@ void screen_set_button_text(int num, const char *text);
|
||||
|
||||
// --- Encoding ---
|
||||
|
||||
typedef struct {
|
||||
u8 lsb;
|
||||
u8 msb;
|
||||
} WordB2;
|
||||
|
||||
typedef struct {
|
||||
u8 lsb;
|
||||
u8 msb;
|
||||
u8 xsb;
|
||||
} WordB3;
|
||||
|
||||
typedef enum {
|
||||
CS_USASCII = 'B',
|
||||
CS_UKASCII = 'A',
|
||||
@@ -115,9 +127,6 @@ typedef enum {
|
||||
CS_DOS_437 = '1',
|
||||
} CHARSET;
|
||||
|
||||
/** Encode number to two nice ASCII bytes */
|
||||
void encode2B(u16 number, WordB2 *stru);
|
||||
|
||||
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
|
||||
|
||||
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
#include "ansi_parser.h"
|
||||
#include "syscfg.h"
|
||||
|
||||
#define LOGBUF_SIZE 1500
|
||||
#define LOGBUF_SIZE 2048
|
||||
static char logbuf[LOGBUF_SIZE];
|
||||
static u32 lb_nw = 1;
|
||||
static u32 lb_ls = 0;
|
||||
|
||||
+11
-11
@@ -45,9 +45,9 @@ UART_AsyncBufferInit(uint32 buf_size)
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
struct UartBuffer *pBuff = (struct UartBuffer *) os_malloc(sizeof(struct UartBuffer));
|
||||
struct UartBuffer *pBuff = (struct UartBuffer *) malloc(sizeof(struct UartBuffer));
|
||||
pBuff->UartBuffSize = buf_size;
|
||||
pBuff->pUartBuff = (uint8 *) os_malloc(pBuff->UartBuffSize);
|
||||
pBuff->pUartBuff = (uint8 *) malloc(pBuff->UartBuffSize);
|
||||
pBuff->pInPos = pBuff->pUartBuff;
|
||||
pBuff->pOutPos = pBuff->pUartBuff;
|
||||
pBuff->Space = (uint16) pBuff->UartBuffSize;
|
||||
@@ -68,17 +68,17 @@ static void UART_WriteToAsyncBuffer(struct UartBuffer *pCur, const char *pdata,
|
||||
|
||||
uint16 tail_len = (uint16) (pCur->pUartBuff + pCur->UartBuffSize - pCur->pInPos);
|
||||
if (tail_len >= data_len) { //do not need to loop back the queue
|
||||
os_memcpy(pCur->pInPos, pdata, data_len);
|
||||
memcpy(pCur->pInPos, pdata, data_len);
|
||||
pCur->pInPos += (data_len);
|
||||
pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
|
||||
pCur->Space -= data_len;
|
||||
}
|
||||
else {
|
||||
os_memcpy(pCur->pInPos, pdata, tail_len);
|
||||
memcpy(pCur->pInPos, pdata, tail_len);
|
||||
pCur->pInPos += (tail_len);
|
||||
pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
|
||||
pCur->Space -= tail_len;
|
||||
os_memcpy(pCur->pInPos, pdata + tail_len, data_len - tail_len);
|
||||
memcpy(pCur->pInPos, pdata + tail_len, data_len - tail_len);
|
||||
pCur->pInPos += (data_len - tail_len);
|
||||
pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
|
||||
pCur->Space -= (data_len - tail_len);
|
||||
@@ -93,8 +93,8 @@ static void UART_WriteToAsyncBuffer(struct UartBuffer *pCur, const char *pdata,
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR UART_FreeAsyncBuffer(struct UartBuffer *pBuff)
|
||||
{
|
||||
os_free(pBuff->pUartBuff);
|
||||
os_free(pBuff);
|
||||
free(pBuff->pUartBuff);
|
||||
free(pBuff);
|
||||
}
|
||||
|
||||
u16 ICACHE_FLASH_ATTR UART_AsyncRxCount(void)
|
||||
@@ -116,19 +116,19 @@ UART_ReadAsync(char *pdata, uint16 data_len)
|
||||
uint16 len_tmp = 0;
|
||||
len_tmp = ((data_len > buf_len) ? buf_len : data_len);
|
||||
if (pRxBuffer->pOutPos <= pRxBuffer->pInPos) {
|
||||
os_memcpy(pdata, pRxBuffer->pOutPos, len_tmp);
|
||||
memcpy(pdata, pRxBuffer->pOutPos, len_tmp);
|
||||
pRxBuffer->pOutPos += len_tmp;
|
||||
pRxBuffer->Space += len_tmp;
|
||||
}
|
||||
else {
|
||||
if (len_tmp > tail_len) {
|
||||
os_memcpy(pdata, pRxBuffer->pOutPos, tail_len);
|
||||
memcpy(pdata, pRxBuffer->pOutPos, tail_len);
|
||||
pRxBuffer->pOutPos += tail_len;
|
||||
pRxBuffer->pOutPos = (pRxBuffer->pUartBuff +
|
||||
(pRxBuffer->pOutPos - pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize);
|
||||
pRxBuffer->Space += tail_len;
|
||||
|
||||
os_memcpy(pdata + tail_len, pRxBuffer->pOutPos, len_tmp - tail_len);
|
||||
memcpy(pdata + tail_len, pRxBuffer->pOutPos, len_tmp - tail_len);
|
||||
pRxBuffer->pOutPos += (len_tmp - tail_len);
|
||||
pRxBuffer->pOutPos = (pRxBuffer->pUartBuff +
|
||||
(pRxBuffer->pOutPos - pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize);
|
||||
@@ -136,7 +136,7 @@ UART_ReadAsync(char *pdata, uint16 data_len)
|
||||
}
|
||||
else {
|
||||
//os_printf("case 3 in rx deq\n\r");
|
||||
os_memcpy(pdata, pRxBuffer->pOutPos, len_tmp);
|
||||
memcpy(pdata, pRxBuffer->pOutPos, len_tmp);
|
||||
pRxBuffer->pOutPos += len_tmp;
|
||||
pRxBuffer->pOutPos = (pRxBuffer->pUartBuff +
|
||||
(pRxBuffer->pOutPos - pRxBuffer->pUartBuff) % pRxBuffer->UartBuffSize);
|
||||
|
||||
+3
-3
@@ -6,11 +6,11 @@
|
||||
#define ESP_VT100_FIRMWARE_VERSION_H
|
||||
|
||||
#define FW_V_MAJOR 0
|
||||
#define FW_V_MINOR 6
|
||||
#define FW_V_PATCH 9
|
||||
#define FW_V_MINOR 7
|
||||
#define FW_V_PATCH 1
|
||||
|
||||
#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) "+" GIT_HASH
|
||||
#define FIRMWARE_VERSION_NUM (FW_V_MAJOR*10000 + FW_V_MINOR*100 + FW_V_PATCH) // this is used in ID queries
|
||||
#define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware"
|
||||
#define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/ESPTerm"
|
||||
|
||||
#endif //ESP_VT100_FIRMWARE_VERSION_H
|
||||
|
||||
Reference in New Issue
Block a user