Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca410736f7 | ||
|
|
2ca8bf1483 | ||
|
|
35393827a9 | ||
|
|
ea84d1889d | ||
|
|
bc41a21f23 | ||
|
|
e48180ac10 | ||
|
|
e47a5477f9 | ||
|
|
e944a994a7 | ||
|
|
6535b49551 | ||
|
|
2cb8586c94 | ||
|
|
ec1cebdd4c | ||
|
|
af5a3a64ed | ||
|
|
7b94eef448 | ||
|
|
c8067122d5 | ||
|
|
12385b7a5f | ||
|
|
5564147f67 | ||
|
|
32ba7dc0d7 | ||
|
|
80721c1715 | ||
|
|
e22d3f26d9 |
@@ -145,6 +145,7 @@ add_definitions(
|
||||
-DFLAG_GZIP=2
|
||||
-DADMIN_PASSWORD="asdf"
|
||||
-DGIT_HASH="blabla"
|
||||
-DDEBUG_ANSI=1
|
||||
-DESPFS_HEATSHRINK)
|
||||
|
||||
add_executable(esp_vt100_firmware ${SOURCE_FILES})
|
||||
|
||||
@@ -65,8 +65,15 @@ LIBS += esphttpd
|
||||
# compiler flags using during compilation of source files
|
||||
CFLAGS = -Os -ggdb -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
|
||||
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \
|
||||
-Wno-address -Wno-unused -DHTTPD_MAX_BACKLOG_SIZE=4096 -DADMIN_PASSWORD=$(ADMIN_PASSWORD) \
|
||||
-DGIT_HASH='"$(shell git rev-parse --short HEAD)"'
|
||||
-Wno-address -Wno-unused
|
||||
|
||||
CFLAGS += -DHTTPD_MAX_BACKLOG_SIZE=10240
|
||||
CFLAGS += -DGIT_HASH='"$(shell git rev-parse --short HEAD)"'
|
||||
CFLAGS += -DADMIN_PASSWORD=$(ADMIN_PASSWORD)
|
||||
|
||||
# Debug logging
|
||||
CFLAGS += -DDEBUG_ANSI=1
|
||||
CFLAGS += -DDEBUG_INPUT=0
|
||||
|
||||
# linker flags used to generate the main object file
|
||||
LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static
|
||||
|
||||
+18
-3
@@ -1152,9 +1152,6 @@ body.term #botnav {
|
||||
position: absolute;
|
||||
top: -9999px; }
|
||||
|
||||
.nb {
|
||||
font-weight: normal !important; }
|
||||
|
||||
.theme-0 .fg0 {
|
||||
color: #111213; }
|
||||
.theme-0 .bg0 {
|
||||
@@ -1548,6 +1545,24 @@ body.term #botnav {
|
||||
.bold {
|
||||
font-weight: bold !important; }
|
||||
|
||||
.faint span {
|
||||
opacity: 0.6; }
|
||||
|
||||
.italic {
|
||||
font-style: italic; }
|
||||
|
||||
.under {
|
||||
text-decoration: underline; }
|
||||
|
||||
.strike {
|
||||
text-decoration: line-through; }
|
||||
|
||||
.underline.strike {
|
||||
text-decoration: underline line-through; }
|
||||
|
||||
.blink-hide .blink {
|
||||
color: transparent; }
|
||||
|
||||
.Row.color-preview {
|
||||
font-family: monospace;
|
||||
font-size: 16pt;
|
||||
|
||||
+521
-63
@@ -701,6 +701,302 @@
|
||||
// Set Chibi's global namespace here ($)
|
||||
w.$ = chibi;
|
||||
}());
|
||||
// keymaster.js
|
||||
// (c) 2011-2013 Thomas Fuchs
|
||||
// keymaster.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function(global){
|
||||
var k,
|
||||
_handlers = {},
|
||||
_mods = { 16: false, 18: false, 17: false, 91: false },
|
||||
_scope = 'all',
|
||||
// modifier keys
|
||||
_MODIFIERS = {
|
||||
'⇧': 16, shift: 16,
|
||||
'⌥': 18, alt: 18, option: 18,
|
||||
'⌃': 17, ctrl: 17, control: 17,
|
||||
'⌘': 91, command: 91
|
||||
},
|
||||
// special keys
|
||||
_MAP = {
|
||||
backspace: 8, tab: 9, clear: 12,
|
||||
enter: 13, 'return': 13,
|
||||
esc: 27, escape: 27, space: 32,
|
||||
left: 37, up: 38,
|
||||
right: 39, down: 40,
|
||||
del: 46, 'delete': 46,
|
||||
home: 36, end: 35,
|
||||
pageup: 33, pagedown: 34, insert: 45, // added insert
|
||||
',': 188, '.': 190, '/': 191,
|
||||
'`': 192, '-': 189, '=': 187,
|
||||
';': 186, '\'': 222,
|
||||
'[': 219, ']': 221, '\\': 220
|
||||
},
|
||||
code = function(x){
|
||||
return _MAP[x] || x.toUpperCase().charCodeAt(0);
|
||||
},
|
||||
_downKeys = [];
|
||||
|
||||
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
|
||||
|
||||
// IE doesn't support Array#indexOf, so have a simple replacement
|
||||
function index(array, item){
|
||||
var i = array.length;
|
||||
while(i--) if(array[i]===item) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// for comparing mods before unassignment
|
||||
function compareArray(a1, a2) {
|
||||
if (a1.length != a2.length) return false;
|
||||
for (var i = 0; i < a1.length; i++) {
|
||||
if (a1[i] !== a2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var modifierMap = {
|
||||
16:'shiftKey',
|
||||
18:'altKey',
|
||||
17:'ctrlKey',
|
||||
91:'metaKey'
|
||||
};
|
||||
function updateModifierKey(event) {
|
||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
||||
};
|
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) {
|
||||
var key, handler, k, i, modifiersMatch, scope;
|
||||
key = event.keyCode;
|
||||
|
||||
if (index(_downKeys, key) == -1) {
|
||||
_downKeys.push(key);
|
||||
}
|
||||
|
||||
// if a modifier key, set the key.<modifierkeyname> property to true and return
|
||||
if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
|
||||
if(key in _mods) {
|
||||
_mods[key] = true;
|
||||
// 'assignKey' from inside this closure is exported to window.key
|
||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
|
||||
return;
|
||||
}
|
||||
updateModifierKey(event);
|
||||
|
||||
// see if we need to ignore the keypress (filter() can can be overridden)
|
||||
// by default ignore key presses if a select, textarea, or input is focused
|
||||
if(!assignKey.filter.call(this, event)) return;
|
||||
|
||||
// abort if no potentially matching shortcuts found
|
||||
if (!(key in _handlers)) return;
|
||||
|
||||
scope = getScope();
|
||||
|
||||
// for each potential shortcut
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
handler = _handlers[key][i];
|
||||
|
||||
// see if it's in the current scope
|
||||
if(handler.scope == scope || handler.scope == 'all'){
|
||||
// check if modifiers match if any
|
||||
modifiersMatch = handler.mods.length > 0;
|
||||
for(k in _mods)
|
||||
if((!_mods[k] && index(handler.mods, +k) > -1) ||
|
||||
(_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
|
||||
// call the handler and stop the event if neccessary
|
||||
if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
|
||||
if(handler.method(event, handler)===false){
|
||||
if(event.preventDefault) event.preventDefault();
|
||||
else event.returnValue = false;
|
||||
if(event.stopPropagation) event.stopPropagation();
|
||||
if(event.cancelBubble) event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unset modifier keys on keyup
|
||||
function clearModifier(event){
|
||||
var key = event.keyCode, k,
|
||||
i = index(_downKeys, key);
|
||||
|
||||
// remove key from _downKeys
|
||||
if (i >= 0) {
|
||||
_downKeys.splice(i, 1);
|
||||
}
|
||||
|
||||
if(key == 93 || key == 224) key = 91;
|
||||
if(key in _mods) {
|
||||
_mods[key] = false;
|
||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
|
||||
}
|
||||
};
|
||||
|
||||
function resetModifiers() {
|
||||
for(k in _mods) _mods[k] = false;
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
};
|
||||
|
||||
// parse and assign shortcut
|
||||
function assignKey(key, scope, method){
|
||||
var keys, mods;
|
||||
keys = getKeys(key);
|
||||
if (method === undefined) {
|
||||
method = scope;
|
||||
scope = 'all';
|
||||
}
|
||||
|
||||
// for each shortcut
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
// set modifier keys if any
|
||||
mods = [];
|
||||
key = keys[i].split('+');
|
||||
if (key.length > 1){
|
||||
mods = getMods(key);
|
||||
key = [key[key.length-1]];
|
||||
}
|
||||
// convert to keycode and...
|
||||
key = key[0]
|
||||
key = code(key);
|
||||
// ...store handler
|
||||
if (!(key in _handlers)) _handlers[key] = [];
|
||||
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
|
||||
}
|
||||
};
|
||||
|
||||
// unbind all handlers for given key in current scope
|
||||
function unbindKey(key, scope) {
|
||||
var multipleKeys, keys,
|
||||
mods = [],
|
||||
i, j, obj;
|
||||
|
||||
multipleKeys = getKeys(key);
|
||||
|
||||
for (j = 0; j < multipleKeys.length; j++) {
|
||||
keys = multipleKeys[j].split('+');
|
||||
|
||||
if (keys.length > 1) {
|
||||
mods = getMods(keys);
|
||||
}
|
||||
|
||||
key = keys[keys.length - 1];
|
||||
key = code(key);
|
||||
|
||||
if (scope === undefined) {
|
||||
scope = getScope();
|
||||
}
|
||||
if (!_handlers[key]) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
obj = _handlers[key][i];
|
||||
// only clear handlers if correct scope and mods match
|
||||
if (obj.scope === scope && compareArray(obj.mods, mods)) {
|
||||
_handlers[key][i] = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true if the key with code 'keyCode' is currently down
|
||||
// Converts strings into key codes.
|
||||
function isPressed(keyCode) {
|
||||
if (typeof(keyCode)=='string') {
|
||||
keyCode = code(keyCode);
|
||||
}
|
||||
return index(_downKeys, keyCode) != -1;
|
||||
}
|
||||
|
||||
function getPressedKeyCodes() {
|
||||
return _downKeys.slice(0);
|
||||
}
|
||||
|
||||
function filter(event){
|
||||
var tagName = (event.target || event.srcElement).tagName;
|
||||
// ignore keypressed in any elements that support keyboard data input
|
||||
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
|
||||
}
|
||||
|
||||
// initialize key.<modifier> to false
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
|
||||
// set current scope (default 'all')
|
||||
function setScope(scope){ _scope = scope || 'all' };
|
||||
function getScope(){ return _scope || 'all' };
|
||||
|
||||
// delete all handlers for a given scope
|
||||
function deleteScope(scope){
|
||||
var key, handlers, i;
|
||||
|
||||
for (key in _handlers) {
|
||||
handlers = _handlers[key];
|
||||
for (i = 0; i < handlers.length; ) {
|
||||
if (handlers[i].scope === scope) handlers.splice(i, 1);
|
||||
else i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// abstract key logic for assign and unassign
|
||||
function getKeys(key) {
|
||||
var keys;
|
||||
key = key.replace(/\s/g, '');
|
||||
keys = key.split(',');
|
||||
if ((keys[keys.length - 1]) == '') {
|
||||
keys[keys.length - 2] += ',';
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// abstract mods logic for assign and unassign
|
||||
function getMods(key) {
|
||||
var mods = key.slice(0, key.length - 1);
|
||||
for (var mi = 0; mi < mods.length; mi++)
|
||||
mods[mi] = _MODIFIERS[mods[mi]];
|
||||
return mods;
|
||||
}
|
||||
|
||||
// cross-browser events
|
||||
function addEvent(object, event, method) {
|
||||
if (object.addEventListener)
|
||||
object.addEventListener(event, method, false);
|
||||
else if(object.attachEvent)
|
||||
object.attachEvent('on'+event, function(){ method(window.event) });
|
||||
};
|
||||
|
||||
// set the handlers globally on document
|
||||
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
|
||||
addEvent(document, 'keyup', clearModifier);
|
||||
|
||||
// reset modifiers to false whenever the window is (re)focused.
|
||||
addEvent(window, 'focus', resetModifiers);
|
||||
|
||||
// store previously defined key
|
||||
var previousKey = global.key;
|
||||
|
||||
// restore previously defined key and return reference to our key object
|
||||
function noConflict() {
|
||||
var k = global.key;
|
||||
global.key = previousKey;
|
||||
return k;
|
||||
}
|
||||
|
||||
// set window.key and window.key.set/get/deleteScope, and the default filter
|
||||
global.key = assignKey;
|
||||
global.key.setScope = setScope;
|
||||
global.key.getScope = getScope;
|
||||
global.key.deleteScope = deleteScope;
|
||||
global.key.filter = filter;
|
||||
global.key.isPressed = isPressed;
|
||||
global.key.getPressedKeyCodes = getPressedKeyCodes;
|
||||
global.key.noConflict = noConflict;
|
||||
global.key.unbind = unbindKey;
|
||||
|
||||
if(typeof module !== 'undefined') module.exports = assignKey;
|
||||
|
||||
})(this);
|
||||
/** Make a node */
|
||||
function mk(e) {return document.createElement(e)}
|
||||
|
||||
@@ -1022,6 +1318,70 @@ $.ready(function() {
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
if (!String.fromCodePoint) {
|
||||
(function() {
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch(error) {}
|
||||
return result;
|
||||
}());
|
||||
var stringFromCharCode = String.fromCharCode;
|
||||
var floor = Math.floor;
|
||||
var fromCodePoint = function() {
|
||||
var MAX_SIZE = 0x4000;
|
||||
var codeUnits = [];
|
||||
var highSurrogate;
|
||||
var lowSurrogate;
|
||||
var index = -1;
|
||||
var length = arguments.length;
|
||||
if (!length) {
|
||||
return '';
|
||||
}
|
||||
var result = '';
|
||||
while (++index < length) {
|
||||
var codePoint = Number(arguments[index]);
|
||||
if (
|
||||
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
|
||||
codePoint < 0 || // not a valid Unicode code point
|
||||
codePoint > 0x10FFFF || // not a valid Unicode code point
|
||||
floor(codePoint) != codePoint // not an integer
|
||||
) {
|
||||
throw RangeError('Invalid code point: ' + codePoint);
|
||||
}
|
||||
if (codePoint <= 0xFFFF) { // BMP code point
|
||||
codeUnits.push(codePoint);
|
||||
} else { // Astral code point; split in surrogate halves
|
||||
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
codePoint -= 0x10000;
|
||||
highSurrogate = (codePoint >> 10) + 0xD800;
|
||||
lowSurrogate = (codePoint % 0x400) + 0xDC00;
|
||||
codeUnits.push(highSurrogate, lowSurrogate);
|
||||
}
|
||||
if (index + 1 == length || codeUnits.length > MAX_SIZE) {
|
||||
result += stringFromCharCode.apply(null, codeUnits);
|
||||
codeUnits.length = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String, 'fromCodePoint', {
|
||||
'value': fromCodePoint,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.fromCodePoint = fromCodePoint;
|
||||
}
|
||||
}());
|
||||
}
|
||||
// Generated from PHP locale file
|
||||
var _tr = {
|
||||
"wifi.connected_ip_is": "Connected, IP is ",
|
||||
@@ -1200,7 +1560,7 @@ var Screen = (function () {
|
||||
y: 0,
|
||||
fg: 7, // colors 0-15
|
||||
bg: 0,
|
||||
bold: false,
|
||||
attrs: 0,
|
||||
suppress: false, // do not turn on in blink interval (for safe moving)
|
||||
hidden: false // do not show
|
||||
};
|
||||
@@ -1208,35 +1568,19 @@ var Screen = (function () {
|
||||
var screen = [];
|
||||
var blinkIval;
|
||||
|
||||
/** Clear screen */
|
||||
function _clear() {
|
||||
for (var i = W*H-1; i>=0; i--) {
|
||||
var cell = screen[i];
|
||||
cell.t = ' ';
|
||||
cell.bg = cursor.bg;
|
||||
cell.fg = cursor.fg;
|
||||
cell.bold = false;
|
||||
_draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set text and color at XY */
|
||||
function _cellAt(y, x) {
|
||||
return screen[y*W+x];
|
||||
}
|
||||
var frakturExceptions = {
|
||||
'C': '\u212d',
|
||||
'H': '\u210c',
|
||||
'I': '\u2111',
|
||||
'R': '\u211c',
|
||||
'Z': '\u2128',
|
||||
};
|
||||
|
||||
/** Get cell under cursor */
|
||||
function _curCell() {
|
||||
return screen[cursor.y*W + cursor.x];
|
||||
}
|
||||
|
||||
/** Enable or disable cursor visibility */
|
||||
function _cursorEnable(enable) {
|
||||
cursor.hidden = !enable;
|
||||
cursor.a &= enable;
|
||||
_draw(_curCell());
|
||||
}
|
||||
|
||||
/** Safely move cursor */
|
||||
function cursorSet(y, x) {
|
||||
// Hide and prevent from showing up during the move
|
||||
@@ -1255,13 +1599,44 @@ var Screen = (function () {
|
||||
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
|
||||
}
|
||||
|
||||
var e = cell.e, fg, bg;
|
||||
var elem = cell.e, fg, bg, cn, t;
|
||||
// Colors
|
||||
fg = inv ? cell.bg : cell.fg;
|
||||
bg = inv ? cell.fg : cell.bg;
|
||||
// Update
|
||||
e.innerText = (cell.t + ' ')[0];
|
||||
e.className = 'fg' + fg + ' bg' + bg + (cell.bold ? ' bold' : '');
|
||||
elem.textContent = t = (cell.t + ' ')[0];
|
||||
|
||||
cn = 'fg' + fg + ' bg' + bg;
|
||||
if (cell.attrs & (1<<0)) cn += ' bold';
|
||||
if (cell.attrs & (1<<2)) cn += ' italic';
|
||||
if (cell.attrs & (1<<3)) cn += ' under';
|
||||
if (cell.attrs & (1<<4)) cn += ' blink';
|
||||
if (cell.attrs & (1<<5)) {
|
||||
cn += ' fraktur';
|
||||
// perform substitution
|
||||
if (t >= 'a' && t <= 'z') {
|
||||
t = String.fromCodePoint(0x1d51e - 97 + t.charCodeAt(0));
|
||||
}
|
||||
else if (t >= 'A' && t <= 'Z') {
|
||||
// this set is incomplete, some exceptions are needed
|
||||
if (frakturExceptions.hasOwnProperty(t)) {
|
||||
t = frakturExceptions[t];
|
||||
} else {
|
||||
t = String.fromCodePoint(0x1d504 - 65 + t.charCodeAt(0));
|
||||
}
|
||||
}
|
||||
elem.textContent = t;
|
||||
}
|
||||
if (cell.attrs & (1<<6)) cn += ' strike';
|
||||
|
||||
if (cell.attrs & (1<<1)) {
|
||||
cn += ' faint';
|
||||
// faint requires special html - otherwise it would also dim the background.
|
||||
// we use opacity on the text...
|
||||
elem.innerHTML = '<span>' + e(elem.textContent) + '</span>';
|
||||
}
|
||||
|
||||
elem.className = cn;
|
||||
}
|
||||
|
||||
/** Show entire screen */
|
||||
@@ -1305,6 +1680,7 @@ var Screen = (function () {
|
||||
t: ' ',
|
||||
fg: cursor.fg,
|
||||
bg: cursor.bg,
|
||||
attrs: 0,
|
||||
e: e,
|
||||
x: i % W,
|
||||
y: Math.floor(i / W),
|
||||
@@ -1328,6 +1704,15 @@ var Screen = (function () {
|
||||
_draw(_curCell(), cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// blink attribute
|
||||
setInterval(function () {
|
||||
$('#screen').removeClass('blink-hide');
|
||||
setTimeout(function() {
|
||||
$('#screen').addClass('blink-hide');
|
||||
}, 800); // 200 ms ON
|
||||
}, 1000);
|
||||
|
||||
inited = true;
|
||||
}
|
||||
|
||||
@@ -1336,11 +1721,18 @@ var Screen = (function () {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
var SEQ_SET_COLOR = 1;
|
||||
/** 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;
|
||||
var SEQ_SET_ATTR = 4;
|
||||
|
||||
function _load_content(str) {
|
||||
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, bold, cell;
|
||||
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, attrs, cell;
|
||||
|
||||
if (!inited) _init();
|
||||
|
||||
@@ -1362,25 +1754,31 @@ var Screen = (function () {
|
||||
num = parse2B(str, i); i += 2; // fg bg bold hidden
|
||||
cursor.fg = num & 0x0F;
|
||||
cursor.bg = (num & 0xF0) >> 4;
|
||||
cursor.bold = !!(num & 0x100);
|
||||
cursor.hidden = !(num & 0x200);
|
||||
// console.log("FG ",cursor.fg, ", BG ", cursor.bg,", BOLD ", cursor.bold, ", HIDE ", cursor.hidden);
|
||||
cursor.hidden = !(num & 0x100);
|
||||
|
||||
fg = cursor.fg;
|
||||
bg = cursor.bg;
|
||||
bold = cursor.bold;
|
||||
attrs = 0;
|
||||
|
||||
// Here come the content
|
||||
while(i < str.length && ci<W*H) {
|
||||
|
||||
j = str[i++];
|
||||
jc = j.charCodeAt(0);
|
||||
if (jc == SEQ_SET_COLOR) {
|
||||
if (jc == SEQ_SET_COLOR_ATTR) {
|
||||
num = parse3B(str, i); i += 3;
|
||||
fg = num & 0x0F;
|
||||
bg = (num & 0xF0) >> 4;
|
||||
attrs = (num & 0xFF00)>>8;
|
||||
}
|
||||
else if (jc == SEQ_SET_COLOR) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
fg = num & 0x0F;
|
||||
bg = (num & 0xF0) >> 4;
|
||||
bold = !!(num & 0x100);
|
||||
// console.log("Switch to ",fg,bg,bold);
|
||||
}
|
||||
else if (jc == SEQ_SET_ATTR) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
attrs = num & 0xFF;
|
||||
}
|
||||
else if (jc == SEQ_REPEAT) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
@@ -1390,7 +1788,7 @@ var Screen = (function () {
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.t = t;
|
||||
cell.bold = bold;
|
||||
cell.attrs = attrs;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1399,7 +1797,7 @@ var Screen = (function () {
|
||||
t = cell.t = j;
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.bold = bold;
|
||||
cell.attrs = attrs;
|
||||
// console.log("Symbol ", j);
|
||||
}
|
||||
}
|
||||
@@ -1450,12 +1848,14 @@ var Conn = (function() {
|
||||
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
|
||||
setTimeout(function() {
|
||||
init();
|
||||
}, 1000);
|
||||
}, 200);
|
||||
// this happens when the buffer gets fucked up via invalid unicode.
|
||||
// we basically use polling instead of socket then
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
console.log("RX: ", evt.data);
|
||||
//console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
} catch(e) {
|
||||
@@ -1515,31 +1915,89 @@ var Input = (function() {
|
||||
Conn.send("BTN:"+n);
|
||||
}
|
||||
|
||||
function _initKeys() {
|
||||
// This takes care of text characters typed
|
||||
window.addEventListener('keypress', function(evt) {
|
||||
var str = '';
|
||||
if (evt.key) str = evt.key;
|
||||
else if (evt.which) str = String.fromCodePoint(evt.which);
|
||||
if (str.length>0 && str.charCodeAt(0) >= 32) {
|
||||
// console.log("Typed ", str);
|
||||
sendStrMsg(str);
|
||||
}
|
||||
});
|
||||
|
||||
var keymap = {
|
||||
'tab': '\x09',
|
||||
'backspace': '\x08',
|
||||
'enter': '\x0d',
|
||||
'ctrl+enter': '\x0a',
|
||||
'esc': '\x1b',
|
||||
'up': '\x1b[A',
|
||||
'down': '\x1b[B',
|
||||
'right': '\x1b[C',
|
||||
'left': '\x1b[D',
|
||||
'home': '\x1b[1~',
|
||||
'insert': '\x1b[2~',
|
||||
'delete': '\x1b[3~',
|
||||
'end': '\x1b[4~',
|
||||
'pageup': '\x1b[5~',
|
||||
'pagedown': '\x1b[6~',
|
||||
'f1': '\x1b[11~',
|
||||
'f2': '\x1b[12~',
|
||||
'f3': '\x1b[13~',
|
||||
'f4': '\x1b[14~',
|
||||
'f5': '\x1b[15~', // disconnect
|
||||
'f6': '\x1b[17~',
|
||||
'f7': '\x1b[18~',
|
||||
'f8': '\x1b[19~',
|
||||
'f9': '\x1b[20~',
|
||||
'f10': '\x1b[21~', // disconnect
|
||||
'f11': '\x1b[23~',
|
||||
'f12': '\x1b[24~',
|
||||
'shift+f1': '\x1b[25~',
|
||||
'shift+f2': '\x1b[26~', // disconnect
|
||||
'shift+f3': '\x1b[28~',
|
||||
'shift+f4': '\x1b[29~', // disconnect
|
||||
'shift+f5': '\x1b[31~',
|
||||
'shift+f6': '\x1b[32~',
|
||||
'shift+f7': '\x1b[33~',
|
||||
'shift+f8': '\x1b[34~',
|
||||
};
|
||||
|
||||
function bind(combo, str) {
|
||||
// mac fix - allow also cmd
|
||||
if (combo.indexOf('ctrl+') !== -1) {
|
||||
combo += ',' + combo.replace('ctrl', 'command');
|
||||
}
|
||||
key(combo, function (e) {
|
||||
e.preventDefault();
|
||||
// console.log(combo);
|
||||
sendStrMsg(str)
|
||||
});
|
||||
}
|
||||
|
||||
for (var k in keymap) {
|
||||
if (keymap.hasOwnProperty(k)) {
|
||||
bind(k, keymap[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// ctrl-letter codes are sent as simple low ASCII codes
|
||||
for (var i = 1; i<=26;i++) {
|
||||
bind('ctrl+' + String.fromCharCode(96+i), String.fromCharCode(i));
|
||||
}
|
||||
bind('ctrl+]', '\x1b'); // alternate way to enter ESC
|
||||
bind('ctrl+\\', '\x1c');
|
||||
bind('ctrl+[', '\x1d');
|
||||
bind('ctrl+^', '\x1e');
|
||||
bind('ctrl+_', '\x1f');
|
||||
}
|
||||
|
||||
function init() {
|
||||
window.addEventListener('keypress', function(e) {
|
||||
var code = +e.which;
|
||||
if (code >= 32 && code < 127) {
|
||||
var ch = String.fromCharCode(code);
|
||||
//console.log("Typed ", ch, "code", code, e);
|
||||
sendStrMsg(ch);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', function(e) {
|
||||
var code = e.keyCode;
|
||||
//console.log("Down ", code, e);
|
||||
switch(code) {
|
||||
case 8: sendStrMsg('\x08'); break;
|
||||
case 10:
|
||||
case 13: sendStrMsg('\x0d\x0a'); break;
|
||||
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences
|
||||
case 37: sendStrMsg('\x1b[D'); break;
|
||||
case 38: sendStrMsg('\x1b[A'); break;
|
||||
case 39: sendStrMsg('\x1b[C'); break;
|
||||
case 40: sendStrMsg('\x1b[B'); break;
|
||||
}
|
||||
});
|
||||
_initKeys();
|
||||
|
||||
// Button presses
|
||||
qsa('#buttons button').forEach(function(s) {
|
||||
s.addEventListener('click', function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
|
||||
@@ -123,3 +123,67 @@ $.ready(function() {
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
||||
if (!String.fromCodePoint) {
|
||||
(function() {
|
||||
var defineProperty = (function() {
|
||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
||||
try {
|
||||
var object = {};
|
||||
var $defineProperty = Object.defineProperty;
|
||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
||||
} catch(error) {}
|
||||
return result;
|
||||
}());
|
||||
var stringFromCharCode = String.fromCharCode;
|
||||
var floor = Math.floor;
|
||||
var fromCodePoint = function() {
|
||||
var MAX_SIZE = 0x4000;
|
||||
var codeUnits = [];
|
||||
var highSurrogate;
|
||||
var lowSurrogate;
|
||||
var index = -1;
|
||||
var length = arguments.length;
|
||||
if (!length) {
|
||||
return '';
|
||||
}
|
||||
var result = '';
|
||||
while (++index < length) {
|
||||
var codePoint = Number(arguments[index]);
|
||||
if (
|
||||
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
|
||||
codePoint < 0 || // not a valid Unicode code point
|
||||
codePoint > 0x10FFFF || // not a valid Unicode code point
|
||||
floor(codePoint) != codePoint // not an integer
|
||||
) {
|
||||
throw RangeError('Invalid code point: ' + codePoint);
|
||||
}
|
||||
if (codePoint <= 0xFFFF) { // BMP code point
|
||||
codeUnits.push(codePoint);
|
||||
} else { // Astral code point; split in surrogate halves
|
||||
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
codePoint -= 0x10000;
|
||||
highSurrogate = (codePoint >> 10) + 0xD800;
|
||||
lowSurrogate = (codePoint % 0x400) + 0xDC00;
|
||||
codeUnits.push(highSurrogate, lowSurrogate);
|
||||
}
|
||||
if (index + 1 == length || codeUnits.length > MAX_SIZE) {
|
||||
result += stringFromCharCode.apply(null, codeUnits);
|
||||
codeUnits.length = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
if (defineProperty) {
|
||||
defineProperty(String, 'fromCodePoint', {
|
||||
'value': fromCodePoint,
|
||||
'configurable': true,
|
||||
'writable': true
|
||||
});
|
||||
} else {
|
||||
String.fromCodePoint = fromCodePoint;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
// keymaster.js
|
||||
// (c) 2011-2013 Thomas Fuchs
|
||||
// keymaster.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function(global){
|
||||
var k,
|
||||
_handlers = {},
|
||||
_mods = { 16: false, 18: false, 17: false, 91: false },
|
||||
_scope = 'all',
|
||||
// modifier keys
|
||||
_MODIFIERS = {
|
||||
'⇧': 16, shift: 16,
|
||||
'⌥': 18, alt: 18, option: 18,
|
||||
'⌃': 17, ctrl: 17, control: 17,
|
||||
'⌘': 91, command: 91
|
||||
},
|
||||
// special keys
|
||||
_MAP = {
|
||||
backspace: 8, tab: 9, clear: 12,
|
||||
enter: 13, 'return': 13,
|
||||
esc: 27, escape: 27, space: 32,
|
||||
left: 37, up: 38,
|
||||
right: 39, down: 40,
|
||||
del: 46, 'delete': 46,
|
||||
home: 36, end: 35,
|
||||
pageup: 33, pagedown: 34, insert: 45, // added insert
|
||||
',': 188, '.': 190, '/': 191,
|
||||
'`': 192, '-': 189, '=': 187,
|
||||
';': 186, '\'': 222,
|
||||
'[': 219, ']': 221, '\\': 220
|
||||
},
|
||||
code = function(x){
|
||||
return _MAP[x] || x.toUpperCase().charCodeAt(0);
|
||||
},
|
||||
_downKeys = [];
|
||||
|
||||
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
|
||||
|
||||
// IE doesn't support Array#indexOf, so have a simple replacement
|
||||
function index(array, item){
|
||||
var i = array.length;
|
||||
while(i--) if(array[i]===item) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// for comparing mods before unassignment
|
||||
function compareArray(a1, a2) {
|
||||
if (a1.length != a2.length) return false;
|
||||
for (var i = 0; i < a1.length; i++) {
|
||||
if (a1[i] !== a2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var modifierMap = {
|
||||
16:'shiftKey',
|
||||
18:'altKey',
|
||||
17:'ctrlKey',
|
||||
91:'metaKey'
|
||||
};
|
||||
function updateModifierKey(event) {
|
||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
||||
};
|
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) {
|
||||
var key, handler, k, i, modifiersMatch, scope;
|
||||
key = event.keyCode;
|
||||
|
||||
if (index(_downKeys, key) == -1) {
|
||||
_downKeys.push(key);
|
||||
}
|
||||
|
||||
// if a modifier key, set the key.<modifierkeyname> property to true and return
|
||||
if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
|
||||
if(key in _mods) {
|
||||
_mods[key] = true;
|
||||
// 'assignKey' from inside this closure is exported to window.key
|
||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
|
||||
return;
|
||||
}
|
||||
updateModifierKey(event);
|
||||
|
||||
// see if we need to ignore the keypress (filter() can can be overridden)
|
||||
// by default ignore key presses if a select, textarea, or input is focused
|
||||
if(!assignKey.filter.call(this, event)) return;
|
||||
|
||||
// abort if no potentially matching shortcuts found
|
||||
if (!(key in _handlers)) return;
|
||||
|
||||
scope = getScope();
|
||||
|
||||
// for each potential shortcut
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
handler = _handlers[key][i];
|
||||
|
||||
// see if it's in the current scope
|
||||
if(handler.scope == scope || handler.scope == 'all'){
|
||||
// check if modifiers match if any
|
||||
modifiersMatch = handler.mods.length > 0;
|
||||
for(k in _mods)
|
||||
if((!_mods[k] && index(handler.mods, +k) > -1) ||
|
||||
(_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
|
||||
// call the handler and stop the event if neccessary
|
||||
if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
|
||||
if(handler.method(event, handler)===false){
|
||||
if(event.preventDefault) event.preventDefault();
|
||||
else event.returnValue = false;
|
||||
if(event.stopPropagation) event.stopPropagation();
|
||||
if(event.cancelBubble) event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unset modifier keys on keyup
|
||||
function clearModifier(event){
|
||||
var key = event.keyCode, k,
|
||||
i = index(_downKeys, key);
|
||||
|
||||
// remove key from _downKeys
|
||||
if (i >= 0) {
|
||||
_downKeys.splice(i, 1);
|
||||
}
|
||||
|
||||
if(key == 93 || key == 224) key = 91;
|
||||
if(key in _mods) {
|
||||
_mods[key] = false;
|
||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
|
||||
}
|
||||
};
|
||||
|
||||
function resetModifiers() {
|
||||
for(k in _mods) _mods[k] = false;
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
};
|
||||
|
||||
// parse and assign shortcut
|
||||
function assignKey(key, scope, method){
|
||||
var keys, mods;
|
||||
keys = getKeys(key);
|
||||
if (method === undefined) {
|
||||
method = scope;
|
||||
scope = 'all';
|
||||
}
|
||||
|
||||
// for each shortcut
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
// set modifier keys if any
|
||||
mods = [];
|
||||
key = keys[i].split('+');
|
||||
if (key.length > 1){
|
||||
mods = getMods(key);
|
||||
key = [key[key.length-1]];
|
||||
}
|
||||
// convert to keycode and...
|
||||
key = key[0]
|
||||
key = code(key);
|
||||
// ...store handler
|
||||
if (!(key in _handlers)) _handlers[key] = [];
|
||||
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
|
||||
}
|
||||
};
|
||||
|
||||
// unbind all handlers for given key in current scope
|
||||
function unbindKey(key, scope) {
|
||||
var multipleKeys, keys,
|
||||
mods = [],
|
||||
i, j, obj;
|
||||
|
||||
multipleKeys = getKeys(key);
|
||||
|
||||
for (j = 0; j < multipleKeys.length; j++) {
|
||||
keys = multipleKeys[j].split('+');
|
||||
|
||||
if (keys.length > 1) {
|
||||
mods = getMods(keys);
|
||||
}
|
||||
|
||||
key = keys[keys.length - 1];
|
||||
key = code(key);
|
||||
|
||||
if (scope === undefined) {
|
||||
scope = getScope();
|
||||
}
|
||||
if (!_handlers[key]) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
obj = _handlers[key][i];
|
||||
// only clear handlers if correct scope and mods match
|
||||
if (obj.scope === scope && compareArray(obj.mods, mods)) {
|
||||
_handlers[key][i] = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true if the key with code 'keyCode' is currently down
|
||||
// Converts strings into key codes.
|
||||
function isPressed(keyCode) {
|
||||
if (typeof(keyCode)=='string') {
|
||||
keyCode = code(keyCode);
|
||||
}
|
||||
return index(_downKeys, keyCode) != -1;
|
||||
}
|
||||
|
||||
function getPressedKeyCodes() {
|
||||
return _downKeys.slice(0);
|
||||
}
|
||||
|
||||
function filter(event){
|
||||
var tagName = (event.target || event.srcElement).tagName;
|
||||
// ignore keypressed in any elements that support keyboard data input
|
||||
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
|
||||
}
|
||||
|
||||
// initialize key.<modifier> to false
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
|
||||
// set current scope (default 'all')
|
||||
function setScope(scope){ _scope = scope || 'all' };
|
||||
function getScope(){ return _scope || 'all' };
|
||||
|
||||
// delete all handlers for a given scope
|
||||
function deleteScope(scope){
|
||||
var key, handlers, i;
|
||||
|
||||
for (key in _handlers) {
|
||||
handlers = _handlers[key];
|
||||
for (i = 0; i < handlers.length; ) {
|
||||
if (handlers[i].scope === scope) handlers.splice(i, 1);
|
||||
else i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// abstract key logic for assign and unassign
|
||||
function getKeys(key) {
|
||||
var keys;
|
||||
key = key.replace(/\s/g, '');
|
||||
keys = key.split(',');
|
||||
if ((keys[keys.length - 1]) == '') {
|
||||
keys[keys.length - 2] += ',';
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// abstract mods logic for assign and unassign
|
||||
function getMods(key) {
|
||||
var mods = key.slice(0, key.length - 1);
|
||||
for (var mi = 0; mi < mods.length; mi++)
|
||||
mods[mi] = _MODIFIERS[mods[mi]];
|
||||
return mods;
|
||||
}
|
||||
|
||||
// cross-browser events
|
||||
function addEvent(object, event, method) {
|
||||
if (object.addEventListener)
|
||||
object.addEventListener(event, method, false);
|
||||
else if(object.attachEvent)
|
||||
object.attachEvent('on'+event, function(){ method(window.event) });
|
||||
};
|
||||
|
||||
// set the handlers globally on document
|
||||
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
|
||||
addEvent(document, 'keyup', clearModifier);
|
||||
|
||||
// reset modifiers to false whenever the window is (re)focused.
|
||||
addEvent(window, 'focus', resetModifiers);
|
||||
|
||||
// store previously defined key
|
||||
var previousKey = global.key;
|
||||
|
||||
// restore previously defined key and return reference to our key object
|
||||
function noConflict() {
|
||||
var k = global.key;
|
||||
global.key = previousKey;
|
||||
return k;
|
||||
}
|
||||
|
||||
// set window.key and window.key.set/get/deleteScope, and the default filter
|
||||
global.key = assignKey;
|
||||
global.key.setScope = setScope;
|
||||
global.key.getScope = getScope;
|
||||
global.key.deleteScope = deleteScope;
|
||||
global.key.filter = filter;
|
||||
global.key.isPressed = isPressed;
|
||||
global.key.getPressedKeyCodes = getPressedKeyCodes;
|
||||
global.key.noConflict = noConflict;
|
||||
global.key.unbind = unbindKey;
|
||||
|
||||
if(typeof module !== 'undefined') module.exports = assignKey;
|
||||
|
||||
})(this);
|
||||
+161
-63
@@ -8,7 +8,7 @@ var Screen = (function () {
|
||||
y: 0,
|
||||
fg: 7, // colors 0-15
|
||||
bg: 0,
|
||||
bold: false,
|
||||
attrs: 0,
|
||||
suppress: false, // do not turn on in blink interval (for safe moving)
|
||||
hidden: false // do not show
|
||||
};
|
||||
@@ -16,35 +16,19 @@ var Screen = (function () {
|
||||
var screen = [];
|
||||
var blinkIval;
|
||||
|
||||
/** Clear screen */
|
||||
function _clear() {
|
||||
for (var i = W*H-1; i>=0; i--) {
|
||||
var cell = screen[i];
|
||||
cell.t = ' ';
|
||||
cell.bg = cursor.bg;
|
||||
cell.fg = cursor.fg;
|
||||
cell.bold = false;
|
||||
_draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set text and color at XY */
|
||||
function _cellAt(y, x) {
|
||||
return screen[y*W+x];
|
||||
}
|
||||
var frakturExceptions = {
|
||||
'C': '\u212d',
|
||||
'H': '\u210c',
|
||||
'I': '\u2111',
|
||||
'R': '\u211c',
|
||||
'Z': '\u2128',
|
||||
};
|
||||
|
||||
/** Get cell under cursor */
|
||||
function _curCell() {
|
||||
return screen[cursor.y*W + cursor.x];
|
||||
}
|
||||
|
||||
/** Enable or disable cursor visibility */
|
||||
function _cursorEnable(enable) {
|
||||
cursor.hidden = !enable;
|
||||
cursor.a &= enable;
|
||||
_draw(_curCell());
|
||||
}
|
||||
|
||||
/** Safely move cursor */
|
||||
function cursorSet(y, x) {
|
||||
// Hide and prevent from showing up during the move
|
||||
@@ -63,13 +47,44 @@ var Screen = (function () {
|
||||
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
|
||||
}
|
||||
|
||||
var e = cell.e, fg, bg;
|
||||
var elem = cell.e, fg, bg, cn, t;
|
||||
// Colors
|
||||
fg = inv ? cell.bg : cell.fg;
|
||||
bg = inv ? cell.fg : cell.bg;
|
||||
// Update
|
||||
e.innerText = (cell.t + ' ')[0];
|
||||
e.className = 'fg' + fg + ' bg' + bg + (cell.bold ? ' bold' : '');
|
||||
elem.textContent = t = (cell.t + ' ')[0];
|
||||
|
||||
cn = 'fg' + fg + ' bg' + bg;
|
||||
if (cell.attrs & (1<<0)) cn += ' bold';
|
||||
if (cell.attrs & (1<<2)) cn += ' italic';
|
||||
if (cell.attrs & (1<<3)) cn += ' under';
|
||||
if (cell.attrs & (1<<4)) cn += ' blink';
|
||||
if (cell.attrs & (1<<5)) {
|
||||
cn += ' fraktur';
|
||||
// perform substitution
|
||||
if (t >= 'a' && t <= 'z') {
|
||||
t = String.fromCodePoint(0x1d51e - 97 + t.charCodeAt(0));
|
||||
}
|
||||
else if (t >= 'A' && t <= 'Z') {
|
||||
// this set is incomplete, some exceptions are needed
|
||||
if (frakturExceptions.hasOwnProperty(t)) {
|
||||
t = frakturExceptions[t];
|
||||
} else {
|
||||
t = String.fromCodePoint(0x1d504 - 65 + t.charCodeAt(0));
|
||||
}
|
||||
}
|
||||
elem.textContent = t;
|
||||
}
|
||||
if (cell.attrs & (1<<6)) cn += ' strike';
|
||||
|
||||
if (cell.attrs & (1<<1)) {
|
||||
cn += ' faint';
|
||||
// faint requires special html - otherwise it would also dim the background.
|
||||
// we use opacity on the text...
|
||||
elem.innerHTML = '<span>' + e(elem.textContent) + '</span>';
|
||||
}
|
||||
|
||||
elem.className = cn;
|
||||
}
|
||||
|
||||
/** Show entire screen */
|
||||
@@ -113,6 +128,7 @@ var Screen = (function () {
|
||||
t: ' ',
|
||||
fg: cursor.fg,
|
||||
bg: cursor.bg,
|
||||
attrs: 0,
|
||||
e: e,
|
||||
x: i % W,
|
||||
y: Math.floor(i / W),
|
||||
@@ -136,6 +152,15 @@ var Screen = (function () {
|
||||
_draw(_curCell(), cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// blink attribute
|
||||
setInterval(function () {
|
||||
$('#screen').removeClass('blink-hide');
|
||||
setTimeout(function() {
|
||||
$('#screen').addClass('blink-hide');
|
||||
}, 800); // 200 ms ON
|
||||
}, 1000);
|
||||
|
||||
inited = true;
|
||||
}
|
||||
|
||||
@@ -144,11 +169,18 @@ var Screen = (function () {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
var SEQ_SET_COLOR = 1;
|
||||
/** 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;
|
||||
var SEQ_SET_ATTR = 4;
|
||||
|
||||
function _load_content(str) {
|
||||
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, bold, cell;
|
||||
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, attrs, cell;
|
||||
|
||||
if (!inited) _init();
|
||||
|
||||
@@ -170,25 +202,31 @@ var Screen = (function () {
|
||||
num = parse2B(str, i); i += 2; // fg bg bold hidden
|
||||
cursor.fg = num & 0x0F;
|
||||
cursor.bg = (num & 0xF0) >> 4;
|
||||
cursor.bold = !!(num & 0x100);
|
||||
cursor.hidden = !(num & 0x200);
|
||||
// console.log("FG ",cursor.fg, ", BG ", cursor.bg,", BOLD ", cursor.bold, ", HIDE ", cursor.hidden);
|
||||
cursor.hidden = !(num & 0x100);
|
||||
|
||||
fg = cursor.fg;
|
||||
bg = cursor.bg;
|
||||
bold = cursor.bold;
|
||||
attrs = 0;
|
||||
|
||||
// Here come the content
|
||||
while(i < str.length && ci<W*H) {
|
||||
|
||||
j = str[i++];
|
||||
jc = j.charCodeAt(0);
|
||||
if (jc == SEQ_SET_COLOR) {
|
||||
if (jc == SEQ_SET_COLOR_ATTR) {
|
||||
num = parse3B(str, i); i += 3;
|
||||
fg = num & 0x0F;
|
||||
bg = (num & 0xF0) >> 4;
|
||||
attrs = (num & 0xFF00)>>8;
|
||||
}
|
||||
else if (jc == SEQ_SET_COLOR) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
fg = num & 0x0F;
|
||||
bg = (num & 0xF0) >> 4;
|
||||
bold = !!(num & 0x100);
|
||||
// console.log("Switch to ",fg,bg,bold);
|
||||
}
|
||||
else if (jc == SEQ_SET_ATTR) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
attrs = num & 0xFF;
|
||||
}
|
||||
else if (jc == SEQ_REPEAT) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
@@ -198,7 +236,7 @@ var Screen = (function () {
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.t = t;
|
||||
cell.bold = bold;
|
||||
cell.attrs = attrs;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -207,7 +245,7 @@ var Screen = (function () {
|
||||
t = cell.t = j;
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.bold = bold;
|
||||
cell.attrs = attrs;
|
||||
// console.log("Symbol ", j);
|
||||
}
|
||||
}
|
||||
@@ -258,12 +296,14 @@ var Conn = (function() {
|
||||
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
|
||||
setTimeout(function() {
|
||||
init();
|
||||
}, 1000);
|
||||
}, 200);
|
||||
// this happens when the buffer gets fucked up via invalid unicode.
|
||||
// we basically use polling instead of socket then
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
console.log("RX: ", evt.data);
|
||||
//console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
} catch(e) {
|
||||
@@ -323,31 +363,89 @@ var Input = (function() {
|
||||
Conn.send("BTN:"+n);
|
||||
}
|
||||
|
||||
function _initKeys() {
|
||||
// This takes care of text characters typed
|
||||
window.addEventListener('keypress', function(evt) {
|
||||
var str = '';
|
||||
if (evt.key) str = evt.key;
|
||||
else if (evt.which) str = String.fromCodePoint(evt.which);
|
||||
if (str.length>0 && str.charCodeAt(0) >= 32) {
|
||||
// console.log("Typed ", str);
|
||||
sendStrMsg(str);
|
||||
}
|
||||
});
|
||||
|
||||
var keymap = {
|
||||
'tab': '\x09',
|
||||
'backspace': '\x08',
|
||||
'enter': '\x0d',
|
||||
'ctrl+enter': '\x0a',
|
||||
'esc': '\x1b',
|
||||
'up': '\x1b[A',
|
||||
'down': '\x1b[B',
|
||||
'right': '\x1b[C',
|
||||
'left': '\x1b[D',
|
||||
'home': '\x1b[1~',
|
||||
'insert': '\x1b[2~',
|
||||
'delete': '\x1b[3~',
|
||||
'end': '\x1b[4~',
|
||||
'pageup': '\x1b[5~',
|
||||
'pagedown': '\x1b[6~',
|
||||
'f1': '\x1b[11~',
|
||||
'f2': '\x1b[12~',
|
||||
'f3': '\x1b[13~',
|
||||
'f4': '\x1b[14~',
|
||||
'f5': '\x1b[15~', // disconnect
|
||||
'f6': '\x1b[17~',
|
||||
'f7': '\x1b[18~',
|
||||
'f8': '\x1b[19~',
|
||||
'f9': '\x1b[20~',
|
||||
'f10': '\x1b[21~', // disconnect
|
||||
'f11': '\x1b[23~',
|
||||
'f12': '\x1b[24~',
|
||||
'shift+f1': '\x1b[25~',
|
||||
'shift+f2': '\x1b[26~', // disconnect
|
||||
'shift+f3': '\x1b[28~',
|
||||
'shift+f4': '\x1b[29~', // disconnect
|
||||
'shift+f5': '\x1b[31~',
|
||||
'shift+f6': '\x1b[32~',
|
||||
'shift+f7': '\x1b[33~',
|
||||
'shift+f8': '\x1b[34~',
|
||||
};
|
||||
|
||||
function bind(combo, str) {
|
||||
// mac fix - allow also cmd
|
||||
if (combo.indexOf('ctrl+') !== -1) {
|
||||
combo += ',' + combo.replace('ctrl', 'command');
|
||||
}
|
||||
key(combo, function (e) {
|
||||
e.preventDefault();
|
||||
// console.log(combo);
|
||||
sendStrMsg(str)
|
||||
});
|
||||
}
|
||||
|
||||
for (var k in keymap) {
|
||||
if (keymap.hasOwnProperty(k)) {
|
||||
bind(k, keymap[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// ctrl-letter codes are sent as simple low ASCII codes
|
||||
for (var i = 1; i<=26;i++) {
|
||||
bind('ctrl+' + String.fromCharCode(96+i), String.fromCharCode(i));
|
||||
}
|
||||
bind('ctrl+]', '\x1b'); // alternate way to enter ESC
|
||||
bind('ctrl+\\', '\x1c');
|
||||
bind('ctrl+[', '\x1d');
|
||||
bind('ctrl+^', '\x1e');
|
||||
bind('ctrl+_', '\x1f');
|
||||
}
|
||||
|
||||
function init() {
|
||||
window.addEventListener('keypress', function(e) {
|
||||
var code = +e.which;
|
||||
if (code >= 32 && code < 127) {
|
||||
var ch = String.fromCharCode(code);
|
||||
//console.log("Typed ", ch, "code", code, e);
|
||||
sendStrMsg(ch);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', function(e) {
|
||||
var code = e.keyCode;
|
||||
//console.log("Down ", code, e);
|
||||
switch(code) {
|
||||
case 8: sendStrMsg('\x08'); break;
|
||||
case 10:
|
||||
case 13: sendStrMsg('\x0d\x0a'); break;
|
||||
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences
|
||||
case 37: sendStrMsg('\x1b[D'); break;
|
||||
case 38: sendStrMsg('\x1b[A'); break;
|
||||
case 39: sendStrMsg('\x1b[C'); break;
|
||||
case 40: sendStrMsg('\x1b[B'); break;
|
||||
}
|
||||
});
|
||||
_initKeys();
|
||||
|
||||
// Button presses
|
||||
qsa('#buttons button').forEach(function(s) {
|
||||
s.addEventListener('click', function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
|
||||
+17
-2
@@ -16,6 +16,11 @@ return [
|
||||
|
||||
'title.term' => 'Terminal',
|
||||
|
||||
'term_nav.config' => 'Config',
|
||||
'term_nav.wifi' => 'WiFi',
|
||||
'term_nav.help' => 'Help',
|
||||
'term_nav.about' => 'About',
|
||||
|
||||
'net.ap' => 'DHCP Server (AP)',
|
||||
'net.sta' => 'DHCP Client (Station)',
|
||||
'net.sta_mac' => 'Station MAC',
|
||||
@@ -100,10 +105,20 @@ return [
|
||||
|
||||
'wifi.conn.status' => 'Status:',
|
||||
'wifi.conn.back_to_config' => 'Back to WiFi config',
|
||||
'wifi.conn.telemetry_lost' => 'Telemetry lost, something went wrong. Try again...',
|
||||
'wifi.conn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.',
|
||||
'wifi.conn.explain_android_sucks' => '
|
||||
If you\'re configuring ESPTerm via a smartphone, or were connected
|
||||
from another external network, your device may lose connection and this
|
||||
progress indicator won\'t work. Please wait a while (~ 15 seconds),
|
||||
then check if the connection succeeded.',
|
||||
|
||||
'wifi.conn.explain_reset' => '
|
||||
To force enable the built-in AP, hold the BOOT
|
||||
button until the blue LED starts flashing. Hold the button longer (until the LED
|
||||
flashes rapidly) for a "factory reset".',
|
||||
|
||||
'wifi.conn.disabled' =>"Station mode is disabled.",
|
||||
'wifi.conn.idle' =>"Idle, not connected and with no IP.",
|
||||
'wifi.conn.idle' =>"Idle, not connected and has no IP.",
|
||||
'wifi.conn.success' => "Connected! Received IP ",
|
||||
'wifi.conn.working' => "Connecting to selected AP",
|
||||
'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
echo "Packing js..."
|
||||
|
||||
cat jssrc/chibi.js \
|
||||
jssrc/keymaster.js \
|
||||
jssrc/utils.js \
|
||||
jssrc/modal.js \
|
||||
jssrc/notif.js \
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-2">
|
||||
<form class="Box str mobcol expanded" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-2">
|
||||
<h2 tabindex=0><?= tr('wifi.sta') ?></h2>
|
||||
|
||||
<div class="Row checkbox x-sta-toggle">
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
<a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wifi.conn.back_to_config') ?></a>
|
||||
</div>
|
||||
|
||||
<div class="Box">
|
||||
<p><?= tr('wifi.conn.explain_android_sucks') ?></p>
|
||||
<p><?= tr('wifi.conn.explain_reset') ?></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var xhr = new XMLHttpRequest();
|
||||
var abortTmeo;
|
||||
var failCounter = 0;
|
||||
|
||||
var messages = <?= json_encode([
|
||||
'disabled' => tr('wifi.conn.disabled'),
|
||||
@@ -17,33 +23,63 @@
|
||||
'fail' => tr('wifi.conn.fail'),
|
||||
]) ?>;
|
||||
|
||||
function onFail() {
|
||||
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
|
||||
$('.anim-dots').addClass('hidden');
|
||||
}
|
||||
|
||||
function getStatus() {
|
||||
xhr.open("GET", 'http://'+_root+'<?= url('wifi_connstatus', true) ?>');
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
|
||||
clearTimeout(abortTmeo);
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
var done = false;
|
||||
var msg = messages[data.status] || '...';
|
||||
if (data.status == 'success') msg += data.ip;
|
||||
if (data.status == 'fail') msg += data.cause;
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
clearTimeout(abortTmeo);
|
||||
|
||||
$("#status").html(msg);
|
||||
try {
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
var done = false;
|
||||
var msg = messages[data.status] || '...';
|
||||
|
||||
if (done) {
|
||||
// $('#backbtn').removeClass('hidden');
|
||||
$('.anim-dots').addClass('hidden');
|
||||
if (data.status == 'success') {
|
||||
msg += data.ip;
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (data.status == 'fail') {
|
||||
msg += data.cause;
|
||||
done = true;
|
||||
}
|
||||
|
||||
$("#status").html(msg);
|
||||
|
||||
if (done) {
|
||||
// $('#backbtn').removeClass('hidden');
|
||||
$('.anim-dots').addClass('hidden');
|
||||
} else {
|
||||
// ask again after a short delay
|
||||
window.setTimeout(getStatus, 1000);
|
||||
}
|
||||
} catch(e) {
|
||||
failCounter++;
|
||||
console.log(e);
|
||||
// repeat
|
||||
if (failCounter > 5) {
|
||||
onFail();
|
||||
}
|
||||
else {
|
||||
window.setTimeout(getStatus, 1000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
window.setTimeout(getStatus, 1000);
|
||||
onFail();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// XHR timeout
|
||||
abortTmeo = setTimeout(function () {
|
||||
xhr.abort();
|
||||
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
|
||||
// $('#backbtn').removeClass('hidden');
|
||||
$('.anim-dots').addClass('hidden');
|
||||
onFail();
|
||||
}, 4000);
|
||||
|
||||
xhr.send();
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
|
||||
<nav id="botnav">
|
||||
<a href="#" onclick="toggleSoftKb(true); return false" class="icn-keyboard mq-tablet-max"></a><!--
|
||||
--><a href="<?= url('cfg_term') ?>"><?= tr('menu.settings') ?></a><!--
|
||||
--><a href="<?= url('help') ?>">Help</a><!--
|
||||
--><a href="<?= url('about') ?>">About</a>
|
||||
--><a href="<?= url('cfg_term') ?>"><?= tr('term_nav.config') ?></a><!--
|
||||
--><a href="<?= url('cfg_wifi') ?>"><?= tr('term_nav.wifi') ?></a><!--
|
||||
--><a href="<?= url('help') ?>"><?= tr('term_nav.help') ?></a><!--
|
||||
--><a href="<?= url('about') ?>"><?= tr('term_nav.about') ?></a>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -86,11 +86,6 @@ body.term {
|
||||
top: -9999px;
|
||||
}
|
||||
|
||||
// "non-bold"
|
||||
.nb {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
// Tango
|
||||
.theme-0 {
|
||||
$term-colors:
|
||||
@@ -163,10 +158,36 @@ body.term {
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes
|
||||
.bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.faint span { // content of faint is wrapped in span
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.under {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.strike {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.underline.strike {
|
||||
text-decoration: underline line-through;
|
||||
}
|
||||
|
||||
.blink-hide .blink {
|
||||
color: transparent;
|
||||
}
|
||||
//
|
||||
|
||||
.Row.color-preview {
|
||||
font-family: monospace;
|
||||
font-size: 16pt;
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Helper that retrieves an arg from `connData->getArgs` and stores it in `buff`. Returns 1 on success
|
||||
*/
|
||||
#define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) > 0)
|
||||
#define GET_ARG(key) (httpdFindArg(connData->getArgs, key, buff, sizeof(buff)) >= 0)
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
+348
-207
@@ -11,23 +11,27 @@ static const char _ansi_actions[] = {
|
||||
0, 1, 0, 1, 1, 1, 2, 1,
|
||||
3, 1, 4, 1, 5, 1, 6, 1,
|
||||
7, 1, 8, 1, 9, 1, 10, 1,
|
||||
11, 1, 12, 1, 13, 1, 14, 2,
|
||||
9, 10, 2, 9, 11
|
||||
11, 1, 12, 1, 13, 1, 14, 1,
|
||||
15, 1, 16, 1, 17, 2, 2, 5,
|
||||
2, 10, 11, 2, 10, 12
|
||||
};
|
||||
|
||||
static const char _ansi_eof_actions[] = {
|
||||
0, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 0, 0, 0, 0, 0
|
||||
13, 13, 13, 13, 13, 13, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static const int ansi_start = 1;
|
||||
static const int ansi_first_final = 26;
|
||||
static const int ansi_first_final = 30;
|
||||
static const int ansi_error = 0;
|
||||
|
||||
static const int ansi_en_CSI_body = 3;
|
||||
static const int ansi_en_OSC_body = 5;
|
||||
static const int ansi_en_CSI_body = 5;
|
||||
static const int ansi_en_OSC_body = 7;
|
||||
static const int ansi_en_TITLE_body = 27;
|
||||
static const int ansi_en_charsetcmd_body = 29;
|
||||
static const int ansi_en_main = 1;
|
||||
|
||||
|
||||
@@ -36,16 +40,45 @@ static const int ansi_en_main = 1;
|
||||
|
||||
static volatile int cs = -1;
|
||||
|
||||
static ETSTimer resetTim;
|
||||
volatile u32 ansi_parser_char_cnt = 0;
|
||||
|
||||
static void ICACHE_FLASH_ATTR
|
||||
resetParserCb(void *arg) {
|
||||
void ICACHE_FLASH_ATTR
|
||||
ansi_parser_reset(void) {
|
||||
if (cs != ansi_start) {
|
||||
cs = ansi_start;
|
||||
warn("Parser timeout, state reset");
|
||||
apars_reset_utf8buffer();
|
||||
ansi_warn("Parser timeout, state reset");
|
||||
}
|
||||
}
|
||||
|
||||
#define HISTORY_LEN 16
|
||||
|
||||
#if DEBUG_ANSI
|
||||
static char history[HISTORY_LEN + 1];
|
||||
#endif
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_badseq(void)
|
||||
{
|
||||
#if DEBUG_ANSI
|
||||
char buf1[HISTORY_LEN*3+2];
|
||||
char buf2[HISTORY_LEN*3+2];
|
||||
char *b1 = buf1;
|
||||
char *b2 = buf2;
|
||||
char c;
|
||||
|
||||
for(int i=0;i<HISTORY_LEN;i++) {
|
||||
c = history[i];
|
||||
b1 += sprintf(b1, "%2X ", c);
|
||||
if (c < 32 || c > 127) c = '.';
|
||||
b2 += sprintf(b2, "%c ", c);
|
||||
}
|
||||
|
||||
ansi_dbg("Context: %s", buf2);
|
||||
ansi_dbg(" %s", buf1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Linear ANSI chars stream parser
|
||||
*
|
||||
@@ -65,11 +98,14 @@ ansi_parser(const char *newdata, size_t len)
|
||||
// The CSI code is built here
|
||||
static char csi_leading; //!< Leading char, 0 if none
|
||||
static int csi_ni; //!< Number of the active digit
|
||||
static int csi_cnt; //!< Digit count
|
||||
static int csi_n[CSI_N_MAX]; //!< Param digits
|
||||
static char csi_char; //!< CSI action char (end)
|
||||
static char osc_buffer[OSC_CHAR_MAX];
|
||||
static int osc_bi;
|
||||
|
||||
ansi_parser_char_cnt++;
|
||||
|
||||
if (len == 0) len = strlen(newdata);
|
||||
|
||||
// Load new data to Ragel vars
|
||||
@@ -80,24 +116,28 @@ ansi_parser(const char *newdata, size_t len)
|
||||
// Init Ragel on the first run
|
||||
if (cs == -1) {
|
||||
|
||||
/* #line 84 "user/ansi_parser.c" */
|
||||
/* #line 120 "user/ansi_parser.c" */
|
||||
{
|
||||
cs = ansi_start;
|
||||
}
|
||||
|
||||
/* #line 57 "user/ansi_parser.rl" */
|
||||
/* #line 89 "user/ansi_parser.rl" */
|
||||
|
||||
#if DEBUG_ANSI
|
||||
memset(history, 0, sizeof(history));
|
||||
#endif
|
||||
}
|
||||
|
||||
// schedule state reset
|
||||
if (termconf->parser_tout_ms > 0) {
|
||||
os_timer_disarm(&resetTim);
|
||||
os_timer_setfn(&resetTim, resetParserCb, NULL);
|
||||
os_timer_arm(&resetTim, termconf->parser_tout_ms, 0);
|
||||
#if DEBUG_ANSI
|
||||
for(int i=len; i<HISTORY_LEN; i++) {
|
||||
history[i-len] = history[i];
|
||||
}
|
||||
strcpy(&history[HISTORY_LEN-len], newdata);
|
||||
#endif
|
||||
|
||||
// The parser
|
||||
|
||||
/* #line 101 "user/ansi_parser.c" */
|
||||
/* #line 141 "user/ansi_parser.c" */
|
||||
{
|
||||
const char *_acts;
|
||||
unsigned int _nacts;
|
||||
@@ -114,254 +154,329 @@ case 1:
|
||||
goto tr0;
|
||||
case 2:
|
||||
switch( (*p) ) {
|
||||
case 55: goto tr3;
|
||||
case 56: goto tr4;
|
||||
case 91: goto tr5;
|
||||
case 93: goto tr6;
|
||||
case 99: goto tr7;
|
||||
case 32: goto tr3;
|
||||
case 35: goto tr4;
|
||||
case 91: goto tr7;
|
||||
case 93: goto tr8;
|
||||
case 107: goto tr9;
|
||||
}
|
||||
goto tr2;
|
||||
case 0:
|
||||
goto _out;
|
||||
case 26:
|
||||
if ( (*p) == 27 )
|
||||
goto tr1;
|
||||
goto tr0;
|
||||
case 3:
|
||||
if ( (*p) == 59 )
|
||||
goto tr10;
|
||||
if ( (*p) < 60 ) {
|
||||
if ( (*p) > 47 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr9;
|
||||
} else if ( (*p) >= 32 )
|
||||
goto tr8;
|
||||
} else if ( (*p) > 64 ) {
|
||||
goto tr6;
|
||||
} else if ( (*p) >= 40 )
|
||||
goto tr5;
|
||||
} else if ( (*p) > 62 ) {
|
||||
if ( (*p) > 90 ) {
|
||||
if ( 97 <= (*p) && (*p) <= 122 )
|
||||
goto tr11;
|
||||
goto tr6;
|
||||
} else if ( (*p) >= 65 )
|
||||
goto tr11;
|
||||
goto tr6;
|
||||
} else
|
||||
goto tr8;
|
||||
goto tr6;
|
||||
goto tr2;
|
||||
case 4:
|
||||
if ( (*p) == 59 )
|
||||
case 0:
|
||||
goto _out;
|
||||
case 3:
|
||||
if ( 70 <= (*p) && (*p) <= 71 )
|
||||
goto tr10;
|
||||
if ( (*p) < 65 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr9;
|
||||
} else if ( (*p) > 90 ) {
|
||||
if ( 97 <= (*p) && (*p) <= 122 )
|
||||
goto tr11;
|
||||
} else
|
||||
goto tr11;
|
||||
goto tr2;
|
||||
case 27:
|
||||
case 30:
|
||||
if ( (*p) == 27 )
|
||||
goto tr1;
|
||||
goto tr0;
|
||||
case 4:
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr11;
|
||||
goto tr2;
|
||||
case 5:
|
||||
switch( (*p) ) {
|
||||
case 66: goto tr12;
|
||||
case 84: goto tr13;
|
||||
case 87: goto tr14;
|
||||
case 59: goto tr14;
|
||||
case 64: goto tr15;
|
||||
}
|
||||
if ( (*p) < 60 ) {
|
||||
if ( (*p) > 47 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr13;
|
||||
} else if ( (*p) >= 32 )
|
||||
goto tr12;
|
||||
} else if ( (*p) > 63 ) {
|
||||
if ( (*p) > 90 ) {
|
||||
if ( 96 <= (*p) && (*p) <= 122 )
|
||||
goto tr16;
|
||||
} else if ( (*p) >= 65 )
|
||||
goto tr16;
|
||||
} else
|
||||
goto tr12;
|
||||
goto tr2;
|
||||
case 6:
|
||||
if ( (*p) == 84 )
|
||||
goto tr15;
|
||||
goto tr2;
|
||||
case 7:
|
||||
if ( (*p) == 78 )
|
||||
if ( (*p) == 59 )
|
||||
goto tr14;
|
||||
if ( (*p) < 64 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr13;
|
||||
} else if ( (*p) > 90 ) {
|
||||
if ( 96 <= (*p) && (*p) <= 122 )
|
||||
goto tr16;
|
||||
} else
|
||||
goto tr16;
|
||||
goto tr2;
|
||||
case 31:
|
||||
goto tr2;
|
||||
case 32:
|
||||
if ( (*p) == 59 )
|
||||
goto tr14;
|
||||
if ( (*p) < 64 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr13;
|
||||
} else if ( (*p) > 90 ) {
|
||||
if ( 96 <= (*p) && (*p) <= 122 )
|
||||
goto tr16;
|
||||
} else
|
||||
goto tr16;
|
||||
goto tr2;
|
||||
case 7:
|
||||
switch( (*p) ) {
|
||||
case 48: goto tr17;
|
||||
case 66: goto tr18;
|
||||
case 84: goto tr19;
|
||||
case 87: goto tr20;
|
||||
}
|
||||
goto tr2;
|
||||
case 8:
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr17;
|
||||
if ( (*p) == 59 )
|
||||
goto tr21;
|
||||
goto tr2;
|
||||
case 9:
|
||||
if ( (*p) == 61 )
|
||||
goto tr18;
|
||||
goto tr2;
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr23;
|
||||
case 27: goto tr24;
|
||||
}
|
||||
goto tr22;
|
||||
case 33:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr23;
|
||||
case 27: goto tr24;
|
||||
}
|
||||
goto tr22;
|
||||
case 10:
|
||||
if ( (*p) == 27 )
|
||||
goto tr2;
|
||||
goto tr19;
|
||||
case 11:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr20;
|
||||
case 27: goto tr21;
|
||||
}
|
||||
goto tr19;
|
||||
case 28:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr20;
|
||||
case 27: goto tr21;
|
||||
}
|
||||
goto tr19;
|
||||
case 12:
|
||||
if ( (*p) == 92 )
|
||||
goto tr22;
|
||||
goto tr2;
|
||||
case 29:
|
||||
goto tr2;
|
||||
case 13:
|
||||
if ( (*p) == 73 )
|
||||
goto tr23;
|
||||
goto tr2;
|
||||
case 14:
|
||||
if ( (*p) == 84 )
|
||||
goto tr24;
|
||||
goto tr2;
|
||||
case 15:
|
||||
if ( (*p) == 76 )
|
||||
goto tr25;
|
||||
goto tr2;
|
||||
case 16:
|
||||
if ( (*p) == 69 )
|
||||
case 34:
|
||||
goto tr2;
|
||||
case 11:
|
||||
if ( (*p) == 84 )
|
||||
goto tr26;
|
||||
goto tr2;
|
||||
case 17:
|
||||
if ( (*p) == 61 )
|
||||
case 12:
|
||||
if ( (*p) == 78 )
|
||||
goto tr27;
|
||||
goto tr2;
|
||||
case 18:
|
||||
if ( (*p) == 27 )
|
||||
goto tr2;
|
||||
goto tr28;
|
||||
case 19:
|
||||
case 13:
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr28;
|
||||
goto tr2;
|
||||
case 14:
|
||||
if ( (*p) == 61 )
|
||||
goto tr29;
|
||||
goto tr2;
|
||||
case 15:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr29;
|
||||
case 27: goto tr30;
|
||||
case 7: goto tr31;
|
||||
case 27: goto tr32;
|
||||
}
|
||||
goto tr28;
|
||||
case 30:
|
||||
goto tr30;
|
||||
case 35:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr29;
|
||||
case 27: goto tr30;
|
||||
case 7: goto tr31;
|
||||
case 27: goto tr32;
|
||||
}
|
||||
goto tr28;
|
||||
case 20:
|
||||
goto tr30;
|
||||
case 16:
|
||||
if ( (*p) == 92 )
|
||||
goto tr31;
|
||||
goto tr33;
|
||||
goto tr2;
|
||||
case 17:
|
||||
if ( (*p) == 73 )
|
||||
goto tr34;
|
||||
goto tr2;
|
||||
case 18:
|
||||
if ( (*p) == 84 )
|
||||
goto tr35;
|
||||
goto tr2;
|
||||
case 19:
|
||||
if ( (*p) == 76 )
|
||||
goto tr36;
|
||||
goto tr2;
|
||||
case 20:
|
||||
if ( (*p) == 69 )
|
||||
goto tr37;
|
||||
goto tr2;
|
||||
case 21:
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr32;
|
||||
if ( (*p) == 61 )
|
||||
goto tr38;
|
||||
goto tr2;
|
||||
case 22:
|
||||
if ( (*p) == 59 )
|
||||
goto tr33;
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr32;
|
||||
goto tr39;
|
||||
goto tr2;
|
||||
case 23:
|
||||
if ( (*p) == 59 )
|
||||
goto tr40;
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr34;
|
||||
goto tr39;
|
||||
goto tr2;
|
||||
case 24:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr35;
|
||||
case 27: goto tr36;
|
||||
}
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr34;
|
||||
goto tr41;
|
||||
goto tr2;
|
||||
case 25:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr42;
|
||||
case 27: goto tr43;
|
||||
}
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto tr41;
|
||||
goto tr2;
|
||||
case 26:
|
||||
if ( (*p) == 92 )
|
||||
goto tr35;
|
||||
goto tr42;
|
||||
goto tr2;
|
||||
case 27:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr45;
|
||||
case 27: goto tr46;
|
||||
}
|
||||
goto tr44;
|
||||
case 36:
|
||||
switch( (*p) ) {
|
||||
case 7: goto tr45;
|
||||
case 27: goto tr46;
|
||||
}
|
||||
goto tr44;
|
||||
case 28:
|
||||
if ( (*p) == 92 )
|
||||
goto tr47;
|
||||
goto tr2;
|
||||
case 37:
|
||||
goto tr2;
|
||||
case 29:
|
||||
goto tr48;
|
||||
case 38:
|
||||
goto tr2;
|
||||
}
|
||||
|
||||
tr2: cs = 0; goto f0;
|
||||
tr0: cs = 1; goto f1;
|
||||
tr1: cs = 2; goto _again;
|
||||
tr8: cs = 4; goto f7;
|
||||
tr9: cs = 4; goto f8;
|
||||
tr10: cs = 4; goto f9;
|
||||
tr12: cs = 6; goto _again;
|
||||
tr15: cs = 7; goto _again;
|
||||
tr16: cs = 8; goto _again;
|
||||
tr17: cs = 9; goto f8;
|
||||
tr18: cs = 10; goto _again;
|
||||
tr19: cs = 11; goto f11;
|
||||
tr21: cs = 12; goto _again;
|
||||
tr13: cs = 13; goto _again;
|
||||
tr23: cs = 14; goto _again;
|
||||
tr24: cs = 15; goto _again;
|
||||
tr25: cs = 16; goto _again;
|
||||
tr26: cs = 17; goto _again;
|
||||
tr27: cs = 18; goto _again;
|
||||
tr28: cs = 19; goto f11;
|
||||
tr30: cs = 20; goto _again;
|
||||
tr14: cs = 21; goto _again;
|
||||
tr32: cs = 22; goto f8;
|
||||
tr33: cs = 23; goto f9;
|
||||
tr34: cs = 24; goto f8;
|
||||
tr36: cs = 25; goto _again;
|
||||
tr3: cs = 26; goto f2;
|
||||
tr4: cs = 26; goto f3;
|
||||
tr5: cs = 26; goto f4;
|
||||
tr6: cs = 26; goto f5;
|
||||
tr7: cs = 26; goto f6;
|
||||
tr11: cs = 27; goto f10;
|
||||
tr20: cs = 28; goto f12;
|
||||
tr22: cs = 29; goto f13;
|
||||
tr31: cs = 29; goto f15;
|
||||
tr35: cs = 29; goto f16;
|
||||
tr29: cs = 30; goto f14;
|
||||
tr3: cs = 3; goto _again;
|
||||
tr4: cs = 4; goto _again;
|
||||
tr12: cs = 6; goto f9;
|
||||
tr13: cs = 6; goto f10;
|
||||
tr14: cs = 6; goto f11;
|
||||
tr17: cs = 8; goto _again;
|
||||
tr21: cs = 9; goto _again;
|
||||
tr22: cs = 9; goto f14;
|
||||
tr24: cs = 10; goto _again;
|
||||
tr18: cs = 11; goto _again;
|
||||
tr26: cs = 12; goto _again;
|
||||
tr27: cs = 13; goto _again;
|
||||
tr28: cs = 14; goto f10;
|
||||
tr29: cs = 15; goto _again;
|
||||
tr30: cs = 15; goto f14;
|
||||
tr32: cs = 16; goto _again;
|
||||
tr19: cs = 17; goto _again;
|
||||
tr34: cs = 18; goto _again;
|
||||
tr35: cs = 19; goto _again;
|
||||
tr36: cs = 20; goto _again;
|
||||
tr37: cs = 21; goto _again;
|
||||
tr20: cs = 22; goto _again;
|
||||
tr39: cs = 23; goto f10;
|
||||
tr40: cs = 24; goto f11;
|
||||
tr41: cs = 25; goto f10;
|
||||
tr43: cs = 26; goto _again;
|
||||
tr44: cs = 27; goto f14;
|
||||
tr46: cs = 28; goto _again;
|
||||
tr5: cs = 30; goto f2;
|
||||
tr6: cs = 30; goto f3;
|
||||
tr7: cs = 30; goto f4;
|
||||
tr8: cs = 30; goto f5;
|
||||
tr9: cs = 30; goto f6;
|
||||
tr10: cs = 30; goto f7;
|
||||
tr11: cs = 30; goto f8;
|
||||
tr16: cs = 31; goto f13;
|
||||
tr15: cs = 32; goto f12;
|
||||
tr23: cs = 33; goto f15;
|
||||
tr38: cs = 34; goto f6;
|
||||
tr25: cs = 34; goto f16;
|
||||
tr33: cs = 34; goto f18;
|
||||
tr42: cs = 34; goto f19;
|
||||
tr31: cs = 35; goto f17;
|
||||
tr45: cs = 36; goto f15;
|
||||
tr47: cs = 37; goto f16;
|
||||
tr48: cs = 38; goto f20;
|
||||
|
||||
f1: _acts = _ansi_actions + 1; goto execFuncs;
|
||||
f4: _acts = _ansi_actions + 3; goto execFuncs;
|
||||
f7: _acts = _ansi_actions + 5; goto execFuncs;
|
||||
f8: _acts = _ansi_actions + 7; goto execFuncs;
|
||||
f9: _acts = _ansi_actions + 9; goto execFuncs;
|
||||
f10: _acts = _ansi_actions + 11; goto execFuncs;
|
||||
f9: _acts = _ansi_actions + 5; goto execFuncs;
|
||||
f10: _acts = _ansi_actions + 7; goto execFuncs;
|
||||
f11: _acts = _ansi_actions + 9; goto execFuncs;
|
||||
f13: _acts = _ansi_actions + 11; goto execFuncs;
|
||||
f0: _acts = _ansi_actions + 13; goto execFuncs;
|
||||
f5: _acts = _ansi_actions + 15; goto execFuncs;
|
||||
f16: _acts = _ansi_actions + 17; goto execFuncs;
|
||||
f11: _acts = _ansi_actions + 19; goto execFuncs;
|
||||
f15: _acts = _ansi_actions + 21; goto execFuncs;
|
||||
f13: _acts = _ansi_actions + 23; goto execFuncs;
|
||||
f6: _acts = _ansi_actions + 25; goto execFuncs;
|
||||
f2: _acts = _ansi_actions + 27; goto execFuncs;
|
||||
f6: _acts = _ansi_actions + 17; goto execFuncs;
|
||||
f19: _acts = _ansi_actions + 19; goto execFuncs;
|
||||
f14: _acts = _ansi_actions + 21; goto execFuncs;
|
||||
f16: _acts = _ansi_actions + 23; goto execFuncs;
|
||||
f18: _acts = _ansi_actions + 25; goto execFuncs;
|
||||
f8: _acts = _ansi_actions + 27; goto execFuncs;
|
||||
f3: _acts = _ansi_actions + 29; goto execFuncs;
|
||||
f14: _acts = _ansi_actions + 31; goto execFuncs;
|
||||
f12: _acts = _ansi_actions + 34; goto execFuncs;
|
||||
f7: _acts = _ansi_actions + 31; goto execFuncs;
|
||||
f2: _acts = _ansi_actions + 33; goto execFuncs;
|
||||
f20: _acts = _ansi_actions + 35; goto execFuncs;
|
||||
f12: _acts = _ansi_actions + 37; goto execFuncs;
|
||||
f15: _acts = _ansi_actions + 40; goto execFuncs;
|
||||
f17: _acts = _ansi_actions + 43; goto execFuncs;
|
||||
|
||||
execFuncs:
|
||||
_nacts = *_acts++;
|
||||
while ( _nacts-- > 0 ) {
|
||||
switch ( *_acts++ ) {
|
||||
case 0:
|
||||
/* #line 76 "user/ansi_parser.rl" */
|
||||
/* #line 112 "user/ansi_parser.rl" */
|
||||
{
|
||||
apars_handle_plainchar((*p));
|
||||
if ((*p) != 0) {
|
||||
apars_handle_plainchar((*p));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* #line 83 "user/ansi_parser.rl" */
|
||||
/* #line 121 "user/ansi_parser.rl" */
|
||||
{
|
||||
// Reset the CSI builder
|
||||
csi_leading = csi_char = 0;
|
||||
csi_ni = 0;
|
||||
csi_cnt = 0;
|
||||
|
||||
// Zero out digits
|
||||
for(int i = 0; i < CSI_N_MAX; i++) {
|
||||
csi_n[i] = 0;
|
||||
}
|
||||
|
||||
{cs = 3;goto _again;}
|
||||
{cs = 5;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* #line 96 "user/ansi_parser.rl" */
|
||||
/* #line 135 "user/ansi_parser.rl" */
|
||||
{
|
||||
csi_leading = (*p);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* #line 100 "user/ansi_parser.rl" */
|
||||
/* #line 139 "user/ansi_parser.rl" */
|
||||
{
|
||||
if (csi_cnt == 0) csi_cnt = 1;
|
||||
// x10 + digit
|
||||
if (csi_ni < CSI_N_MAX) {
|
||||
csi_n[csi_ni] = csi_n[csi_ni]*10 + ((*p) - '0');
|
||||
@@ -369,30 +484,31 @@ execFuncs:
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
/* #line 107 "user/ansi_parser.rl" */
|
||||
/* #line 147 "user/ansi_parser.rl" */
|
||||
{
|
||||
if (csi_cnt == 0) csi_cnt = 1; // handle case when first arg is empty
|
||||
csi_cnt++;
|
||||
csi_ni++;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
/* #line 111 "user/ansi_parser.rl" */
|
||||
/* #line 153 "user/ansi_parser.rl" */
|
||||
{
|
||||
csi_char = (*p);
|
||||
|
||||
apars_handle_CSI(csi_leading, csi_n, csi_char);
|
||||
|
||||
apars_handle_CSI(csi_leading, csi_n, csi_cnt, csi_char);
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
/* #line 119 "user/ansi_parser.rl" */
|
||||
/* #line 159 "user/ansi_parser.rl" */
|
||||
{
|
||||
ansi_warn("Invalid escape sequence discarded.");
|
||||
apars_handle_badseq();
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
/* #line 136 "user/ansi_parser.rl" */
|
||||
/* #line 177 "user/ansi_parser.rl" */
|
||||
{
|
||||
csi_ni = 0;
|
||||
|
||||
@@ -404,61 +520,83 @@ execFuncs:
|
||||
osc_bi = 0;
|
||||
osc_buffer[0] = '\0';
|
||||
|
||||
{cs = 5;goto _again;}
|
||||
{cs = 7;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
/* #line 150 "user/ansi_parser.rl" */
|
||||
/* #line 192 "user/ansi_parser.rl" */
|
||||
{
|
||||
osc_bi = 0;
|
||||
osc_buffer[0] = '\0';
|
||||
{cs = 27;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
/* #line 198 "user/ansi_parser.rl" */
|
||||
{
|
||||
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
/* #line 155 "user/ansi_parser.rl" */
|
||||
case 10:
|
||||
/* #line 203 "user/ansi_parser.rl" */
|
||||
{
|
||||
osc_buffer[osc_bi++] = (*p);
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
/* #line 159 "user/ansi_parser.rl" */
|
||||
case 11:
|
||||
/* #line 207 "user/ansi_parser.rl" */
|
||||
{
|
||||
osc_buffer[osc_bi++] = '\0';
|
||||
apars_handle_OSC_SetTitle(osc_buffer);
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
/* #line 165 "user/ansi_parser.rl" */
|
||||
case 12:
|
||||
/* #line 213 "user/ansi_parser.rl" */
|
||||
{
|
||||
osc_buffer[osc_bi++] = '\0';
|
||||
apars_handle_OSC_SetButton(csi_n[0], osc_buffer);
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
/* #line 177 "user/ansi_parser.rl" */
|
||||
{
|
||||
// Reset screen
|
||||
apars_handle_RESET_cmd();
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
/* #line 183 "user/ansi_parser.rl" */
|
||||
/* #line 245 "user/ansi_parser.rl" */
|
||||
{
|
||||
apars_handle_saveCursorAttrs();
|
||||
apars_handle_hashCode((*p));
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
/* #line 188 "user/ansi_parser.rl" */
|
||||
/* #line 250 "user/ansi_parser.rl" */
|
||||
{
|
||||
apars_handle_restoreCursorAttrs();
|
||||
apars_handle_shortCode((*p));
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
/* #line 462 "user/ansi_parser.c" */
|
||||
case 15:
|
||||
/* #line 255 "user/ansi_parser.rl" */
|
||||
{
|
||||
apars_handle_setXCtrls((*p));
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
/* #line 260 "user/ansi_parser.rl" */
|
||||
{
|
||||
// abuse the buffer for storing the leading char
|
||||
osc_buffer[0] = (*p);
|
||||
{cs = 29;goto _again;}
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
/* #line 266 "user/ansi_parser.rl" */
|
||||
{
|
||||
apars_handle_characterSet(osc_buffer[0], (*p));
|
||||
{cs = 1;goto _again;}
|
||||
}
|
||||
break;
|
||||
/* #line 600 "user/ansi_parser.c" */
|
||||
}
|
||||
}
|
||||
goto _again;
|
||||
@@ -476,15 +614,16 @@ _again:
|
||||
while ( __nacts-- > 0 ) {
|
||||
switch ( *__acts++ ) {
|
||||
case 6:
|
||||
/* #line 119 "user/ansi_parser.rl" */
|
||||
/* #line 159 "user/ansi_parser.rl" */
|
||||
{
|
||||
ansi_warn("Invalid escape sequence discarded.");
|
||||
apars_handle_badseq();
|
||||
{cs = 1; if ( p == pe )
|
||||
goto _test_eof;
|
||||
goto _again;}
|
||||
}
|
||||
break;
|
||||
/* #line 488 "user/ansi_parser.c" */
|
||||
/* #line 627 "user/ansi_parser.c" */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,6 +631,8 @@ goto _again;}
|
||||
_out: {}
|
||||
}
|
||||
|
||||
/* #line 208 "user/ansi_parser.rl" */
|
||||
/* #line 290 "user/ansi_parser.rl" */
|
||||
|
||||
}
|
||||
|
||||
// 'ESC k blah OSC_end' is a shortcut for setting title (k is defined in GNU screen as Title Definition String)
|
||||
|
||||
+24
-6
@@ -5,18 +5,33 @@
|
||||
#include <screen.h>
|
||||
|
||||
// Max nr of CSI parameters
|
||||
#define CSI_N_MAX 3
|
||||
#define CSI_N_MAX 10
|
||||
#define OSC_CHAR_MAX TERM_TITLE_LEN
|
||||
|
||||
extern void apars_handle_badseq(void);
|
||||
extern void apars_handle_CSI(char leadchar, int *params, char keychar);
|
||||
extern void apars_handle_RESET_cmd(void);
|
||||
extern void apars_handle_plainchar(char c);
|
||||
extern void apars_handle_CSI(char leadchar, int *params, int count, char keychar);
|
||||
extern void apars_handle_OSC_SetScreenSize(int rows, int cols);
|
||||
extern void apars_handle_saveCursorAttrs(void);
|
||||
extern void apars_handle_restoreCursorAttrs(void);
|
||||
extern void apars_handle_OSC_SetButton(int num, const char *buffer);
|
||||
extern void apars_handle_OSC_SetTitle(const char *buffer);
|
||||
extern void apars_handle_shortCode(char c);
|
||||
extern void apars_handle_hashCode(char c);
|
||||
extern void apars_handle_characterSet(char leadchar, char c);
|
||||
extern void apars_handle_setXCtrls(char c);
|
||||
extern void apars_reset_utf8buffer(void);
|
||||
|
||||
void ansi_parser_reset(void);
|
||||
|
||||
extern volatile u32 ansi_parser_char_cnt;
|
||||
|
||||
// defined in the makefile
|
||||
#if DEBUG_ANSI
|
||||
#define ansi_warn warn
|
||||
#define ansi_dbg dbg
|
||||
#else
|
||||
#define ansi_warn(...)
|
||||
#define ansi_dbg(...)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* \brief Linear ANSI chars stream parser
|
||||
@@ -33,4 +48,7 @@ extern void apars_handle_OSC_SetTitle(const char *buffer);
|
||||
*/
|
||||
void ansi_parser(const char *newdata, size_t len);
|
||||
|
||||
/** This shows a short error message and prints the history (if any) */
|
||||
void apars_handle_badseq(void);
|
||||
|
||||
#endif // ANSI_PARSER_H
|
||||
|
||||
+106
-22
@@ -10,16 +10,45 @@
|
||||
|
||||
static volatile int cs = -1;
|
||||
|
||||
static ETSTimer resetTim;
|
||||
volatile u32 ansi_parser_char_cnt = 0;
|
||||
|
||||
static void ICACHE_FLASH_ATTR
|
||||
resetParserCb(void *arg) {
|
||||
void ICACHE_FLASH_ATTR
|
||||
ansi_parser_reset(void) {
|
||||
if (cs != ansi_start) {
|
||||
cs = ansi_start;
|
||||
warn("Parser timeout, state reset");
|
||||
apars_reset_utf8buffer();
|
||||
ansi_warn("Parser timeout, state reset");
|
||||
}
|
||||
}
|
||||
|
||||
#define HISTORY_LEN 16
|
||||
|
||||
#if DEBUG_ANSI
|
||||
static char history[HISTORY_LEN + 1];
|
||||
#endif
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_badseq(void)
|
||||
{
|
||||
#if DEBUG_ANSI
|
||||
char buf1[HISTORY_LEN*3+2];
|
||||
char buf2[HISTORY_LEN*3+2];
|
||||
char *b1 = buf1;
|
||||
char *b2 = buf2;
|
||||
char c;
|
||||
|
||||
for(int i=0;i<HISTORY_LEN;i++) {
|
||||
c = history[i];
|
||||
b1 += sprintf(b1, "%2X ", c);
|
||||
if (c < 32 || c > 127) c = '.';
|
||||
b2 += sprintf(b2, "%c ", c);
|
||||
}
|
||||
|
||||
ansi_dbg("Context: %s", buf2);
|
||||
ansi_dbg(" %s", buf1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Linear ANSI chars stream parser
|
||||
*
|
||||
@@ -39,11 +68,14 @@ ansi_parser(const char *newdata, size_t len)
|
||||
// The CSI code is built here
|
||||
static char csi_leading; //!< Leading char, 0 if none
|
||||
static int csi_ni; //!< Number of the active digit
|
||||
static int csi_cnt; //!< Digit count
|
||||
static int csi_n[CSI_N_MAX]; //!< Param digits
|
||||
static char csi_char; //!< CSI action char (end)
|
||||
static char osc_buffer[OSC_CHAR_MAX];
|
||||
static int osc_bi;
|
||||
|
||||
ansi_parser_char_cnt++;
|
||||
|
||||
if (len == 0) len = strlen(newdata);
|
||||
|
||||
// Load new data to Ragel vars
|
||||
@@ -54,14 +86,18 @@ ansi_parser(const char *newdata, size_t len)
|
||||
// Init Ragel on the first run
|
||||
if (cs == -1) {
|
||||
%% write init;
|
||||
|
||||
#if DEBUG_ANSI
|
||||
memset(history, 0, sizeof(history));
|
||||
#endif
|
||||
}
|
||||
|
||||
// schedule state reset
|
||||
if (termconf->parser_tout_ms > 0) {
|
||||
os_timer_disarm(&resetTim);
|
||||
os_timer_setfn(&resetTim, resetParserCb, NULL);
|
||||
os_timer_arm(&resetTim, termconf->parser_tout_ms, 0);
|
||||
#if DEBUG_ANSI
|
||||
for(int i=len; i<HISTORY_LEN; i++) {
|
||||
history[i-len] = history[i];
|
||||
}
|
||||
strcpy(&history[HISTORY_LEN-len], newdata);
|
||||
#endif
|
||||
|
||||
// The parser
|
||||
%%{
|
||||
@@ -74,7 +110,9 @@ ansi_parser(const char *newdata, size_t len)
|
||||
# --- Regular characters to be printed ---
|
||||
|
||||
action plain_char {
|
||||
apars_handle_plainchar(fc);
|
||||
if (fc != 0) {
|
||||
apars_handle_plainchar(fc);
|
||||
}
|
||||
}
|
||||
|
||||
# --- CSI CSI commands (Select Graphic Rendition) ---
|
||||
@@ -84,6 +122,7 @@ ansi_parser(const char *newdata, size_t len)
|
||||
// Reset the CSI builder
|
||||
csi_leading = csi_char = 0;
|
||||
csi_ni = 0;
|
||||
csi_cnt = 0;
|
||||
|
||||
// Zero out digits
|
||||
for(int i = 0; i < CSI_N_MAX; i++) {
|
||||
@@ -98,6 +137,7 @@ ansi_parser(const char *newdata, size_t len)
|
||||
}
|
||||
|
||||
action CSI_digit {
|
||||
if (csi_cnt == 0) csi_cnt = 1;
|
||||
// x10 + digit
|
||||
if (csi_ni < CSI_N_MAX) {
|
||||
csi_n[csi_ni] = csi_n[csi_ni]*10 + (fc - '0');
|
||||
@@ -105,18 +145,19 @@ ansi_parser(const char *newdata, size_t len)
|
||||
}
|
||||
|
||||
action CSI_semi {
|
||||
if (csi_cnt == 0) csi_cnt = 1; // handle case when first arg is empty
|
||||
csi_cnt++;
|
||||
csi_ni++;
|
||||
}
|
||||
|
||||
action CSI_end {
|
||||
csi_char = fc;
|
||||
|
||||
apars_handle_CSI(csi_leading, csi_n, csi_char);
|
||||
|
||||
apars_handle_CSI(csi_leading, csi_n, csi_cnt, csi_char);
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
action errBadSeq {
|
||||
ansi_warn("Invalid escape sequence discarded.");
|
||||
apars_handle_badseq();
|
||||
fgoto main;
|
||||
}
|
||||
@@ -127,7 +168,7 @@ ansi_parser(const char *newdata, size_t len)
|
||||
|
||||
CSI_body := ((32..47|60..64) @CSI_leading)?
|
||||
((digit @CSI_digit)* ';' @CSI_semi)*
|
||||
(digit @CSI_digit)* alpha @CSI_end $!errBadSeq;
|
||||
(digit @CSI_digit)* (alpha|'`'|'@') @CSI_end $!errBadSeq;
|
||||
|
||||
|
||||
# --- OSC commands (Operating System Commands) ---
|
||||
@@ -147,6 +188,13 @@ ansi_parser(const char *newdata, size_t len)
|
||||
fgoto OSC_body;
|
||||
}
|
||||
|
||||
# collecting title string; this can also be entered by ESC k
|
||||
action SetTitle_start {
|
||||
osc_bi = 0;
|
||||
osc_buffer[0] = '\0';
|
||||
fgoto TITLE_body;
|
||||
}
|
||||
|
||||
action OSC_resize {
|
||||
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
|
||||
fgoto main;
|
||||
@@ -167,13 +215,17 @@ ansi_parser(const char *newdata, size_t len)
|
||||
apars_handle_OSC_SetButton(csi_n[0], osc_buffer);
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
|
||||
# 0; is xterm title hack
|
||||
OSC_body := (
|
||||
("BTN" digit @CSI_digit '=' (NOESC @OSC_text_char)+ OSC_END @OSC_button) |
|
||||
("TITLE=" (NOESC @OSC_text_char)+ OSC_END @OSC_title) |
|
||||
("BTN" digit @CSI_digit '=' (NOESC @OSC_text_char)* OSC_END @OSC_button) |
|
||||
("TITLE=" @SetTitle_start) |
|
||||
("0;" (NOESC @OSC_text_char)* OSC_END @OSC_title) |
|
||||
('W' (digit @CSI_digit)+ ';' @CSI_semi (digit @CSI_digit)+ OSC_END @OSC_resize)
|
||||
) $!errBadSeq;
|
||||
|
||||
TITLE_body := (NOESC @OSC_text_char)* OSC_END @OSC_title $!errBadSeq;
|
||||
|
||||
action RESET_cmd {
|
||||
// Reset screen
|
||||
apars_handle_RESET_cmd();
|
||||
@@ -190,16 +242,46 @@ ansi_parser(const char *newdata, size_t len)
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
action HASH_code {
|
||||
apars_handle_hashCode(fc);
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
action SHORT_code {
|
||||
apars_handle_shortCode(fc);
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
action SetXCtrls {
|
||||
apars_handle_setXCtrls(fc);
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
action CharsetCmd_start {
|
||||
// abuse the buffer for storing the leading char
|
||||
osc_buffer[0] = fc;
|
||||
fgoto charsetcmd_body;
|
||||
}
|
||||
|
||||
action CharsetCmd_end {
|
||||
apars_handle_characterSet(osc_buffer[0], fc);
|
||||
fgoto main;
|
||||
}
|
||||
|
||||
charsetcmd_body := (any @CharsetCmd_end) $!errBadSeq;
|
||||
|
||||
# --- Main parser loop ---
|
||||
|
||||
main :=
|
||||
(
|
||||
(NOESC @plain_char)* ESC (
|
||||
'[' @CSI_start |
|
||||
']' @OSC_start |
|
||||
'c' @RESET_cmd |
|
||||
'7' @CSI_SaveCursorAttrs |
|
||||
'8' @CSI_RestoreCursorAttrs
|
||||
('[' @CSI_start) |
|
||||
(']' @OSC_start) |
|
||||
('#' digit @HASH_code) |
|
||||
('k' @SetTitle_start) |
|
||||
([a-jl-zA-Z0-9=<>] @SHORT_code) |
|
||||
([()*+-./] @CharsetCmd_start) |
|
||||
(' ' [FG] @SetXCtrls)
|
||||
)
|
||||
)+ $!errBadSeq;
|
||||
|
||||
@@ -207,3 +289,5 @@ ansi_parser(const char *newdata, size_t len)
|
||||
#*/
|
||||
}%%
|
||||
}
|
||||
|
||||
// 'ESC k blah OSC_end' is a shortcut for setting title (k is defined in GNU screen as Title Definition String)
|
||||
|
||||
+305
-98
@@ -9,7 +9,8 @@
|
||||
#include "screen.h"
|
||||
#include "ansi_parser.h"
|
||||
#include "uart_driver.h"
|
||||
#include "persist.h"
|
||||
|
||||
// screen manpage - https://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html
|
||||
|
||||
static char utf_collect[4];
|
||||
static int utf_i = 0;
|
||||
@@ -21,9 +22,17 @@ static int utf_j = 0;
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_plainchar(char c)
|
||||
{
|
||||
if (c == 7) return; // BELL - beep (TODO play beep in browser)
|
||||
|
||||
// collecting unicode glyphs...
|
||||
if (c & 0x80) {
|
||||
if (utf_i == 0) {
|
||||
// start
|
||||
if (c == 192 || c == 193 || c >= 245) {
|
||||
// forbidden codes
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((c & 0xE0) == 0xC0) {
|
||||
utf_i = 2;
|
||||
}
|
||||
@@ -33,33 +42,68 @@ apars_handle_plainchar(char c)
|
||||
else if ((c & 0xF8) == 0xF0) {
|
||||
utf_i = 4;
|
||||
}
|
||||
else {
|
||||
// chars over 127 that don't start unicode sequences
|
||||
goto fail;
|
||||
}
|
||||
|
||||
utf_collect[0] = c;
|
||||
utf_j = 1;
|
||||
}
|
||||
else {
|
||||
utf_collect[utf_j++] = c;
|
||||
if (utf_j >= utf_i) {
|
||||
screen_putchar(utf_collect);
|
||||
utf_i = 0;
|
||||
utf_j = 0;
|
||||
memset(utf_collect, 0, 4);
|
||||
if ((c & 0xC0) != 0x80) {
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
utf_collect[utf_j++] = c;
|
||||
if (utf_j >= utf_i) {
|
||||
screen_putchar(utf_collect);
|
||||
apars_reset_utf8buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (c == 14) {
|
||||
screen_set_charset_n(1);
|
||||
return;
|
||||
}
|
||||
if (c == 15) {
|
||||
screen_set_charset_n(0);
|
||||
return;
|
||||
}
|
||||
|
||||
utf_collect[0] = c;
|
||||
utf_collect[1] = 0; // just to make sure it's closed...
|
||||
screen_putchar(utf_collect);
|
||||
}
|
||||
|
||||
return;
|
||||
fail:
|
||||
ansi_warn("Bad UTF-8");
|
||||
apars_reset_utf8buffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bad sequence received, show warning
|
||||
*/
|
||||
void apars_handle_badseq(void)
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_reset_utf8buffer(void)
|
||||
{
|
||||
warn("Invalid escape sequence discarded.");
|
||||
utf_i = 0;
|
||||
utf_j = 0;
|
||||
memset(utf_collect, 0, 4);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_characterSet(char leadchar, char c)
|
||||
{
|
||||
if (leadchar == '(') screen_set_charset(0, c);
|
||||
else if (leadchar == ')') screen_set_charset(1, c);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_setXCtrls(char c)
|
||||
{
|
||||
// this does not seem to do anything, sent by some unix programs
|
||||
// ansi_warn("NOIMPL Select %cbit ctrls", c=='F'? '7':'8');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,47 +113,31 @@ void apars_handle_badseq(void)
|
||||
* \param keychar - the char terminating the sequence
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_CSI(char leadchar, int *params, char keychar)
|
||||
apars_handle_CSI(char leadchar, int *params, int count, char keychar)
|
||||
{
|
||||
/*
|
||||
Implemented codes (from Wikipedia)
|
||||
|
||||
CSI n A CUU – Cursor Up
|
||||
CSI n B CUD – Cursor Down
|
||||
CSI n C CUF – Cursor Forward
|
||||
CSI n D CUB – Cursor Back
|
||||
CSI n E CNL – Cursor Next Line
|
||||
CSI n F CPL – Cursor Previous Line
|
||||
CSI n G CHA – Cursor Horizontal Absolute
|
||||
CSI n ; m H CUP – Cursor Position
|
||||
CSI n J ED – Erase Display
|
||||
CSI n K EL – Erase in Line
|
||||
CSI n S SU – Scroll Up
|
||||
CSI n T SD – Scroll Down
|
||||
CSI n ; m f HVP – Horizontal and Vertical Position
|
||||
CSI n m SGR – Select Graphic Rendition (Implemented only some)
|
||||
CSI 6n DSR – Device Status Report
|
||||
CSI s SCP – Save Cursor Position
|
||||
CSI u RCP – Restore Cursor Position
|
||||
CSI ?25l DECTCEM Hides the cursor
|
||||
CSI ?25h DECTCEM Shows the cursor
|
||||
*/
|
||||
|
||||
int n1 = params[0];
|
||||
int n2 = params[1];
|
||||
// int n3 = params[2];
|
||||
int n3 = params[2];
|
||||
static char buf[20];
|
||||
|
||||
// defaults
|
||||
switch (keychar) {
|
||||
case 'A':
|
||||
case 'A': // move
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'G':
|
||||
case 'S':
|
||||
case 'G': // set X
|
||||
case '`':
|
||||
case 'S': // scrolling
|
||||
case 'T':
|
||||
case 'X': // clear in line
|
||||
case 'd': // set Y
|
||||
case 'L':
|
||||
case 'M':
|
||||
case '@':
|
||||
case 'P':
|
||||
if (n1 == 0) n1 = 1;
|
||||
break;
|
||||
|
||||
@@ -127,60 +155,73 @@ apars_handle_CSI(char leadchar, int *params, char keychar)
|
||||
|
||||
switch (keychar) {
|
||||
// CUU CUD CUF CUB
|
||||
case 'A': screen_cursor_move(-n1, 0); break;
|
||||
case 'B': screen_cursor_move(n1, 0); break;
|
||||
case 'C': screen_cursor_move(0, n1); break;
|
||||
case 'D': screen_cursor_move(0, -n1); break;
|
||||
case 'A': screen_cursor_move(-n1, 0, false); break;
|
||||
case 'B': screen_cursor_move(n1, 0, false); break;
|
||||
case 'C': screen_cursor_move(0, n1, false); break;
|
||||
case 'D': screen_cursor_move(0, -n1, false); break;
|
||||
|
||||
case 'E': // CNL
|
||||
screen_cursor_move(n1, 0);
|
||||
case 'E': // CNL - Cursor Next Line
|
||||
screen_cursor_move(n1, 0, false);
|
||||
screen_cursor_set_x(0);
|
||||
break;
|
||||
|
||||
case 'F': // CPL
|
||||
screen_cursor_move(-n1, 0);
|
||||
case 'F': // CPL - Cursor Prev Line
|
||||
screen_cursor_move(-n1, 0, false);
|
||||
screen_cursor_set_x(0);
|
||||
break;
|
||||
|
||||
// CHA
|
||||
// Set X
|
||||
case 'G':
|
||||
screen_cursor_set_x(n1 - 1); break; // 1-based
|
||||
case '`': // alternate code
|
||||
screen_cursor_set_x(n1 - 1);
|
||||
break; // 1-based
|
||||
|
||||
// SU, SD
|
||||
// Set Y
|
||||
case 'd':
|
||||
screen_cursor_set_y(n1 - 1);
|
||||
break; // 1-based
|
||||
|
||||
// clear in line
|
||||
case 'X':
|
||||
screen_clear_in_line(n1);
|
||||
break; // 1-based
|
||||
|
||||
// SU, SD - scroll up/down
|
||||
case 'S': screen_scroll_up(n1); break;
|
||||
case 'T': screen_scroll_down(n1); break;
|
||||
|
||||
// CUP,HVP
|
||||
// CUP,HVP - set position
|
||||
case 'H':
|
||||
case 'f':
|
||||
screen_cursor_set(n1-1, n2-1); break; // 1-based
|
||||
screen_cursor_set(n1-1, n2-1);
|
||||
break; // 1-based
|
||||
|
||||
case 'J': // ED
|
||||
case 'J': // ED - clear screen
|
||||
if (n1 == 0) {
|
||||
screen_clear(CLEAR_TO_CURSOR);
|
||||
} else if (n1 == 1) {
|
||||
screen_clear(CLEAR_FROM_CURSOR);
|
||||
} else if (n1 == 1) {
|
||||
screen_clear(CLEAR_TO_CURSOR);
|
||||
} else {
|
||||
screen_clear(CLEAR_ALL);
|
||||
screen_cursor_set(0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'K': // EL
|
||||
case 'K': // EL - clear line
|
||||
if (n1 == 0) {
|
||||
screen_clear_line(CLEAR_TO_CURSOR);
|
||||
} else if (n1 == 1) {
|
||||
screen_clear_line(CLEAR_FROM_CURSOR);
|
||||
} else if (n1 == 1) {
|
||||
screen_clear_line(CLEAR_TO_CURSOR);
|
||||
} else {
|
||||
screen_clear_line(CLEAR_ALL);
|
||||
}
|
||||
break;
|
||||
|
||||
// SCP, RCP
|
||||
// SCP, RCP - save/restore position
|
||||
case 's': screen_cursor_save(0); break;
|
||||
case 'u': screen_cursor_restore(0); break;
|
||||
|
||||
case 'n':
|
||||
case 'n': // queries
|
||||
if (n1 == 6) {
|
||||
// Query cursor position
|
||||
char buf[20];
|
||||
@@ -193,71 +234,237 @@ apars_handle_CSI(char leadchar, int *params, char keychar)
|
||||
// Query device status - reply "Device is OK"
|
||||
UART_WriteString(UART0, "\033[0n", UART_TIMEOUT_US);
|
||||
}
|
||||
else {
|
||||
ansi_warn("NOIMPL query %d", n1);
|
||||
}
|
||||
break;
|
||||
|
||||
// DECTCEM feature enable / disable
|
||||
|
||||
case 'h':
|
||||
case 'h': // feature enable
|
||||
if (leadchar == '?') {
|
||||
if (n1 == 25) {
|
||||
screen_cursor_enable(1);
|
||||
} else if (n1 == 7) {
|
||||
screen_wrap_enable(1);
|
||||
if (n1 == 25) screen_cursor_enable(1);
|
||||
else if (n1 == 7) screen_wrap_enable(1);
|
||||
else if (n1 == 1) {
|
||||
// TODO something with arrow keys??
|
||||
}
|
||||
else {
|
||||
ansi_warn("NOIMPL DEC opt %d", n1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (n1 == 4) {
|
||||
// TODO insert mode, i think this is to suppress user input
|
||||
}
|
||||
else {
|
||||
ansi_warn("NOIMPL flag %d", n1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
case 'l': // feature disable
|
||||
if (leadchar == '?') {
|
||||
if (n1 == 25) {
|
||||
screen_cursor_enable(0);
|
||||
} else if (n1 == 7) {
|
||||
screen_wrap_enable(0);
|
||||
if (n1 == 25) screen_cursor_enable(0);
|
||||
else if (n1 == 7) screen_wrap_enable(0);
|
||||
else if (n1 == 1) {
|
||||
// TODO see above
|
||||
}
|
||||
else {
|
||||
ansi_warn("NOIMPL DEC opt %d", n1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (n1 == 4) {
|
||||
// TODO see above
|
||||
}
|
||||
else {
|
||||
ansi_warn("NOIMPL flag %d", n1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm': // SGR
|
||||
case 'm': // SGR - graphics rendition aka attributes
|
||||
if (count == 0) {
|
||||
count = 1; // this makes it work as 0 (reset)
|
||||
}
|
||||
|
||||
// iterate arguments
|
||||
for (int i = 0; i < CSI_N_MAX; i++) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
int n = params[i];
|
||||
|
||||
if (i == 0 && n == 0) { // reset SGR
|
||||
if (n == 0) { // reset SGR
|
||||
screen_reset_cursor(); // resets colors, inverse and bold.
|
||||
break; // cannot combine reset with others
|
||||
}
|
||||
else if (n >= 30 && n <= 37) screen_set_fg(n-30); // ANSI normal fg
|
||||
else if (n >= 40 && n <= 47) screen_set_bg(n-40); // ANSI normal bg
|
||||
else if (n == 39) screen_set_fg(7); // default fg
|
||||
else if (n == 49) screen_set_bg(false); // default bg
|
||||
else if (n == 7) screen_inverse(true); // inverse
|
||||
else if (n == 27) screen_inverse(false); // positive
|
||||
else if (n == 1) screen_set_bold(true); // bold
|
||||
else if (n == 21 || n == 22) screen_set_bold(false); // bold off
|
||||
else if (n >= 90 && n <= 97) screen_set_fg(n-90+8); // AIX bright fg
|
||||
else if (n >= 100 && n <= 107) screen_set_bg(n-100+8); // AIX bright bg
|
||||
else if (n >= 30 && n <= 37) screen_set_fg((Color) (n - 30)); // ANSI normal fg
|
||||
else if (n >= 40 && n <= 47) screen_set_bg((Color) (n - 40)); // ANSI normal bg
|
||||
else if (n == 39) screen_set_fg(termconf_scratch.default_fg); // default fg
|
||||
else if (n == 49) screen_set_bg(termconf_scratch.default_bg); // default bg
|
||||
|
||||
else if (n == 1) screen_attr_enable(ATTR_BOLD);
|
||||
else if (n == 2) screen_attr_enable(ATTR_FAINT);
|
||||
else if (n == 3) screen_attr_enable(ATTR_ITALIC);
|
||||
else if (n == 4) screen_attr_enable(ATTR_UNDERLINE);
|
||||
else if (n == 5 || n == 6) screen_attr_enable(ATTR_BLINK); // 6 - rapid blink, not supported
|
||||
else if (n == 7) screen_inverse_enable(true);
|
||||
else if (n == 9) screen_attr_enable(ATTR_STRIKE);
|
||||
|
||||
else if (n == 20) screen_attr_enable(ATTR_FRAKTUR);
|
||||
else if (n == 21) screen_attr_disable(ATTR_BOLD);
|
||||
else if (n == 22) screen_attr_disable(ATTR_FAINT);
|
||||
else if (n == 23) screen_attr_disable(ATTR_ITALIC|ATTR_FRAKTUR);
|
||||
else if (n == 24) screen_attr_disable(ATTR_UNDERLINE);
|
||||
else if (n == 25) screen_attr_disable(ATTR_BLINK);
|
||||
else if (n == 27) screen_inverse_enable(false);
|
||||
else if (n == 29) screen_attr_disable(ATTR_STRIKE);
|
||||
|
||||
else if (n >= 90 && n <= 97) screen_set_fg((Color) (n - 90 + 8)); // AIX bright fg
|
||||
else if (n >= 100 && n <= 107) screen_set_bg((Color) (n - 100 + 8)); // AIX bright bg
|
||||
else {
|
||||
ansi_warn("NOIMPL SGR attr %d", n);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 't': // xterm hacks
|
||||
switch(n1) {
|
||||
case 8: // set size
|
||||
screen_resize(n2, n3);
|
||||
break;
|
||||
case 18: // report size
|
||||
printf(buf, "\033[8;%d;%dt", termconf_scratch.height, termconf_scratch.width);
|
||||
UART_WriteString(UART0, buf, UART_TIMEOUT_US);
|
||||
break;
|
||||
case 11: // Report iconified -> is not iconified
|
||||
UART_WriteString(UART0, "\033[1t", UART_TIMEOUT_US);
|
||||
break;
|
||||
case 21: // Report title
|
||||
UART_WriteString(UART0, "\033]L", UART_TIMEOUT_US);
|
||||
UART_WriteString(UART0, termconf_scratch.title, UART_TIMEOUT_US);
|
||||
UART_WriteString(UART0, "\033\\", UART_TIMEOUT_US);
|
||||
break;
|
||||
case 24: // Set Height only
|
||||
screen_resize(n2, termconf_scratch.width);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
screen_insert_lines(n1);
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
screen_delete_lines(n1);
|
||||
break;
|
||||
|
||||
case '@':
|
||||
screen_insert_characters(n1);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
screen_delete_characters(n1);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
// TODO scrolling region
|
||||
// ansi_warn("NOIMPL scrolling region");
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
// TODO clear tab
|
||||
ansi_warn("NOIMPL clear tab");
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
// TODO back tab
|
||||
ansi_warn("NOIMPL cursor back tab");
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
// TODO setmode (??)
|
||||
ansi_warn("NOIMPL CSI setmode %d", n1);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (leadchar == '!') {
|
||||
info("SOFT RESET!");
|
||||
system_restart();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
// report capabilities (pretend we're vt4xx)
|
||||
UART_WriteString(UART0, "\033[?64;22;c", UART_TIMEOUT_US);
|
||||
break;
|
||||
|
||||
default:
|
||||
ansi_warn("Unknown CSI: %c", keychar);
|
||||
apars_handle_badseq();
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR apars_handle_saveCursorAttrs(void)
|
||||
/** codes in the format ESC # n */
|
||||
void ICACHE_FLASH_ATTR apars_handle_hashCode(char c)
|
||||
{
|
||||
screen_cursor_save(1);
|
||||
switch(c) {
|
||||
case '8':
|
||||
screen_fill_with_E();
|
||||
break;
|
||||
|
||||
default:
|
||||
ansi_warn("Unknown # sequence: %c", c);
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR apars_handle_restoreCursorAttrs(void)
|
||||
/** those are single-character escape codes (ESC x) */
|
||||
void ICACHE_FLASH_ATTR apars_handle_shortCode(char c)
|
||||
{
|
||||
screen_cursor_restore(1);
|
||||
}
|
||||
switch(c) {
|
||||
case 'c': // screen reset
|
||||
screen_reset();
|
||||
break;
|
||||
|
||||
/**
|
||||
* \brief Handle a request to reset the display device
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
apars_handle_RESET_cmd(void)
|
||||
{
|
||||
screen_reset();
|
||||
case '7': // save cursor + attrs
|
||||
screen_cursor_save(true);
|
||||
break;
|
||||
|
||||
case '8': // restore cursor + attrs
|
||||
screen_cursor_restore(true);
|
||||
break;
|
||||
|
||||
case 'E': // same as CR LF
|
||||
screen_cursor_move(1, 0, false);
|
||||
screen_cursor_set_x(0);
|
||||
break;
|
||||
|
||||
case 'D': // move cursor down, scroll screen up if needed
|
||||
screen_cursor_move(1, 0, true);
|
||||
break;
|
||||
|
||||
case 'M': // move cursor up, scroll screen down if needed
|
||||
screen_cursor_move(-1, 0, true);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
// TODO set tab
|
||||
// ansi_warn("NOIMPL set tab");
|
||||
break;
|
||||
|
||||
// TODO those don't seem to do anything
|
||||
case '>':
|
||||
// ansi_warn("NOIMPL NUMKP");
|
||||
break;
|
||||
|
||||
case '<':
|
||||
// ansi_warn("NOIMPL SETANSI");
|
||||
break;
|
||||
|
||||
case '=':
|
||||
// ansi_warn("NOIMPL ALTKP");
|
||||
break;
|
||||
|
||||
default:
|
||||
ansi_warn("Unknown 1-char seq %c", c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#ifndef ANSI_PARSER_CALLBACKS_H
|
||||
#define ANSI_PARSER_CALLBACKS_H
|
||||
|
||||
void apars_handle_badseq(void);
|
||||
void apars_handle_CSI(char leadchar, int *params, char keychar);
|
||||
void apars_handle_RESET_cmd(void);
|
||||
void apars_handle_plainchar(char c);
|
||||
void apars_handle_CSI(char leadchar, int *params, int count, char keychar);
|
||||
void apars_handle_OSC_SetScreenSize(int rows, int cols);
|
||||
void apars_handle_saveCursorAttrs(void);
|
||||
void apars_handle_restoreCursorAttrs(void);
|
||||
void apars_handle_OSC_SetButton(int num, const char *buffer);
|
||||
void apars_handle_OSC_SetTitle(const char *buffer);
|
||||
void apars_handle_shortCode(char c);
|
||||
void apars_handle_hashCode(char c);
|
||||
void apars_handle_characterSet(char leadchar, char c);
|
||||
void apars_handle_setXCtrls(char c);
|
||||
void apars_reset_utf8buffer(void);
|
||||
|
||||
#endif //ESP_VT100_FIRMWARE_ANSI_PARSER_CALLBACKS_H
|
||||
|
||||
+5
-5
@@ -98,7 +98,7 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
|
||||
// TODO re-implement those, use single byte markers and B2 encoding
|
||||
|
||||
dbg("Sock RX str: %s, len %d", data, len);
|
||||
ws_dbg("Sock RX str: %s, len %d", data, len);
|
||||
|
||||
if (strstarts(data, "STR:")) {
|
||||
// pass string verbatim
|
||||
@@ -134,24 +134,24 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
|
||||
}
|
||||
|
||||
if (!screen_isCoordValid(y, x)) {
|
||||
warn("Mouse input at invalid coordinates");
|
||||
ws_warn("Mouse input at invalid coordinates");
|
||||
return;
|
||||
}
|
||||
|
||||
dbg("Screen clicked at row %d, col %d", y+1, x+1);
|
||||
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_WriteString(UART0, buf, UART_TIMEOUT_US);
|
||||
}
|
||||
else {
|
||||
warn("Bad command.");
|
||||
ws_warn("Bad command.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Socket connected for updates */
|
||||
void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
|
||||
{
|
||||
info("Socket connected to "URL_WS_UPDATE);
|
||||
ws_info("Socket connected to "URL_WS_UPDATE);
|
||||
ws->recvCb = updateSockRx;
|
||||
}
|
||||
|
||||
@@ -9,4 +9,15 @@
|
||||
void updateSockConnect(Websock *ws);
|
||||
void screen_notifyChange(ScreenNotifyChangeTopic topic);
|
||||
|
||||
// defined in the makefile
|
||||
#if DEBUG_INPUT
|
||||
#define ws_warn warn
|
||||
#define ws_dbg dbg
|
||||
#define ws_info info
|
||||
#else
|
||||
#define ws_warn(...)
|
||||
#define ws_dbg(...)
|
||||
#define ws_info(...)
|
||||
#endif
|
||||
|
||||
#endif //CGI_SOCKETS_H
|
||||
|
||||
+21
-5
@@ -22,6 +22,8 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
char redir_url_buf[100];
|
||||
int n, w, h;
|
||||
|
||||
bool shall_clear_screen = false;
|
||||
|
||||
char *redir_url = redir_url_buf;
|
||||
redir_url += sprintf(redir_url, SET_REDIR_ERR);
|
||||
// we'll test if anything was printed by looking for \0 in failed_keys_buf
|
||||
@@ -41,8 +43,11 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
h = atoi(buff);
|
||||
if (h > 1) {
|
||||
if (w * h <= MAX_SCREEN_SIZE) {
|
||||
termconf->width = w;
|
||||
termconf->height = h;
|
||||
if (termconf->width != w || termconf->height != h) {
|
||||
termconf->width = w;
|
||||
termconf->height = h;
|
||||
shall_clear_screen = true;
|
||||
}
|
||||
} else {
|
||||
warn("Bad dimensions: %d x %d (total %d)", w, h, w*h);
|
||||
redir_url += sprintf(redir_url, "term_width,term_height,");
|
||||
@@ -66,7 +71,10 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
dbg("Screen default BG: %s", buff);
|
||||
n = atoi(buff);
|
||||
if (n >= 0 && n < 16) {
|
||||
termconf->default_bg = (u8) n;
|
||||
if (termconf->default_bg != n) {
|
||||
termconf->default_bg = (u8) n;
|
||||
shall_clear_screen = true;
|
||||
}
|
||||
} else {
|
||||
warn("Bad color %s", buff);
|
||||
redir_url += sprintf(redir_url, "default_bg,");
|
||||
@@ -88,7 +96,10 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
dbg("Screen default FG: %s", buff);
|
||||
n = atoi(buff);
|
||||
if (n >= 0 && n < 16) {
|
||||
termconf->default_fg = (u8) n;
|
||||
if (termconf->default_fg != n) {
|
||||
termconf->default_fg = (u8) n;
|
||||
shall_clear_screen = true;
|
||||
}
|
||||
} else {
|
||||
warn("Bad color %s", buff);
|
||||
redir_url += sprintf(redir_url, "default_fg,");
|
||||
@@ -123,9 +134,14 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
// All was OK
|
||||
info("Set term params - success, saving...");
|
||||
|
||||
terminal_apply_settings();
|
||||
persist_store();
|
||||
|
||||
if (shall_clear_screen) {
|
||||
terminal_apply_settings();
|
||||
} else {
|
||||
terminal_apply_settings_noclear();
|
||||
}
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
} else {
|
||||
warn("Some settings did not validate, asking for correction");
|
||||
|
||||
+40
-6
@@ -193,6 +193,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData)
|
||||
int len;
|
||||
char buff[256];
|
||||
|
||||
if (connData->conn == NULL) {
|
||||
//Connection aborted. Clean up.
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
// auto-turn on STA
|
||||
if ((wificonf->opmode & STATION_MODE) == 0) {
|
||||
wificonf->opmode |= STATION_MODE;
|
||||
@@ -265,6 +270,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
|
||||
char buff[100];
|
||||
struct ip_info info;
|
||||
|
||||
buff[0] = 0; // avoid unitialized read
|
||||
|
||||
if (connData->conn == NULL) {
|
||||
//Connection aborted. Clean up.
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
httpdStartResponse(connData, 200);
|
||||
httpdHeader(connData, "Content-Type", "application/json");
|
||||
httpdEndHeaders(connData);
|
||||
@@ -276,6 +288,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
|
||||
}
|
||||
|
||||
STATION_STATUS st = wifi_station_get_connect_status();
|
||||
dbg("CONN STATE = %d", st);
|
||||
switch(st) {
|
||||
case STATION_IDLE:
|
||||
sprintf(buff, "{\"status\": \"idle\"}"); // unclear when this is used
|
||||
@@ -301,9 +314,14 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
|
||||
wifi_get_ip_info(STATION_IF, &info);
|
||||
sprintf(buff, "{\"status\": \"success\", \"ip\": \""IPSTR"\"}", GOOD_IP2STR(info.ip.addr));
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(buff, "{\"status\": \"working\", \"wtf\": \"state = %d\"}", st);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
tplSend(connData, buff, -1);
|
||||
httpdSend(connData, buff, -1);
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
@@ -336,6 +354,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
bool sta_turned_on = false;
|
||||
bool sta_ssid_pw_changed = false;
|
||||
|
||||
// ---- WiFi opmode ----
|
||||
|
||||
if (GET_ARG("opmode")) {
|
||||
@@ -366,6 +387,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
|
||||
if (enable) {
|
||||
wificonf->opmode |= STATION_MODE;
|
||||
sta_turned_on = true;
|
||||
} else {
|
||||
wificonf->opmode &= ~STATION_MODE;
|
||||
}
|
||||
@@ -463,6 +485,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
info("Setting station SSID to: \"%s\"", buff);
|
||||
strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN);
|
||||
wifi_change_flags.sta = true;
|
||||
sta_ssid_pw_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,12 +497,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
info("Setting station password to: \"%s\"", buff);
|
||||
strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN);
|
||||
wifi_change_flags.sta = true;
|
||||
sta_ssid_pw_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
|
||||
// All was OK
|
||||
info("Set WiFi params - success, applying in 1000 ms");
|
||||
info("Set WiFi params - success, applying in 2000 ms");
|
||||
|
||||
// Settings are applied only if all was OK
|
||||
//
|
||||
@@ -492,9 +516,19 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
|
||||
// If user connects via the Station IF, they may not even notice the connection reset.
|
||||
os_timer_disarm(&timer);
|
||||
os_timer_setfn(&timer, applyWifiSettingsLaterCb, NULL);
|
||||
os_timer_arm(&timer, 1000, false);
|
||||
os_timer_arm(&timer, 2000, false);
|
||||
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
if ((sta_ssid_pw_changed || sta_turned_on)
|
||||
&& wificonf->opmode != SOFTAP_MODE
|
||||
&& wificonf->sta_ssid[0] != 0) {
|
||||
// User wants to connect
|
||||
|
||||
info("User wants to connect to SSID, redirecting to ConnStatus page.");
|
||||
httpdRedirect(connData, "/cfg/wifi/connecting");
|
||||
}
|
||||
else {
|
||||
httpdRedirect(connData, SET_REDIR_SUC);
|
||||
}
|
||||
} else {
|
||||
warn("Some WiFi settings did not validate, asking for correction");
|
||||
// Some errors, appended to the URL as ?err=
|
||||
@@ -558,7 +592,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
|
||||
// For display of our current SSID
|
||||
connectStatus = wifi_station_get_connect_status();
|
||||
x = wifi_get_opmode();
|
||||
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) {
|
||||
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
|
||||
strcpy(buff, "");
|
||||
}
|
||||
else {
|
||||
@@ -571,7 +605,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
|
||||
x = wifi_get_opmode();
|
||||
connectStatus = wifi_station_get_connect_status();
|
||||
|
||||
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) {
|
||||
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
|
||||
strcpy(buff, "");
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "ansi_parser_callbacks.h"
|
||||
#include "wifimgr.h"
|
||||
#include "persist.h"
|
||||
#include "screen.h"
|
||||
#include "ansi_parser.h"
|
||||
|
||||
#define BTNGPIO 0
|
||||
|
||||
@@ -21,6 +23,28 @@ static bool enable_ap_button = false;
|
||||
|
||||
static ETSTimer resetBtntimer;
|
||||
static ETSTimer blinkyTimer;
|
||||
static ETSTimer ansiParserResetTimer;
|
||||
|
||||
static void ICACHE_FLASH_ATTR ansiParserResetTimerCb(void *arg) {
|
||||
static u32 last_charcnt = 0;
|
||||
static int same_charcnt = -1;
|
||||
if (termconf->parser_tout_ms == 0) return;
|
||||
|
||||
if (last_charcnt != ansi_parser_char_cnt) {
|
||||
last_charcnt = ansi_parser_char_cnt;
|
||||
same_charcnt = 0;
|
||||
}
|
||||
else {
|
||||
if (same_charcnt != -1) {
|
||||
same_charcnt++;
|
||||
if (same_charcnt > termconf->parser_tout_ms) {
|
||||
ansi_parser_reset();
|
||||
same_charcnt = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Holding BOOT pin triggers AP reset, then Factory Reset.
|
||||
// Indicate that by blinking the on-board LED.
|
||||
@@ -102,6 +126,10 @@ void ICACHE_FLASH_ATTR ioInit() {
|
||||
os_timer_setfn(&resetBtntimer, resetBtnTimerCb, NULL);
|
||||
os_timer_arm(&resetBtntimer, 500, 1);
|
||||
|
||||
os_timer_disarm(&ansiParserResetTimer);
|
||||
os_timer_setfn(&ansiParserResetTimer, ansiParserResetTimerCb, NULL);
|
||||
os_timer_arm(&ansiParserResetTimer, 1, 1);
|
||||
|
||||
// One way to enter AP mode - hold GPIO0 low.
|
||||
if (GPIO_INPUT_GET(BTNGPIO) == 0) {
|
||||
// starting "in BOOT mode" - do not install the AP reset timer
|
||||
|
||||
+310
-82
@@ -8,6 +8,9 @@
|
||||
TerminalConfigBundle * const termconf = &persist.current.termconf;
|
||||
TerminalConfigBundle termconf_scratch;
|
||||
|
||||
// forward declare
|
||||
static void utf8_remap(char* out, char g, char table);
|
||||
|
||||
#define W termconf_scratch.width
|
||||
#define H termconf_scratch.height
|
||||
|
||||
@@ -31,6 +34,12 @@ void terminal_restore_defaults(void)
|
||||
* Apply settings after eg. restore from defaults
|
||||
*/
|
||||
void terminal_apply_settings(void)
|
||||
{
|
||||
terminal_apply_settings_noclear();
|
||||
screen_init();
|
||||
}
|
||||
|
||||
void terminal_apply_settings_noclear(void)
|
||||
{
|
||||
memcpy(&termconf_scratch, termconf, sizeof(TerminalConfigBundle));
|
||||
if (W*H >= MAX_SCREEN_SIZE) {
|
||||
@@ -39,8 +48,8 @@ void terminal_apply_settings(void)
|
||||
terminal_restore_defaults();
|
||||
persist_store();
|
||||
memcpy(&termconf_scratch, termconf, sizeof(TerminalConfigBundle));
|
||||
screen_init();
|
||||
}
|
||||
screen_init();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +64,7 @@ typedef struct __attribute__((packed)){
|
||||
char c[4]; // space for a full unicode character
|
||||
Color fg : 4;
|
||||
Color bg : 4;
|
||||
bool bold : 1;
|
||||
u8 attrs;
|
||||
} Cell;
|
||||
|
||||
/**
|
||||
@@ -69,10 +78,15 @@ static Cell screen[MAX_SCREEN_SIZE];
|
||||
static struct {
|
||||
int x; //!< X coordinate
|
||||
int y; //!< Y coordinate
|
||||
bool visible; //!< Visible
|
||||
bool inverse; //!< Inverse colors
|
||||
bool autowrap; //!< Wrapping when EOL
|
||||
bool bold; //!< Bold style
|
||||
bool visible; //!< Visible (not attribute, DEC special)
|
||||
bool inverse;
|
||||
u8 attrs;
|
||||
|
||||
char charset0;
|
||||
char charset1;
|
||||
int charsetN;
|
||||
|
||||
Color fg; //!< Foreground color for writing
|
||||
Color bg; //!< Background color for writing
|
||||
} cursor;
|
||||
@@ -83,10 +97,10 @@ static struct {
|
||||
static struct {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
// optionally saved attrs
|
||||
// mark that attrs are saved
|
||||
bool withAttrs;
|
||||
bool inverse;
|
||||
u8 attrs;
|
||||
bool inverse; // attribute that's not in the bitfield
|
||||
Color fg;
|
||||
Color bg;
|
||||
} cursor_sav;
|
||||
@@ -114,16 +128,20 @@ static inline void
|
||||
clear_range(unsigned int from, unsigned int to)
|
||||
{
|
||||
if (to >= W*H) to = W*H-1;
|
||||
Color fg = cursor.inverse ? cursor.bg : cursor.fg;
|
||||
Color bg = cursor.inverse ? cursor.fg : cursor.bg;
|
||||
Color fg = (cursor.inverse) ? cursor.bg : cursor.fg;
|
||||
Color bg = (cursor.inverse) ? cursor.fg : cursor.bg;
|
||||
|
||||
Cell sample;
|
||||
sample.c[0] = ' ';
|
||||
sample.c[1] = 0;
|
||||
sample.c[2] = 0;
|
||||
sample.c[3] = 0;
|
||||
sample.fg = fg;
|
||||
sample.bg = bg;
|
||||
sample.attrs = 0;
|
||||
|
||||
for (unsigned int i = from; i <= to; i++) {
|
||||
screen[i].c[0] = ' ';
|
||||
screen[i].c[1] = 0;
|
||||
screen[i].c[2] = 0;
|
||||
screen[i].c[3] = 0;
|
||||
screen[i].fg = fg;
|
||||
screen[i].bg = bg;
|
||||
screen[i].bold = false;
|
||||
memcpy(&screen[i], &sample, sizeof(Cell));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +156,12 @@ cursor_reset(void)
|
||||
cursor.fg = termconf_scratch.default_fg;
|
||||
cursor.bg = termconf_scratch.default_bg;
|
||||
cursor.visible = 1;
|
||||
cursor.inverse = 0;
|
||||
cursor.autowrap = 1;
|
||||
cursor.bold = 0;
|
||||
cursor.attrs = 0;
|
||||
|
||||
cursor.charset0 = 'B';
|
||||
cursor.charset1 = '0';
|
||||
cursor.charsetN = 0;
|
||||
}
|
||||
|
||||
//endregion
|
||||
@@ -176,12 +197,10 @@ screen_reset(void)
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_reset_cursor(void)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
cursor.fg = termconf_scratch.default_fg;
|
||||
cursor.bg = termconf_scratch.default_bg;
|
||||
cursor.inverse = 0;
|
||||
cursor.bold = 0;
|
||||
NOTIFY_DONE();
|
||||
cursor.attrs = 0;
|
||||
cursor.inverse = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,10 +249,121 @@ screen_clear_line(ClearMode mode)
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_clear_in_line(unsigned int count)
|
||||
{
|
||||
if (cursor.x + count >= W) {
|
||||
screen_clear_line(CLEAR_FROM_CURSOR);
|
||||
}
|
||||
else {
|
||||
NOTIFY_LOCK();
|
||||
clear_range(cursor.y * W + cursor.x, cursor.y * W + cursor.x + count - 1);
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_fill_with_E(void)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
Cell sample;
|
||||
sample.c[0] = 'E';
|
||||
sample.c[1] = 0;
|
||||
sample.c[2] = 0;
|
||||
sample.c[3] = 0;
|
||||
sample.fg = termconf_scratch.default_fg;
|
||||
sample.bg = termconf_scratch.default_bg;
|
||||
sample.attrs = 0;
|
||||
|
||||
for (unsigned int i = 0; i <= W*H-1; i++) {
|
||||
memcpy(&screen[i], &sample, sizeof(Cell));
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Screen manipulation
|
||||
|
||||
void screen_insert_lines(unsigned int lines)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
// shift the following lines
|
||||
int targetStart = cursor.y + lines;
|
||||
if (targetStart >= H) {
|
||||
targetStart = H;
|
||||
} else {
|
||||
// do the moving
|
||||
for (int i = H-1; i >= targetStart; i--) {
|
||||
memcpy(screen+i*W, screen+(i-lines)*W, sizeof(Cell)*W);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the first line
|
||||
screen_clear_line(CLEAR_ALL);
|
||||
// copy it to the rest of the cleared region
|
||||
for (int i = cursor.y+1; i < targetStart; i++) {
|
||||
memcpy(screen+i*W, screen+cursor.y*W, sizeof(Cell)*W);
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
void screen_delete_lines(unsigned int lines)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
// shift lines up
|
||||
int targetEnd = H - 1 - lines;
|
||||
if (targetEnd <= cursor.y) {
|
||||
targetEnd = cursor.y;
|
||||
} else {
|
||||
// do the moving
|
||||
for (int i = cursor.y; i <= targetEnd; i++) {
|
||||
memcpy(screen+i*W, screen+(i+lines)*W, sizeof(Cell)*W);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the rest
|
||||
clear_range(targetEnd*W, W*H-1);
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
void screen_insert_characters(unsigned int count)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
int targetStart = cursor.x + count;
|
||||
if (targetStart >= W) {
|
||||
targetStart = W-1;
|
||||
} else {
|
||||
// do the moving
|
||||
for (int i = W-1; i >= targetStart; i--) {
|
||||
memcpy(screen+cursor.y*W+i, screen+cursor.y*W+(i-count), sizeof(Cell));
|
||||
}
|
||||
}
|
||||
|
||||
clear_range(cursor.y*W+cursor.x, cursor.y*W+targetStart-1);
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
void screen_delete_characters(unsigned int count)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
int targetEnd = W - count;
|
||||
if (targetEnd > cursor.x) {
|
||||
// do the moving
|
||||
for (int i = cursor.x; i <= targetEnd; i++) {
|
||||
memcpy(screen+cursor.y*W+i, screen+cursor.y*W+(i+count), sizeof(Cell));
|
||||
}
|
||||
}
|
||||
|
||||
if (targetEnd <= cursor.x) {
|
||||
screen_clear_line(CLEAR_FROM_CURSOR);
|
||||
}
|
||||
else {
|
||||
clear_range(cursor.y * W + (W - count), (cursor.y + 1) * W - 1);
|
||||
}
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the screen size
|
||||
*
|
||||
@@ -373,7 +503,7 @@ screen_cursor_set_y(int y)
|
||||
* Relative cursor move
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_cursor_move(int dy, int dx)
|
||||
screen_cursor_move(int dy, int dx, bool scroll)
|
||||
{
|
||||
NOTIFY_LOCK();
|
||||
int move;
|
||||
@@ -386,13 +516,13 @@ screen_cursor_move(int dy, int dx)
|
||||
if (cursor.y < 0) {
|
||||
move = -cursor.y;
|
||||
cursor.y = 0;
|
||||
screen_scroll_down((unsigned int)move);
|
||||
if (scroll) screen_scroll_down((unsigned int)move);
|
||||
}
|
||||
|
||||
if (cursor.y >= H) {
|
||||
move = cursor.y - (H - 1);
|
||||
cursor.y = H - 1;
|
||||
screen_scroll_up((unsigned int)move);
|
||||
if (scroll) screen_scroll_up((unsigned int)move);
|
||||
}
|
||||
|
||||
NOTIFY_DONE();
|
||||
@@ -412,11 +542,11 @@ screen_cursor_save(bool withAttrs)
|
||||
if (withAttrs) {
|
||||
cursor_sav.fg = cursor.fg;
|
||||
cursor_sav.bg = cursor.bg;
|
||||
cursor_sav.inverse = cursor.inverse;
|
||||
cursor_sav.attrs = cursor.attrs;
|
||||
} else {
|
||||
cursor_sav.fg = termconf_scratch.default_fg;
|
||||
cursor_sav.bg = termconf_scratch.default_bg;
|
||||
cursor_sav.inverse = 0;
|
||||
cursor_sav.attrs = 0; // avoid leftovers if the wrong restore is used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +563,7 @@ screen_cursor_restore(bool withAttrs)
|
||||
if (withAttrs) {
|
||||
cursor.fg = cursor_sav.fg;
|
||||
cursor.bg = cursor_sav.bg;
|
||||
cursor.inverse = cursor_sav.inverse;
|
||||
cursor.attrs = cursor_sav.attrs;
|
||||
}
|
||||
|
||||
NOTIFY_DONE();
|
||||
@@ -485,43 +615,19 @@ screen_set_bg(Color color)
|
||||
cursor.bg = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cursor foreground and background color
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_colors(Color fg, Color bg)
|
||||
void screen_attr_enable(u8 attrs)
|
||||
{
|
||||
screen_set_fg(fg);
|
||||
screen_set_bg(bg);
|
||||
cursor.attrs |= attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert colors
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_inverse(bool inverse)
|
||||
void screen_attr_disable(u8 attrs)
|
||||
{
|
||||
cursor.inverse = inverse;
|
||||
cursor.attrs &= ~attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make foreground bright.
|
||||
*
|
||||
* This relates to the '1' SGR command which originally means
|
||||
* "bold font". We interpret that as "Bright", similar to other
|
||||
* terminal emulators.
|
||||
*
|
||||
* Note that the bright colors can be used without bold using the 90+ codes
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_set_bold(bool bold)
|
||||
void screen_inverse_enable(bool ena)
|
||||
{
|
||||
if (!bold) {
|
||||
cursor.fg = (Color) (cursor.fg % 8);
|
||||
} else {
|
||||
cursor.fg = (Color) ((cursor.fg % 8) + 8); // change anything to the bright colors
|
||||
}
|
||||
cursor.bold = bold;
|
||||
cursor.inverse = ena;
|
||||
}
|
||||
|
||||
//endregion
|
||||
@@ -538,12 +644,26 @@ bool ICACHE_FLASH_ATTR screen_isCoordValid(int y, int x)
|
||||
return x >= 0 && y >= 0 && x < W && y < H;
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR screen_set_charset_n(int Gx)
|
||||
{
|
||||
if (Gx < 0 || Gx > 1) return; // bad n
|
||||
cursor.charsetN = Gx;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR screen_set_charset(int Gx, char charset)
|
||||
{
|
||||
if (Gx == 0) cursor.charset0 = charset;
|
||||
if (Gx == 1) cursor.charset1 = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a character in the cursor color, move to right with wrap.
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
screen_putchar(const char *ch)
|
||||
{
|
||||
char buf[4];
|
||||
NOTIFY_LOCK();
|
||||
|
||||
Cell *c = &screen[cursor.x + cursor.y * W];
|
||||
@@ -555,7 +675,7 @@ screen_putchar(const char *ch)
|
||||
goto done;
|
||||
|
||||
case '\n':
|
||||
screen_cursor_move(1, 0);
|
||||
screen_cursor_move(1, 0, true); // can scroll
|
||||
goto done;
|
||||
|
||||
case 8: // BS
|
||||
@@ -568,15 +688,11 @@ screen_putchar(const char *ch)
|
||||
cursor.y--;
|
||||
}
|
||||
}
|
||||
// erase target cell
|
||||
c = &screen[cursor.x + cursor.y * W];
|
||||
c->c[0] = ' ';
|
||||
c->c[1] = 0;
|
||||
c->c[2] = 0;
|
||||
c->c[3] = 0;
|
||||
// apparently backspace should not clear the cell
|
||||
goto done;
|
||||
|
||||
case 9: // TAB
|
||||
// TODO change if tab setting is ever implemented
|
||||
if (cursor.x<((W-1)-(W-1)%4)) {
|
||||
c->c[0] = ' ';
|
||||
c->c[1] = 0;
|
||||
@@ -591,13 +707,19 @@ screen_putchar(const char *ch)
|
||||
default:
|
||||
if (ch[0] < ' ') {
|
||||
// Discard
|
||||
warn("Ignoring control char %d", (int)ch);
|
||||
warn("Ignoring control char %d", (int)ch[0]);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// copy unicode char
|
||||
strncpy(c->c, ch, 4);
|
||||
if (ch[1] == 0 && ch[0] <= 0x7f) {
|
||||
// we have len=1 and ASCII
|
||||
utf8_remap(c->c, ch[0], (cursor.charsetN == 0) ? cursor.charset0 : cursor.charset1);
|
||||
}
|
||||
else {
|
||||
// copy unicode char
|
||||
strncpy(c->c, ch, 4);
|
||||
}
|
||||
|
||||
if (cursor.inverse) {
|
||||
c->fg = cursor.bg;
|
||||
@@ -606,7 +728,7 @@ screen_putchar(const char *ch)
|
||||
c->fg = cursor.fg;
|
||||
c->bg = cursor.bg;
|
||||
}
|
||||
c->bold = cursor.bold;
|
||||
c->attrs = cursor.attrs;
|
||||
|
||||
cursor.x++;
|
||||
// X wrap
|
||||
@@ -629,6 +751,84 @@ done:
|
||||
NOTIFY_DONE();
|
||||
}
|
||||
|
||||
/**
|
||||
* translates VT100 ACS escape codes to Unicode values.
|
||||
* Based on rxvt-unicode screen.C table.
|
||||
*/
|
||||
static const u16 vt100_to_unicode[62] =
|
||||
{
|
||||
// ? ? ? ? ? ? ?
|
||||
// A=UPARR B=DNARR C=RTARR D=LFARR E=FLBLK F=3/4BL G=SNOMN
|
||||
0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603,
|
||||
// H= I= J= K= L= M= N=
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
// O= P= Q= R= S= T= U=
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
// V= W= X= Y= Z= [= \=
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
// ? ? v->0 v->1 v->2 v->3 v->4
|
||||
// ]= ^= _=SPC `=DIAMN a=HSMED b=HT c=FF
|
||||
0, 0, 0x0020, 0x25c6, 0x2592, 0x2409, 0x240c,
|
||||
// v->5 v->6 v->7 v->8 v->9 v->a v->b
|
||||
// d=CR e=LF f=DEGRE g=PLSMN h=NL i=VT j=SL-BR
|
||||
0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518,
|
||||
// v->c v->d v->e v->f v->10 v->11 v->12
|
||||
// k=SL-TR l=SL-TL m=SL-BL n=SL-+ o=SL-T1 p=SL-T2 q=SL-HZ
|
||||
0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500,
|
||||
// v->13 v->14 v->15 v->16 v->17 v->18 v->19
|
||||
// r=SL-T4 s=SL-T5 t=SL-VR u=SL-VL v=SL-HU w=Sl-HD x=SL-VT
|
||||
0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502,
|
||||
// v->1a v->1b b->1c v->1d v->1e/a3 v->1f
|
||||
// y=LT-EQ z=GT-EQ {=PI |=NOTEQ }=POUND ~=DOT
|
||||
0x2264, 0x2265, 0x03c0, 0x2260, 0x20a4, 0x00b7
|
||||
};
|
||||
|
||||
/**
|
||||
* UTF remap
|
||||
* @param out - output char[4]
|
||||
* @param g - ASCII char
|
||||
* @param table - table name (0, A, B)
|
||||
*/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
utf8_remap(char *out, char g, char table)
|
||||
{
|
||||
u16 utf = 0;
|
||||
|
||||
switch (table)
|
||||
{
|
||||
case '0': /* DEC Special Character & Line Drawing Set */
|
||||
if ((g >= 0x41) && (g <= 0x7e) && (vt100_to_unicode[g - 0x41])) {
|
||||
utf = vt100_to_unicode[g - 0x41];
|
||||
}
|
||||
break;
|
||||
case 'A': /* UK, replaces # with GBP */
|
||||
if (g == '#') utf = 0x20a4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (utf > 0x7F) {
|
||||
// formulas taken from: https://gist.github.com/yamamushi/5823402
|
||||
if ((utf >= 0x80) && (utf <= 0x07FF)) {
|
||||
out[0] = (char) ((utf >> 0x06) ^ 0xC0);
|
||||
out[1] = (char) (((utf ^ 0xFFC0) | 0x80) & ~0x40);
|
||||
out[2]=0;
|
||||
}
|
||||
else if ((utf >= 0x0800) && (utf <= 0xFFFF)) {
|
||||
out[0] = (char) (((utf ^ 0xFC0FFF) >> 0x0C) | 0xE0);
|
||||
out[1] = (char) ((((utf ^ 0xFFF03F) >> 0x06) | 0x80) & ~0x40);
|
||||
out[2] = (char) (((utf ^ 0xFFFC0) | 0x80) & ~0x40);
|
||||
out[3]=0;
|
||||
} else {
|
||||
out[0] = g;
|
||||
out[1] = 0;
|
||||
}
|
||||
} else {
|
||||
out[0] = g;
|
||||
out[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//region Serialization
|
||||
|
||||
@@ -670,7 +870,7 @@ void screen_dd(void)
|
||||
struct ScreenSerializeState {
|
||||
Color lastFg;
|
||||
Color lastBg;
|
||||
bool lastBold;
|
||||
bool lastAttrs;
|
||||
char lastChar[4];
|
||||
int index;
|
||||
};
|
||||
@@ -679,8 +879,24 @@ void ICACHE_FLASH_ATTR
|
||||
encode2B(u16 number, WordB2 *stru)
|
||||
{
|
||||
stru->lsb = (u8) (number % 127);
|
||||
stru->msb = (u8) ((number - stru->lsb) / 127 + 1);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -726,6 +942,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
|
||||
Cell *cell, *cell0;
|
||||
WordB2 w1, w2, w3, w4, w5;
|
||||
WordB3 lw1;
|
||||
|
||||
size_t remain = buf_len; int used = 0;
|
||||
char *bb = buffer;
|
||||
@@ -741,7 +958,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
ss->index = 0;
|
||||
ss->lastBg = 0;
|
||||
ss->lastFg = 0;
|
||||
ss->lastBold = false;
|
||||
ss->lastAttrs = 0;
|
||||
memset(ss->lastChar, 0, 4); // this ensures the first char is never "repeat"
|
||||
|
||||
encode2B((u16) H, &w1);
|
||||
@@ -751,8 +968,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
encode2B((u16) (
|
||||
cursor.fg |
|
||||
(cursor.bg<<4) |
|
||||
(cursor.bold?0x100:0) |
|
||||
(cursor.visible?0x200:0))
|
||||
(cursor.visible ? 1<<8 : 0))
|
||||
, &w5);
|
||||
|
||||
// H W X Y Attribs
|
||||
@@ -768,7 +984,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
while (i < W*H
|
||||
&& cell->fg == ss->lastFg
|
||||
&& cell->bg == ss->lastBg
|
||||
&& cell->bold == ss->lastBold
|
||||
&& cell->attrs == ss->lastAttrs
|
||||
&& strneq(cell->c, ss->lastChar, 4)) {
|
||||
// Repeat
|
||||
repCnt++;
|
||||
@@ -777,13 +993,25 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
|
||||
if (repCnt == 0) {
|
||||
// No repeat
|
||||
if (cell0->bold != ss->lastBold || cell0->fg != ss->lastFg || cell0->bg != ss->lastBg) {
|
||||
encode2B((u16) (
|
||||
bool changeAttrs = cell0->attrs != ss->lastAttrs;
|
||||
bool changeColors = (cell0->fg != ss->lastFg || cell0->bg != ss->lastBg);
|
||||
if (!changeAttrs && changeColors) {
|
||||
encode2B(cell0->fg | (cell0->bg<<4), &w1);
|
||||
bufprint("\x03%c%c", w1.lsb, w1.msb);
|
||||
}
|
||||
else if (changeAttrs && !changeColors) {
|
||||
// attrs only
|
||||
encode2B(cell0->attrs, &w1);
|
||||
bufprint("\x04%c%c", w1.lsb, w1.msb);
|
||||
}
|
||||
else if (changeAttrs && changeColors) {
|
||||
// colors and attrs
|
||||
encode3B((u32) (
|
||||
cell0->fg |
|
||||
(cell0->bg<<4) |
|
||||
(cell0->bold?0x100:0))
|
||||
, &w1);
|
||||
bufprint("\x01%c%c", w1.lsb, w1.msb);
|
||||
(cell0->attrs<<8))
|
||||
, &lw1);
|
||||
bufprint("\x01%c%c%c", lw1.lsb, lw1.msb, lw1.xsb);
|
||||
}
|
||||
|
||||
// copy the symbol, until first 0 or reached 4 bytes
|
||||
@@ -795,7 +1023,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
|
||||
ss->lastFg = cell0->fg;
|
||||
ss->lastBg = cell0->bg;
|
||||
ss->lastBold = cell0->bold;
|
||||
ss->lastAttrs = cell0->attrs;
|
||||
memcpy(ss->lastChar, cell0->c, 4);
|
||||
|
||||
i++;
|
||||
|
||||
+34
-28
@@ -46,6 +46,14 @@ typedef enum {
|
||||
CHANGE_LABELS,
|
||||
} ScreenNotifyChangeTopic;
|
||||
|
||||
#define ATTR_BOLD (1<<0)
|
||||
#define ATTR_FAINT (1<<1)
|
||||
#define ATTR_ITALIC (1<<2)
|
||||
#define ATTR_UNDERLINE (1<<3)
|
||||
#define ATTR_BLINK (1<<4)
|
||||
#define ATTR_FRAKTUR (1<<5)
|
||||
#define ATTR_STRIKE (1<<6)
|
||||
|
||||
#define SCREEN_NOTIFY_DELAY_MS 20
|
||||
|
||||
typedef struct {
|
||||
@@ -70,6 +78,7 @@ extern TerminalConfigBundle termconf_scratch;
|
||||
|
||||
void terminal_restore_defaults(void);
|
||||
void terminal_apply_settings(void);
|
||||
void terminal_apply_settings_noclear(void); // the same, but with no screen reset / init
|
||||
|
||||
/**
|
||||
* Maximum screen size (determines size of the static data array)
|
||||
@@ -89,21 +98,24 @@ httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **dat
|
||||
|
||||
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
|
||||
|
||||
|
||||
typedef struct {
|
||||
u8 lsb;
|
||||
u8 msb;
|
||||
} WordB2;
|
||||
|
||||
typedef struct {
|
||||
u8 lsb;
|
||||
u8 msb;
|
||||
u8 xsb;
|
||||
} WordB3;
|
||||
|
||||
/** Encode number to two nice ASCII bytes */
|
||||
void encode2B(u16 number, WordB2 *stru);
|
||||
|
||||
/** Init the screen */
|
||||
void screen_init(void);
|
||||
|
||||
/** Change the screen size */
|
||||
void screen_resize(int rows, int cols);
|
||||
|
||||
/** Check if coord is valid */
|
||||
bool screen_isCoordValid(int y, int x);
|
||||
|
||||
@@ -111,48 +123,45 @@ bool screen_isCoordValid(int y, int x);
|
||||
|
||||
/** Screen reset to default state */
|
||||
void screen_reset(void);
|
||||
|
||||
/** Clear entire screen, set all to 7 on 0 */
|
||||
/** Clear entire screen */
|
||||
void screen_clear(ClearMode mode);
|
||||
|
||||
/** Line reset to gray-on-white, empty */
|
||||
/** Clear line */
|
||||
void screen_clear_line(ClearMode mode);
|
||||
|
||||
/** Clear part of line */
|
||||
void screen_clear_in_line(unsigned int count);
|
||||
/** Shift screen upwards */
|
||||
void screen_scroll_up(unsigned int lines);
|
||||
|
||||
/** Shift screen downwards */
|
||||
void screen_scroll_down(unsigned int lines);
|
||||
/** esc # 8 - fill entire screen with E of default colors (DEC alignment display) */
|
||||
void screen_fill_with_E(void);
|
||||
|
||||
// --- insert / delete ---
|
||||
void screen_insert_lines(unsigned int lines);
|
||||
void screen_delete_lines(unsigned int lines);
|
||||
void screen_insert_characters(unsigned int count);
|
||||
void screen_delete_characters(unsigned int count);
|
||||
|
||||
// --- Cursor control ---
|
||||
|
||||
/** Set cursor position */
|
||||
void screen_cursor_set(int y, int x);
|
||||
|
||||
/** Read cursor pos to given vars */
|
||||
void screen_cursor_get(int *y, int *x);
|
||||
|
||||
/** Set cursor X position */
|
||||
void screen_cursor_set_x(int x);
|
||||
|
||||
/** Set cursor Y position */
|
||||
void screen_cursor_set_y(int y);
|
||||
|
||||
/** Reset cursor attribs */
|
||||
void screen_reset_cursor(void);
|
||||
|
||||
/** Relative cursor move */
|
||||
void screen_cursor_move(int dy, int dx);
|
||||
|
||||
void screen_cursor_move(int dy, int dx, bool scroll);
|
||||
/** Save the cursor pos */
|
||||
void screen_cursor_save(bool withAttrs);
|
||||
|
||||
/** Restore the cursor pos */
|
||||
void screen_cursor_restore(bool withAttrs);
|
||||
|
||||
/** Enable cursor display */
|
||||
void screen_cursor_enable(bool enable);
|
||||
|
||||
/** Enable auto wrap */
|
||||
void screen_wrap_enable(bool enable);
|
||||
|
||||
@@ -160,19 +169,16 @@ void screen_wrap_enable(bool enable);
|
||||
|
||||
/** Set cursor foreground color */
|
||||
void screen_set_fg(Color color);
|
||||
|
||||
/** Set cursor background coloor */
|
||||
void screen_set_bg(Color color);
|
||||
|
||||
/** make foreground bright */
|
||||
void screen_set_bold(bool bold);
|
||||
|
||||
/** Set cursor foreground and background color */
|
||||
void screen_set_colors(Color fg, Color bg);
|
||||
|
||||
/** Invert colors */
|
||||
void screen_inverse(bool inverse);
|
||||
// enable or disable attrs by bitmask
|
||||
void screen_attr_enable(u8 attrs);
|
||||
void screen_attr_disable(u8 attrs);
|
||||
void screen_inverse_enable(bool ena);
|
||||
|
||||
void screen_set_charset_n(int Gx);
|
||||
void screen_set_charset(int Gx, char charset);
|
||||
|
||||
/**
|
||||
* Set a character in the cursor color, move to right with wrap.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef USER_MAIN_H_H
|
||||
#define USER_MAIN_H_H
|
||||
|
||||
#define FIRMWARE_VERSION "0.6.1+" GIT_HASH
|
||||
#define FIRMWARE_VERSION "0.6.7+" GIT_HASH
|
||||
#define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware"
|
||||
|
||||
#endif //USER_MAIN_H_H
|
||||
|
||||
Reference in New Issue
Block a user