some more missing commands, added code pages, fixed the xterm size command and some other stuff. also FRAKTUR

pull/111/merge
Ondřej Hruška 7 years ago
parent e48180ac10
commit bc41a21f23
  1. 2
      CMakeLists.txt
  2. 2
      Makefile
  3. 21
      html_orig/css/app.css
  4. 185
      html_orig/js/app.js
  5. 64
      html_orig/jssrc/appcommon.js
  6. 121
      html_orig/jssrc/term.js
  7. 31
      html_orig/sass/pages/_term.scss
  8. 4
      user/ansi_parser.h
  9. 124
      user/ansi_parser_callbacks.c
  10. 247
      user/screen.c
  11. 30
      user/screen.h

@ -124,7 +124,7 @@ set(SOURCE_FILES
user/persist.h user/persist.h
include/helpers.h include/helpers.h
user/syscfg.c user/syscfg.c
user/syscfg.h) user/syscfg.h user/ansi_utf.c user/ansi_utf.h)
include_directories(include) include_directories(include)
include_directories(user) include_directories(user)

@ -67,7 +67,7 @@ CFLAGS = -Os -ggdb -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fn
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \ -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \
-Wno-address -Wno-unused -Wno-address -Wno-unused
CFLAGS += -DHTTPD_MAX_BACKLOG_SIZE=8192 CFLAGS += -DHTTPD_MAX_BACKLOG_SIZE=10240
CFLAGS += -DGIT_HASH='"$(shell git rev-parse --short HEAD)"' CFLAGS += -DGIT_HASH='"$(shell git rev-parse --short HEAD)"'
CFLAGS += -DADMIN_PASSWORD=$(ADMIN_PASSWORD) CFLAGS += -DADMIN_PASSWORD=$(ADMIN_PASSWORD)

@ -1152,9 +1152,6 @@ body.term #botnav {
position: absolute; position: absolute;
top: -9999px; } top: -9999px; }
.nb {
font-weight: normal !important; }
.theme-0 .fg0 { .theme-0 .fg0 {
color: #111213; } color: #111213; }
.theme-0 .bg0 { .theme-0 .bg0 {
@ -1548,6 +1545,24 @@ body.term #botnav {
.bold { .bold {
font-weight: bold !important; } 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 { .Row.color-preview {
font-family: monospace; font-family: monospace;
font-size: 16pt; font-size: 16pt;

@ -1022,6 +1022,70 @@ $.ready(function() {
}, 1); }, 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 // Generated from PHP locale file
var _tr = { var _tr = {
"wifi.connected_ip_is": "Connected, IP is ", "wifi.connected_ip_is": "Connected, IP is ",
@ -1200,7 +1264,7 @@ var Screen = (function () {
y: 0, y: 0,
fg: 7, // colors 0-15 fg: 7, // colors 0-15
bg: 0, bg: 0,
bold: false, attrs: 0,
suppress: false, // do not turn on in blink interval (for safe moving) suppress: false, // do not turn on in blink interval (for safe moving)
hidden: false // do not show hidden: false // do not show
}; };
@ -1208,35 +1272,19 @@ var Screen = (function () {
var screen = []; var screen = [];
var blinkIval; var blinkIval;
/** Clear screen */ var frakturExceptions = {
function _clear() { 'C': '\u212d',
for (var i = W*H-1; i>=0; i--) { 'H': '\u210c',
var cell = screen[i]; 'I': '\u2111',
cell.t = ' '; 'R': '\u211c',
cell.bg = cursor.bg; 'Z': '\u2128',
cell.fg = cursor.fg; };
cell.bold = false;
_draw(cell);
}
}
/** Set text and color at XY */
function _cellAt(y, x) {
return screen[y*W+x];
}
/** Get cell under cursor */ /** Get cell under cursor */
function _curCell() { function _curCell() {
return screen[cursor.y*W + cursor.x]; 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 */ /** Safely move cursor */
function cursorSet(y, x) { function cursorSet(y, x) {
// Hide and prevent from showing up during the move // Hide and prevent from showing up during the move
@ -1255,13 +1303,44 @@ var Screen = (function () {
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y; 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 // Colors
fg = inv ? cell.bg : cell.fg; fg = inv ? cell.bg : cell.fg;
bg = inv ? cell.fg : cell.bg; bg = inv ? cell.fg : cell.bg;
// Update // Update
e.innerText = (cell.t + ' ')[0]; elem.textContent = t = (cell.t + ' ')[0];
e.className = 'fg' + fg + ' bg' + bg + (cell.bold ? ' bold' : '');
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 */ /** Show entire screen */
@ -1305,6 +1384,7 @@ var Screen = (function () {
t: ' ', t: ' ',
fg: cursor.fg, fg: cursor.fg,
bg: cursor.bg, bg: cursor.bg,
attrs: 0,
e: e, e: e,
x: i % W, x: i % W,
y: Math.floor(i / W), y: Math.floor(i / W),
@ -1328,6 +1408,15 @@ var Screen = (function () {
_draw(_curCell(), cursor.a); _draw(_curCell(), cursor.a);
} }
}, 500); }, 500);
// blink attribute
setInterval(function () {
$('#screen').removeClass('blink-hide');
setTimeout(function() {
$('#screen').addClass('blink-hide');
}, 800); // 200 ms ON
}, 1000);
inited = true; inited = true;
} }
@ -1336,11 +1425,18 @@ var Screen = (function () {
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; 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_REPEAT = 2;
var SEQ_SET_COLOR = 3;
var SEQ_SET_ATTR = 4;
function _load_content(str) { 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(); if (!inited) _init();
@ -1362,25 +1458,31 @@ var Screen = (function () {
num = parse2B(str, i); i += 2; // fg bg bold hidden num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.fg = num & 0x0F; cursor.fg = num & 0x0F;
cursor.bg = (num & 0xF0) >> 4; cursor.bg = (num & 0xF0) >> 4;
cursor.bold = !!(num & 0x100); cursor.hidden = !(num & 0x100);
cursor.hidden = !(num & 0x200);
// console.log("FG ",cursor.fg, ", BG ", cursor.bg,", BOLD ", cursor.bold, ", HIDE ", cursor.hidden);
fg = cursor.fg; fg = cursor.fg;
bg = cursor.bg; bg = cursor.bg;
bold = cursor.bold; attrs = 0;
// Here come the content // Here come the content
while(i < str.length && ci<W*H) { while(i < str.length && ci<W*H) {
j = str[i++]; j = str[i++];
jc = j.charCodeAt(0); 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; num = parse2B(str, i); i += 2;
fg = num & 0x0F; fg = num & 0x0F;
bg = (num & 0xF0) >> 4; 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) { else if (jc == SEQ_REPEAT) {
num = parse2B(str, i); i += 2; num = parse2B(str, i); i += 2;
@ -1390,7 +1492,7 @@ var Screen = (function () {
cell.fg = fg; cell.fg = fg;
cell.bg = bg; cell.bg = bg;
cell.t = t; cell.t = t;
cell.bold = bold; cell.attrs = attrs;
} }
} }
else { else {
@ -1399,7 +1501,7 @@ var Screen = (function () {
t = cell.t = j; t = cell.t = j;
cell.fg = fg; cell.fg = fg;
cell.bg = bg; cell.bg = bg;
cell.bold = bold; cell.attrs = attrs;
// console.log("Symbol ", j); // console.log("Symbol ", j);
} }
} }
@ -1450,12 +1552,14 @@ var Conn = (function() {
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting..."); console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
setTimeout(function() { setTimeout(function() {
init(); 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) { function onMessage(evt) {
try { try {
console.log("RX: ", evt.data); //console.log("RX: ", evt.data);
// Assume all our messages are screen updates // Assume all our messages are screen updates
Screen.load(evt.data); Screen.load(evt.data);
} catch(e) { } catch(e) {
@ -1530,6 +1634,7 @@ var Input = (function() {
//console.log("Down ", code, e); //console.log("Down ", code, e);
switch(code) { switch(code) {
case 8: sendStrMsg('\x08'); break; case 8: sendStrMsg('\x08'); break;
case 9: sendStrMsg('\x09'); break;
case 10: case 10:
case 13: sendStrMsg('\x0d\x0a'); break; case 13: sendStrMsg('\x0d\x0a'); break;
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences

@ -123,3 +123,67 @@ $.ready(function() {
}, 1); }, 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;
}
}());
}

@ -8,7 +8,7 @@ var Screen = (function () {
y: 0, y: 0,
fg: 7, // colors 0-15 fg: 7, // colors 0-15
bg: 0, bg: 0,
bold: false, attrs: 0,
suppress: false, // do not turn on in blink interval (for safe moving) suppress: false, // do not turn on in blink interval (for safe moving)
hidden: false // do not show hidden: false // do not show
}; };
@ -16,35 +16,19 @@ var Screen = (function () {
var screen = []; var screen = [];
var blinkIval; var blinkIval;
/** Clear screen */ var frakturExceptions = {
function _clear() { 'C': '\u212d',
for (var i = W*H-1; i>=0; i--) { 'H': '\u210c',
var cell = screen[i]; 'I': '\u2111',
cell.t = ' '; 'R': '\u211c',
cell.bg = cursor.bg; 'Z': '\u2128',
cell.fg = cursor.fg; };
cell.bold = false;
_draw(cell);
}
}
/** Set text and color at XY */
function _cellAt(y, x) {
return screen[y*W+x];
}
/** Get cell under cursor */ /** Get cell under cursor */
function _curCell() { function _curCell() {
return screen[cursor.y*W + cursor.x]; 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 */ /** Safely move cursor */
function cursorSet(y, x) { function cursorSet(y, x) {
// Hide and prevent from showing up during the move // 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; 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 // Colors
fg = inv ? cell.bg : cell.fg; fg = inv ? cell.bg : cell.fg;
bg = inv ? cell.fg : cell.bg; bg = inv ? cell.fg : cell.bg;
// Update // Update
e.innerText = (cell.t + ' ')[0]; elem.textContent = t = (cell.t + ' ')[0];
e.className = 'fg' + fg + ' bg' + bg + (cell.bold ? ' bold' : '');
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 */ /** Show entire screen */
@ -113,6 +128,7 @@ var Screen = (function () {
t: ' ', t: ' ',
fg: cursor.fg, fg: cursor.fg,
bg: cursor.bg, bg: cursor.bg,
attrs: 0,
e: e, e: e,
x: i % W, x: i % W,
y: Math.floor(i / W), y: Math.floor(i / W),
@ -136,6 +152,15 @@ var Screen = (function () {
_draw(_curCell(), cursor.a); _draw(_curCell(), cursor.a);
} }
}, 500); }, 500);
// blink attribute
setInterval(function () {
$('#screen').removeClass('blink-hide');
setTimeout(function() {
$('#screen').addClass('blink-hide');
}, 800); // 200 ms ON
}, 1000);
inited = true; inited = true;
} }
@ -144,11 +169,18 @@ var Screen = (function () {
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; 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_REPEAT = 2;
var SEQ_SET_COLOR = 3;
var SEQ_SET_ATTR = 4;
function _load_content(str) { 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(); if (!inited) _init();
@ -170,25 +202,31 @@ var Screen = (function () {
num = parse2B(str, i); i += 2; // fg bg bold hidden num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.fg = num & 0x0F; cursor.fg = num & 0x0F;
cursor.bg = (num & 0xF0) >> 4; cursor.bg = (num & 0xF0) >> 4;
cursor.bold = !!(num & 0x100); cursor.hidden = !(num & 0x100);
cursor.hidden = !(num & 0x200);
// console.log("FG ",cursor.fg, ", BG ", cursor.bg,", BOLD ", cursor.bold, ", HIDE ", cursor.hidden);
fg = cursor.fg; fg = cursor.fg;
bg = cursor.bg; bg = cursor.bg;
bold = cursor.bold; attrs = 0;
// Here come the content // Here come the content
while(i < str.length && ci<W*H) { while(i < str.length && ci<W*H) {
j = str[i++]; j = str[i++];
jc = j.charCodeAt(0); 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; num = parse2B(str, i); i += 2;
fg = num & 0x0F; fg = num & 0x0F;
bg = (num & 0xF0) >> 4; 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) { else if (jc == SEQ_REPEAT) {
num = parse2B(str, i); i += 2; num = parse2B(str, i); i += 2;
@ -198,7 +236,7 @@ var Screen = (function () {
cell.fg = fg; cell.fg = fg;
cell.bg = bg; cell.bg = bg;
cell.t = t; cell.t = t;
cell.bold = bold; cell.attrs = attrs;
} }
} }
else { else {
@ -207,7 +245,7 @@ var Screen = (function () {
t = cell.t = j; t = cell.t = j;
cell.fg = fg; cell.fg = fg;
cell.bg = bg; cell.bg = bg;
cell.bold = bold; cell.attrs = attrs;
// console.log("Symbol ", j); // console.log("Symbol ", j);
} }
} }
@ -258,12 +296,14 @@ var Conn = (function() {
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting..."); console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
setTimeout(function() { setTimeout(function() {
init(); 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) { function onMessage(evt) {
try { try {
console.log("RX: ", evt.data); //console.log("RX: ", evt.data);
// Assume all our messages are screen updates // Assume all our messages are screen updates
Screen.load(evt.data); Screen.load(evt.data);
} catch(e) { } catch(e) {
@ -338,6 +378,7 @@ var Input = (function() {
//console.log("Down ", code, e); //console.log("Down ", code, e);
switch(code) { switch(code) {
case 8: sendStrMsg('\x08'); break; case 8: sendStrMsg('\x08'); break;
case 9: sendStrMsg('\x09'); break;
case 10: case 10:
case 13: sendStrMsg('\x0d\x0a'); break; case 13: sendStrMsg('\x0d\x0a'); break;
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences

@ -86,11 +86,6 @@ body.term {
top: -9999px; top: -9999px;
} }
// "non-bold"
.nb {
font-weight: normal !important;
}
// Tango // Tango
.theme-0 { .theme-0 {
$term-colors: $term-colors:
@ -163,10 +158,36 @@ body.term {
} }
} }
// Attributes
.bold { .bold {
font-weight: bold !important; 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 { .Row.color-preview {
font-family: monospace; font-family: monospace;
font-size: 16pt; font-size: 16pt;

@ -5,7 +5,7 @@
#include <screen.h> #include <screen.h>
// Max nr of CSI parameters // Max nr of CSI parameters
#define CSI_N_MAX 3 #define CSI_N_MAX 10
#define OSC_CHAR_MAX TERM_TITLE_LEN #define OSC_CHAR_MAX TERM_TITLE_LEN
extern void apars_handle_plainchar(char c); extern void apars_handle_plainchar(char c);
@ -22,7 +22,7 @@ extern void apars_reset_utf8buffer(void);
// defined in the makefile // defined in the makefile
#if DEBUG_ANSI #if DEBUG_ANSI
#define ansi_warn warn #define ansi_warn warn
#define ansi_dbg warn #define ansi_dbg dbg
#else #else
#define ansi_warn(...) #define ansi_warn(...)
#define ansi_dbg(...) #define ansi_dbg(...)

@ -22,9 +22,17 @@ static int utf_j = 0;
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
apars_handle_plainchar(char c) apars_handle_plainchar(char c)
{ {
if (c == 7) return; // BELL - beep (TODO play beep in browser)
// collecting unicode glyphs... // collecting unicode glyphs...
if (c & 0x80) { if (c & 0x80) {
if (utf_i == 0) { if (utf_i == 0) {
// start
if (c == 192 || c == 193 || c >= 245) {
// forbidden codes
goto fail;
}
if ((c & 0xE0) == 0xC0) { if ((c & 0xE0) == 0xC0) {
utf_i = 2; utf_i = 2;
} }
@ -34,15 +42,17 @@ apars_handle_plainchar(char c)
else if ((c & 0xF8) == 0xF0) { else if ((c & 0xF8) == 0xF0) {
utf_i = 4; utf_i = 4;
} }
else {
// chars over 127 that don't start unicode sequences
goto fail;
}
utf_collect[0] = c; utf_collect[0] = c;
utf_j = 1; utf_j = 1;
} }
else { else {
if ((c & 0xC0) != 0x80) { if ((c & 0xC0) != 0x80) {
// bad UTF goto fail;
ansi_warn("Bad UTF-8");
apars_reset_utf8buffer();
} }
else { else {
utf_collect[utf_j++] = c; utf_collect[utf_j++] = c;
@ -54,9 +64,12 @@ apars_handle_plainchar(char c)
} }
} }
else { else {
if (c == 14 || c == 15) { // pushIn, pushOut if (c == 14) {
//ansi_warn("char %d ignored", c); screen_set_charset_n(1);
// TODO implement for charset switching return;
}
if (c == 15) {
screen_set_charset_n(0);
return; return;
} }
@ -64,6 +77,11 @@ apars_handle_plainchar(char c)
utf_collect[1] = 0; // just to make sure it's closed... utf_collect[1] = 0; // just to make sure it's closed...
screen_putchar(utf_collect); screen_putchar(utf_collect);
} }
return;
fail:
ansi_warn("Bad UTF-8");
apars_reset_utf8buffer();
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -77,8 +95,8 @@ apars_reset_utf8buffer(void)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
apars_handle_characterSet(char leadchar, char c) apars_handle_characterSet(char leadchar, char c)
{ {
// TODO implement for charset switching if (leadchar == '(') screen_set_charset(0, c);
// ansi_warn("NOIMPL charset cmd %c%c", leadchar, c); else if (leadchar == ')') screen_set_charset(1, c);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -97,35 +115,10 @@ apars_handle_setXCtrls(char c)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
apars_handle_CSI(char leadchar, int *params, int count, 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
and some others
*/
int n1 = params[0]; int n1 = params[0];
int n2 = params[1]; int n2 = params[1];
// int n3 = params[2]; int n3 = params[2];
static char buf[20];
// defaults // defaults
switch (keychar) { switch (keychar) {
@ -306,20 +299,53 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar)
else if (n >= 40 && n <= 47) screen_set_bg((Color) (n - 40)); // ANSI normal bg 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 == 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 == 49) screen_set_bg(termconf_scratch.default_bg); // default bg
else if (n == 7) screen_inverse(true); // inverse
else if (n == 27) screen_inverse(false); // positive else if (n == 1) screen_attr_enable(ATTR_BOLD);
else if (n == 1) screen_set_bold(true); // bold else if (n == 2) screen_attr_enable(ATTR_FAINT);
else if (n == 21 || n == 22) screen_set_bold(false); // bold off 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 >= 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 if (n >= 100 && n <= 107) screen_set_bg((Color) (n - 100 + 8)); // AIX bright bg
else { else {
ansi_warn("NOIMPL SGR attr %d", n); ansi_warn("NOIMPL SGR attr %d", n);
} }
} }
break; break;
case 't': // SunView code to set screen size (from GNU Screen) case 't': // xterm hacks
screen_resize(n1, n2); 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; break;
case 'L': case 'L':
@ -340,7 +366,7 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar)
case 'r': case 'r':
// TODO scrolling region // TODO scrolling region
ansi_warn("NOIMPL scrolling region"); // ansi_warn("NOIMPL scrolling region");
break; break;
case 'g': case 'g':
@ -358,6 +384,18 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar)
ansi_warn("NOIMPL CSI setmode %d", n1); ansi_warn("NOIMPL CSI setmode %d", n1);
break; 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: default:
ansi_warn("Unknown CSI: %c", keychar); ansi_warn("Unknown CSI: %c", keychar);
apars_handle_badseq(); apars_handle_badseq();

@ -8,6 +8,9 @@
TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle * const termconf = &persist.current.termconf;
TerminalConfigBundle termconf_scratch; TerminalConfigBundle termconf_scratch;
// forward declare
static void utf8_remap(char* out, char g, char table);
#define W termconf_scratch.width #define W termconf_scratch.width
#define H termconf_scratch.height #define H termconf_scratch.height
@ -61,7 +64,7 @@ typedef struct __attribute__((packed)){
char c[4]; // space for a full unicode character char c[4]; // space for a full unicode character
Color fg : 4; Color fg : 4;
Color bg : 4; Color bg : 4;
bool bold : 1; u8 attrs;
} Cell; } Cell;
/** /**
@ -75,10 +78,15 @@ static Cell screen[MAX_SCREEN_SIZE];
static struct { static struct {
int x; //!< X coordinate int x; //!< X coordinate
int y; //!< Y coordinate int y; //!< Y coordinate
bool visible; //!< Visible
bool inverse; //!< Inverse colors
bool autowrap; //!< Wrapping when EOL 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 fg; //!< Foreground color for writing
Color bg; //!< Background color for writing Color bg; //!< Background color for writing
} cursor; } cursor;
@ -89,10 +97,10 @@ static struct {
static struct { static struct {
int x; int x;
int y; int y;
// mark that attrs are saved
// optionally saved attrs
bool withAttrs; bool withAttrs;
bool inverse; u8 attrs;
bool inverse; // attribute that's not in the bitfield
Color fg; Color fg;
Color bg; Color bg;
} cursor_sav; } cursor_sav;
@ -120,8 +128,8 @@ static inline void
clear_range(unsigned int from, unsigned int to) clear_range(unsigned int from, unsigned int to)
{ {
if (to >= W*H) to = W*H-1; if (to >= W*H) to = W*H-1;
Color fg = cursor.inverse ? cursor.bg : cursor.fg; Color fg = (cursor.inverse) ? cursor.bg : cursor.fg;
Color bg = cursor.inverse ? cursor.fg : cursor.bg; Color bg = (cursor.inverse) ? cursor.fg : cursor.bg;
Cell sample; Cell sample;
sample.c[0] = ' '; sample.c[0] = ' ';
@ -130,7 +138,7 @@ clear_range(unsigned int from, unsigned int to)
sample.c[3] = 0; sample.c[3] = 0;
sample.fg = fg; sample.fg = fg;
sample.bg = bg; sample.bg = bg;
sample.bold = false; sample.attrs = 0;
for (unsigned int i = from; i <= to; i++) { for (unsigned int i = from; i <= to; i++) {
memcpy(&screen[i], &sample, sizeof(Cell)); memcpy(&screen[i], &sample, sizeof(Cell));
@ -148,9 +156,12 @@ cursor_reset(void)
cursor.fg = termconf_scratch.default_fg; cursor.fg = termconf_scratch.default_fg;
cursor.bg = termconf_scratch.default_bg; cursor.bg = termconf_scratch.default_bg;
cursor.visible = 1; cursor.visible = 1;
cursor.inverse = 0;
cursor.autowrap = 1; cursor.autowrap = 1;
cursor.bold = 0; cursor.attrs = 0;
cursor.charset0 = 'B';
cursor.charset1 = '0';
cursor.charsetN = 0;
} }
//endregion //endregion
@ -188,8 +199,8 @@ screen_reset_cursor(void)
{ {
cursor.fg = termconf_scratch.default_fg; cursor.fg = termconf_scratch.default_fg;
cursor.bg = termconf_scratch.default_bg; cursor.bg = termconf_scratch.default_bg;
cursor.inverse = 0; cursor.attrs = 0;
cursor.bold = 0; cursor.inverse = false;
} }
/** /**
@ -262,7 +273,7 @@ screen_fill_with_E(void)
sample.c[3] = 0; sample.c[3] = 0;
sample.fg = termconf_scratch.default_fg; sample.fg = termconf_scratch.default_fg;
sample.bg = termconf_scratch.default_bg; sample.bg = termconf_scratch.default_bg;
sample.bold = false; sample.attrs = 0;
for (unsigned int i = 0; i <= W*H-1; i++) { for (unsigned int i = 0; i <= W*H-1; i++) {
memcpy(&screen[i], &sample, sizeof(Cell)); memcpy(&screen[i], &sample, sizeof(Cell));
@ -531,11 +542,11 @@ screen_cursor_save(bool withAttrs)
if (withAttrs) { if (withAttrs) {
cursor_sav.fg = cursor.fg; cursor_sav.fg = cursor.fg;
cursor_sav.bg = cursor.bg; cursor_sav.bg = cursor.bg;
cursor_sav.inverse = cursor.inverse; cursor_sav.attrs = cursor.attrs;
} else { } else {
cursor_sav.fg = termconf_scratch.default_fg; cursor_sav.fg = termconf_scratch.default_fg;
cursor_sav.bg = termconf_scratch.default_bg; cursor_sav.bg = termconf_scratch.default_bg;
cursor_sav.inverse = 0; cursor_sav.attrs = 0; // avoid leftovers if the wrong restore is used
} }
} }
@ -552,7 +563,7 @@ screen_cursor_restore(bool withAttrs)
if (withAttrs) { if (withAttrs) {
cursor.fg = cursor_sav.fg; cursor.fg = cursor_sav.fg;
cursor.bg = cursor_sav.bg; cursor.bg = cursor_sav.bg;
cursor.inverse = cursor_sav.inverse; cursor.attrs = cursor_sav.attrs;
} }
NOTIFY_DONE(); NOTIFY_DONE();
@ -604,43 +615,19 @@ screen_set_bg(Color color)
cursor.bg = color; cursor.bg = color;
} }
/** void screen_attr_enable(u8 attrs)
* Set cursor foreground and background color
*/
void ICACHE_FLASH_ATTR
screen_set_colors(Color fg, Color bg)
{ {
screen_set_fg(fg); cursor.attrs |= attrs;
screen_set_bg(bg);
} }
/** void screen_attr_disable(u8 attrs)
* Invert colors
*/
void ICACHE_FLASH_ATTR
screen_inverse(bool inverse)
{ {
cursor.inverse = inverse; cursor.attrs &= ~attrs;
} }
/** void screen_inverse_enable(bool ena)
* 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)
{ {
if (!bold) { cursor.inverse = ena;
cursor.fg = (Color) (cursor.fg % 8);
} else {
cursor.fg = (Color) ((cursor.fg % 8) + 8); // change anything to the bright colors
}
cursor.bold = bold;
} }
//endregion //endregion
@ -657,12 +644,26 @@ bool ICACHE_FLASH_ATTR screen_isCoordValid(int y, int x)
return x >= 0 && y >= 0 && x < W && y < H; 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. * Set a character in the cursor color, move to right with wrap.
*/ */
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_putchar(const char *ch) screen_putchar(const char *ch)
{ {
char buf[4];
NOTIFY_LOCK(); NOTIFY_LOCK();
Cell *c = &screen[cursor.x + cursor.y * W]; Cell *c = &screen[cursor.x + cursor.y * W];
@ -687,15 +688,11 @@ screen_putchar(const char *ch)
cursor.y--; cursor.y--;
} }
} }
// erase target cell // apparently backspace should not clear the cell
c = &screen[cursor.x + cursor.y * W];
c->c[0] = ' ';
c->c[1] = 0;
c->c[2] = 0;
c->c[3] = 0;
goto done; goto done;
case 9: // TAB case 9: // TAB
// TODO change if tab setting is ever implemented
if (cursor.x<((W-1)-(W-1)%4)) { if (cursor.x<((W-1)-(W-1)%4)) {
c->c[0] = ' '; c->c[0] = ' ';
c->c[1] = 0; c->c[1] = 0;
@ -715,8 +712,14 @@ screen_putchar(const char *ch)
} }
} }
// copy unicode char if (ch[1] == 0 && ch[0] <= 0x7f) {
strncpy(c->c, ch, 4); // 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) { if (cursor.inverse) {
c->fg = cursor.bg; c->fg = cursor.bg;
@ -725,7 +728,7 @@ screen_putchar(const char *ch)
c->fg = cursor.fg; c->fg = cursor.fg;
c->bg = cursor.bg; c->bg = cursor.bg;
} }
c->bold = cursor.bold; c->attrs = cursor.attrs;
cursor.x++; cursor.x++;
// X wrap // X wrap
@ -748,6 +751,84 @@ done:
NOTIFY_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 //region Serialization
@ -789,7 +870,7 @@ void screen_dd(void)
struct ScreenSerializeState { struct ScreenSerializeState {
Color lastFg; Color lastFg;
Color lastBg; Color lastBg;
bool lastBold; bool lastAttrs;
char lastChar[4]; char lastChar[4];
int index; int index;
}; };
@ -798,8 +879,24 @@ void ICACHE_FLASH_ATTR
encode2B(u16 number, WordB2 *stru) encode2B(u16 number, WordB2 *stru)
{ {
stru->lsb = (u8) (number % 127); 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->lsb += 1;
stru->msb = (u8) (number % 127);
number = (number - stru->msb) / 127;
stru->msb += 1;
stru->xsb = (u8) (number + 1);
} }
/** /**
@ -845,6 +942,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
Cell *cell, *cell0; Cell *cell, *cell0;
WordB2 w1, w2, w3, w4, w5; WordB2 w1, w2, w3, w4, w5;
WordB3 lw1;
size_t remain = buf_len; int used = 0; size_t remain = buf_len; int used = 0;
char *bb = buffer; char *bb = buffer;
@ -860,7 +958,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
ss->index = 0; ss->index = 0;
ss->lastBg = 0; ss->lastBg = 0;
ss->lastFg = 0; ss->lastFg = 0;
ss->lastBold = false; ss->lastAttrs = 0;
memset(ss->lastChar, 0, 4); // this ensures the first char is never "repeat" memset(ss->lastChar, 0, 4); // this ensures the first char is never "repeat"
encode2B((u16) H, &w1); encode2B((u16) H, &w1);
@ -870,8 +968,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
encode2B((u16) ( encode2B((u16) (
cursor.fg | cursor.fg |
(cursor.bg<<4) | (cursor.bg<<4) |
(cursor.bold?0x100:0) | (cursor.visible ? 1<<8 : 0))
(cursor.visible?0x200:0))
, &w5); , &w5);
// H W X Y Attribs // H W X Y Attribs
@ -887,7 +984,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
while (i < W*H while (i < W*H
&& cell->fg == ss->lastFg && cell->fg == ss->lastFg
&& cell->bg == ss->lastBg && cell->bg == ss->lastBg
&& cell->bold == ss->lastBold && cell->attrs == ss->lastAttrs
&& strneq(cell->c, ss->lastChar, 4)) { && strneq(cell->c, ss->lastChar, 4)) {
// Repeat // Repeat
repCnt++; repCnt++;
@ -896,13 +993,25 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
if (repCnt == 0) { if (repCnt == 0) {
// No repeat // No repeat
if (cell0->bold != ss->lastBold || cell0->fg != ss->lastFg || cell0->bg != ss->lastBg) { bool changeAttrs = cell0->attrs != ss->lastAttrs;
encode2B((u16) ( 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->fg |
(cell0->bg<<4) | (cell0->bg<<4) |
(cell0->bold?0x100:0)) (cell0->attrs<<8))
, &w1); , &lw1);
bufprint("\x01%c%c", w1.lsb, w1.msb); bufprint("\x01%c%c%c", lw1.lsb, lw1.msb, lw1.xsb);
} }
// copy the symbol, until first 0 or reached 4 bytes // copy the symbol, until first 0 or reached 4 bytes
@ -914,7 +1023,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
ss->lastFg = cell0->fg; ss->lastFg = cell0->fg;
ss->lastBg = cell0->bg; ss->lastBg = cell0->bg;
ss->lastBold = cell0->bold; ss->lastAttrs = cell0->attrs;
memcpy(ss->lastChar, cell0->c, 4); memcpy(ss->lastChar, cell0->c, 4);
i++; i++;

@ -46,6 +46,14 @@ typedef enum {
CHANGE_LABELS, CHANGE_LABELS,
} ScreenNotifyChangeTopic; } 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 #define SCREEN_NOTIFY_DELAY_MS 20
typedef struct { typedef struct {
@ -95,6 +103,12 @@ typedef struct {
u8 msb; u8 msb;
} WordB2; } WordB2;
typedef struct {
u8 lsb;
u8 msb;
u8 xsb;
} WordB3;
/** Encode number to two nice ASCII bytes */ /** Encode number to two nice ASCII bytes */
void encode2B(u16 number, WordB2 *stru); void encode2B(u16 number, WordB2 *stru);
@ -119,7 +133,7 @@ void screen_clear_in_line(unsigned int count);
void screen_scroll_up(unsigned int lines); void screen_scroll_up(unsigned int lines);
/** Shift screen downwards */ /** Shift screen downwards */
void screen_scroll_down(unsigned int lines); void screen_scroll_down(unsigned int lines);
/** esc # 8 - fill entire screen with E of default colors */ /** esc # 8 - fill entire screen with E of default colors (DEC alignment display) */
void screen_fill_with_E(void); void screen_fill_with_E(void);
// --- insert / delete --- // --- insert / delete ---
@ -157,12 +171,14 @@ void screen_wrap_enable(bool enable);
void screen_set_fg(Color color); void screen_set_fg(Color color);
/** Set cursor background coloor */ /** Set cursor background coloor */
void screen_set_bg(Color color); void screen_set_bg(Color color);
/** make foreground bright */
void screen_set_bold(bool bold); // enable or disable attrs by bitmask
/** Set cursor foreground and background color */ void screen_attr_enable(u8 attrs);
void screen_set_colors(Color fg, Color bg); void screen_attr_disable(u8 attrs);
/** Invert colors */ void screen_inverse_enable(bool ena);
void screen_inverse(bool inverse);
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. * Set a character in the cursor color, move to right with wrap.

Loading…
Cancel
Save