BangleApps/apps/terminal/app.js

263 lines
6.5 KiB
JavaScript
Raw Permalink Normal View History

2024-01-22 14:39:04 +00:00
/* 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();
2024-03-04 20:34:50 +00:00
setInterval(() => {
2024-01-22 14:39:04 +00:00
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();