#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include <keyboard.h>
#include "cons.h"
enum{
Ehost = 4,
};
char *menutext2[] = {
"backup",
"forward",
"reset",
"clear",
"send",
"page",
0
};
char *menutext3[] = {
"24x80",
"crnl",
"nl",
"raw",
"exit",
0
};
/* variables associated with the screen */
int x, y; /* character positions */
char *backp;
int backc;
int atend;
int nbacklines;
int xmax, ymax;
int blocked;
int resize_flag;
int pagemode;
int olines;
int peekc;
int cursoron = 1;
Menu menu2;
Menu menu3;
char *histp;
char hist[HISTSIZ];
int yscrmin, yscrmax;
int attr, defattr;
int wctlout;
Image *bordercol;
Image *cursback;
Image *colors[8];
Image *hicolors[8];
Image *red;
Image *fgcolor;
Image *bgcolor;
Image *fgdefault;
Image *bgdefault;
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, 1} };
int NS;
int CW;
Consstate *cs;
Mouse mouse;
int debug;
int nocolor;
int logfd = -1;
int outfd = -1;
Biobuf *snarffp = 0;
char *host_buf;
char *hostp; /* input from host */
int host_bsize = 2*BSIZE;
int hostlength; /* amount of input from host */
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 *sendp = sendbuf;
char *term;
struct funckey *fk;
/* functions */
void initialize(int, char **);
void ebegin(int);
int waitchar(void);
int rcvchar(void);
void set_input(char *);
void set_host(Event *);
void bigscroll(void);
void readmenu(void);
void eresized(int);
void resize(void);
void send_interrupt(void);
int alnum(int);
void escapedump(int,uchar *,int);
void
main(int argc, char **argv)
{
initialize(argc, argv);
emulate();
}
void
usage(void)
{
fprint(2, "usage: %s [-2abcx] [-f font] [-l logfile]\n", argv0);
exits("usage");
}
void
initialize(int argc, char **argv)
{
int i, blkbg;
char *fontname, *p;
rfork(RFNAMEG|RFNOTEG);
fontname = nil;
term = "vt100";
fk = vt100fk;
blkbg = nocolor = 0;
ARGBEGIN{
case '2':
term = "vt220";
fk = vt220fk;
break;
case 'a':
term = "ansi";
fk = ansifk;
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, 0666);
if(logfd < 0)
sysfatal("could not create log file: %s: %r", p);
break;
case 'x':
fk = xtermfk;
term = "xterm";
break;
default:
usage();
break;
}ARGEND;
host_buf = malloc(host_bsize);
hostp = host_buf;
hostlength = 0;
if(initdraw(0, fontname, term) < 0){
fprint(2, "%s: initdraw failed: %r\n", term);
exits("initdraw");
}
werrstr(""); /* clear spurious error messages */
ebegin(Ehost);
histp = hist;
menu2.item = menutext2;
menu3.item = menutext3;
pagemode = 0;
blocked = 0;
NS = font->height;
CW = stringwidth(font, "m");
red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
bordercol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCCC);
cursback = allocimage(display, Rect(0, 0, CW+1, NS+1), screen->chan, 0, DNofill);
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]);
}
bgdefault = (blkbg? display->black: display->white);
fgdefault = (blkbg? display->white: display->black);
bgcolor = bgdefault;
fgcolor = fgdefault;
resize();
if(argc > 0) {
sendnchars(strlen(argv[0]),argv[0]);
sendnchars(1,"\n");
}
}
void
clear(Rectangle r)
{
draw(screen, r, bgcolor, nil, ZP);
}
void
newline(void)
{
nbacklines--;
if(y >= yscrmax) {
y = yscrmax;
if(pagemode && olines >= yscrmax) {
blocked = 1;
return;
}
scroll(yscrmin+1, yscrmax+1, yscrmin, yscrmax);
} else
y++;
olines++;
}
void
cursoff(void)
{
draw(screen, Rpt(pt(x, y), addpt(pt(x, y), Pt(CW,NS))),
cursback, nil, cursback->r.min);
}
void
curson(int bl)
{
Image *col;
if(!cursoron){
cursoff();
return;
}
draw(cursback, cursback->r, screen, nil, pt(x, y));
if(bl)
col = red;
else
col = bordercol;
border(screen, Rpt(pt(x, y), addpt(pt(x, y), Pt(CW,NS))), 2, col, ZP);
}
int
get_next_char(void)
{
int c = peekc;
uchar buf[1];
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 = (uchar)waitchar();
if(c > 0 && logfd >= 0) {
buf[0] = c;
write(logfd, buf, 1);
}
}
*histp++ = c;
if(histp >= &hist[HISTSIZ])
histp = hist;
*histp = '\0';
return(c);
}
int
canon(char *ep, int c)
{
if(c&0200)
return(SCROLL);
switch(c) {
case '\b':
if(sendp > sendbuf)
sendp--;
*ep++ = '\b';
*ep++ = ' ';
*ep++ = '\b';
break;
case 0x15: /* ^U line kill */
sendp = sendbuf;
*ep++ = '^';
*ep++ = 'U';
*ep++ = '\n';
break;
case 0x17: /* ^W word kill */
while(sendp > sendbuf && !alnum(*sendp)) {
*ep++ = '\b';
*ep++ = ' ';
*ep++ = '\b';
sendp--;
}
while(sendp > sendbuf && alnum(*sendp)) {
*ep++ = '\b';
*ep++ = ' ';
*ep++ = '\b';
sendp--;
}
break;
case '\177': /* interrupt */
sendp = sendbuf;
send_interrupt();
return(NEWLINE);
case '\021': /* quit */
case '\r':
case '\n':
if(sendp < &sendbuf[512])
*sendp++ = '\n';
sendnchars((int)(sendp-sendbuf), sendbuf);
sendp = sendbuf;
if(c == '\n' || c == '\r') {
*ep++ = '\n';
}
*ep = 0;
return(NEWLINE);
case '\004': /* EOT */
if(sendp == sendbuf) {
sendnchars(0,sendbuf);
*ep = 0;
return(NEWLINE);
}
/* fall through */
default:
if(sendp < &sendbuf[512])
*sendp++ = c;
*ep++ = c;
break;
}
*ep = 0;
return(OTHER);
}
void
sendfk(char *name)
{
int i;
static int fd;
for(i=0; fk[i].name; i++)
if(strcmp(name, fk[i].name)==0){
sendnchars2(strlen(fk[i].sequence), fk[i].sequence);
return;
}
}
int
waitchar(void)
{
Event e;
int c;
char c2;
int newmouse;
int wasblocked;
int kbdchar = -1;
char echobuf[3*BSIZE];
static int lastc = -1;
for(;;) {
if(resize_flag)
resize();
wasblocked = blocked;
if(backp)
return(0);
if(ecanmouse() && (button2() || button3()))
readmenu();
if(snarffp) {
if((c = Bgetc(snarffp)) < 0) {
if(lastc != '\n')
write(outfd,"\n",1);
Bterm(snarffp);
snarffp = 0;
if(lastc != '\n') {
lastc = -1;
return('\n');
}
lastc = -1;
continue;
}
lastc = c;
c2 = c;
write(outfd, &c2, 1);
return(c);
}
if(!blocked && host_avail())
return(rcvchar());
if(kbdchar > 0) {
if(blocked)
resize();
if(cs->raw) {
switch(kbdchar){
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 Kpgup:
sendfk("page up");
break;
case Kpgdown:
sendfk("page down");
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:
echobuf[0] = kbdchar;
sendnchars(1, echobuf);
break;
}
} else if(canon(echobuf,kbdchar) == SCROLL) {
if(!blocked)
bigscroll();
} else
strcat(echo_input,echobuf);
blocked = 0;
kbdchar = -1;
continue;
}
curson(wasblocked); /* turn on cursor while we're waiting */
do {
newmouse = 0;
switch(eread(blocked ? Emouse|Ekeyboard :
Emouse|Ekeyboard|Ehost, &e)) {
case Emouse:
mouse = e.mouse;
if(button2() || button3())
readmenu();
else if(resize_flag == 0) {
/* eresized() is triggered by special mouse event */
newmouse = 1;
}
break;
case Ekeyboard:
kbdchar = e.kbdc;
break;
case Ehost:
set_host(&e);
break;
default:
perror("protocol violation");
exits("protocol violation");
}
} while(newmouse == 1);
cursoff(); /* turn cursor back off */
}
}
void
eresized(int new)
{
resize_flag = 1+new;
}
void
putenvint(char *name, int x)
{
char buf[20];
snprint(buf, sizeof buf, "%d", x);
putenv(name, buf);
}
void
exportsize(void)
{
putenvint("XPIXELS", Dx(screen->r)-2*XMARGIN);
putenvint("YPIXELS", Dy(screen->r)-2*XMARGIN);
putenvint("LINES", ymax+1);
putenvint("COLS", xmax+1);
putenv("TERM", term);
}
void
resize(void)
{
if(resize_flag > 1 && getwindow(display, Refnone) < 0){
fprint(2, "can't reattach to window: %r\n");
exits("can't reattach to window");
}
xmax = (Dx(screen->r)-2*XMARGIN)/CW-1;
ymax = (Dy(screen->r)-2*YMARGIN)/NS-1;
if(xmax == 0 || ymax == 0)
exits("window gone");
x = 0;
y = 0;
yscrmin = 0;
yscrmax = ymax;
olines = 0;
exportsize();
clear(screen->r);
resize_flag = 0;
werrstr(""); /* clear spurious error messages */
}
void
setdim(int ht, int wid)
{
int fd;
Rectangle r;
if(ht != -1)
ymax = ht-1;
if(wid != -1)
xmax = wid-1;
r.min = screen->r.min;
r.max = addpt(screen->r.min,
Pt((xmax+1)*CW+2*XMARGIN+2*INSET,
(ymax+1)*NS+2*YMARGIN+2*INSET));
fd = open("/dev/wctl", OWRITE);
if(fd < 0 || fprint(fd, "resize -dx %d -dy %d\n", Dx(r)+2*Borderwidth,
Dy(r)+2*Borderwidth) < 0){
border(screen, r, INSET, bordercol, ZP);
exportsize();
}
if(fd >= 0)
close(fd);
}
void
readmenu(void)
{
if(button3()) {
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";
switch(emenuhit(3, &mouse, &menu3)) {
case 0: /* 24x80 */
setdim(24, 80);
return;
case 1: /* newline after cr? */
ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl;
return;
case 2: /* cr after newline? */
ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr;
return;
case 3: /* switch raw mode */
cs->raw = !cs->raw;
return;
case 4:
exits(0);
}
return;
}
menu2.item[5] = pagemode? "scroll": "page";
switch(emenuhit(2, &mouse, &menu2)) {
case 0: /* back up */
if(atend == 0) {
backc++;
backup(backc);
}
return;
case 1: /* move forward */
backc--;
if(backc >= 0)
backup(backc);
else
backc = 0;
return;
case 2: /* reset */
backc = 0;
backup(0);
return;
case 3: /* clear screen */
eresized(0);
return;
case 4: /* send the snarf buffer */
snarffp = Bopen("/dev/snarf",OREAD);
return;
case 5: /* pause and clear at end of screen */
pagemode = 1-pagemode;
if(blocked && !pagemode) {
eresized(0);
blocked = 0;
}
return;
}
}
void
backup(int count)
{
register n;
register char *cp;
eresized(0);
n = 3*(count+1)*ymax/4;
cp = histp;
atend = 0;
while (n >= 0) {
cp--;
if(cp < hist)
cp = &hist[HISTSIZ-1];
if(*cp == '\0') {
atend = 1;
break;
}
if(*cp == '\n')
n--;
}
cp++;
if(cp >= &hist[HISTSIZ])
cp = hist;
backp = cp;
nbacklines = ymax-2;
}
Point
pt(int x, int y)
{
return addpt(screen->r.min, Pt(x*CW+XMARGIN,y*NS+YMARGIN));
}
void
scroll(int sy, int ly, int dy, int cy) /* source, limit, dest, which line to clear */
{
draw(screen, Rpt(pt(0, dy), pt(xmax+1, dy+ly-sy)), screen, nil, pt(0, sy));
clear(Rpt(pt(0, cy), pt(xmax+1, cy+1)));
flushimage(display, 1);
}
void
bigscroll(void) /* scroll up half a page */
{
int half = ymax/3;
if(x == 0 && y == 0)
return;
if(y < half) {
clear(Rpt(pt(0,0),pt(xmax+1,ymax+1)));
x = y = 0;
return;
}
draw(screen, Rpt(pt(0, 0), pt(xmax+1, ymax+1)), screen, nil, pt(0, half));
clear(Rpt(pt(0,y-half+1),pt(xmax+1,ymax+1)));
y -= half;
if(olines)
olines -= half;
flushimage(display, 1);
}
int
number(char *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 */
void
sendnchars(int n,char *p)
{
sendnchars2(n, p);
p[n+1] = 0;
}
void
sendnchars2(int n,char *p)
{
if(write(outfd,p,n) < 0) {
close(outfd);
close(0);
close(1);
close(2);
exits("write");
}
}
int
host_avail(void)
{
return(*echop || ((hostp - host_buf) < hostlength));
}
int
rcvchar(void)
{
int c;
if(*echop) {
c = *echop++;
if(!*echop) {
echop = echo_input;
*echop = 0;
}
return c;
}
return *hostp++;
}
void
set_host(Event *e)
{
hostlength = e->n;
if(hostlength > host_bsize) {
host_bsize *= 2;
host_buf = realloc(host_buf,host_bsize);
}
hostp = host_buf;
memmove(host_buf,e->data,hostlength);
host_buf[hostlength]=0;
}
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
funckey(int key)
{
if(key >= NKEYS)
return;
if(fk[key].name == 0)
return;
sendnchars2(strlen(fk[key].sequence), fk[key].sequence);
}
void
drawstring(Point p, char *str, int attr)
{
int i;
Image *txt, *bg, *tmp;
txt = fgcolor;
bg = bgcolor;
if(attr & TReverse){
tmp = txt;
txt = bg;
bg = tmp;
}
if(attr & THighIntensity){
for(i=0; i<8; i++)
if(txt == colors[i])
txt = hicolors[i];
}
draw(screen, Rpt(p, addpt(p, stringsize(font, str))), bg, nil, p);
string(screen, p, txt, ZP, font, str);
}
|