plan9fox/sys/src/cmd/vt/main.c
Xiao-Yong Jin 0aa0096066 vt: increase buffer size
This patch increases the buffer sizes in vt(1) to reduce
the latency from the roundtrip between terminal and cpu
server.
2022-05-11 04:02:57 +00:00

1459 lines
25 KiB
C

#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 <plumb.h>
enum menuact2{
Mbackup,
Mforward,
Mreset,
Mpaste,
Msnarf,
Mplumb,
Mpage,
};
enum menuact3{
M24x80,
Mcrnl,
Mnl,
Mraw,
Mblocksel,
Mexit,
};
char *menutext2[] = {
[Mbackup] "backup",
[Mforward] "forward",
[Mreset] "reset",
[Mpaste] "paste",
[Msnarf] "snarf",
[Mplumb] "plumb",
[Mpage] "page",
nil
};
char *menutext3[] = {
[M24x80] "24x80",
[Mcrnl] "crnl",
[Mnl] "nl",
[Mraw] "raw",
[Mblocksel] "blocksel",
[Mexit] "exit",
nil
};
/* variables associated with the screen */
int x, y; /* character positions */
Rune *backp;
int backc;
int nbacklines;
int xmax, ymax;
int blocked;
int winchgen;
int resize_flag = 1;
int pagemode;
int olines;
int peekc;
int blocksel = 0;
int cursoron = 1;
int chording = 0;
int hostclosed = 0;
Menu menu2;
Menu menu3;
Rune *histp;
Rune hist[HISTSIZ];
Rune *onscreenrbuf;
uchar *onscreenabuf;
uchar *onscreencbuf;
#define onscreenr(x, y) &onscreenrbuf[((y)*(xmax+2) + (x))]
#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;
Rectangle selrect;
Image *cursorsave;
Image *bordercol;
Image *colors[8];
Image *hicolors[8];
Image *red;
Image *green;
Image *fgcolor;
Image *bgcolor;
Image *fgselected;
Image *bgselected;
Image *highlight;
uint rgbacolors[8] = {
0x000000FF, /* black */
0xAA0000FF, /* red */
0x00AA00FF, /* green */
0xFF5500FF, /* brown */
0x0000FFFF, /* blue */
0xAA00AAFF, /* purple */
0x00AAAAFF, /* cyan */
0x7F7F7FFF, /* white */
};
ulong rgbahicolors[8] = {
0x555555FF, /* light black aka grey */
0xFF5555FF, /* light red */
0x55FF55FF, /* light green */
0xFFFF55FF, /* light brown aka yellow */
0x5555FFFF, /* light blue */
0xFF55FFFF, /* light purple */
0x55FFFFFF, /* light cyan */
0xFFFFFFFF, /* light grey aka white */
};
/* terminal control */
struct ttystate ttystate[2] = { {0, 1}, {0, 0} };
Point margin;
Point ftsize;
Rune kbdchar;
#define button(num) (mc->buttons == (1<<((num)-1)))
Mousectl *mc;
Keyboardctl *kc;
Channel *hc[2];
Consstate cs[1];
int nocolor;
int logfd = -1;
int hostpid = -1;
Biobuf *snarffp = 0;
Rune *hostbuf, *hostbufp;
char *hostin;
char echo_input[BSIZE];
char *echop = echo_input; /* characters to echo, after canon */
char sendbuf[BSIZE]; /* hope you can't type ahead more than BSIZE chars */
char *sendbufp = sendbuf;
char *term;
struct funckey *fk, *appfk;
/* functions */
int input(void);
int waitchar(void);
void waitio(void);
int rcvchar(void);
void bigscroll(void);
void readmenu(void);
void selecting(void);
int selected(int, int);
void resized(void);
void drawcursor(void);
void send_interrupt(void);
int alnum(int);
void escapedump(int,uchar *,int);
void paste(void);
void snarfsel(void);
void plumbsel(void);
static Channel *pidchan;
static void
runcmd(void *args)
{
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++;
}
procexecl(pidchan, "/bin/rc", "rcX", cmd == nil ? nil : "-c", cmd, nil);
sysfatal("%r");
}
void
send_interrupt(void)
{
if(hostpid > 0)
postnote(PNGROUP, hostpid, "interrupt");
}
void
sendnchars(int n, char *p)
{
if((n = utfnlen(p, n)) < 1)
return;
hostin = smprint("%.*s", n, p);
while(hostin != nil){
if(nbsendp(hc[0], hostin)){
hostin = nil;
break;
}
drawcursor();
waitio();
if(resize_flag)
resized();
}
}
static void
shutdown(void)
{
send_interrupt();
threadexitsall(nil);
}
static void
catch(void*, char*)
{
shutdown();
}
void
usage(void)
{
fprint(2, "usage: %s [-2abcrx] [-f font] [-l logfile] [cmd...]\n", argv0);
exits("usage");
}
void
threadmain(int argc, char **argv)
{
int rflag;
int i, blkbg;
char *fontname, *p;
fontname = nil;
fk = ansifk;
term = "vt100";
blkbg = 0;
rflag = 0;
attr = defattr;
ARGBEGIN{
case '2':
fk = vt220fk;
term = "vt220";
break;
case 'a':
term = "ansi";
break;
case 'b':
blkbg = 1; /* e.g., for linux colored output */
break;
case 'c':
nocolor = 1;
break;
case 'f':
fontname = EARGF(usage());
break;
case 'l':
p = EARGF(usage());
logfd = create(p, OWRITE|OCEXEC, 0666);
if(logfd < 0)
sysfatal("could not create log file: %s: %r", p);
break;
case 'x':
fk = vt220fk;
term = "xterm";
break;
case 'r':
rflag = 1;
break;
default:
usage();
break;
}ARGEND;
if(rfork(RFENVG) < 0)
sysfatal("rfork: %r");
quotefmtinstall();
notify(catch);
atexit(shutdown);
if(initdraw(0, fontname, term) < 0)
sysfatal("inidraw failed: %r");
if((mc = initmouse("/dev/mouse", screen)) == nil)
sysfatal("initmouse failed: %r");
if((kc = initkeyboard("/dev/cons")) == nil)
sysfatal("initkeyboard failed: %r");
hc[0] = chancreate(sizeof(char*), 256); /* input to host */
hc[1] = chancreate(sizeof(Rune*), 256); /* output from host */
cs->raw = rflag;
histp = hist;
menu2.item = menutext2;
menu3.item = menutext3;
pagemode = 0;
blocked = 0;
ftsize.y = font->height;
ftsize.x = stringwidth(font, "m");
red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen);
bordercol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCCC);
highlight = allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
for(i=0; i<8; i++){
colors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
rgbacolors[i]);
hicolors[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,
rgbahicolors[i]);
}
bgcolor = (blkbg? display->black: display->white);
fgcolor = (blkbg? display->white: display->black);
bgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? 0x333333FF : 0xCCCCCCFF);
fgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? 0xCCCCCCFF : 0x333333FF);
resized();
pidchan = chancreate(sizeof(int), 0);
proccreate(runcmd, argv, 16*1024);
hostpid = recvul(pidchan);
emulate();
}
Image*
bgcol(int a, int c, int sel)
{
if(sel)
return bgselected;
if(nocolor || (c & (1<<0)) == 0){
if(a & TReverse)
return fgcolor;
return bgcolor;
}
if((a & TReverse) != 0)
c >>= 4;
return colors[(c>>1)&7];
}
Image*
fgcol(int a, int c, int sel)
{
if(sel)
return fgselected;
if(nocolor || (c & (1<<4)) == 0){
if(a & TReverse)
return bgcolor;
return fgcolor;
}
if((a & TReverse) == 0)
c >>= 4;
if(a & THighIntensity)
return hicolors[(c>>1)&7];
return colors[(c>>1)&7];
}
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;
uchar *ap, *cp;
Image *c;
Rune *rp;
Point p, q;
hidecursor();
if(scrolloff && scrolloff <= ymax)
draw(screen, Rpt(pt(0,0), pt(xmax+2, ymax+1-scrolloff)),
screen, nil, pt(0, scrolloff));
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, selected(x, y));
for(n = 1; x+n <= xmax && bgcol(ap[n], cp[n], selected(x + n, y)) == 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);
for(x = 0; x <= xmax; x += n){
rp = onscreenr(x, y);
if(*rp == 0){
n = 1;
continue;
}
ap = onscreena(x, y);
cp = onscreenc(x, y);
c = fgcol(*ap, *cp, selected(x, y));
for(n = 1; x+n <= xmax && rp[n] != 0 && fgcol(ap[n], cp[n], selected(x + n, y)) == c
&& ((ap[n] ^ *ap) & TUnderline) == 0; n++)
;
p = pt(x, y);
q = runestringn(screen, p, c, ZP, font, rp, n);
if(*ap & TUnderline){
p.y += font->ascent+1;
q.y += font->ascent+2;
draw(screen, Rpt(p, q), c, nil, ZP);
}
}
if(*onscreenr(x, y) == 0)
runestringn(screen, pt(x, y),
bordercol,
ZP, font, L">", 1);
}
scrolloff = 0;
}
void
drawcursor(void)
{
Image *col;
Rectangle r;
hidecursor();
if(cursoron == 0)
return;
col = (hostin != nil || 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
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);
memset(onscreenc(x1, y1), c, x2-x1);
}
if(x2 > xmax)
*onscreenr(xmax+1, y1) = '\n';
y1++;
}
}
void
newline(void)
{
if(x > xmax)
*onscreenr(xmax+1, y) = 0; /* wrap arround, remove hidden newline */
nbacklines--;
if(y >= yscrmax) {
y = yscrmax;
if(pagemode && olines >= yscrmax){
blocked = 1;
return;
}
scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax);
} else
y++;
olines++;
}
int
get_next_char(void)
{
int c = peekc;
peekc = 0;
if(c > 0)
return(c);
while(c <= 0) {
if(backp) {
c = *backp;
if(c && nbacklines >= 0){
backp++;
if(backp >= &hist[HISTSIZ])
backp = hist;
return(c);
}
backp = 0;
}
c = waitchar();
if(c > 0 && logfd >= 0)
fprint(logfd, "%C", (Rune)c);
}
*histp++ = c;
if(histp >= &hist[HISTSIZ])
histp = hist;
*histp = '\0';
return(c);
}
char*
backrune(char *start, char *cp)
{
char *ep;
ep = cp;
cp -= UTFmax;
if(cp < start)
cp = start;
while(cp < ep){
Rune r;
int n;
n = chartorune(&r, cp);
if(cp + n >= ep)
break;
cp += n;
}
return cp;
}
int
canon(char *ep, Rune c)
{
switch(c) {
case Kdown:
case Kpgdown:
return SCROLL;
case '\b':
if(sendbufp > sendbuf){
sendbufp = backrune(sendbuf, sendbufp);
*ep++ = '\b';
*ep++ = ' ';
*ep++ = '\b';
}
break;
case 0x15: /* ^U line kill */
sendbufp = sendbuf;
*ep++ = '^';
*ep++ = 'U';
*ep++ = '\n';
break;
case 0x17: /* ^W word kill */
while(sendbufp > sendbuf && !alnum(*sendbufp)) {
sendbufp = backrune(sendbuf, sendbufp);
*ep++ = '\b';
*ep++ = ' ';
*ep++ = '\b';
}
while(sendbufp > sendbuf && alnum(*sendbufp)) {
sendbufp = backrune(sendbuf, sendbufp);
*ep++ = '\b';
*ep++ = ' ';
*ep++ = '\b';
}
break;
case '\177': /* interrupt */
sendbufp = sendbuf;
send_interrupt();
return(NEWLINE);
case '\021': /* quit */
case '\r':
case '\n':
if(sendbufp < &sendbuf[BSIZE])
*sendbufp++ = '\n';
sendnchars((int)(sendbufp-sendbuf), sendbuf);
sendbufp = sendbuf;
if(c == '\n' || c == '\r')
*ep++ = '\n';
*ep = 0;
return(NEWLINE);
case '\004': /* EOT */
if(sendbufp == sendbuf) {
sendnchars(0,sendbuf);
*ep = 0;
return(NEWLINE);
}
/* fall through */
default:
if(sendbufp < &sendbuf[BSIZE-UTFmax])
sendbufp += runetochar(sendbufp, &c);
ep += runetochar(ep, &c);
break;
}
*ep = 0;
return(OTHER);
}
char*
lookfk(struct funckey *fk, char *name)
{
int i;
for(i=0; fk[i].name; i++){
if(strcmp(name, fk[i].name)==0)
return fk[i].sequence;
}
return nil;
}
int
sendfk(char *name)
{
char *s = lookfk(appfk != nil ? appfk : fk, name);
if(s == nil && appfk != nil)
s = lookfk(fk, name);
if(s != nil){
sendnchars(strlen(s), s);
return 1;
}
return 0;
}
int
input(void)
{
static char echobuf[4*BSIZE];
Again:
if(resize_flag)
resized();
if(backp)
return(0);
if(snarffp) {
int c;
if((c = Bgetrune(snarffp)) < 0) {
Bterm(snarffp);
snarffp = nil;
goto Again;
}
kbdchar = c;
}
if(kbdchar) {
if(backc){
backc = 0;
backup(backc);
}
if(blocked)
resize_flag = 1;
if(cs->raw) {
switch(kbdchar){
case Kins:
if(!sendfk("insert"))
goto Send;
break;
case Kdel:
if(!sendfk("delete"))
goto Send;
break;
case Khome:
if(!sendfk("home"))
goto Send;
break;
case Kend:
if(!sendfk("end"))
goto Send;
break;
case Kpgup:
sendfk("page up");
break;
case Kpgdown:
sendfk("page down");
break;
case Kup:
sendfk("up key");
break;
case Kdown:
sendfk("down key");
break;
case Kleft:
sendfk("left key");
break;
case Kright:
sendfk("right key");
break;
case KF|1:
sendfk("F1");
break;
case KF|2:
sendfk("F2");
break;
case KF|3:
sendfk("F3");
break;
case KF|4:
sendfk("F4");
break;
case KF|5:
sendfk("F5");
break;
case KF|6:
sendfk("F6");
break;
case KF|7:
sendfk("F7");
break;
case KF|8:
sendfk("F8");
break;
case KF|9:
sendfk("F9");
break;
case KF|10:
sendfk("F10");
break;
case KF|11:
sendfk("F11");
break;
case KF|12:
sendfk("F12");
break;
case '\n':
echobuf[0] = '\r';
sendnchars(1, echobuf);
break;
case '\r':
echobuf[0] = '\n';
sendnchars(1, echobuf);
break;
default:
Send:
sendnchars(runetochar(echobuf, &kbdchar), echobuf);
break;
}
} else {
switch(canon(echobuf, kbdchar)){
case SCROLL:
if(!blocked)
bigscroll();
break;
default:
strcat(echo_input,echobuf);
}
}
blocked = 0;
kbdchar = 0;
goto Again;
} else if(nbrecv(kc->c, &kbdchar))
goto Again;
if(!blocked){
if(host_avail())
return(rcvchar());
free(hostbuf);
hostbufp = hostbuf = nbrecvp(hc[1]);
if(host_avail() && nrand(32))
return(rcvchar());
}
return -1;
}
int
waitchar(void)
{
int r;
for(;;) {
r = input();
if(r != -1)
return r;
drawscreen();
drawcursor();
waitio();
}
}
void
waitio(void)
{
enum { AMOUSE, ARESIZE, AKBD, AHOSTIN, AHOSTOUT, AEND, };
Alt a[AEND+1] = {
{ mc->c, &mc->Mouse, CHANRCV },
{ mc->resizec, nil, CHANRCV },
{ kc->c, &kbdchar, CHANRCV },
{ hc[0], &hostin, CHANSND },
{ hc[1], &hostbuf, CHANRCV },
{ nil, nil, CHANEND },
};
if(kbdchar != 0)
a[AKBD].op = CHANNOP;
if(hostin == nil)
a[AHOSTIN].op = CHANNOP;
if(blocked)
a[AHOSTOUT].op = CHANNOP;
else if(hostbuf != nil)
a[AHOSTOUT].op = CHANNOBLK;
Next:
if(display->bufp > display->buf)
flushimage(display, 1);
switch(alt(a)){
case AMOUSE:
if(button(1) || chording)
selecting();
else if(button(2) || button(3))
readmenu();
else if(button(4))
backup(backc+1);
else if(button(5) && backc > 0)
backup(--backc);
else if(resize_flag == 0)
goto Next;
break;
case ARESIZE:
resize_flag = 2;
break;
case AHOSTIN:
hostin = nil;
break;
case AHOSTOUT:
hostbufp = hostbuf;
if(hostbuf == nil)
hostclosed = 1;
break;
}
}
void
putenvint(char *name, int x)
{
char buf[20];
snprint(buf, sizeof buf, "%d", x);
putenv(name, buf);
}
void
exportsize(void)
{
putenvint("WINCH", ++winchgen);
putenvint("XPIXELS", (xmax+1)*ftsize.x);
putenvint("YPIXELS", (ymax+1)*ftsize.y);
putenvint("LINES", ymax+1);
putenvint("COLS", xmax+1);
putenv("TERM", term);
if(cs->winch)
send_interrupt();
}
void
setdim(int ht, int wid)
{
int fd;
if(wid > 0) xmax = wid-1;
if(ht > 0) ymax = ht-1;
x = 0;
y = 0;
yscrmin = 0;
yscrmax = ymax;
olines = 0;
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;
selrect = ZR;
free(onscreenrbuf);
onscreenrbuf = emalloc9p((ymax+1)*(xmax+2)*sizeof(Rune));
free(onscreenabuf);
onscreenabuf = emalloc9p((ymax+1)*(xmax+2));
free(onscreencbuf);
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;
exportsize();
fd = open("/dev/wctl", OWRITE);
if(fd >= 0){
ht = (ymax+1) * ftsize.y + 2*INSET + 2*Borderwidth;
wid = (xmax+1) * ftsize.x + ftsize.x + 2*INSET + 2*Borderwidth;
fprint(fd, "resize -dx %d -dy %d\n", wid, ht);
close(fd);
}
}
void
resized(void)
{
if(resize_flag > 1 && getwindow(display, Refnone) < 0){
fprint(2, "can't reattach to window: %r\n");
exits("can't reattach to window");
}
setdim((Dy(screen->r) - 2*INSET)/ftsize.y, (Dx(screen->r) - 2*INSET - ftsize.x)/ftsize.x);
exportsize();
if(resize_flag > 1)
backup(backc);
resize_flag = 0;
werrstr(""); /* clear spurious error messages */
}
char*
selrange(char *d, int x0, int y0, int x1, int y1)
{
Rune *s, *e;
int z, p;
s = onscreenr(x0, y0);
e = onscreenr(x1, y1);
for(z = p = 0; s < e; s++){
if(*s){
if(*s == '\n')
z = p = 0;
else if(p++ == 0){
while(z-- > 0) *d++ = ' ';
}
d += runetochar(d, s);
} else {
z++;
}
}
return d;
}
char*
selection(void)
{
char *s, *p;
int y;
/* generous, but we can spare a few bytes for a few microseconds */
s = p = malloc(UTFmax*(xmax+1)*(Dy(selrect)+1)+1);
if(s == nil)
return nil;
if(blocksel){
for(y = selrect.min.y; y <= selrect.max.y; y++){
p = selrange(p, selrect.min.x, y, selrect.max.x, y);
*p++ = '\n';
}
} else {
p = selrange(p, selrect.min.x, selrect.min.y, selrect.max.x, selrect.max.y);
}
*p = 0;
return s;
}
void
snarfsel(void)
{
Biobuf *b;
char *s;
if((s = selection()) == nil)
return;
if((b = Bopen("/dev/snarf", OWRITE|OTRUNC)) == nil){
free(s);
return;
}
Bprint(b, "%s", s);
Bterm(b);
free(s);
}
void
plumbsel(void)
{
char *s, wdir[1024];
int plumb;
s = selection();
if(s == nil || *s == 0)
return;
if(getwd(wdir, sizeof wdir) == nil){
free(s);
return;
}
if((plumb = plumbopen("send", OWRITE)) < 0){
free(s);
return;
}
plumbsendtext(plumb, "vt", nil, wdir, s);
close(plumb);
free(s);
}
void
paste(void)
{
if(snarffp == nil)
snarffp = Bopen("/dev/snarf",OREAD);
}
int
isalnum(Rune c)
{
/*
* Hard to get absolutely right. Use what we know about ASCII
* and assume anything above the Latin control characters is
* potentially an alphanumeric.
*/
if(c <= ' ')
return 0;
if(0x7F<=c && c<=0xA0)
return 0;
if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
return 0;
return 1;
}
int
isspace(Rune c)
{
return c == 0 || c == ' ' || c == '\t' ||
c == '\n' || c == '\r' || c == '\v';
}
void
unselect(void)
{
int y;
for(y = selrect.min.y; y <= selrect.max.y; y++)
screenchange(y) = 1;
selrect = ZR;
}
int
inmode(Rune r, int mode)
{
return (mode == 1) ? isalnum(r) : r && !isspace(r);
}
/*
* Selects different things based on mode.
* 0: selects swept-over text.
* 1: selects alphanumeric segment
* 2: selects non-whitespace segment.
*/
void
select(Point p, Point q, int mode)
{
if(onscreenr(p.x, p.y) > onscreenr(q.x, q.y)){
select(q, p, mode);
return;
}
unselect();
if(p.y < 0 || p.y > ymax)
return;
if(p.y < 0){
p.y = 0;
if(!blocksel) p.x = 0;
}
if(q.y > ymax){
q.y = ymax;
if(!blocksel) q.x = xmax+1;
}
if(mode != 0 && eqpt(p, q)){
while(p.x > 0 && inmode(*onscreenr(p.x-1, p.y), mode))
p.x--;
while(q.x <= xmax && inmode(*onscreenr(q.x, q.y), mode))
q.x++;
if(p.x != q.x)
mode = 0;
}
if(p.x < 0 || mode)
p.x = 0;
if(q.x > xmax+1 || mode)
q.x = xmax+1;
selrect = Rpt(p, q);
for(; p.y <= q.y; p.y++)
screenchange(p.y) = 1;
}
void
selecting(void)
{
Point p, q;
static ulong t, mode;
if(!chording){
p = pos(mc->xy);
t += mc->msec;
mode++;
do{
q = pos(mc->xy);
if(t > 200)
mode = 0;
if(mode > 2)
mode = 2;
select(p, q, mode);
drawscreen();
readmouse(mc);
} while(button(1));
}
if(mc->buttons != chording){
switch(mc->buttons & 0x7){
case 0: /* nothing */ break;
case 3: snarfsel(); break;
case 5: paste(); break;
}
}
drawscreen();
t = -mc->msec;
chording = mc->buttons;
}
int
selected(int x, int y)
{
int s;
s = y >= selrect.min.y && y <= selrect.max.y;
if (blocksel)
s = s && x >= selrect.min.x && x < selrect.max.x;
else{
if(y == selrect.min.y)
s = s && x >= selrect.min.x;
if(y == selrect.max.y)
s = s && x < selrect.max.x;
if(y > selrect.min.y && y < selrect.max.y)
s = 1;
}
return s;
}
void
readmenu(void)
{
Point p;
p = pos(mc->xy);
if(button(3)) {
menu3.item[1] = ttystate[cs->raw].crnl ? "cr" : "crnl";
menu3.item[2] = ttystate[cs->raw].nlcr ? "nl" : "nlcr";
menu3.item[3] = cs->raw ? "cooked" : "raw";
menu3.item[4] = blocksel ? "linesel" : "blocksel";
switch(menuhit(3, mc, &menu3, nil)) {
case M24x80: /* 24x80 */
setdim(24, 80);
backup(backc);
return;
case Mcrnl: /* newline after cr? */
ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl;
return;
case Mnl: /* cr after newline? */
ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr;
return;
case Mraw: /* switch raw mode */
cs->raw = !cs->raw;
return;
case Mblocksel:
unselect();
blocksel = !blocksel;
return;
case Mexit:
exits(0);
}
return;
}
menu2.item[Mpage] = pagemode? "scroll": "page";
switch(menuhit(2, mc, &menu2, nil)) {
case Mbackup: /* back up */
backup(backc+1);
return;
case Mforward: /* move forward */
if(backc > 0)
backup(--backc);
return;
case Mreset: /* reset */
backc = 0;
backup(0);
return;
case Mpaste: /* paste the snarf buffer */
paste();
return;
case Msnarf: /* send the snarf buffer */
snarfsel();
return;
case Mplumb:
plumbsel();
return;
case Mpage: /* pause and clear at end of screen */
pagemode = 1-pagemode;
if(blocked && !pagemode) {
resize_flag = 1;
blocked = 0;
}
return;
}
}
void
backup(int count)
{
Rune *cp;
int left, n;
unselect();
resize_flag = 1;
if(count == 0 && !pagemode) {
n = ymax;
nbacklines = HISTSIZ; /* make sure we scroll to the very end */
} else{
n = 3*(count+1)*ymax/4;
nbacklines = ymax-1;
}
cp = histp;
left = 1;
while (n >= 0) {
cp--;
if(cp < hist)
cp = &hist[HISTSIZ-1];
if(*cp == '\0') {
left = 0;
break;
}
if(*cp == '\n')
n--;
}
cp++;
if(cp >= &hist[HISTSIZ])
cp = hist;
backp = cp;
if(left)
backc = count;
}
Point
pt(int x, int y)
{
return addpt(screen->r.min, Pt(x*ftsize.x+margin.x,y*ftsize.y+margin.y));
}
Point
pos(Point pt)
{
pt.x -= screen->r.min.x + margin.x;
pt.y -= screen->r.min.y + margin.y;
pt.x /= ftsize.x;
pt.y /= ftsize.y;
if(pt.x < 0)
pt.x = 0;
else if(pt.x > xmax+1)
pt.x = xmax+1;
if(pt.y < 0)
pt.y = 0;
else if(pt.y > ymax+1)
pt.y = ymax+1;
return pt;
}
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);
}
void
scroll(int sy, int ly, int dy, int cy) /* source, limit, dest, which line to clear */
{
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));
}
/* move selection */
selrect.min.y -= d;
selrect.max.y -= d;
select(selrect.min, selrect.max, 0);
clear(0, cy, xmax+1, cy+1);
}
void
bigscroll(void) /* scroll up half a page */
{
int half = ymax/3;
if(x == 0 && y == 0)
return;
if(y < half) {
clear(0, 0, xmax+1, ymax+1);
scrolloff = 0;
x = y = 0;
return;
}
scroll(half, ymax+1, 0, ymax);
clear(0, y-half+1, xmax+1, ymax+1);
y -= half;
if(olines)
olines -= half;
}
int
number(Rune *p, int *got)
{
int c, n = 0;
if(got)
*got = 0;
while ((c = get_next_char()) >= '0' && c <= '9'){
if(got)
*got = 1;
n = n*10 + c - '0';
}
*p = c;
return(n);
}
/* stubs */
int
host_avail(void)
{
if(*echop != 0 && fullrune(echop, strlen(echop)))
return 1;
if(hostbuf == nil)
return 0;
return *hostbufp != 0;
}
int
rcvchar(void)
{
Rune r;
if(*echop != 0) {
echop += chartorune(&r, echop);
if(*echop == 0) {
echop = echo_input;
*echop = 0;
}
return r;
}
return *hostbufp++;
}
void
ringbell(void){
}
int
alnum(int c)
{
if(c >= 'a' && c <= 'z')
return 1;
if(c >= 'A' && c <= 'Z')
return 1;
if(c >= '0' && c <= '9')
return 1;
return 0;
}
void
escapedump(int fd,uchar *str,int len)
{
int i;
for(i = 0; i < len; i++) {
if((str[i] < ' ' || str[i] > '\177') &&
str[i] != '\n' && str[i] != '\t') fprint(fd,"^%c",str[i]+64);
else if(str[i] == '\177') fprint(fd,"^$");
else if(str[i] == '\n') fprint(fd,"^J\n");
else fprint(fd,"%c",str[i]);
}
}
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);
}