mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
libcurses (ncurses) starter, and switch less to using it
This commit is contained in:
parent
cb5de9ed6d
commit
0fe4544107
7 changed files with 282 additions and 112 deletions
|
|
@ -322,6 +322,7 @@ clite.commands.load('less',function(args,env,io) {
|
|||
var stdlib = io.include('stdlib');
|
||||
var stdio = io.include('stdio');
|
||||
var term = io.include('term');
|
||||
var curses = io.include('curses');
|
||||
var clite = io.include('clite');
|
||||
var file = null;
|
||||
|
||||
|
|
@ -354,18 +355,20 @@ Options:
|
|||
}
|
||||
}
|
||||
|
||||
var cols;
|
||||
var rows;
|
||||
var lines = [];
|
||||
var topLine = 0;
|
||||
var bottomLine = 0;
|
||||
|
||||
function showAt(line) {
|
||||
var start = line;
|
||||
var end = line+tty.lines;
|
||||
var end = line+rows;
|
||||
if (end > lines.length)
|
||||
end = lines.length;
|
||||
tty.clear();
|
||||
curses.clear();
|
||||
for (var i=start; i<end; i++) {
|
||||
tty.draw(lines[i]+'\n');
|
||||
curses.printw(lines[i]+'\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,15 +389,15 @@ Options:
|
|||
|
||||
var parts = txt.split('\n');
|
||||
parts.forEach(function(line) {
|
||||
if (line.length > tty.chars) {
|
||||
var sects = line.match(new RegExp('(.{1,'+tty.chars+'})','g'));
|
||||
if (line.length > cols) {
|
||||
var sects = line.match(new RegExp('(.{1,'+cols+'})','g'));
|
||||
lines.push.apply(lines,sects);
|
||||
return;
|
||||
}
|
||||
lines.push(line);
|
||||
});
|
||||
|
||||
bottomLine = (lines.length-tty.lines);
|
||||
bottomLine = (lines.length-rows);
|
||||
|
||||
showAt(0);
|
||||
}
|
||||
|
|
@ -415,15 +418,19 @@ Options:
|
|||
}
|
||||
}
|
||||
|
||||
var tty = term.opentty();
|
||||
tty.setRaw(false);
|
||||
curses.initscr();
|
||||
cols = curses.getmaxx();
|
||||
rows = curses.getmaxy();
|
||||
curses.cbreak();
|
||||
curses.printw('Loading...');
|
||||
|
||||
tty.handleKeys(function(e) {
|
||||
switch (e.key) {
|
||||
function input(key) {
|
||||
switch (key) {
|
||||
case 'Escape':
|
||||
case 'q':
|
||||
term.closetty(tty);
|
||||
curses.endwin();
|
||||
io.exit(0);
|
||||
return;
|
||||
break;
|
||||
case 'Up':
|
||||
case 'ArrowUp':
|
||||
|
|
@ -441,7 +448,7 @@ Options:
|
|||
break;
|
||||
case 'PageUp':
|
||||
if (topLine > 0) {
|
||||
topLine -= tty.lines-1;
|
||||
topLine -= rows-1;
|
||||
if (topLine < 0)
|
||||
topLine = 0;
|
||||
showAt(topLine);
|
||||
|
|
@ -449,7 +456,7 @@ Options:
|
|||
break;
|
||||
case 'PageDown':
|
||||
if (topLine < bottomLine) {
|
||||
topLine += tty.lines-1;
|
||||
topLine += rows-1;
|
||||
if (topLine >= bottomLine)
|
||||
topLine = bottomLine;
|
||||
showAt(topLine);
|
||||
|
|
@ -464,8 +471,10 @@ Options:
|
|||
showAt(topLine);
|
||||
break;
|
||||
}
|
||||
});
|
||||
tty.draw('Loading...');
|
||||
curses.getch(input);
|
||||
}
|
||||
|
||||
curses.getch(input);
|
||||
|
||||
return null;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ header h1 {line-height:40px; font-size:30px;}
|
|||
section {}
|
||||
section.content {position:relative; overflow:hidden;}
|
||||
section.content div#terminal {width:100%; min-width: 500px; max-width:1000px; max-height:100%; font-size:14px; line-height:20px; margin:0 auto; overflow:hidden; overflow-y:auto; scrollbar-width:none;}
|
||||
section.content div#tty {width:100%; min-width: 500px; max-width:1000px; margin:0 auto; overflow:hidden; overflow-y:auto; scrollbar-width:none; z-index:99; background:#000000;}
|
||||
section.content div#termalt {width:100%; min-width: 500px; max-width:1000px; margin:0 auto; overflow:hidden; overflow-y:auto; scrollbar-width:none; z-index:99; background:#000000;}
|
||||
section.content article {unicode-bidi: embed; white-space: pre-wrap;}
|
||||
section.content form {display:flex;}
|
||||
section.content form label, section.content form input, section.content form input:focus {display:block; border:none; margin:0; padding:0; font-family:monospace; font-size:14px; line-height:20px; background-color:#000000; color:#FFFFFF; outline:none;}
|
||||
|
|
|
|||
221
clite/core.js
221
clite/core.js
|
|
@ -433,6 +433,11 @@ readme.txt:/usr/clite/readme:0:0:-rw-r--r--`;
|
|||
}
|
||||
|
||||
function init8(data) {
|
||||
clite.core.execSafe(clite.libs.data);
|
||||
clite.core.load.script('clite/libcurses.js',init9);
|
||||
}
|
||||
|
||||
function init9(data) {
|
||||
clite.core.execSafe(clite.libs.data);
|
||||
clite.libs.data = null;
|
||||
clite.libs.load = null;
|
||||
|
|
@ -466,10 +471,10 @@ readme.txt:/usr/clite/readme:0:0:-rw-r--r--`;
|
|||
exit:null, // filled in by fork()
|
||||
include:null, // filled in by fork()
|
||||
};
|
||||
var pid = clite.lib.fork(env,io,init9);
|
||||
var pid = clite.lib.fork(env,io,init10);
|
||||
}
|
||||
}
|
||||
function init9(env,io) {
|
||||
function init10(env,io) {
|
||||
clite.proc.setLogin(io.pid,1);
|
||||
clite.lib.exec('/bin/sh',['sh'],env,io);
|
||||
}
|
||||
|
|
@ -1789,12 +1794,19 @@ clite.log = {
|
|||
clite.term = {
|
||||
data:{
|
||||
type:'text',
|
||||
show:true,
|
||||
show:false,
|
||||
isalt:false,
|
||||
form:null,
|
||||
field:null,
|
||||
handler:null,
|
||||
prompt:'',
|
||||
buff:''
|
||||
buff:'',
|
||||
alt:{
|
||||
israwOut:false,
|
||||
israwIn:false,
|
||||
lines:0,
|
||||
cols:0
|
||||
}
|
||||
},
|
||||
events:{
|
||||
keydown:function(e) {
|
||||
|
|
@ -1805,8 +1817,13 @@ clite.term = {
|
|||
return true;
|
||||
},
|
||||
keyup:function(e) {
|
||||
if (clite.term.tty.data != null)
|
||||
return clite.term.tty.data.data.onkeypress(e);
|
||||
if (clite.term.data.isalt && clite.term.data.alt.israwIn) {
|
||||
if (clite.term.data.handler != null)
|
||||
clite.term.data.handler(e.key);
|
||||
e.target.value = '';
|
||||
clite.term.data.buff = '';
|
||||
return false;
|
||||
}
|
||||
if (e.key.length > 1 && e.key != 'Spacebar' && clite.term.data.handler != null) {
|
||||
if (e.key == 'Enter') {
|
||||
clite.term.data.buff = '';
|
||||
|
|
@ -1829,23 +1846,76 @@ clite.term = {
|
|||
});
|
||||
}
|
||||
},
|
||||
tty:{
|
||||
data:null
|
||||
},
|
||||
clear:function() {
|
||||
if (clite.term.data.isalt) {
|
||||
document.getElementById('termalt').innerHTML = '';
|
||||
return;
|
||||
}
|
||||
document.getElementById('terminal').innerHTML = '';
|
||||
},
|
||||
writeLine:function(txt) {
|
||||
if (clite.term.data.isalt) {
|
||||
var t = document.getElementById('termalt');
|
||||
if (!t)
|
||||
t = clite.term.genAlt();
|
||||
if (clite.term.data.alt.israwOut) {
|
||||
t.innerHTML += txt;
|
||||
if (clite.term.data.show)
|
||||
clite.term.genForm();
|
||||
return;
|
||||
}
|
||||
var a = document.createElement('article');
|
||||
a.innerHTML = clite.lib.htmlEncode(txt);
|
||||
t.appendChild(a);
|
||||
if (clite.term.data.show)
|
||||
clite.term.genForm();
|
||||
return;
|
||||
}
|
||||
var a = document.createElement('article');
|
||||
a.innerHTML = clite.lib.htmlEncode(txt);
|
||||
var t = document.getElementById('terminal');
|
||||
if (!t)
|
||||
return;
|
||||
t.appendChild(a);
|
||||
if (document.getElementById('form'))
|
||||
if (clite.term.data.show)
|
||||
clite.term.genForm();
|
||||
t.scrollTop = t.scrollHeight;
|
||||
},
|
||||
genAlt:function() {
|
||||
var t = document.getElementById('termalt');
|
||||
if (!t) {
|
||||
t = document.createElement('div');
|
||||
t.id = 'termalt';
|
||||
}
|
||||
|
||||
var term = document.getElementById('terminal');
|
||||
var p = term.parentNode;
|
||||
|
||||
t.style.width = term.offsetWidth+'px';
|
||||
if (p.offsetHeight > term.offsetHeight) {
|
||||
t.style.minHeight = p.offsetHeight+'px';
|
||||
}else{
|
||||
t.style.minHeight = term.offsetHeight+'px';
|
||||
}
|
||||
t.style.position = 'absolute';
|
||||
t.style.top = term.offsetTop+'px';
|
||||
t.style.left = term.offsetLeft+'px';
|
||||
p.appendChild(t);
|
||||
|
||||
// some calculations to help with putting content in
|
||||
var s = document.createElement('span');
|
||||
s.innerText = 'W';
|
||||
t.appendChild(s);
|
||||
// 20 should not be hardcoded
|
||||
clite.term.data.alt.lines = parseInt((t.offsetHeight/20)-1);
|
||||
// TODO: shouldn't need the 0.8 thing, but urgh
|
||||
clite.term.data.alt.cols = parseInt((t.offsetWidth/s.offsetWidth)*0.8);
|
||||
t.removeChild(s);
|
||||
|
||||
clite.term.data.buff = '';
|
||||
|
||||
return t;
|
||||
},
|
||||
genForm:function() {
|
||||
var f = document.getElementById('form');
|
||||
if (!clite.term.data.show) {
|
||||
|
|
@ -1857,19 +1927,28 @@ clite.term = {
|
|||
f = document.createElement('form');
|
||||
f.id = 'form';
|
||||
f.innerHTML = '';
|
||||
var l = document.createElement('label');
|
||||
l.innerHTML = clite.lib.htmlEncode(clite.term.data.prompt);
|
||||
f.appendChild(l);
|
||||
if (!clite.term.isalt) {
|
||||
var l = document.createElement('label');
|
||||
l.innerHTML = clite.lib.htmlEncode(clite.term.data.prompt);
|
||||
f.appendChild(l);
|
||||
}
|
||||
var i = document.createElement('input');
|
||||
i.type = clite.term.getType();
|
||||
i.value = clite.term.data.buff;
|
||||
f.appendChild(i);
|
||||
clite.term.data.form = f;
|
||||
clite.term.data.field = i;
|
||||
var t = document.getElementById('terminal');
|
||||
if (!t)
|
||||
return;
|
||||
t.appendChild(f);
|
||||
if (clite.term.data.isalt) {
|
||||
var t = document.getElementById('termalt');
|
||||
if (!t)
|
||||
t = clite.term.genAlt();
|
||||
t.appendChild(f);
|
||||
}else{
|
||||
var t = document.getElementById('terminal');
|
||||
if (!t)
|
||||
return;
|
||||
t.appendChild(f);
|
||||
}
|
||||
i.onkeydown = clite.term.events.keydown;
|
||||
i.onkeyup = clite.term.events.keyup;
|
||||
i.onfocusout = clite.term.events.refocus;
|
||||
|
|
@ -1893,6 +1972,7 @@ clite.term = {
|
|||
}
|
||||
}
|
||||
f.onsubmit = function() {return false;};
|
||||
clite.term.events.refocus();
|
||||
},
|
||||
setPass:function(is) {
|
||||
clite.term.data.type = (!!is) ? 'password' : 'text';
|
||||
|
|
@ -1944,78 +2024,45 @@ clite.term = {
|
|||
clite.term.data.show = true;
|
||||
clite.term.genForm();
|
||||
break;
|
||||
case 'alt': // switch to the alternate frame buffer
|
||||
if (typeof v === 'boolean') {
|
||||
clite.term.data.isalt = v;
|
||||
}
|
||||
if (!clite.term.data.isalt) {
|
||||
var t = document.getElementById('termalt');
|
||||
if (t)
|
||||
t.parentNode.removeChild(t);
|
||||
}else{
|
||||
clite.term.genAlt();
|
||||
}
|
||||
clite.term.genForm();
|
||||
return clite.term.data.isalt;
|
||||
break;
|
||||
case 'rawout':
|
||||
if (!clite.term.data.isalt)
|
||||
return false;
|
||||
if (typeof v === 'boolean') {
|
||||
clite.term.data.alt.israwOut = v;
|
||||
}
|
||||
return clite.term.data.alt.israwOut;
|
||||
break;
|
||||
case 'rawin':
|
||||
if (!clite.term.data.isalt)
|
||||
return false;
|
||||
if (typeof v === 'boolean') {
|
||||
clite.term.data.alt.israwIn = v;
|
||||
}
|
||||
return clite.term.data.alt.israwIn;
|
||||
break;
|
||||
case 'atton':
|
||||
break;
|
||||
case 'attoff':
|
||||
break;
|
||||
case 'echo':
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
api:{
|
||||
// gets an empty element with key events (allows view/less to display content in a clean element)
|
||||
opentty:function() {
|
||||
var tty = {
|
||||
handleKeys:function(cb) {
|
||||
this.data.cb = cb;
|
||||
},
|
||||
draw:function(data) {
|
||||
if (typeof data == 'string') {
|
||||
this.data.el.innerHTML += clite.lib.htmlEncode(data);
|
||||
return true;
|
||||
}else if (data instanceof HTMLImageElement) {
|
||||
this.data.el.appendChild(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
clear:function() {
|
||||
this.data.el.innerHTML = '';
|
||||
},
|
||||
setRaw:function(r) {
|
||||
this.data.el.style.whiteSpace = ((!!r) ? 'normal' : 'pre-wrap');
|
||||
},
|
||||
data:{
|
||||
cb:null,
|
||||
el:null,
|
||||
onkeypress:function(e) {
|
||||
try{
|
||||
return this.cb(e);
|
||||
}catch(err) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
clite.term.tty.data = tty;
|
||||
tty.data.el = document.createElement('div');
|
||||
tty.data.el.id = 'tty';
|
||||
tty.data.el.onkeypress = tty.data.onkeypress;
|
||||
var term = document.getElementById('terminal');
|
||||
var p = term.parentNode;
|
||||
tty.data.el.style.width = term.offsetWidth+'px';
|
||||
if (p.offsetHeight > term.offsetHeight) {
|
||||
tty.data.el.style.minHeight = p.offsetHeight+'px';
|
||||
}else{
|
||||
tty.data.el.style.minHeight = term.offsetHeight+'px';
|
||||
}
|
||||
tty.data.el.style.position = 'absolute';
|
||||
tty.data.el.style.top = term.offsetTop+'px';
|
||||
tty.data.el.style.left = term.offsetLeft+'px';
|
||||
p.appendChild(tty.data.el);
|
||||
// some calculations to help with putting content in
|
||||
var s = document.createElement('span');
|
||||
s.innerText = 'W';
|
||||
tty.data.el.appendChild(s);
|
||||
// 20 should not be hardcoded
|
||||
tty.lines = parseInt((tty.data.el.offsetHeight/20)-1);
|
||||
// TODO: shouldn't need the 0.8 thing, but urgh
|
||||
tty.chars = parseInt((tty.data.el.offsetWidth/s.offsetWidth)*0.8);
|
||||
tty.data.el.removeChild(s);
|
||||
clite.term.events.refocus();
|
||||
return tty;
|
||||
},
|
||||
closetty:function(tty) {
|
||||
try{
|
||||
tty.data.el.parentNode.removeChild(tty.data.el);
|
||||
clite.term.tty.data = null;
|
||||
delete tty;
|
||||
}catch(err){}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2144,7 +2191,5 @@ clite.lib = {
|
|||
exit:function(pid,v) {
|
||||
if (pid > -1)
|
||||
clite.proc.exit(pid);
|
||||
if (clite.term.tty.data != null)
|
||||
clite.term.api.closetty(clite.term.tty.data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,18 @@ return Object.create({
|
|||
parts.push(s);
|
||||
// TODO: resolve all paths passed as arguments? - catches ~ and * etc
|
||||
return parts;
|
||||
},
|
||||
|
||||
ptrnew:function() {
|
||||
return Object.create({value:null});
|
||||
},
|
||||
ptrset:function(ptr,val) {
|
||||
ptr.value = val;
|
||||
},
|
||||
ptrget:function(ptr) {
|
||||
if (typeof ptr.value === 'undefined')
|
||||
return null;
|
||||
return ptr.value;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
108
clite/libcurses.js
Normal file
108
clite/libcurses.js
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
clite.libs.data = function() {
|
||||
|
||||
clite.libs.load('libcurses','curses',function(io,env) {
|
||||
|
||||
return Object.create({
|
||||
|
||||
isinit:false,
|
||||
types:{
|
||||
A_NULL:0,
|
||||
A_BOLD:1,
|
||||
},
|
||||
attmap:function(t) {
|
||||
switch (t) {
|
||||
case this.types.A_BOLD:
|
||||
return 'bold';
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
initscr:function() {
|
||||
// curses only works on a tty
|
||||
if (!clite.io.isatty(io.pid,io.stdin))
|
||||
return false;
|
||||
this.isinit = clite.term.ttyctrl('alt',true);
|
||||
},
|
||||
|
||||
endwin:function() {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
clite.term.ttyctrl('rawin',false);
|
||||
clite.term.ttyctrl('rawout',false);
|
||||
this.isinit = clite.term.ttyctrl('alt',false);
|
||||
},
|
||||
|
||||
cbreak:function() {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
clite.term.ttyctrl('rawin',true);
|
||||
},
|
||||
|
||||
clear:function() {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
clite.term.clear();
|
||||
},
|
||||
|
||||
noecho:function() {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
clite.term.ttyctrl('echo',false);
|
||||
},
|
||||
|
||||
printw:function(fmt) {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
var args = Array.from(arguments);
|
||||
args.shift();
|
||||
clite.io.vfprintf(io.pid,io.stdout,fmt,args);
|
||||
},
|
||||
|
||||
getch:function(cb) {
|
||||
if (!this.isinit)
|
||||
return false;
|
||||
return clite.io.read(io.pid,io.stdin,cb);
|
||||
},
|
||||
|
||||
refresh:function() {
|
||||
// no op, drawing isn't queued
|
||||
},
|
||||
|
||||
attron:function(a) {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
a = this.attmap(a);
|
||||
clite.term.ttyctrl('atton',a);
|
||||
},
|
||||
attroff:function(a) {
|
||||
if (!this.isinit)
|
||||
return;
|
||||
a = this.attmap(a);
|
||||
clite.term.ttyctrl('attoff',a);
|
||||
},
|
||||
|
||||
getmaxx:function() {
|
||||
if (!this.isinit)
|
||||
return 0;
|
||||
return clite.term.data.alt.cols;
|
||||
},
|
||||
getmaxy:function() {
|
||||
if (!this.isinit)
|
||||
return 0;
|
||||
return clite.term.data.alt.lines;
|
||||
},
|
||||
getmaxyx:function(ptry,ptrx) {
|
||||
if (!this.isinit)
|
||||
return false;
|
||||
ptrx.value = clite.term.data.alt.cols;
|
||||
ptry.value = clite.term.data.alt.lines;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
|
@ -11,14 +11,6 @@ return Object.create({
|
|||
|
||||
ttyctrl:function(fn,v) {
|
||||
return clite.term.ttyctrl(fn,v);
|
||||
},
|
||||
|
||||
// gets an empty element with key events (allows view/less to display content in a clean element)
|
||||
opentty:function() {
|
||||
return clite.term.api.opentty();
|
||||
},
|
||||
closetty:function(tty) {
|
||||
return clite.term.api.closetty(tty);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -367,6 +367,10 @@ clite.commands.load('sh',function(args,env,io) {
|
|||
}
|
||||
// set the prompt
|
||||
term.ttyctrl('prompt',prompt);
|
||||
// ensure input is enabled
|
||||
term.ttyctrl('show');
|
||||
// and that we're on the main framebuffer
|
||||
term.ttyctrl('alt',false);
|
||||
// then read from stdin
|
||||
stdio.read(io.stdin,input);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue