w.i.p. Terminal Themes added, improved terminal color picker, new compression in front-end
This commit is contained in:
@@ -14,12 +14,13 @@ return [
|
||||
'%btn4%' => '4',
|
||||
'%btn5%' => '5',
|
||||
|
||||
'%screenData%' => '{
|
||||
'%screenData%' => 'HELLOxNRE3',//'\u000b\u0001\u001b\u0001\u0001\u0001\u0001\u0001\f\u0005\u0001\u0010\u0003HELLOx\u0002\u000b\u0001\u0001N\u0001RE\u00023\u0001', //,
|
||||
/*'{
|
||||
"w": 26, "h": 10,
|
||||
"x": 0, "y": 0,
|
||||
"cv": 1,
|
||||
"screen": "70 t259"
|
||||
}',
|
||||
"x": 10, "y": 5,
|
||||
"cv": 1, "fg": 10, "bg": 2,
|
||||
"screen": ""
|
||||
}',//"70 t259"*/
|
||||
|
||||
'%opmode%' => '2',
|
||||
'%sta_enable%' => '0',
|
||||
@@ -65,4 +66,6 @@ return [
|
||||
'%uart_baud%' => 115200,
|
||||
'%uart_stopbits%' => 1,
|
||||
'%uart_parity%' => 2,
|
||||
|
||||
'%theme%' => 5,
|
||||
];
|
||||
|
||||
+437
-110
File diff suppressed because one or more lines are too long
Binary file not shown.
+311
-280
@@ -1182,306 +1182,337 @@ function tr(key) { return _tr[key] || '?'+key+'?'; }
|
||||
w.init = wifiInit;
|
||||
w.startScanning = startScanning;
|
||||
})(window.WiFi = {});
|
||||
(function() {
|
||||
/**
|
||||
* Terminal module
|
||||
*/
|
||||
var Term = (function () {
|
||||
var W, H;
|
||||
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false};
|
||||
var screen = [];
|
||||
var blinkIval;
|
||||
var Screen = (function () {
|
||||
var W, H; // dimensions
|
||||
var inited = false;
|
||||
|
||||
/*
|
||||
/!** Clear screen *!/
|
||||
function cls() {
|
||||
screen.forEach(function(cell, i) {
|
||||
cell.t = ' ';
|
||||
cell.fg = 7;
|
||||
cell.bg = 0;
|
||||
blit(cell);
|
||||
});
|
||||
var cursor = {
|
||||
a: false, // active (blink state)
|
||||
x: 0, // 0-based coordinates
|
||||
y: 0,
|
||||
fg: 7, // colors 0-15
|
||||
bg: 0,
|
||||
bold: false,
|
||||
suppress: false, // do not turn on in blink interval (for safe moving)
|
||||
hidden: false // do not show
|
||||
};
|
||||
|
||||
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];
|
||||
/** Set text and color at XY */
|
||||
function _cellAt(y, x) {
|
||||
return screen[y*W+x];
|
||||
}
|
||||
|
||||
/** 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
|
||||
cursor.suppress = true;
|
||||
_draw(_curCell(), false);
|
||||
cursor.x = x;
|
||||
cursor.y = y;
|
||||
// Show again
|
||||
cursor.suppress = false;
|
||||
_draw(_curCell());
|
||||
}
|
||||
|
||||
/** Update cell on display. inv = invert (for cursor) */
|
||||
function _draw(cell, inv) {
|
||||
if (typeof inv == 'undefined') {
|
||||
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
|
||||
}
|
||||
|
||||
/** Get cell under cursor */
|
||||
function cursorCell() {
|
||||
return cellAt(cursor.y, cursor.x);
|
||||
var e = cell.e, fg, bg;
|
||||
// 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' : '');
|
||||
}
|
||||
|
||||
/** Show entire screen */
|
||||
function _drawAll() {
|
||||
for (var i = W*H-1; i>=0; i--) {
|
||||
_draw(screen[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/!** Enable or disable cursor visibility *!/
|
||||
function cursorEnable(enable) {
|
||||
cursor.hidden = !enable;
|
||||
cursor.a &= enable;
|
||||
blit(cursorCell(), cursor.a);
|
||||
function _rebuild(rows, cols) {
|
||||
W = cols;
|
||||
H = rows;
|
||||
|
||||
/* Build screen & show */
|
||||
var e, cell, scr = qs('#screen');
|
||||
|
||||
// Empty the screen node
|
||||
while (scr.firstChild) scr.removeChild(scr.firstChild);
|
||||
|
||||
screen = [];
|
||||
|
||||
for(var i = 0; i < W*H; i++) {
|
||||
e = mk('span');
|
||||
|
||||
(function() {
|
||||
var x = i % W;
|
||||
var y = Math.floor(i / W);
|
||||
e.addEventListener('click', function () {
|
||||
Input.onTap(y, x);
|
||||
});
|
||||
})();
|
||||
|
||||
/* End of line */
|
||||
if ((i > 0) && (i % W == 0)) {
|
||||
scr.appendChild(mk('br'));
|
||||
}
|
||||
/* The cell */
|
||||
scr.appendChild(e);
|
||||
|
||||
cell = {
|
||||
t: ' ',
|
||||
fg: cursor.fg,
|
||||
bg: cursor.bg,
|
||||
e: e,
|
||||
x: i % W,
|
||||
y: Math.floor(i / W),
|
||||
};
|
||||
screen.push(cell);
|
||||
_draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
/!** Safely move cursor *!/
|
||||
function cursorSet(y, x) {
|
||||
// Hide and prevent from showing up during the move
|
||||
cursor.suppress = true;
|
||||
blit(cursorCell(), false);
|
||||
|
||||
cursor.x = x;
|
||||
cursor.y = y;
|
||||
|
||||
// Show again
|
||||
cursor.suppress = false;
|
||||
blit(cursorCell(), cursor.a);
|
||||
}
|
||||
*/
|
||||
|
||||
/** Update cell on display. inv = invert (for cursor) */
|
||||
function blit(cell, inv) {
|
||||
var e = cell.e, fg, bg;
|
||||
// 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;
|
||||
}
|
||||
|
||||
/** Show entire screen */
|
||||
function blitAll() {
|
||||
screen.forEach(function(cell, i) {
|
||||
/* Invert if under cursor & cursor active */
|
||||
var inv = cursor.a && (i == cursor.y*W+cursor.x);
|
||||
blit(cell, inv);
|
||||
});
|
||||
}
|
||||
|
||||
/** Load screen content from a 'binary' sequence */
|
||||
function load(obj) {
|
||||
cursor.x = obj.x;
|
||||
cursor.y = obj.y;
|
||||
cursor.hidden = !obj.cv;
|
||||
|
||||
// full re-init if size changed
|
||||
if (obj.w != W || obj.h != H) {
|
||||
Term.init(obj);
|
||||
return;
|
||||
/** Init the terminal */
|
||||
function _init() {
|
||||
/* Cursor blinking */
|
||||
clearInterval(blinkIval);
|
||||
blinkIval = setInterval(function () {
|
||||
cursor.a = !cursor.a;
|
||||
if (cursor.hidden) {
|
||||
cursor.a = false;
|
||||
}
|
||||
|
||||
// Simple compression - hexFG hexBG 'ASCII' (r/s/t/u NUM{1,2,3,4})?
|
||||
// comma instead of both colors = same as before
|
||||
if (!cursor.suppress) {
|
||||
_draw(_curCell(), cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
inited = true;
|
||||
}
|
||||
|
||||
var i = 0, ci = 0, str = obj.screen;
|
||||
var fg = 7, bg = 0;
|
||||
while(i < str.length && ci<W*H) {
|
||||
var cell = screen[ci++];
|
||||
/** Decode two-byte number */
|
||||
function parse2B(s, i) {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
var j = str[i];
|
||||
if (j != ',') { // comma = repeat last colors
|
||||
fg = cell.fg = parseInt(str[i++], 16);
|
||||
bg = cell.bg = parseInt(str[i++], 16);
|
||||
} else {
|
||||
i++;
|
||||
var SEQ_SET_COLOR = 1;
|
||||
var SEQ_REPEAT = 2;
|
||||
|
||||
/** Load screen content from a binary sequence (new) */
|
||||
function load(str) {
|
||||
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, bold, cell;
|
||||
|
||||
if (!inited) _init();
|
||||
|
||||
// Set size
|
||||
num = parse2B(str, i); i += 2;
|
||||
num2 = parse2B(str, i); i += 2;
|
||||
if (num != H || num2 != W) {
|
||||
_rebuild(num, num2);
|
||||
}
|
||||
console.log("Size ",num, num2);
|
||||
|
||||
// Cursor position
|
||||
num = parse2B(str, i); i += 2;
|
||||
num2 = parse2B(str, i); i += 2;
|
||||
cursorSet(num, num2);
|
||||
console.log("Cursor at ",num, num2);
|
||||
|
||||
// Attributes
|
||||
num = parse2B(str, i); i += 2;
|
||||
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);
|
||||
|
||||
fg = cursor.fg;
|
||||
bg = cursor.bg;
|
||||
bold = cursor.bold;
|
||||
|
||||
// Here come the content
|
||||
while(i < str.length && ci<W*H) {
|
||||
|
||||
j = str[i++];
|
||||
jc = j.charCodeAt(0);
|
||||
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_REPEAT) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
console.log("Repeat x ",num);
|
||||
for (; num>0 && ci<W*H; num--) {
|
||||
cell = screen[ci++];
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
}
|
||||
|
||||
var t = cell.t = str[i++];
|
||||
|
||||
var repchars = 0;
|
||||
switch(str[i]) {
|
||||
case 'r': repchars = 1; break;
|
||||
case 's': repchars = 2; break;
|
||||
case 't': repchars = 3; break;
|
||||
case 'u': repchars = 4; break;
|
||||
default: repchars = 0;
|
||||
}
|
||||
|
||||
if (repchars > 0) {
|
||||
var rep = parseInt(str.substr(i+1,repchars));
|
||||
i = i + repchars + 1;
|
||||
for (; rep>0 && ci<W*H; rep--) {
|
||||
cell = screen[ci++];
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.t = t;
|
||||
}
|
||||
cell.t = t;
|
||||
cell.bold = bold;
|
||||
}
|
||||
}
|
||||
|
||||
blitAll();
|
||||
}
|
||||
|
||||
/** Init the terminal */
|
||||
function init(obj) {
|
||||
W = obj.w;
|
||||
H = obj.h;
|
||||
|
||||
/* Build screen & show */
|
||||
var e, cell, scr = qs('#screen');
|
||||
|
||||
// Empty the screen node
|
||||
while (scr.firstChild) scr.removeChild(scr.firstChild);
|
||||
|
||||
screen = [];
|
||||
|
||||
for(var i = 0; i < W*H; i++) {
|
||||
e = mk('span');
|
||||
|
||||
(function() {
|
||||
var x = i % W;
|
||||
var y = Math.floor(i / W);
|
||||
e.addEventListener('click', function () {
|
||||
Input.onTap(y, x);
|
||||
});
|
||||
})();
|
||||
|
||||
/* End of line */
|
||||
if ((i > 0) && (i % W == 0)) {
|
||||
scr.appendChild(mk('br'));
|
||||
}
|
||||
/* The cell */
|
||||
scr.appendChild(e);
|
||||
|
||||
cell = {t: ' ', fg: 7, bg: 0, e: e};
|
||||
screen.push(cell);
|
||||
blit(cell);
|
||||
}
|
||||
|
||||
/* Cursor blinking */
|
||||
clearInterval(blinkIval);
|
||||
blinkIval = setInterval(function() {
|
||||
cursor.a = !cursor.a;
|
||||
if (cursor.hidden) {
|
||||
cursor.a = false;
|
||||
}
|
||||
|
||||
if (!cursor.suppress) {
|
||||
blit(cursorCell(), cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
load(obj);
|
||||
}
|
||||
|
||||
// publish
|
||||
return {
|
||||
init: init,
|
||||
load: load
|
||||
};
|
||||
})();
|
||||
|
||||
/** Handle connections */
|
||||
var Conn = (function() {
|
||||
var ws;
|
||||
|
||||
function onOpen(evt) {
|
||||
console.log("CONNECTED");
|
||||
}
|
||||
|
||||
function onClose(evt) {
|
||||
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
|
||||
setTimeout(function() {
|
||||
init();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Term.load(JSON.parse(evt.data));
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
else {
|
||||
cell = screen[ci++];
|
||||
// Unique cell character
|
||||
t = cell.t = j;
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.bold = bold;
|
||||
console.log("Symbol ", j);
|
||||
}
|
||||
}
|
||||
|
||||
function doSend(message) {
|
||||
console.log("TX: ", message);
|
||||
if (ws.readyState != 1) {
|
||||
console.error("Socket not ready");
|
||||
return;
|
||||
}
|
||||
if (typeof message != "string") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
ws.send(message);
|
||||
}
|
||||
_drawAll();
|
||||
}
|
||||
|
||||
function init() {
|
||||
ws = new WebSocket("ws://"+_root+"/ws/update.cgi");
|
||||
ws.onopen = onOpen;
|
||||
ws.onclose = onClose;
|
||||
ws.onmessage = onMessage;
|
||||
|
||||
console.log("Opening socket.");
|
||||
}
|
||||
|
||||
return {
|
||||
ws: null,
|
||||
init: init,
|
||||
send: doSend
|
||||
};
|
||||
})();
|
||||
|
||||
//
|
||||
// Keyboard (& mouse) input
|
||||
//
|
||||
var Input = (function() {
|
||||
function sendStrMsg(str) {
|
||||
Conn.send("STR:"+str);
|
||||
}
|
||||
|
||||
function sendPosMsg(y, x) {
|
||||
Conn.send("TAP:"+y+','+x);
|
||||
}
|
||||
|
||||
function sendBtnMsg(n) {
|
||||
Conn.send("BTN:"+n);
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
});
|
||||
|
||||
qsa('#buttons button').forEach(function(s) {
|
||||
s.addEventListener('click', function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onTap: sendPosMsg
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
window.termInit = function (obj) {
|
||||
Term.init(obj);
|
||||
Conn.init();
|
||||
Input.init();
|
||||
return {
|
||||
load: load, // full load (string)
|
||||
};
|
||||
})();
|
||||
|
||||
/** Handle connections */
|
||||
var Conn = (function() {
|
||||
var ws;
|
||||
|
||||
function onOpen(evt) {
|
||||
console.log("CONNECTED");
|
||||
}
|
||||
|
||||
function onClose(evt) {
|
||||
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
|
||||
setTimeout(function() {
|
||||
init();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function doSend(message) {
|
||||
console.log("TX: ", message);
|
||||
if (!ws) return; // for dry testing
|
||||
if (ws.readyState != 1) {
|
||||
console.error("Socket not ready");
|
||||
return;
|
||||
}
|
||||
if (typeof message != "string") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
ws.send(message);
|
||||
}
|
||||
|
||||
function init() {
|
||||
ws = new WebSocket("ws://"+_root+"/ws/update.cgi");
|
||||
ws.onopen = onOpen;
|
||||
ws.onclose = onClose;
|
||||
ws.onmessage = onMessage;
|
||||
|
||||
console.log("Opening socket.");
|
||||
}
|
||||
|
||||
return {
|
||||
ws: null,
|
||||
init: init,
|
||||
send: doSend
|
||||
};
|
||||
})();
|
||||
|
||||
/** User input */
|
||||
var Input = (function() {
|
||||
function sendStrMsg(str) {
|
||||
Conn.send("STR:"+str);
|
||||
}
|
||||
|
||||
function sendPosMsg(y, x) {
|
||||
Conn.send("TAP:"+y+','+x);
|
||||
}
|
||||
|
||||
function sendBtnMsg(n) {
|
||||
Conn.send("BTN:"+n);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
qsa('#buttons button').forEach(function(s) {
|
||||
s.addEventListener('click', function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onTap: sendPosMsg
|
||||
};
|
||||
})();
|
||||
|
||||
window.termInit = function (str) {
|
||||
Screen.load(str);
|
||||
Conn.init();
|
||||
Input.init();
|
||||
};
|
||||
|
||||
+311
-280
@@ -1,303 +1,334 @@
|
||||
(function() {
|
||||
/**
|
||||
* Terminal module
|
||||
*/
|
||||
var Term = (function () {
|
||||
var W, H;
|
||||
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false};
|
||||
var screen = [];
|
||||
var blinkIval;
|
||||
var Screen = (function () {
|
||||
var W, H; // dimensions
|
||||
var inited = false;
|
||||
|
||||
/*
|
||||
/!** Clear screen *!/
|
||||
function cls() {
|
||||
screen.forEach(function(cell, i) {
|
||||
cell.t = ' ';
|
||||
cell.fg = 7;
|
||||
cell.bg = 0;
|
||||
blit(cell);
|
||||
});
|
||||
var cursor = {
|
||||
a: false, // active (blink state)
|
||||
x: 0, // 0-based coordinates
|
||||
y: 0,
|
||||
fg: 7, // colors 0-15
|
||||
bg: 0,
|
||||
bold: false,
|
||||
suppress: false, // do not turn on in blink interval (for safe moving)
|
||||
hidden: false // do not show
|
||||
};
|
||||
|
||||
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];
|
||||
/** Set text and color at XY */
|
||||
function _cellAt(y, x) {
|
||||
return screen[y*W+x];
|
||||
}
|
||||
|
||||
/** 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
|
||||
cursor.suppress = true;
|
||||
_draw(_curCell(), false);
|
||||
cursor.x = x;
|
||||
cursor.y = y;
|
||||
// Show again
|
||||
cursor.suppress = false;
|
||||
_draw(_curCell());
|
||||
}
|
||||
|
||||
/** Update cell on display. inv = invert (for cursor) */
|
||||
function _draw(cell, inv) {
|
||||
if (typeof inv == 'undefined') {
|
||||
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
|
||||
}
|
||||
|
||||
/** Get cell under cursor */
|
||||
function cursorCell() {
|
||||
return cellAt(cursor.y, cursor.x);
|
||||
var e = cell.e, fg, bg;
|
||||
// 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' : '');
|
||||
}
|
||||
|
||||
/** Show entire screen */
|
||||
function _drawAll() {
|
||||
for (var i = W*H-1; i>=0; i--) {
|
||||
_draw(screen[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/!** Enable or disable cursor visibility *!/
|
||||
function cursorEnable(enable) {
|
||||
cursor.hidden = !enable;
|
||||
cursor.a &= enable;
|
||||
blit(cursorCell(), cursor.a);
|
||||
function _rebuild(rows, cols) {
|
||||
W = cols;
|
||||
H = rows;
|
||||
|
||||
/* Build screen & show */
|
||||
var e, cell, scr = qs('#screen');
|
||||
|
||||
// Empty the screen node
|
||||
while (scr.firstChild) scr.removeChild(scr.firstChild);
|
||||
|
||||
screen = [];
|
||||
|
||||
for(var i = 0; i < W*H; i++) {
|
||||
e = mk('span');
|
||||
|
||||
(function() {
|
||||
var x = i % W;
|
||||
var y = Math.floor(i / W);
|
||||
e.addEventListener('click', function () {
|
||||
Input.onTap(y, x);
|
||||
});
|
||||
})();
|
||||
|
||||
/* End of line */
|
||||
if ((i > 0) && (i % W == 0)) {
|
||||
scr.appendChild(mk('br'));
|
||||
}
|
||||
/* The cell */
|
||||
scr.appendChild(e);
|
||||
|
||||
cell = {
|
||||
t: ' ',
|
||||
fg: cursor.fg,
|
||||
bg: cursor.bg,
|
||||
e: e,
|
||||
x: i % W,
|
||||
y: Math.floor(i / W),
|
||||
};
|
||||
screen.push(cell);
|
||||
_draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
/!** Safely move cursor *!/
|
||||
function cursorSet(y, x) {
|
||||
// Hide and prevent from showing up during the move
|
||||
cursor.suppress = true;
|
||||
blit(cursorCell(), false);
|
||||
|
||||
cursor.x = x;
|
||||
cursor.y = y;
|
||||
|
||||
// Show again
|
||||
cursor.suppress = false;
|
||||
blit(cursorCell(), cursor.a);
|
||||
}
|
||||
*/
|
||||
|
||||
/** Update cell on display. inv = invert (for cursor) */
|
||||
function blit(cell, inv) {
|
||||
var e = cell.e, fg, bg;
|
||||
// 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;
|
||||
}
|
||||
|
||||
/** Show entire screen */
|
||||
function blitAll() {
|
||||
screen.forEach(function(cell, i) {
|
||||
/* Invert if under cursor & cursor active */
|
||||
var inv = cursor.a && (i == cursor.y*W+cursor.x);
|
||||
blit(cell, inv);
|
||||
});
|
||||
}
|
||||
|
||||
/** Load screen content from a 'binary' sequence */
|
||||
function load(obj) {
|
||||
cursor.x = obj.x;
|
||||
cursor.y = obj.y;
|
||||
cursor.hidden = !obj.cv;
|
||||
|
||||
// full re-init if size changed
|
||||
if (obj.w != W || obj.h != H) {
|
||||
Term.init(obj);
|
||||
return;
|
||||
/** Init the terminal */
|
||||
function _init() {
|
||||
/* Cursor blinking */
|
||||
clearInterval(blinkIval);
|
||||
blinkIval = setInterval(function () {
|
||||
cursor.a = !cursor.a;
|
||||
if (cursor.hidden) {
|
||||
cursor.a = false;
|
||||
}
|
||||
|
||||
// Simple compression - hexFG hexBG 'ASCII' (r/s/t/u NUM{1,2,3,4})?
|
||||
// comma instead of both colors = same as before
|
||||
if (!cursor.suppress) {
|
||||
_draw(_curCell(), cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
inited = true;
|
||||
}
|
||||
|
||||
var i = 0, ci = 0, str = obj.screen;
|
||||
var fg = 7, bg = 0;
|
||||
while(i < str.length && ci<W*H) {
|
||||
var cell = screen[ci++];
|
||||
/** Decode two-byte number */
|
||||
function parse2B(s, i) {
|
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
|
||||
}
|
||||
|
||||
var j = str[i];
|
||||
if (j != ',') { // comma = repeat last colors
|
||||
fg = cell.fg = parseInt(str[i++], 16);
|
||||
bg = cell.bg = parseInt(str[i++], 16);
|
||||
} else {
|
||||
i++;
|
||||
var SEQ_SET_COLOR = 1;
|
||||
var SEQ_REPEAT = 2;
|
||||
|
||||
/** Load screen content from a binary sequence (new) */
|
||||
function load(str) {
|
||||
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, bold, cell;
|
||||
|
||||
if (!inited) _init();
|
||||
|
||||
// Set size
|
||||
num = parse2B(str, i); i += 2;
|
||||
num2 = parse2B(str, i); i += 2;
|
||||
if (num != H || num2 != W) {
|
||||
_rebuild(num, num2);
|
||||
}
|
||||
console.log("Size ",num, num2);
|
||||
|
||||
// Cursor position
|
||||
num = parse2B(str, i); i += 2;
|
||||
num2 = parse2B(str, i); i += 2;
|
||||
cursorSet(num, num2);
|
||||
console.log("Cursor at ",num, num2);
|
||||
|
||||
// Attributes
|
||||
num = parse2B(str, i); i += 2;
|
||||
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);
|
||||
|
||||
fg = cursor.fg;
|
||||
bg = cursor.bg;
|
||||
bold = cursor.bold;
|
||||
|
||||
// Here come the content
|
||||
while(i < str.length && ci<W*H) {
|
||||
|
||||
j = str[i++];
|
||||
jc = j.charCodeAt(0);
|
||||
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_REPEAT) {
|
||||
num = parse2B(str, i); i += 2;
|
||||
console.log("Repeat x ",num);
|
||||
for (; num>0 && ci<W*H; num--) {
|
||||
cell = screen[ci++];
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
}
|
||||
|
||||
var t = cell.t = str[i++];
|
||||
|
||||
var repchars = 0;
|
||||
switch(str[i]) {
|
||||
case 'r': repchars = 1; break;
|
||||
case 's': repchars = 2; break;
|
||||
case 't': repchars = 3; break;
|
||||
case 'u': repchars = 4; break;
|
||||
default: repchars = 0;
|
||||
}
|
||||
|
||||
if (repchars > 0) {
|
||||
var rep = parseInt(str.substr(i+1,repchars));
|
||||
i = i + repchars + 1;
|
||||
for (; rep>0 && ci<W*H; rep--) {
|
||||
cell = screen[ci++];
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.t = t;
|
||||
}
|
||||
cell.t = t;
|
||||
cell.bold = bold;
|
||||
}
|
||||
}
|
||||
|
||||
blitAll();
|
||||
}
|
||||
|
||||
/** Init the terminal */
|
||||
function init(obj) {
|
||||
W = obj.w;
|
||||
H = obj.h;
|
||||
|
||||
/* Build screen & show */
|
||||
var e, cell, scr = qs('#screen');
|
||||
|
||||
// Empty the screen node
|
||||
while (scr.firstChild) scr.removeChild(scr.firstChild);
|
||||
|
||||
screen = [];
|
||||
|
||||
for(var i = 0; i < W*H; i++) {
|
||||
e = mk('span');
|
||||
|
||||
(function() {
|
||||
var x = i % W;
|
||||
var y = Math.floor(i / W);
|
||||
e.addEventListener('click', function () {
|
||||
Input.onTap(y, x);
|
||||
});
|
||||
})();
|
||||
|
||||
/* End of line */
|
||||
if ((i > 0) && (i % W == 0)) {
|
||||
scr.appendChild(mk('br'));
|
||||
}
|
||||
/* The cell */
|
||||
scr.appendChild(e);
|
||||
|
||||
cell = {t: ' ', fg: 7, bg: 0, e: e};
|
||||
screen.push(cell);
|
||||
blit(cell);
|
||||
}
|
||||
|
||||
/* Cursor blinking */
|
||||
clearInterval(blinkIval);
|
||||
blinkIval = setInterval(function() {
|
||||
cursor.a = !cursor.a;
|
||||
if (cursor.hidden) {
|
||||
cursor.a = false;
|
||||
}
|
||||
|
||||
if (!cursor.suppress) {
|
||||
blit(cursorCell(), cursor.a);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
load(obj);
|
||||
}
|
||||
|
||||
// publish
|
||||
return {
|
||||
init: init,
|
||||
load: load
|
||||
};
|
||||
})();
|
||||
|
||||
/** Handle connections */
|
||||
var Conn = (function() {
|
||||
var ws;
|
||||
|
||||
function onOpen(evt) {
|
||||
console.log("CONNECTED");
|
||||
}
|
||||
|
||||
function onClose(evt) {
|
||||
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
|
||||
setTimeout(function() {
|
||||
init();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Term.load(JSON.parse(evt.data));
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
else {
|
||||
cell = screen[ci++];
|
||||
// Unique cell character
|
||||
t = cell.t = j;
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
cell.bold = bold;
|
||||
console.log("Symbol ", j);
|
||||
}
|
||||
}
|
||||
|
||||
function doSend(message) {
|
||||
console.log("TX: ", message);
|
||||
if (ws.readyState != 1) {
|
||||
console.error("Socket not ready");
|
||||
return;
|
||||
}
|
||||
if (typeof message != "string") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
ws.send(message);
|
||||
}
|
||||
_drawAll();
|
||||
}
|
||||
|
||||
function init() {
|
||||
ws = new WebSocket("ws://"+_root+"/ws/update.cgi");
|
||||
ws.onopen = onOpen;
|
||||
ws.onclose = onClose;
|
||||
ws.onmessage = onMessage;
|
||||
|
||||
console.log("Opening socket.");
|
||||
}
|
||||
|
||||
return {
|
||||
ws: null,
|
||||
init: init,
|
||||
send: doSend
|
||||
};
|
||||
})();
|
||||
|
||||
//
|
||||
// Keyboard (& mouse) input
|
||||
//
|
||||
var Input = (function() {
|
||||
function sendStrMsg(str) {
|
||||
Conn.send("STR:"+str);
|
||||
}
|
||||
|
||||
function sendPosMsg(y, x) {
|
||||
Conn.send("TAP:"+y+','+x);
|
||||
}
|
||||
|
||||
function sendBtnMsg(n) {
|
||||
Conn.send("BTN:"+n);
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
});
|
||||
|
||||
qsa('#buttons button').forEach(function(s) {
|
||||
s.addEventListener('click', function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onTap: sendPosMsg
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
window.termInit = function (obj) {
|
||||
Term.init(obj);
|
||||
Conn.init();
|
||||
Input.init();
|
||||
return {
|
||||
load: load, // full load (string)
|
||||
};
|
||||
})();
|
||||
|
||||
/** Handle connections */
|
||||
var Conn = (function() {
|
||||
var ws;
|
||||
|
||||
function onOpen(evt) {
|
||||
console.log("CONNECTED");
|
||||
}
|
||||
|
||||
function onClose(evt) {
|
||||
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
|
||||
setTimeout(function() {
|
||||
init();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
try {
|
||||
console.log("RX: ", evt.data);
|
||||
// Assume all our messages are screen updates
|
||||
Screen.load(evt.data);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function doSend(message) {
|
||||
console.log("TX: ", message);
|
||||
if (!ws) return; // for dry testing
|
||||
if (ws.readyState != 1) {
|
||||
console.error("Socket not ready");
|
||||
return;
|
||||
}
|
||||
if (typeof message != "string") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
ws.send(message);
|
||||
}
|
||||
|
||||
function init() {
|
||||
ws = new WebSocket("ws://"+_root+"/ws/update.cgi");
|
||||
ws.onopen = onOpen;
|
||||
ws.onclose = onClose;
|
||||
ws.onmessage = onMessage;
|
||||
|
||||
console.log("Opening socket.");
|
||||
}
|
||||
|
||||
return {
|
||||
ws: null,
|
||||
init: init,
|
||||
send: doSend
|
||||
};
|
||||
})();
|
||||
|
||||
/** User input */
|
||||
var Input = (function() {
|
||||
function sendStrMsg(str) {
|
||||
Conn.send("STR:"+str);
|
||||
}
|
||||
|
||||
function sendPosMsg(y, x) {
|
||||
Conn.send("TAP:"+y+','+x);
|
||||
}
|
||||
|
||||
function sendBtnMsg(n) {
|
||||
Conn.send("BTN:"+n);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
qsa('#buttons button').forEach(function(s) {
|
||||
s.addEventListener('click', function() {
|
||||
sendBtnMsg(+this.dataset['n']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
onTap: sendPosMsg
|
||||
};
|
||||
})();
|
||||
|
||||
window.termInit = function (str) {
|
||||
Screen.load(str);
|
||||
Conn.init();
|
||||
Input.init();
|
||||
};
|
||||
|
||||
+11
-16
@@ -27,27 +27,22 @@ return [
|
||||
Those are the initial settings used after ESPTerm powers on. They
|
||||
will also be applied immediately after you submit this form.',
|
||||
|
||||
'term.example' => 'Color settings example',
|
||||
'term.example' => 'Default colors preview',
|
||||
|
||||
'term.term_title' => 'Header text',
|
||||
'term.term_width' => 'Screen width',
|
||||
'term.term_height' => 'Screen height',
|
||||
'term.default_fg' => 'Text color',
|
||||
'term.default_bg' => 'Background',
|
||||
'term.btn1' => 'Button 1 text',
|
||||
'term.btn2' => 'Button 2 text',
|
||||
'term.btn3' => 'Button 3 text',
|
||||
'term.btn4' => 'Button 4 text',
|
||||
'term.btn5' => 'Button 5 text',
|
||||
'term.term_width' => 'Screen width / height',
|
||||
'term.default_fg_bg' => 'Text / background',
|
||||
'term.buttons' => 'Button labels',
|
||||
'term.theme' => 'Color scheme',
|
||||
|
||||
// terminal color labels
|
||||
'color.0' => 'Black',
|
||||
'color.1' => 'Dark Red',
|
||||
'color.2' => 'Dark Green',
|
||||
'color.3' => 'Dim Yellow',
|
||||
'color.4' => 'Deep Blue',
|
||||
'color.5' => 'Deep Purple',
|
||||
'color.6' => 'Dark Cyan',
|
||||
'color.1' => 'Red',
|
||||
'color.2' => 'Green',
|
||||
'color.3' => 'Yellow',
|
||||
'color.4' => 'Blue',
|
||||
'color.5' => 'Purple',
|
||||
'color.6' => 'Cyan',
|
||||
'color.7' => 'Silver',
|
||||
'color.8' => 'Gray',
|
||||
'color.9' => 'Light Red',
|
||||
|
||||
@@ -6,21 +6,78 @@
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<div style="font-family:monospace; font-size: 16pt; padding: 5px;" id="color-example"><?= tr("term.example") ?></div>
|
||||
<label for="theme"><?= tr("term.theme") ?></label>
|
||||
<select name="theme" id="theme" class="short" onchange="showColor()">
|
||||
<option value="0">Tango</option>
|
||||
<option value="1">Linux</option>
|
||||
<option value="2">XTerm</option>
|
||||
<option value="3">Rxvt</option>
|
||||
<option value="4">Ambience</option>
|
||||
<option value="5">Solarized</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="Row color-preview">
|
||||
<div class="colorprev">
|
||||
<span data-fg=0 class="bg0 fg0">30</span><!--
|
||||
--><span data-fg=1 class="bg0 fg1">31</span><!--
|
||||
--><span data-fg=2 class="bg0 fg2">32</span><!--
|
||||
--><span data-fg=3 class="bg0 fg3">33</span><!--
|
||||
--><span data-fg=4 class="bg0 fg4">34</span><!--
|
||||
--><span data-fg=5 class="bg0 fg5">35</span><!--
|
||||
--><span data-fg=6 class="bg0 fg6">36</span><!--
|
||||
--><span data-fg=7 class="bg0 fg7">37</span>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span data-fg=8 class="bg0 fg8">90</span><!--
|
||||
--><span data-fg=9 class="bg0 fg9">91</span><!--
|
||||
--><span data-fg=10 class="bg0 fg10">92</span><!--
|
||||
--><span data-fg=11 class="bg0 fg11">93</span><!--
|
||||
--><span data-fg=12 class="bg0 fg12">94</span><!--
|
||||
--><span data-fg=13 class="bg0 fg13">95</span><!--
|
||||
--><span data-fg=14 class="bg0 fg14">96</span><!--
|
||||
--><span data-fg=15 class="bg0 fg15">97</span>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span data-bg=0 class="bg0 fg15">40</span><!--
|
||||
--><span data-bg=1 class="bg1 fg15">41</span><!--
|
||||
--><span data-bg=2 class="bg2 fg15">42</span><!--
|
||||
--><span data-bg=3 class="bg3 fg0">43</span><!--
|
||||
--><span data-bg=4 class="bg4 fg15">44</span><!--
|
||||
--><span data-bg=5 class="bg5 fg15">45</span><!--
|
||||
--><span data-bg=6 class="bg6 fg15">46</span><!--
|
||||
--><span data-bg=7 class="bg7 fg0">47</span>
|
||||
</div>
|
||||
|
||||
<div class="colorprev">
|
||||
<span data-bg=8 class="bg8 fg15">100</span><!--
|
||||
--><span data-bg=9 class="bg9 fg0">101</span><!--
|
||||
--><span data-bg=10 class="bg10 fg0">102</span><!--
|
||||
--><span data-bg=11 class="bg11 fg0">103</span><!--
|
||||
--><span data-bg=12 class="bg12 fg0">104</span><!--
|
||||
--><span data-bg=13 class="bg13 fg0">105</span><!--
|
||||
--><span data-bg=14 class="bg14 fg0">106</span><!--
|
||||
--><span data-bg=15 class="bg15 fg0">107</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Row color-preview">
|
||||
<div style="
|
||||
" id="color-example">
|
||||
<?= tr("term.example") ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="default_fg"><?= tr("term.default_fg") ?></label>
|
||||
<label><?= tr("term.default_fg_bg") ?></label>
|
||||
<select name="default_fg" id="default_fg" class="short" onchange="showColor()">
|
||||
<?php for($i=0; $i<16; $i++): ?>
|
||||
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="default_bg"><?= tr("term.default_bg") ?></label>
|
||||
<select name="default_bg" id="default_bg" class="short" onchange="showColor()">
|
||||
</select> <!--
|
||||
--><select name="default_bg" id="default_bg" class="short" onchange="showColor()">
|
||||
<?php for($i=0; $i<16; $i++): ?>
|
||||
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
|
||||
<?php endfor; ?>
|
||||
@@ -29,12 +86,8 @@
|
||||
|
||||
<div class="Row">
|
||||
<label for="term_width"><?= tr('term.term_width') ?></label>
|
||||
<input type="number" step=1 min=1 max=255 name="term_width" id="term_width" value="%term_width%" required>
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="term_height"><?= tr('term.term_height') ?></label>
|
||||
<input type="number" step=1 min=1 max=255 name="term_height" id="term_height" value="%term_height%" required>
|
||||
<input type="number" step=1 min=1 max=255 name="term_width" id="term_width" value="%term_width%" required> <!--
|
||||
--><input type="number" step=1 min=1 max=255 name="term_height" id="term_height" value="%term_height%" required>
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
@@ -43,27 +96,11 @@
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="btn1"><?= tr("term.btn1") ?></label>
|
||||
<input class="short" type="text" name="btn1" id="btn1" value="%btn1%">
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="btn2"><?= tr("term.btn2") ?></label>
|
||||
<input class="short" type="text" name="btn2" id="btn2" value="%btn2%">
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="btn3"><?= tr("term.btn3") ?></label>
|
||||
<input class="short" type="text" name="btn3" id="btn3" value="%btn3%">
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="btn4"><?= tr("term.btn4") ?></label>
|
||||
<input class="short" type="text" name="btn4" id="btn4" value="%btn4%">
|
||||
</div>
|
||||
|
||||
<div class="Row">
|
||||
<label for="btn5"><?= tr("term.btn5") ?></label>
|
||||
<label><?= tr("term.buttons") ?></label>
|
||||
<input class="short" type="text" name="btn1" id="btn1" value="%btn1%">
|
||||
<input class="short" type="text" name="btn2" id="btn2" value="%btn2%">
|
||||
<input class="short" type="text" name="btn3" id="btn3" value="%btn3%">
|
||||
<input class="short" type="text" name="btn4" id="btn4" value="%btn4%">
|
||||
<input class="short" type="text" name="btn5" id="btn5" value="%btn5%">
|
||||
</div>
|
||||
|
||||
@@ -75,12 +112,25 @@
|
||||
<script>
|
||||
$('#default_fg').val(%default_fg%);
|
||||
$('#default_bg').val(%default_bg%);
|
||||
$('#theme').val(%theme%);
|
||||
|
||||
function showColor() {
|
||||
var ex = qs('#color-example');
|
||||
ex.className = '';
|
||||
ex.classList.add('fg'+$('#default_fg').val());
|
||||
ex.classList.add('bg'+$('#default_bg').val());
|
||||
var th = $('#theme').val();
|
||||
$('.color-preview').forEach(function(e) {
|
||||
e.className = 'Row color-preview theme-'+th;
|
||||
});
|
||||
}
|
||||
showColor();
|
||||
|
||||
$('.colorprev span').on('click', function() {
|
||||
var fg = this.dataset.fg;
|
||||
var bg = this.dataset.bg;
|
||||
if (typeof fg != 'undefined') $('#default_fg').val(fg);
|
||||
if (typeof bg != 'undefined') $('#default_bg').val(bg);
|
||||
showColor()
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
<tr>
|
||||
<td>\e[<n>E</td>
|
||||
<td>[count]</td>
|
||||
<td>Go N line down, start of line</td>
|
||||
<td>Go N lines down, start of line</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>\e[<n>F</td>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Workaround for badly loaded page
|
||||
setTimeout(function() {
|
||||
if (typeof termInit == 'undefined' || typeof $ == 'undefined') {
|
||||
console.error("Page load failed, refreshing…");
|
||||
location.reload(true);
|
||||
}
|
||||
}, 2000);
|
||||
@@ -10,7 +11,7 @@
|
||||
<h1>%term_title%</h1>
|
||||
|
||||
<div id="termwrap">
|
||||
<div id="screen"></div>
|
||||
<div id="screen" class="theme-%theme%"></div>
|
||||
|
||||
<div id="buttons">
|
||||
<button data-n="1" class="btn-blue">%btn1%</button><!--
|
||||
@@ -21,17 +22,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input id="softkb-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
|
||||
|
||||
<nav id="botnav">
|
||||
<a href="<?= url('cfg_wifi') ?>"><?= tr('menu.settings') ?></a><!--
|
||||
--><a href="<?= url('help') ?>">Help</a><!--
|
||||
--><a href="<?= url('about') ?>">About</a>
|
||||
<a href="#" onclick="toggleSoftKb(true); return false" class="icn-keyboard mq-tablet-max"></a><!--
|
||||
--><a href="<?= url('cfg_wifi') ?>"><?= tr('menu.settings') ?></a><!--
|
||||
--><a href="<?= url('help') ?>">Help</a><!--
|
||||
--><a href="<?= url('about') ?>">About</a>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
// TODO cleanup
|
||||
try {
|
||||
termInit(%screenData%);
|
||||
termInit("%screenData%");
|
||||
|
||||
// auto-clear the input box
|
||||
$('#softkb-input').on('input', function(e) {
|
||||
setTimeout(function(){$('#softkb-input').val('');}, 1);
|
||||
});
|
||||
} catch(e) {
|
||||
console.error("Fail, reloading...");
|
||||
console.error(e);
|
||||
console.error("Fail, reloading…");
|
||||
location.reload(true);
|
||||
}
|
||||
|
||||
function toggleSoftKb(yes) {
|
||||
qs('#softkb-input')[yes ? 'focus' : 'blur']();
|
||||
qs('.icn-keyboard').blur();
|
||||
}
|
||||
</script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,24 +0,0 @@
|
||||
$term-colors:
|
||||
// 0 black, 1 red, 2 green, 3 yellow
|
||||
// 4 blue, 5 mag, 6 cyan, 7 white
|
||||
#111213, #CC0000, #4E9A06, #C4A000,
|
||||
#3465A4, #75507B, #06989A, #D3D7CF,
|
||||
// BRIGHT
|
||||
// 8 black, 9 red, 10 green, 11 yellow
|
||||
// 12 blue, 13 mag, 14 cyan, 15 white
|
||||
#555753, #EF2929, #8AE234, #FCE94F,
|
||||
#729FCF, #AD7FA8, #34E2E2, #EEEEEC;
|
||||
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
|
||||
.fg8, .fg9, .fg10, .fg11, .fg12, .fg13, .fg14, .fg15 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nb {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
@@ -33,7 +33,6 @@ $c-form-highlight-a: #2ea1f9;
|
||||
@import "pages/wifi";
|
||||
@import "pages/term";
|
||||
@import "pages/about";
|
||||
@import "term-colors";
|
||||
|
||||
// media queries
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
body.term {
|
||||
h1 {
|
||||
font-size: fsize(5);
|
||||
@include media($phone) {
|
||||
font-size: fsize(3);
|
||||
#content {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
||||
h1 {
|
||||
font-size: fsize(5);
|
||||
@include media($phone) {
|
||||
font-size: fsize(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +41,7 @@ body.term {
|
||||
margin: 0 3px;
|
||||
padding: 8px 5px;
|
||||
//width: 18%;
|
||||
min-width: 65px;
|
||||
min-width: 62px;
|
||||
//max-width: 65px;
|
||||
//min-width: initial;
|
||||
cursor: pointer;
|
||||
@@ -53,16 +58,133 @@ body.term {
|
||||
text-decoration: underline;
|
||||
|
||||
&, &:visited, &:link {
|
||||
color: #2e4d6e;
|
||||
color: #336085;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #5abfff;
|
||||
}
|
||||
}
|
||||
|
||||
.icn-keyboard {
|
||||
text-decoration: none;
|
||||
font-size: 150%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#termwrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#softkb-input {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
}
|
||||
|
||||
// "non-bold"
|
||||
.nb {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
// Tango
|
||||
.theme-0 {
|
||||
$term-colors:
|
||||
#111213, #CC0000, #4E9A06, #C4A000, #3465A4, #75507B, #06989A, #D3D7CF,
|
||||
#555753, #EF2929, #8AE234, #FCE94F, #729FCF, #AD7FA8, #34E2E2, #EEEEEC;
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
}
|
||||
|
||||
// Linux
|
||||
.theme-1 {
|
||||
$term-colors:
|
||||
#000000, #aa0000, #00aa00, #aa5500, #0000aa, #aa00aa, #00aaaa, #aaaaaa,
|
||||
#555555, #ff5555, #55ff55, #ffff55, #5555ff, #ff55ff, #55ffff, #ffffff;
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
}
|
||||
|
||||
// xterm
|
||||
.theme-2 {
|
||||
$term-colors:
|
||||
#000000, #cd0000, #00cd00, #cdcd00, #0000ee, #cd00cd, #00cdcd, #e5e5e5,
|
||||
#7f7f7f, #ff0000, #00ff00, #ffff00, #5c5cff, #ff00ff, #00ffff, #ffffff;
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
}
|
||||
|
||||
// rxvt
|
||||
.theme-3 {
|
||||
$term-colors:
|
||||
#000000, #cd0000, #00cd00, #cdcd00, #0000cd, #cd00cd, #00cdcd, #faebd7,
|
||||
#404040, #ff0000, #00ff00, #ffff00, #0000ff, #ff00ff, #00ffff, #ffffff;
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
}
|
||||
|
||||
// Ambience
|
||||
.theme-4 {
|
||||
$term-colors:
|
||||
#2e3436, #cc0000, #4e9a06, #c4a000, #3465a4, #75507b, #06989a, #d3d7cf,
|
||||
#555753, #ef2929, #8ae234, #fce94f, #729fcf, #ad7fa8, #34e2e2, #eeeeec;
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
}
|
||||
|
||||
// Solarized
|
||||
.theme-5 {
|
||||
$term-colors:
|
||||
#073642, #dc322f, #859900, #b58900, #268bd2, #d33682, #2aa198, #eee8d5,
|
||||
#002b36, #cb4b16, #586e75, #657b83, #839496, #6c71c4, #93a1a1, #fdf6e3;
|
||||
@for $i from 1 through length($term-colors) {
|
||||
$c: nth($term-colors, $i);
|
||||
.fg#{$i - 1} { color: $c; }
|
||||
.bg#{$i - 1} { background-color: $c; }
|
||||
}
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.Row.color-preview {
|
||||
font-family: monospace;
|
||||
font-size: 16pt;
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
|
||||
padding-left: $form-label-w;
|
||||
|
||||
@include media($phone) {
|
||||
padding-left: 0;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.colorprev {
|
||||
display:block;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
#color-example {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
+10
-6
@@ -44,12 +44,16 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
|
||||
else if (streq(token, "btn5")) {
|
||||
httpdSend(connData, termconf->btn5, -1);
|
||||
}
|
||||
else if (streq(token, "default_bg")) {
|
||||
sprintf(buff, "%d", termconf->default_bg);
|
||||
httpdSend(connData, buff, -1);
|
||||
}
|
||||
else if (streq(token, "default_fg")) {
|
||||
sprintf(buff, "%d", termconf->default_fg);
|
||||
// else if (streq(token, "default_bg")) {
|
||||
// sprintf(buff, "%d", termconf->default_bg);
|
||||
// httpdSend(connData, buff, -1);
|
||||
// }
|
||||
// else if (streq(token, "default_fg")) {
|
||||
// sprintf(buff, "%d", termconf->default_fg);
|
||||
// httpdSend(connData, buff, -1);
|
||||
// }
|
||||
else if (streq(token, "theme")) {
|
||||
sprintf(buff, "%d", termconf->theme);
|
||||
httpdSend(connData, buff, -1);
|
||||
}
|
||||
else if (streq(token, "screenData")) {
|
||||
|
||||
@@ -82,6 +82,17 @@ cgiTermCfgSetParams(HttpdConnData *connData)
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("theme")) {
|
||||
dbg("Screen color theme: %s", buff);
|
||||
int theme = atoi(buff);
|
||||
if (theme >= 0 && theme <= 5) { // ALWAYS ADJUST WHEN ADDING NEW THEME!
|
||||
termconf->theme = (u8) theme;
|
||||
} else {
|
||||
warn("Bad theme num: %s", buff);
|
||||
redir_url += sprintf(redir_url, "theme,");
|
||||
}
|
||||
}
|
||||
|
||||
if (GET_ARG("term_title")) {
|
||||
dbg("Terminal title default text: \"%s\"", buff);
|
||||
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
|
||||
@@ -148,6 +159,9 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
|
||||
else if (streq(token, "term_height")) {
|
||||
sprintf(buff, "%d", termconf->height);
|
||||
}
|
||||
else if (streq(token, "theme")) {
|
||||
sprintf(buff, "%d", termconf->theme);
|
||||
}
|
||||
else if (streq(token, "default_bg")) {
|
||||
sprintf(buff, "%d", termconf->default_bg);
|
||||
}
|
||||
|
||||
+8
-3
@@ -105,7 +105,15 @@ persist_load(void)
|
||||
}
|
||||
|
||||
if (hard_reset) {
|
||||
// Zero all out
|
||||
memset(&persist, 0, sizeof(PersistBlock));
|
||||
|
||||
persist_load_hard_default();
|
||||
|
||||
// write them also as defaults
|
||||
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
|
||||
persist_store();
|
||||
|
||||
// this also stores them to flash and applies to modules
|
||||
} else {
|
||||
apply_live_settings();
|
||||
@@ -141,9 +149,6 @@ persist_load_hard_default(void)
|
||||
restore_live_settings_to_hard_defaults();
|
||||
persist_store();
|
||||
|
||||
// // Store current -> default
|
||||
// memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
|
||||
|
||||
info("[Persist] Settings restored to hard defaults.");
|
||||
|
||||
apply_live_settings(); // apply
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
// Changing this could be used to force-erase the config area
|
||||
// after a firmware upgrade
|
||||
#define CHECKSUM_SALT 1
|
||||
#define CHECKSUM_SALT 2
|
||||
|
||||
#define APPCONF_SIZE 2048
|
||||
|
||||
|
||||
+19
-1
@@ -671,7 +671,25 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
|
||||
ss->lastFg = 0;
|
||||
ss->lastChar = '\0';
|
||||
|
||||
bufprint("{\n \"w\": %d, \"h\": %d,\n \"x\": %d, \"y\": %d,\n \"cv\": %d,\n \"screen\": \"", W, H, cursor.x, cursor.y, cursor.visible);
|
||||
// TODO implement the new more efficient encoder!
|
||||
|
||||
bufprint(
|
||||
"{"
|
||||
"\"w\":%d,"
|
||||
"\"h\":%d,"
|
||||
"\"x\":%d,"
|
||||
"\"y\":%d,"
|
||||
"\"fg\":%d,"
|
||||
"\"bg\":%d,"
|
||||
"\"cv\":%d,"
|
||||
"\"screen\":\"",
|
||||
W,
|
||||
H,
|
||||
cursor.x,
|
||||
cursor.y,
|
||||
cursor.fg,
|
||||
cursor.bg,
|
||||
cursor.visible);
|
||||
}
|
||||
|
||||
int i = ss->index;
|
||||
|
||||
@@ -52,6 +52,7 @@ typedef struct {
|
||||
char btn3[TERM_BTN_LEN];
|
||||
char btn4[TERM_BTN_LEN];
|
||||
char btn5[TERM_BTN_LEN];
|
||||
u8 theme;
|
||||
|
||||
u8 _filler[
|
||||
TERMCONF_SIZE
|
||||
@@ -59,6 +60,7 @@ typedef struct {
|
||||
- 4
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- TERM_TITLE_LEN
|
||||
- TERM_BTN_LEN * 5
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user