507 lines
8 KiB
C
507 lines
8 KiB
C
#include <u.h>
|
|
#include <signal.h>
|
|
#include <libc.h>
|
|
#include <ctype.h>
|
|
#include <draw.h>
|
|
#include <thread.h>
|
|
#include <mouse.h>
|
|
#include <cursor.h>
|
|
#include <keyboard.h>
|
|
#include <frame.h>
|
|
#include <plumb.h>
|
|
#include <complete.h>
|
|
#define Extern
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "term.h"
|
|
|
|
int use9wm;
|
|
int mainpid;
|
|
int plumbfd;
|
|
int rcpid;
|
|
int rcfd;
|
|
int sfd;
|
|
int noecho;
|
|
Window *w;
|
|
char *fontname;
|
|
|
|
void derror(Display*, char*);
|
|
void mousethread(void*);
|
|
void keyboardthread(void*);
|
|
void winclosethread(void*);
|
|
void deletethread(void*);
|
|
void rcoutputproc(void*);
|
|
void rcinputproc(void*);
|
|
void hangupnote(void*, char*);
|
|
void resizethread(void*);
|
|
|
|
int errorshouldabort = 0;
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: 9term [-s] [-f font] [-W winsize] [cmd ...]\n");
|
|
threadexitsall("usage");
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char *argv[])
|
|
{
|
|
char *p, *font;
|
|
|
|
rfork(RFNOTEG);
|
|
font = nil;
|
|
_wantfocuschanges = 1;
|
|
mainpid = getpid();
|
|
messagesize = 8192;
|
|
|
|
ARGBEGIN{
|
|
default:
|
|
usage();
|
|
case 'f':
|
|
font = EARGF(usage());
|
|
break;
|
|
case 's':
|
|
scrolling = TRUE;
|
|
break;
|
|
case 'w': /* started from rio or 9wm */
|
|
use9wm = TRUE;
|
|
break;
|
|
case 'W':
|
|
winsize = EARGF(usage());
|
|
break;
|
|
}ARGEND
|
|
|
|
if(font)
|
|
putenv("font", font);
|
|
|
|
p = getenv("tabstop");
|
|
if(p == 0)
|
|
p = getenv("TABSTOP");
|
|
if(p && maxtab <= 0)
|
|
maxtab = strtoul(p, 0, 0);
|
|
if(maxtab <= 0)
|
|
maxtab = 4;
|
|
free(p);
|
|
|
|
startdir = ".";
|
|
|
|
initdraw(derror, nil, "9term");
|
|
notify(hangupnote);
|
|
noteenable("sys: child");
|
|
// servedevtext();
|
|
|
|
mousectl = initmouse(nil, screen);
|
|
if(mousectl == nil)
|
|
error("cannot find mouse");
|
|
keyboardctl = initkeyboard(nil);
|
|
if(keyboardctl == nil)
|
|
error("cannot find keyboard");
|
|
if((plumbfd = plumbopen("send", OWRITE)) < 0)
|
|
fprint(2, "9term: plumbopen: %r\n");
|
|
mouse = &mousectl->m;
|
|
|
|
winclosechan = chancreate(sizeof(Window*), 0);
|
|
deletechan = chancreate(sizeof(char*), 0);
|
|
|
|
timerinit();
|
|
rcpid = rcstart(argc, argv, &rcfd, &sfd);
|
|
w = new(screen, FALSE, scrolling, rcpid, ".", nil, nil);
|
|
|
|
threadcreate(keyboardthread, nil, STACK);
|
|
threadcreate(mousethread, nil, STACK);
|
|
threadcreate(resizethread, nil, STACK);
|
|
|
|
proccreate(rcoutputproc, nil, STACK);
|
|
proccreate(rcinputproc, nil, STACK);
|
|
}
|
|
|
|
void
|
|
derror(Display *d, char *errorstr)
|
|
{
|
|
USED(d);
|
|
error(errorstr);
|
|
}
|
|
|
|
void
|
|
hangupnote(void *a, char *msg)
|
|
{
|
|
if(getpid() != mainpid)
|
|
noted(NDFLT);
|
|
if(strcmp(msg, "hangup") == 0 && rcpid != 0){
|
|
postnote(PNGROUP, rcpid, "hangup");
|
|
noted(NDFLT);
|
|
}
|
|
if(strstr(msg, "child")){
|
|
char buf[128];
|
|
int n;
|
|
|
|
n = awaitnohang(buf, sizeof buf-1);
|
|
if(n > 0){
|
|
buf[n] = 0;
|
|
if(atoi(buf) == rcpid)
|
|
threadexitsall(0);
|
|
}
|
|
noted(NCONT);
|
|
}
|
|
noted(NDFLT);
|
|
}
|
|
|
|
void
|
|
keyboardthread(void *v)
|
|
{
|
|
Rune buf[2][20], *rp;
|
|
int i, n;
|
|
|
|
USED(v);
|
|
threadsetname("keyboardthread");
|
|
n = 0;
|
|
for(;;){
|
|
rp = buf[n];
|
|
n = 1-n;
|
|
recv(keyboardctl->c, rp);
|
|
for(i=1; i<nelem(buf[0])-1; i++)
|
|
if(nbrecv(keyboardctl->c, rp+i) <= 0)
|
|
break;
|
|
rp[i] = L'\0';
|
|
sendp(w->ck, rp);
|
|
}
|
|
}
|
|
|
|
void
|
|
resizethread(void *v)
|
|
{
|
|
USED(v);
|
|
|
|
while(recv(mousectl->resizec, nil) == 1){
|
|
if(getwindow(display, Refnone) < 0)
|
|
sysfatal("can't reattach to window");
|
|
wresize(w, screen, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
mousethread(void *v)
|
|
{
|
|
int sending;
|
|
Mouse tmp;
|
|
|
|
USED(v);
|
|
|
|
sending = FALSE;
|
|
threadsetname("mousethread");
|
|
while(readmouse(mousectl) >= 0){
|
|
if(sending){
|
|
Send:
|
|
/* send to window */
|
|
if(mouse->buttons == 0)
|
|
sending = FALSE;
|
|
else
|
|
wsetcursor(w, 0);
|
|
tmp = mousectl->m;
|
|
send(w->mc.c, &tmp);
|
|
continue;
|
|
}
|
|
if((mouse->buttons&1) || ptinrect(mouse->xy, w->scrollr)){
|
|
sending = TRUE;
|
|
goto Send;
|
|
}else if(mouse->buttons&2)
|
|
button2menu(w);
|
|
else
|
|
/* send to rio */;
|
|
}
|
|
}
|
|
|
|
void
|
|
wborder(Window *w, int type)
|
|
{
|
|
}
|
|
|
|
Window*
|
|
wpointto(Point pt)
|
|
{
|
|
return w;
|
|
}
|
|
|
|
Window*
|
|
new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
|
|
{
|
|
Window *w;
|
|
Mousectl *mc;
|
|
Channel *cm, *ck, *cctl;
|
|
|
|
if(i == nil)
|
|
return nil;
|
|
cm = chancreate(sizeof(Mouse), 0);
|
|
ck = chancreate(sizeof(Rune*), 0);
|
|
cctl = chancreate(sizeof(Wctlmesg), 4);
|
|
if(cm==nil || ck==nil || cctl==nil)
|
|
error("new: channel alloc failed");
|
|
mc = emalloc(sizeof(Mousectl));
|
|
*mc = *mousectl;
|
|
// mc->image = i;
|
|
mc->c = cm;
|
|
w = wmk(i, mc, ck, cctl, scrollit);
|
|
free(mc); /* wmk copies *mc */
|
|
window = erealloc(window, ++nwindow*sizeof(Window*));
|
|
window[nwindow-1] = w;
|
|
if(hideit){
|
|
hidden[nhidden++] = w;
|
|
w->screenr = ZR;
|
|
}
|
|
threadcreate(winctl, w, 8192);
|
|
if(!hideit)
|
|
wcurrent(w);
|
|
flushimage(display, 1);
|
|
wsetpid(w, pid, 1);
|
|
wsetname(w);
|
|
if(dir)
|
|
w->dir = estrdup(dir);
|
|
return w;
|
|
}
|
|
|
|
/*
|
|
* Button 2 menu. Extra entry for always cook
|
|
*/
|
|
int cooked;
|
|
|
|
enum
|
|
{
|
|
Cut,
|
|
Paste,
|
|
Snarf,
|
|
Plumb,
|
|
Send,
|
|
Scroll,
|
|
Cook,
|
|
};
|
|
|
|
char *menu2str[] = {
|
|
"cut",
|
|
"paste",
|
|
"snarf",
|
|
"plumb",
|
|
"send",
|
|
"scroll",
|
|
"cook",
|
|
nil
|
|
};
|
|
|
|
|
|
Menu menu2 =
|
|
{
|
|
menu2str
|
|
};
|
|
|
|
Rune newline[] = { '\n' };
|
|
|
|
void
|
|
button2menu(Window *w)
|
|
{
|
|
if(w->deleted)
|
|
return;
|
|
incref(&w->ref);
|
|
if(w->scrolling)
|
|
menu2str[Scroll] = "noscroll";
|
|
else
|
|
menu2str[Scroll] = "scroll";
|
|
if(cooked)
|
|
menu2str[Cook] = "nocook";
|
|
else
|
|
menu2str[Cook] = "cook";
|
|
|
|
switch(menuhit(2, mousectl, &menu2, wscreen)){
|
|
case Cut:
|
|
wsnarf(w);
|
|
wcut(w);
|
|
wscrdraw(w);
|
|
break;
|
|
|
|
case Snarf:
|
|
wsnarf(w);
|
|
break;
|
|
|
|
case Paste:
|
|
//XXX getsnarf();
|
|
wpaste(w);
|
|
wscrdraw(w);
|
|
break;
|
|
|
|
case Plumb:
|
|
wplumb(w);
|
|
break;
|
|
|
|
case Send:
|
|
//XXX getsnarf();
|
|
wsnarf(w);
|
|
if(nsnarf == 0)
|
|
break;
|
|
if(w->rawing){
|
|
waddraw(w, snarf, nsnarf);
|
|
if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
|
|
waddraw(w, newline, 1);
|
|
}else{
|
|
winsert(w, snarf, nsnarf, w->nr);
|
|
if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
|
|
winsert(w, newline, 1, w->nr);
|
|
}
|
|
wsetselect(w, w->nr, w->nr);
|
|
wshow(w, w->nr);
|
|
break;
|
|
|
|
case Scroll:
|
|
if(w->scrolling ^= 1)
|
|
wshow(w, w->nr);
|
|
break;
|
|
|
|
case Cook:
|
|
cooked ^= 1;
|
|
break;
|
|
}
|
|
wclose(w);
|
|
wsendctlmesg(w, Wakeup, ZR, nil);
|
|
flushimage(display, 1);
|
|
}
|
|
|
|
int
|
|
rawon(void)
|
|
{
|
|
return !cooked && !isecho(sfd);
|
|
}
|
|
|
|
/*
|
|
* I/O with child rc.
|
|
*/
|
|
|
|
int label(Rune*, int);
|
|
|
|
void
|
|
rcoutputproc(void *arg)
|
|
{
|
|
int i, cnt, n, nb, nr;
|
|
static char data[9000];
|
|
Conswritemesg cwm;
|
|
Rune *r;
|
|
Stringpair pair;
|
|
|
|
i = 0;
|
|
cnt = 0;
|
|
for(;;){
|
|
/* XXX Let typing have a go -- maybe there's a rubout waiting. */
|
|
i = 1-i;
|
|
n = read(rcfd, data+cnt, sizeof data-cnt);
|
|
if(n <= 0){
|
|
if(n < 0)
|
|
fprint(2, "9term: rc read error: %r\n");
|
|
threadexitsall("eof on rc output");
|
|
}
|
|
cnt += n;
|
|
r = runemalloc(cnt);
|
|
cvttorunes(data, cnt-UTFmax, r, &nb, &nr, nil);
|
|
/* approach end of buffer */
|
|
while(fullrune(data+nb, cnt-nb)){
|
|
nb += chartorune(&r[nr], data+nb);
|
|
if(r[nr])
|
|
nr++;
|
|
}
|
|
if(nb < cnt)
|
|
memmove(data, data+nb, cnt-nb);
|
|
cnt -= nb;
|
|
|
|
nr = label(r, nr);
|
|
if(nr == 0)
|
|
continue;
|
|
|
|
recv(w->conswrite, &cwm);
|
|
pair.s = r;
|
|
pair.ns = nr;
|
|
send(cwm.cw, &pair);
|
|
}
|
|
}
|
|
|
|
void
|
|
winterrupt(Window *w)
|
|
{
|
|
char rubout[1];
|
|
|
|
USED(w);
|
|
rubout[0] = getintr(sfd);
|
|
write(rcfd, rubout, 1);
|
|
}
|
|
|
|
/*
|
|
* Process in-band messages about window title changes.
|
|
* The messages are of the form:
|
|
*
|
|
* \033];xxx\007
|
|
*
|
|
* where xxx is the new directory. This format was chosen
|
|
* because it changes the label on xterm windows.
|
|
*/
|
|
int
|
|
label(Rune *sr, int n)
|
|
{
|
|
Rune *sl, *el, *er, *r;
|
|
char *p, *dir;
|
|
|
|
er = sr+n;
|
|
for(r=er-1; r>=sr; r--)
|
|
if(*r == '\007')
|
|
break;
|
|
if(r < sr)
|
|
return n;
|
|
|
|
el = r+1;
|
|
for(sl=el-3; sl>=sr; sl--)
|
|
if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
|
|
break;
|
|
if(sl < sr)
|
|
return n;
|
|
|
|
dir = smprint("%.*S", (el-1)-(sl+3), sl+3);
|
|
if(dir){
|
|
drawsetlabel(dir);
|
|
free(w->dir);
|
|
w->dir = dir;
|
|
}
|
|
|
|
/* remove trailing /-sysname if present */
|
|
p = strrchr(dir, '/');
|
|
if(p && *(p+1) == '-'){
|
|
if(p == dir)
|
|
p++;
|
|
*p = 0;
|
|
}
|
|
|
|
runemove(sl, el, er-el);
|
|
n -= (el-sl);
|
|
return n;
|
|
}
|
|
|
|
void
|
|
rcinputproc(void *arg)
|
|
{
|
|
static char data[9000];
|
|
int s;
|
|
Consreadmesg crm;
|
|
Channel *c1, *c2;
|
|
Stringpair pair;
|
|
|
|
for(;;){
|
|
recv(w->consread, &crm);
|
|
c1 = crm.c1;
|
|
c2 = crm.c2;
|
|
|
|
pair.s = data;
|
|
pair.ns = sizeof data;
|
|
send(c1, &pair);
|
|
recv(c2, &pair);
|
|
|
|
s = setecho(sfd, 0);
|
|
if(write(rcfd, pair.s, pair.ns) < 0)
|
|
threadexitsall(nil);
|
|
if(s)
|
|
setecho(sfd, s);
|
|
}
|
|
}
|
|
|