Add VT100 termional app
parent
daf7e745ec
commit
812a4cfaf4
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEw4cA///w8h7fOm2V/8fjkf/Gt58jKf4AtiVJkmSARFICK3PlnyCJ1t03aEcBZjCBVJCP4R/CK9N8gRPpmUEaGSuRHP+1KCJ8m5AjgI4PyNZ+cAQIQCwgjKCgQRNdP4R/CL4PJAQIRWAH4ApA=="))
|
||||||
|
|
@ -0,0 +1,263 @@
|
||||||
|
/* Espruino VT100 JS REPL
|
||||||
|
|
||||||
|
|
||||||
|
TODO: Add option to connect to a remote BLE device
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
var settings = Object.assign({
|
||||||
|
// default values
|
||||||
|
textSize: 1,
|
||||||
|
loopAround: 1,
|
||||||
|
oneToOne: 0,
|
||||||
|
speedScaling: 24
|
||||||
|
}, /*require('Storage').readJSON("repl.settings.json", true) || */{});
|
||||||
|
|
||||||
|
// Key Maps for Keyboard
|
||||||
|
var KEYMAPLOWER = [
|
||||||
|
"`1234567890-=\b\b",
|
||||||
|
"\tqwertyuiop[]\n\n",
|
||||||
|
"\2asdfghjkl;'#\x82\n",
|
||||||
|
"\2\\zxcvbnm,./\x80\x83\x81",
|
||||||
|
];
|
||||||
|
var KEYMAPUPPER = [
|
||||||
|
"¬!\"£$%^&*()_+\b\b",
|
||||||
|
"\tQWERTYUIOP{}\n\n",
|
||||||
|
"\2ASDFGHJKL:@~\x82\n",
|
||||||
|
"\2|ZXCVBNM<>?\x80\x83\x81",
|
||||||
|
];
|
||||||
|
var KEYIMGL = Graphics.createImage(`
|
||||||
|
|
||||||
|
# #
|
||||||
|
## #
|
||||||
|
######
|
||||||
|
## #
|
||||||
|
# #
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
###
|
||||||
|
#####
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
`);
|
||||||
|
KEYIMGL.transparent = 0;
|
||||||
|
var KEYIMGR = Graphics.createImage(`
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
##
|
||||||
|
#########
|
||||||
|
##
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#######
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####
|
||||||
|
###
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
###
|
||||||
|
#####
|
||||||
|
|
||||||
|
|
||||||
|
# #
|
||||||
|
## ##
|
||||||
|
### ##### ###
|
||||||
|
## ### ##
|
||||||
|
# # #
|
||||||
|
|
||||||
|
`);
|
||||||
|
KEYIMGR.transparent = 0;
|
||||||
|
/* If a char in the keymap is >=128,
|
||||||
|
subtract 128 and look in this array for
|
||||||
|
multi-character key codes*/
|
||||||
|
var KEYEXTRA = [
|
||||||
|
String.fromCharCode(27, 91, 68), // 0x80 left
|
||||||
|
String.fromCharCode(27, 91, 67), // 0x81 right
|
||||||
|
String.fromCharCode(27, 91, 65), // 0x82 up
|
||||||
|
String.fromCharCode(27, 91, 66), // 0x83 down
|
||||||
|
String.fromCharCode(27, 91, 53, 126), // 0x84 page up
|
||||||
|
String.fromCharCode(27, 91, 54, 126), // 0x85 page down
|
||||||
|
];
|
||||||
|
|
||||||
|
// state
|
||||||
|
const R = Bangle.appRect;
|
||||||
|
var kbx = 0,
|
||||||
|
kby = 0,
|
||||||
|
kbdx = 0,
|
||||||
|
kbdy = 0,
|
||||||
|
kbShift = false,
|
||||||
|
flashToggle = false;
|
||||||
|
const PX = 12,
|
||||||
|
PY = 16,
|
||||||
|
DRAGSCALE = settings.speedScaling;
|
||||||
|
var xoff = 0,
|
||||||
|
yoff = g.getHeight() - PY * 4;
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
"ram";
|
||||||
|
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
|
||||||
|
//g.drawImage(KEYIMG,0,yoff);
|
||||||
|
g.reset().setFont("6x8:2");
|
||||||
|
g.clearRect(R.x, yoff, R.x2, R.y2);
|
||||||
|
if (kbx >= 0)
|
||||||
|
g.setColor(g.theme.bgH).fillRect(xoff + kbx * PX, yoff + kby * PY, xoff + (kbx + 1) * PX - 1, yoff + (kby + 1) * PY - 1).setColor(g.theme.fg);
|
||||||
|
g.drawImage(KEYIMGL, xoff - 1, yoff + PY, { scale: 2 });
|
||||||
|
g.drawImage(KEYIMGR, xoff + PX * 12, yoff, { scale: 2 });
|
||||||
|
var replace = /[\x80\x81\x82\x83\x84\x85]/g;
|
||||||
|
g.drawString(map[0].replace(replace," "), xoff, yoff);
|
||||||
|
g.drawString(map[1].replace(replace," "), xoff, yoff + PY);
|
||||||
|
g.drawString(map[2].replace(replace," "), xoff, yoff + PY * 2);
|
||||||
|
g.drawString(map[3].replace(replace," "), xoff, yoff + PY * 3);
|
||||||
|
g.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTerminal(dataOutCallback) {
|
||||||
|
g.reset().clearRect(R);
|
||||||
|
// Set up the terminal
|
||||||
|
term = require("VT100").connect(g, {
|
||||||
|
charWidth: 6,
|
||||||
|
charHeight: 8
|
||||||
|
});
|
||||||
|
term.oy = R.y;
|
||||||
|
term.h = yoff; // we added this - it's not usually part of it
|
||||||
|
term.consoleHeight = 0 | ((term.h - term.oy) / term.charH);
|
||||||
|
term.scrollDown = function() {
|
||||||
|
g.setClipRect(R.x, term.y, R.x2, term.oy + term.h);
|
||||||
|
g.scroll(0, -this.charH);
|
||||||
|
g.setClipRect(R.x, R.y, R.x2, R.y2);
|
||||||
|
this.y--;
|
||||||
|
};
|
||||||
|
term.fgCol = g.theme.fg;
|
||||||
|
term.bgCol = g.theme.bg;
|
||||||
|
draw();
|
||||||
|
var flashInterval = setInterval(() => {
|
||||||
|
flashToggle = !flashToggle;
|
||||||
|
draw();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
function keyPress(kbx, kby) {
|
||||||
|
var map = kbShift ? KEYMAPUPPER : KEYMAPLOWER;
|
||||||
|
var ch = map[kby][kbx];
|
||||||
|
if (ch == "\2")
|
||||||
|
kbShift = !kbShift;
|
||||||
|
else {
|
||||||
|
if (ch.charCodeAt(0) > 127)
|
||||||
|
ch = KEYEXTRA[ch.charCodeAt(0) - 128];
|
||||||
|
dataOutCallback(ch);
|
||||||
|
}
|
||||||
|
Bangle.buzz(20);
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.setUI({
|
||||||
|
mode: "custom",
|
||||||
|
drag: e => {
|
||||||
|
if (settings.oneToOne) {
|
||||||
|
kbx = Math.max(Math.min(Math.floor((e.x - 16) / (6 * 2)), 13), 0);
|
||||||
|
kby = Math.max(Math.min(Math.floor((e.y - 120) / (8 * 2)), 3), 0);
|
||||||
|
//print(e.y, kby, e.x, kbx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.oneToOne) {
|
||||||
|
kbdx += e.dx;
|
||||||
|
kbdy += e.dy;
|
||||||
|
var dx = Math.round(kbdx / DRAGSCALE),
|
||||||
|
dy = Math.round(kbdy / DRAGSCALE);
|
||||||
|
kbdx -= dx * DRAGSCALE;
|
||||||
|
kbdy -= dy * DRAGSCALE;
|
||||||
|
if (dx || dy) {
|
||||||
|
if (settings.loopAround) {
|
||||||
|
kbx = (kbx + dx + 15) % 15;
|
||||||
|
kby = (kby + dy + 4) % 4;
|
||||||
|
} else {
|
||||||
|
kbx = Math.max(Math.min((kbx + dx), 13), 0);
|
||||||
|
kby = Math.max(Math.min((kby + dy), 3), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
|
||||||
|
if (!e.b && e.y > Bangle.appRect.y && settings.oneToOne /*&& settings.releaseToSelect*/ )
|
||||||
|
keyPress(kbx, kby);
|
||||||
|
},
|
||||||
|
touch: () => {
|
||||||
|
if (!settings.oneToOne /*|| !settings.releaseToSelect*/ )
|
||||||
|
keyPress(kbx, kby);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let catchSwipe = () => {
|
||||||
|
E.stopEventPropagation && E.stopEventPropagation();
|
||||||
|
};
|
||||||
|
Bangle.prependListener && Bangle.prependListener('swipe', catchSwipe); // Intercept swipes on fw2v19 and later. Should not break on older firmwares.
|
||||||
|
return {
|
||||||
|
dataReceived : function(d) {
|
||||||
|
g.reset().setFont("6x8");
|
||||||
|
// USB.write(e); // optionally mirror back to the PC
|
||||||
|
// Send characters to the terminal
|
||||||
|
for (var i in d) term.char(d[i]);
|
||||||
|
// update the screen
|
||||||
|
g.flip();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mainMenu() {
|
||||||
|
E.showMenu({
|
||||||
|
"":{title:"Terminal"},
|
||||||
|
/*LANG*/"JS REPL" : function() {
|
||||||
|
var t = startTerminal(function(d) {
|
||||||
|
LoopbackB.write(d);
|
||||||
|
});
|
||||||
|
LoopbackB.on('data', function(d) {
|
||||||
|
t.dataReceived(d);
|
||||||
|
});
|
||||||
|
// Now move the console to Loopback
|
||||||
|
LoopbackA.setConsole();
|
||||||
|
},
|
||||||
|
/*LANG*/"Bluetooth" : function() {
|
||||||
|
Bangle.setUI();
|
||||||
|
E.showMessage(/*LANG*/"Scanning...", /*LANG*/"Bluetooth");
|
||||||
|
NRF.findDevices(function(devices) {
|
||||||
|
if (!devices.length)
|
||||||
|
return E.showAlert("No devices found").then(() => mainMenu());
|
||||||
|
var menu = { "" : { title: /*LANG*/"Bluetooth", back : () => mainMenu() } };
|
||||||
|
devices.forEach(dev => {
|
||||||
|
var name = dev.name || dev.id.substr(0,17);
|
||||||
|
menu[name] = function() {
|
||||||
|
Bangle.setUI();
|
||||||
|
E.showMessage(/*LANG*/"Connecting...", /*LANG*/"Bluetooth");
|
||||||
|
require("ble_uart").connect(dev).then(function(uart) {
|
||||||
|
var t = startTerminal(function(d) {
|
||||||
|
uart.write(d);
|
||||||
|
});
|
||||||
|
t.dataReceived("Connected to:\n "+name+"\n")
|
||||||
|
uart.on('data', function(d) { t.dataReceived(d); });
|
||||||
|
}).catch(err => {
|
||||||
|
E.showAlert(err.toString()).then(() => mainMenu());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
E.showMenu(menu);
|
||||||
|
}, { filters: [{ services: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e'] }], timeout: 2000, active:true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mainMenu();
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 393 B |
|
|
@ -0,0 +1,14 @@
|
||||||
|
{ "id": "terminal",
|
||||||
|
"name": "VT100 Terminal",
|
||||||
|
"shortName":"Terminal",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Terminal and Keyboard that can be used as a REPL. You can type JS commands into Bangle.js's own REPL and execute them, or you can connect to other Bluetooth LE UART devices (like other Espruinos) and issue commands.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"tags": "terminal,tool,bluetooth",
|
||||||
|
"screenshots" : [ { "url":"screenshot1.png" }, { "url":"screenshot2.png" } ],
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"terminal.app.js","url":"app.js"},
|
||||||
|
{"name":"terminal.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
Loading…
Reference in New Issue