ref: b28c3db57884d406d5a39750fdc9e46baeb139af
parent: bc54898807d27e79ba9f1b595ef3e09e3da67522
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Aug 20 15:22:30 EDT 2017
vt: implement /dev/cons and /dev/consctl as a fileserver, winch, incremental redraw we used to bind a pipe to /dev/cons and /dev/consctl with some shared segment hack to pass tty info arround. now we implement this as a fileserver. add support for "winchon"/"winchoff" ctl message to enable interrupt on window size change. (used by ssh) keep track of fullscreen scrolls, avoiding redrawing the whole screen each time.
--- a/sys/src/cmd/vt/cons.h
+++ b/sys/src/cmd/vt/cons.h
@@ -3,10 +3,17 @@
struct Consstate{
int raw;
int hold;
+ int winch;
};
+extern Consstate cs[];
-extern Consstate* consctl(void);
-extern Consstate* cs;
+typedef struct Buf Buf;
+struct Buf
+{
+ int n;
+ char *s;
+ char b[];
+};
#define INSET 2
#define BUFS 32
@@ -75,4 +82,4 @@
extern int nocolor;
extern void setdim(int, int);
-
+extern void mountcons(void);
--- a/sys/src/cmd/vt/consctl.c
+++ /dev/null
@@ -1,71 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include "cons.h"
-
-/*
- * bind a pipe onto consctl and keep reading it to
- * get changes to console state.
- */
-Consstate*
-consctl(void)
-{
- int i, n, fd, tries;
- char buf[128];
- Consstate *x;
- char *field[10];
-
- x = segattach(0, "shared", 0, sizeof *x);
- if(x == (void*)-1)
- sysfatal("segattach: %r");
-
- /* a pipe to simulate consctl */
- if(bind("#|", "/mnt/consctl", MBEFORE) < 0
- || bind("/mnt/consctl/data1", "/dev/consctl", MREPL) < 0)
- sysfatal("bind consctl: %r");
-
- /* a pipe to simulate the /dev/cons */
- if(bind("#|", "/mnt/cons", MREPL) < 0
- || bind("/mnt/cons/data1", "/dev/cons", MREPL) < 0)
- sysfatal("bind cons: %r");
-
- switch(fork()){
- case -1:
- sysfatal("fork: %r");
- case 0:
- break;
- default:
- return x;
- }
-
- notify(0);
-
- for(tries = 0; tries < 100; tries++){
- x->raw = 0;
- x->hold = 0;
- fd = open("/mnt/consctl/data", OREAD);
- if(fd < 0)
- break;
- tries = 0;
- for(;;){
- n = read(fd, buf, sizeof(buf)-1);
- if(n <= 0)
- break;
- buf[n] = 0;
- n = getfields(buf, field, 10, 1, " ");
- for(i = 0; i < n; i++){
- if(strcmp(field[i], "rawon") == 0)
- x->raw = 1;
- else if(strcmp(field[i], "rawoff") == 0)
- x->raw = 0;
- else if(strcmp(field[i], "holdon") == 0)
- x->hold = 1;
- else if(strcmp(field[i], "holdoff") == 0)
- x->hold = 0;
- }
- }
- close(fd);
- }
- exits(0);
- return 0; /* dummy to keep compiler quiet*/
-}
--- /dev/null
+++ b/sys/src/cmd/vt/fs.c
@@ -1,0 +1,213 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+#include "cons.h"
+
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+
+extern Channel *hc[2];
+
+static File *devcons, *devconsctl;
+
+static Channel *readreq;
+static Channel *flushreq;
+
+static void
+fsreader(void*)
+{
+ Req *r, *fr;
+ Buf *b;
+ int n;
+
+ b = nil;
+ r = nil;
+ for(;;){
+ Alt a[] = {
+ { flushreq, &fr, CHANRCV },
+ { readreq, &r, r == nil ? CHANRCV : CHANNOP },
+ { hc[0], &b, b == nil ? CHANRCV : CHANNOP },
+ { nil, nil, b == nil || r == nil ? CHANEND : CHANNOBLK },
+ };
+ if(alt(a) == 0){
+ if(fr->oldreq == r){
+ respond(r, "interrupted");
+ r = nil;
+ }
+ respond(fr, nil);
+ }
+ if(b == nil || r == nil)
+ continue;
+ r->ofcall.count = 0;
+ while((n = r->ifcall.count - r->ofcall.count) > 0){
+ if(n > b->n)
+ n = b->n;
+ memmove((char*)r->ofcall.data + r->ofcall.count, b->s, n);
+ r->ofcall.count += n;
+ b->s += n, b->n -= n;
+ if(b->n <= 0){
+ free(b);
+ if((b = nbrecvp(hc[0])) == nil)
+ break;
+ }
+ }
+ respond(r, nil);
+ r = nil;
+ }
+}
+
+static void
+fsread(Req *r)
+{
+ if(r->fid->file == devcons){
+ sendp(readreq, r);
+ return;
+ }
+ respond(r, "not implemented");
+}
+
+typedef struct Partutf Partutf;
+struct Partutf
+{
+ int n;
+ char s[UTFmax];
+};
+
+static Rune*
+cvtc2r(char *b, int n, Partutf *u)
+{
+ char *cp, *ep;
+ Rune *rp, *rb;
+
+ cp = b, ep = b + n;
+ rp = rb = emalloc9p(sizeof(Rune)*(n+2));
+
+ while(u->n > 0 && cp < ep){
+ u->s[u->n++] = *cp++;
+ if(fullrune(u->s, u->n)){
+ chartorune(rp, u->s);
+ if(*rp != 0)
+ rp++;
+ u->n = 0;
+ break;
+ }
+ }
+ if(u->n == 0){
+ while(cp < ep && fullrune(cp, ep - cp)){
+ cp += chartorune(rp, cp);
+ if(*rp != 0)
+ rp++;
+ }
+ n = ep - cp;
+ if(n > 0){
+ memmove(u->s, cp, n);
+ u->n = n;
+ }
+ }
+ if(rb == rp){
+ free(rb);
+ return nil;
+ }
+ *rp = 0;
+
+ return rb;
+}
+
+static void
+fswrite(Req *r)
+{
+ if(r->fid->file == devcons){
+ Partutf *u;
+ Rune *rp;
+
+ if((u = r->fid->aux) == nil)
+ u = r->fid->aux = emalloc9p(sizeof(*u));
+ if((rp = cvtc2r((char*)r->ifcall.data, r->ifcall.count, u)) != nil)
+ sendp(hc[1], rp);
+
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+ return;
+ }
+ if(r->fid->file == devconsctl){
+ char *s = r->ifcall.data;
+ int n = r->ifcall.count;
+
+ if(n >= 5 && strncmp(s, "rawon", 5) == 0)
+ cs->raw = 1;
+ else if(n >= 6 && strncmp(s, "rawoff", 6) == 0)
+ cs->raw = 0;
+ else if(n >= 6 && strncmp(s, "holdon", 6) == 0)
+ cs->hold = 1;
+ else if(n >= 7 && strncmp(s, "holdoff", 7) == 0)
+ cs->hold = 0;
+ else if(n >= 7 && strncmp(s, "winchon", 7) == 0)
+ cs->winch = 1;
+ else if(n >= 8 && strncmp(s, "winchoff", 8) == 0)
+ cs->winch = 0;
+
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+ return;
+ }
+
+ respond(r, "not implemented");
+}
+
+static void
+fsflush(Req *r)
+{
+ sendp(flushreq, r);
+}
+
+static void
+fsdestroyfid(Fid *f)
+{
+ if(f->file == devconsctl && f->omode >= 0){
+ cs->raw = 0;
+ cs->hold = 0;
+ cs->winch = 0;
+ }
+ if(f->aux != nil){
+ free(f->aux);
+ f->aux = nil;
+ }
+}
+
+static void
+fsstart(Srv*)
+{
+ flushreq = chancreate(sizeof(Req*), 4);
+ readreq = chancreate(sizeof(Req*), 4);
+ proccreate(fsreader, nil, 16*1024);
+}
+
+static void
+fsend(Srv*)
+{
+ sendp(hc[1], nil);
+}
+
+Srv fs = {
+.read=fsread,
+.write=fswrite,
+.flush=fsflush,
+.destroyfid=fsdestroyfid,
+.start=fsstart,
+.end=fsend,
+};
+
+void
+mountcons(void)
+{
+ fs.tree = alloctree("vt", "vt", DMDIR|0555, nil);
+ devcons = createfile(fs.tree->root, "cons", "vt", 0666, nil);
+ if(devcons == nil)
+ sysfatal("creating /dev/cons: %r");
+ devconsctl = createfile(fs.tree->root, "consctl", "vt", 0666, nil);
+ if(devconsctl == nil)
+ sysfatal("creating /dev/consctl: %r");
+ threadpostmountsrv(&fs, nil, "/dev", MBEFORE);
+}
--- a/sys/src/cmd/vt/main.c
+++ b/sys/src/cmd/vt/main.c
@@ -1,11 +1,16 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
+
+#include "cons.h"
+
#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+
+#include <bio.h>
#include <mouse.h>
#include <keyboard.h>
-#include <bio.h>
-#include "cons.h"
char *menutext2[] = {
"backup",
@@ -40,6 +45,7 @@
int olines;
int peekc;
int cursoron = 1;
+int hostclosed = 0;
Menu menu2;
Menu menu3;
Rune *histp;
@@ -52,9 +58,15 @@
#define onscreena(x, y) &onscreenabuf[((y)*(xmax+2) + (x))]
#define onscreenc(x, y) &onscreencbuf[((y)*(xmax+2) + (x))]
+uchar *screenchangebuf;
+uint scrolloff;
+
+#define screenchange(y) screenchangebuf[((y)+scrolloff) % (ymax+1)]
+
int yscrmin, yscrmax;
int attr, defattr;
+Image *cursorsave;
Image *bordercol;
Image *colors[8];
Image *hicolors[8];
@@ -100,13 +112,12 @@
Mousectl *mc;
Keyboardctl *kc;
-Channel *hc;
-Consstate *cs;
+Channel *hc[2];
+Consstate cs[1];
int nocolor;
int logfd = -1;
-int hostfd = -1;
-int hostpid;
+int hostpid = -1;
Biobuf *snarffp = 0;
Rune *hostbuf, *hostbufp;
char echo_input[BSIZE];
@@ -118,7 +129,6 @@
struct funckey *fk, *appfk;
/* functions */
-void initialize(int, char **);
int waitchar(void);
void waitio(void);
int rcvchar(void);
@@ -130,59 +140,53 @@
int alnum(int);
void escapedump(int,uchar *,int);
-int
-start_host(void)
+static Channel *pidchan;
+
+static void
+runcmd(void *args)
{
- switch((hostpid = rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG))) {
- case 0:
- close(0);
- open("/dev/cons", OREAD);
- close(1);
- open("/dev/cons", OWRITE);
- dup(1, 2);
- execl("/bin/rc","rcX",nil);
- fprint(2, "failed to start up rc: %r\n");
- _exits("rc");
- case -1:
- fprint(2,"rc startup: fork: %r\n");
- _exits("rc_fork");
+ char **argv = args;
+ char *cmd;
+
+ rfork(RFNAMEG);
+ mountcons();
+
+ rfork(RFFDG);
+ close(0);
+ open("/dev/cons", OREAD);
+ close(1);
+ open("/dev/cons", OWRITE);
+ dup(1, 2);
+
+ cmd = nil;
+ while(*argv != nil){
+ if(cmd == nil)
+ cmd = strdup(*argv);
+ else
+ cmd = smprint("%s %q", cmd, *argv);
+ argv++;
}
- return open("/mnt/cons/data", ORDWR);
+
+ procexecl(pidchan, "/bin/rc", "rcX", cmd == nil ? nil : "-c", cmd, nil);
+ sysfatal("%r");
}
void
send_interrupt(void)
{
- postnote(PNGROUP, hostpid, "interrupt");
+ if(hostpid > 0)
+ postnote(PNGROUP, hostpid, "interrupt");
}
void
-hostreader(void*)
+sendnchars(int n, char *p)
{
- char cb[BSIZE+1], *cp;
- Rune *rb, *rp;
- int n, r;
+ Buf *b;
- n = 0;
- while((r = read(hostfd, cb+n, BSIZE-n)) > 0){
- n += r;
- rb = mallocz((n+1)*sizeof(Rune), 0);
- for(rp = rb, cp = cb; n > 0; n -= r, cp += r){
- if(!fullrune(cp, n))
- break;
- r = chartorune(rp, cp);
- if(*rp != 0)
- rp++;
- }
- if(rp > rb){
- *rp = 0;
- sendp(hc, rb);
- } else {
- free(rb);
- }
- if(n > 0) memmove(cb, cp, n);
- }
- sendp(hc, nil);
+ b = emalloc9p(sizeof(Buf)+n);
+ memmove(b->s = b->b, p, b->n = n);
+ if(nbsendp(hc[0], b) < 0)
+ free(b);
}
static void
@@ -189,20 +193,10 @@
shutdown(void)
{
send_interrupt();
- postnote(PNGROUP, getpid(), "exit");
threadexitsall(nil);
}
void
-threadmain(int argc, char **argv)
-{
- rfork(RFNAMEG|RFNOTEG|RFENVG);
- atexit(shutdown);
- initialize(argc, argv);
- emulate();
-}
-
-void
usage(void)
{
fprint(2, "usage: %s [-2abcrx] [-f font] [-l logfile] [cmd...]\n", argv0);
@@ -210,7 +204,7 @@
}
void
-initialize(int argc, char **argv)
+threadmain(int argc, char **argv)
{
int rflag;
int i, blkbg;
@@ -257,6 +251,9 @@
break;
}ARGEND;
+ quotefmtinstall();
+ atexit(shutdown);
+
if(initdraw(0, fontname, term) < 0)
sysfatal("inidraw failed: %r");
if((mc = initmouse("/dev/mouse", screen)) == nil)
@@ -263,8 +260,10 @@
sysfatal("initmouse failed: %r");
if((kc = initkeyboard("/dev/cons")) == nil)
sysfatal("initkeyboard failed: %r");
- if((cs = consctl()) == nil)
- sysfatal("consctl failed: %r");
+
+ hc[0] = chancreate(sizeof(Buf*), 8); /* input to host */
+ hc[1] = chancreate(sizeof(Rune*), 8); /* output from host */
+
cs->raw = rflag;
histp = hist;
@@ -290,19 +289,11 @@
fgcolor = (blkbg? display->white: display->black);
resize();
- hc = chancreate(sizeof(Rune*), 5);
- if((hostfd = start_host()) >= 0)
- proccreate(hostreader, nil, BSIZE+1024);
+ pidchan = chancreate(sizeof(int), 0);
+ proccreate(runcmd, argv, 16*1024);
+ hostpid = recvul(pidchan);
- while(*argv != nil){
- sendnchars(strlen(*argv), *argv);
- if(argv[1] == nil){
- sendnchars(1, "\n");
- break;
- }
- sendnchars(1, " ");
- argv++;
- }
+ emulate();
}
Image*
@@ -334,6 +325,16 @@
}
void
+hidecursor(void)
+{
+ if(cursorsave == nil)
+ return;
+ draw(screen, cursorsave->r, cursorsave, nil, cursorsave->r.min);
+ freeimage(cursorsave);
+ cursorsave = nil;
+}
+
+void
drawscreen(void)
{
int x, y, n;
@@ -342,26 +343,28 @@
Rune *rp;
Point p, q;
- draw(screen, screen->r, bgcolor, nil, ZP);
+ hidecursor();
+
+ if(scrolloff != 0){
+ n = scrolloff % (ymax+1);
+ draw(screen, Rpt(pt(0,0), pt(xmax+2, ymax+1-n)), screen, nil, pt(0, n));
+ }
- /* draw background */
for(y = 0; y <= ymax; y++){
+ if(!screenchange(y))
+ continue;
+ screenchange(y) = 0;
+
for(x = 0; x <= xmax; x += n){
cp = onscreenc(x, y);
ap = onscreena(x, y);
c = bgcol(*ap, *cp);
- if(c == bgcolor){
- n = 1;
- continue;
- }
for(n = 1; x+n <= xmax && bgcol(ap[n], cp[n]) == c; n++)
;
draw(screen, Rpt(pt(x, y), pt(x+n, y+1)), c, nil, ZP);
}
- }
+ draw(screen, Rpt(pt(x, y), pt(x+1, y+1)), bgcolor, nil, ZP);
- /* draw foreground */
- for(y = 0; y <= ymax; y++){
for(x = 0; x <= xmax; x += n){
rp = onscreenr(x, y);
if(*rp == 0){
@@ -387,6 +390,8 @@
bordercol,
ZP, font, L">", 1);
}
+
+ scrolloff = 0;
}
void
@@ -393,11 +398,20 @@
drawcursor(void)
{
Image *col;
+ Rectangle r;
+ hidecursor();
if(cursoron == 0)
return;
- col = (blocked || hostfd < 0) ? red : bordercol;
- border(screen, Rpt(pt(x, y), pt(x+1, y+1)), 2, col, ZP);
+
+ col = (blocked || hostclosed) ? red : bordercol;
+ r = Rpt(pt(x, y), pt(x+1, y+1));
+
+ cursorsave = allocimage(display, r, screen->chan, 0, DNofill);
+ draw(cursorsave, r, screen, nil, r.min);
+
+ border(screen, r, 2, col, ZP);
+
}
void
@@ -404,8 +418,12 @@
clear(int x1, int y1, int x2, int y2)
{
int c = (attr & 0x0F00)>>8; /* bgcolor */
+
+ if(y1 < 0 || y1 > ymax || x1 < 0 || x1 > xmax || y2 <= y1 || x2 <= x1)
+ return;
while(y1 < y2){
+ screenchange(y1) = 1;
if(x1 < x2){
memset(onscreenr(x1, y1), 0, (x2-x1)*sizeof(Rune));
memset(onscreena(x1, y1), 0, x2-x1);
@@ -713,8 +731,8 @@
if(host_avail())
return(rcvchar());
free(hostbuf);
- hostbufp = hostbuf = nbrecvp(hc);
- if(host_avail() && nrand(8))
+ hostbufp = hostbuf = nbrecvp(hc[1]);
+ if(host_avail() && nrand(32))
return(rcvchar());
}
drawscreen();
@@ -731,7 +749,7 @@
{ mc->c, &mc->Mouse, CHANRCV },
{ mc->resizec, nil, CHANRCV },
{ kc->c, &kbdchar, CHANRCV },
- { hc, &hostbuf, CHANRCV },
+ { hc[1], &hostbuf, CHANRCV },
{ nil, nil, CHANEND },
};
if(blocked)
@@ -755,10 +773,8 @@
break;
case AHOST:
hostbufp = hostbuf;
- if(hostbuf == nil){
- close(hostfd);
- hostfd = -1;
- }
+ if(hostbuf == nil)
+ hostclosed = 1;
break;
}
}
@@ -780,6 +796,8 @@
putenvint("LINES", ymax+1);
putenvint("COLS", xmax+1);
putenv("TERM", term);
+ if(cs->winch)
+ send_interrupt();
}
void
@@ -799,14 +817,20 @@
margin.x = (Dx(screen->r) - (xmax+1)*ftsize.x) / 2;
margin.y = (Dy(screen->r) - (ymax+1)*ftsize.y) / 2;
+ free(screenchangebuf);
+ screenchangebuf = emalloc9p(ymax+1);
+ scrolloff = 0;
+
free(onscreenrbuf);
- onscreenrbuf = mallocz((ymax+1)*(xmax+2)*sizeof(Rune), 1);
+ onscreenrbuf = emalloc9p((ymax+1)*(xmax+2)*sizeof(Rune));
free(onscreenabuf);
- onscreenabuf = mallocz((ymax+1)*(xmax+2), 1);
+ onscreenabuf = emalloc9p((ymax+1)*(xmax+2));
free(onscreencbuf);
- onscreencbuf = mallocz((ymax+1)*(xmax+2), 1);
+ onscreencbuf = emalloc9p((ymax+1)*(xmax+2));
clear(0,0,xmax+1,ymax+1);
+ draw(screen, screen->r, bgcolor, nil, ZP);
+
if(resize_flag || backc)
return;
@@ -1052,10 +1076,15 @@
void
shift(int x1, int y, int x2, int w)
{
+ if(y < 0 || y > ymax || x1 < 0 || x2 < 0 || w <= 0)
+ return;
+
if(x1+w > xmax+1)
w = xmax+1 - x1;
if(x2+w > xmax+1)
w = xmax+1 - x2;
+
+ screenchange(y) = 1;
memmove(onscreenr(x1, y), onscreenr(x2, y), w*sizeof(Rune));
memmove(onscreena(x1, y), onscreena(x2, y), w);
memmove(onscreenc(x1, y), onscreenc(x2, y), w);
@@ -1064,9 +1093,30 @@
void
scroll(int sy, int ly, int dy, int cy) /* source, limit, dest, which line to clear */
{
- memmove(onscreenr(0, dy), onscreenr(0, sy), (ly-sy)*(xmax+2)*sizeof(Rune));
- memmove(onscreena(0, dy), onscreena(0, sy), (ly-sy)*(xmax+2));
- memmove(onscreenc(0, dy), onscreenc(0, sy), (ly-sy)*(xmax+2));
+ int n, d, i;
+
+ if(sy < 0 || sy > ymax || dy < 0 || dy > ymax)
+ return;
+
+ n = ly - sy;
+ if(sy + n > ymax+1)
+ n = ymax+1 - sy;
+ if(dy + n > ymax+1)
+ n = ymax+1 - dy;
+
+ d = sy - dy;
+ if(n > 0 && d != 0){
+ if(d > 0 && dy == 0 && n >= ymax){
+ scrolloff += d;
+ } else {
+ for(i = 0; i < n; i++)
+ screenchange(dy+i) = 1;
+ }
+ memmove(onscreenr(0, dy), onscreenr(0, sy), n*(xmax+2)*sizeof(Rune));
+ memmove(onscreena(0, dy), onscreena(0, sy), n*(xmax+2));
+ memmove(onscreenc(0, dy), onscreenc(0, sy), n*(xmax+2));
+ }
+
clear(0, cy, xmax+1, cy+1);
}
@@ -1079,13 +1129,13 @@
return;
if(y < half) {
clear(0, 0, xmax+1, ymax+1);
+ scrolloff = 0;
x = y = 0;
return;
}
- memmove(onscreenr(0, 0), onscreenr(0, half), (ymax-half+1)*(xmax+2)*sizeof(Rune));
- memmove(onscreena(0, 0), onscreena(0, half), (ymax-half+1)*(xmax+2));
- memmove(onscreenc(0, 0), onscreenc(0, half), (ymax-half+1)*(xmax+2));
+ scroll(half, ymax+1, 0, ymax);
clear(0, y-half+1, xmax+1, ymax+1);
+
y -= half;
if(olines)
olines -= half;
@@ -1109,17 +1159,6 @@
/* stubs */
-void
-sendnchars(int n,char *p)
-{
- if(hostfd < 0)
- return;
- if(write(hostfd,p,n) < 0){
- close(hostfd);
- hostfd = -1;
- }
-}
-
int
host_avail(void)
{
@@ -1179,6 +1218,7 @@
void
drawstring(Rune *str, int n)
{
+ screenchange(y) = 1;
memmove(onscreenr(x, y), str, n*sizeof(Rune));
memset(onscreena(x, y), attr & 0xFF, n);
memset(onscreenc(x, y), attr >> 8, n);
--- a/sys/src/cmd/vt/mkfile
+++ b/sys/src/cmd/vt/mkfile
@@ -3,9 +3,9 @@
TARG=vt
OFILES=\
- consctl.$O\
main.$O\
vt.$O\
+ fs.$O\
HFILES=cons.h
--- a/sys/src/cmd/vt/vt.c
+++ b/sys/src/cmd/vt/vt.c
@@ -23,9 +23,10 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
-#include <bio.h>
-#include <ctype.h>
+
#include "cons.h"
+
+#include <ctype.h>
int wraparound = 1;
int originrelative = 0;