ref: 7c078308937f364f85ca6b79371508127510372f
dir: /samterm/main.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include "flayer.h"
#include "samterm.h"
Text cmd;
Rune *scratch;
long nscralloc;
Cursor *cursor;
Flayer *which = 0;
Flayer *work = 0;
long snarflen;
long typestart = -1;
long typeend = -1;
long typeesc = -1;
long modified = 0; /* strange lookahead for menus */
char hostlock = 1;
char hasunlocked = 0;
int maxtab = 8;
int autoindent;
int spacesindent;
int mainstacksize = 16*1024;
void
threadmain(int argc, char *argv[])
{
int i, got, nclick, scr, chord;
Text *t;
Rectangle r;
Flayer *nwhich;
ulong p;
rfork(RFENVG|RFNAMEG);
getscreen(argc, argv);
iconinit();
initio();
scratch = alloc(100*sizeof(Rune));
nscralloc = 100;
r = screen->r;
r.max.y = r.min.y + Dy(r)/5;
flstart(screen->clipr);
rinit(&cmd.rasp);
flnew(&cmd.l[0], gettext, 1, &cmd);
flinit(&cmd.l[0], r, font, cmdcols);
cmd.nwin = 1;
which = &cmd.l[0];
cmd.tag = Untagged;
outTs(Tversion, VERSION);
startnewfile(Tstartcmdfile, &cmd);
got = 0;
chord = 0;
for(;;got = waitforio(1)){
if(hasunlocked && RESIZED())
resize();
if(got & (1 << RHost))
rcv();
if(got & (1 << RPlumb)){
for(i = 0; !cmd.l[i].textfn; i++)
;
current(&cmd.l[i]);
flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
type(which, RPlumb);
}
if(got & (1 << RKeyboard)){
if(which)
type(which, RKeyboard);
else
kbdblock();
}
if(got & (1 << RMouse)){
if(hostlock == 2 || !ptinrect(mousep->xy, screen->r)){
mouseunblock();
continue;
}
nwhich = flwhich(mousep->xy);
scr = which && (ptinrect(mousep->xy, which->scroll) ||
mousep->buttons & (8|16));
if(mousep->buttons)
flushtyping(1);
if((mousep->buttons & 1) == 0)
chord = 0;
if(chord && which && which == nwhich){
chord |= mousep->buttons;
t = which->user1;
if(!t->lock){
int w = which-t->l;
if(chord & 2){
cut(t, w, 1, 1);
chord &= ~2;
}
if(chord & 4){
paste(t, w);
chord &= ~4;
}
}
}else if(mousep->buttons & (1|8)){
if(scr)
scroll(which, (mousep->buttons & 8) ? 4 : 1);
else if(nwhich && nwhich != which)
current(nwhich);
else if(ptinrect(mousep->xy, which->f.r)){
t = which->user1;
nclick = flselect(which, &p);
if(nclick > 0){
if(nclick > 1)
outTsl(Ttclick, t->tag, p);
else
outTsl(Tdclick, t->tag, p);
t->lock++;
}else if(t != &cmd)
outcmd();
if(mousep->buttons & 1)
chord = mousep->buttons;
}
}else if((mousep->buttons & 2) && which){
if(scr)
scroll(which, 2);
else
menu2hit();
}else if(mousep->buttons & (4|16)){
if(scr)
scroll(which, (mousep->buttons & 16)? 5 : 3);
else
menu3hit();
}
mouseunblock();
}
}
}
void
resize(void)
{
int i;
flresize(screen->clipr);
for(i = 0; i < nname; i++)
if(text[i])
hcheck(text[i]->tag);
}
void
current(Flayer *nw)
{
Text *t;
if(which)
flborder(which, 0);
if(nw){
flushtyping(1);
flupfront(nw);
flborder(nw, 1);
buttons(Up);
t = nw->user1;
t->front = nw - t->l;
if(t != &cmd)
work = nw;
}
which = nw;
}
void
closeup(Flayer *l)
{
Text *t = l->user1;
int m;
m = whichmenu(t->tag);
if(m < 0)
return;
flclose(l);
if(l == which){
which = 0;
current(flwhich(Pt(0, 0)));
}
if(l == work)
work = 0;
if(--t->nwin == 0){
rclear(&t->rasp);
free((uchar *)t);
text[m] = 0;
}else if(l == &t->l[t->front]){
for(m=0; m < NL; m++) /* find one; any one will do */
if(t->l[m].textfn){
t->front = m;
return;
}
panic("close");
}
}
Flayer*
findl(Text *t)
{
int i;
for(i = 0; i < NL; i++)
if(!t->l[i].textfn)
return &t->l[i];
return 0;
}
void
duplicate(Flayer *l, Rectangle r, Font *f, int close)
{
Text *t = l->user1;
Flayer *nl = findl(t);
Rune *rp;
ulong n;
if(nl){
flnew(nl, gettext, l->user0, (char *)t);
flinit(nl, r, f, l->f.cols);
nl->origin = l->origin;
rp = (*l->textfn)(l, l->f.nchars, &n);
flinsert(nl, rp, rp+n, l->origin);
flsetselect(nl, l->p0, l->p1);
if(close){
flclose(l);
if(l == which)
which = nil;
}else
t->nwin++;
current(nl);
hcheck(t->tag);
}
setcursor(mousectl, cursor);
}
void
buttons(int updown)
{
while(((mousep->buttons & 7) != 0) != updown)
if(readmouse(mousectl) < 0)
panic("mouse");
}
int
getr(Rectangle *rp)
{
Point p;
Rectangle r;
*rp = getrect(3, mousectl);
if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
p = rp->min;
r = cmd.l[cmd.front].entire;
*rp = screen->r;
if(cmd.nwin==1){
if (p.y <= r.min.y)
rp->max.y = r.min.y;
else if (p.y >= r.max.y)
rp->min.y = r.max.y;
if (p.x <= r.min.x)
rp->max.x = r.min.x;
else if (p.x >= r.max.x)
rp->min.x = r.max.x;
}
}
return rectclip(rp, screen->r) &&
rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
}
void
snarf(Text *t, int w)
{
Flayer *l;
l = &t->l[w];
if(l->p1 > l->p0){
snarflen = l->p1 - l->p0;
outTsll(Tsnarf, t->tag, l->p0, l->p1);
}
}
void
cut(Text *t, int w, int save, int check)
{
long p0, p1;
Flayer *l;
l = &t->l[w];
p0 = l->p0;
p1 = l->p1;
if(p0 == p1)
return;
if(p0 < 0)
panic("cut");
if(save)
snarf(t, w);
outTsll(Tcut, t->tag, p0, p1);
flsetselect(l, p0, p0);
t->lock++;
hcut(t->tag, p0, p1 - p0);
if(check)
hcheck(t->tag);
}
void
paste(Text *t, int w)
{
if(snarflen){
cut(t, w, 0, 0);
t->lock++;
outTsl(Tpaste, t->tag, t->l[w].p0);
}
}
Rune
raspc(Rasp *r, long p)
{
ulong n;
rload(r, p, p+1, &n);
if(n)
return scratch[0];
return 0;
}
int
getcol(Rasp *r, long p)
{
int c;
for(c = 0; p > 0; c++)
if(raspc(r, --p) == '\n')
break;
return c;
}
long
del(Rasp *r, long o, long p)
{
int i, col, n;
if(--p < o)
return o;
if(!spacesindent || raspc(r, p) != ' ')
return p;
col = (getcol(r, p) + 1) % maxtab;;
if((n = col) == 0)
n = maxtab;
for(i = 0; p-1>=o && raspc(r, p-1)==' ' && i<n-1; --p, i++)
;
return p>=o? p : o;
}
long
ctlw(Rasp *r, long o, long p)
{
int c;
if(--p < o)
return o;
if(raspc(r, p)=='\n')
return p;
for(; p>=o && !isalpharune(c=raspc(r, p)); --p)
if(c=='\n')
return p+1;
for(; p>o && isalpharune(raspc(r, p-1)); --p)
;
return p>=o? p : o;
}
long
ctlu(Rasp *r, long o, long p)
{
if(--p < o)
return o;
if(raspc(r, p) == '\n')
return p;
for(; p-1 >= o && raspc(r, p-1) != '\n'; --p)
;
return p >= o? p : o;
}
void
request(Text *t, int n, int m)
{
Rasp *r;
int len;
int c;
r = &t->rasp;
do{
len = m - n;
if(len > TBLOCKSIZE)
len = TBLOCKSIZE;
if((c = rcontig(r, n, n+len, 1)) == len)
continue;
outTsls(Trequest, t->tag, n+c, len-c);
t->lock++;
/* waitforio(1) unsets the block. */
setblock(~(1 << RHost));
waitforio(1);
rcv();
}while((n += len) < m);
}
long
bound(Text *t, long a)
{
if(a >= t->rasp.nrunes)
a = t->rasp.nrunes-1;
if(a < 0)
a = 0;
return a;
}
/* todo: cleanup. */
void
center(Flayer *l, long a, int nl)
{
Frame *f = &l->f;
Text *t = l->user1;
Rune r;
int n;
if(t->tag == Untagged)
return;
a = bound(t, a);
if(nl > 0)
a += f->nchars;
n = bound(t, a + (nl? nl: -1) * l->width);
if(n != a)
request(t, n < a? n: a, n < a? a: n);
if(nl <= 0){
while(nl++ <= 0 && a > 0)
for(n = 0; n < l->width && a > 0; n++){
r = raspc(&t->rasp, --a);
if(r == '\n')
break;
if(r == '\t')
n += maxtab - (n % maxtab) - 1;
}
a += a != 0;
}else{
a -= f->nchars;
while(nl-- > 0 && a < t->rasp.nrunes)
for(n = 0; n < l->width && a < t->rasp.nrunes; n++){
r = raspc(&t->rasp, a++);
if(r == '\n')
break;
if(r == '\t')
n += maxtab - (n % maxtab) - 1;
}
}
origin(t->tag, a);
}
void
flushtyping(int clearesc)
{
Text *t;
ulong n;
if(clearesc)
typeesc = -1;
if(typestart == typeend) {
modified = 0;
return;
}
t = which->user1;
if(t != &cmd)
modified = 1;
rload(&t->rasp, typestart, typeend, &n);
scratch[n] = 0;
if(t == &cmd && typeend == t->rasp.nrunes && scratch[typeend-typestart-1] == '\n'){
setlock();
outcmd();
}
outTslS(Ttype, t->tag, typestart, scratch);
typestart = -1;
typeend = -1;
}
int
nontypingkey(int c)
{
switch(c){
case Kup:
case Kdown:
case Khome:
case Kend:
case Kpgdown:
case Kpgup:
case Kleft:
case Kright:
case Ksoh:
case Kenq:
case Kstx:
case Kbel:
return 1;
}
return 0;
}
void
type(Flayer *l, int res) /* what a bloody mess this is */
{
Text *t = l->user1;
Rune buf[100];
Rune *p = buf;
int scrollkey;
int c, i;
long a, a0;
scrollkey = 0;
if(res == RKeyboard)
scrollkey = nontypingkey(qpeekc()); /* ICK */
if(hostlock || t->lock){
kbdblock();
return;
}
a = l->p0;
if(a != l->p1 && !scrollkey){
flushtyping(1);
cut(t, t->front, 1, 1);
return; /* it may now be locked */
}
while((c = kbdchar()) > 0){
if(res == RKeyboard){
if(nontypingkey(c) || c == Kesc)
break;
/* backspace, ctrl-u, ctrl-w, del */
if(c == Kbs || c == Knack || c == Ketb || c == Kdel)
break;
}
if(spacesindent && c == '\t'){
int col, n;
col = getcol(&t->rasp, a);
n = maxtab - col % maxtab;
for(i = 0; i < n && p < buf+nelem(buf); i++)
*p++ = ' ';
} else
*p++ = c;
if(c == '\n' && autoindent && t != &cmd){
/* autoindent */
int cursor, ch;
cursor = ctlu(&t->rasp, 0, a+(p-buf)-1);
while(p < buf+nelem(buf)){
ch = raspc(&t->rasp, cursor++);
if(ch == ' ' || ch == '\t')
*p++ = ch;
else
break;
}
}
if(c == '\n' || p >= buf+nelem(buf))
break;
}
if(p > buf){
if(typestart < 0)
typestart = a;
if(typeesc < 0)
typeesc = a;
hgrow(t->tag, a, p-buf, 0);
t->lock++; /* pretend we Trequest'ed for hdata */
hdata(t->tag, a, buf, p-buf);
a += p-buf;
l->p0 = a;
l->p1 = a;
typeend = a;
if(c == '\n' || typeend-typestart > nelem(buf))
flushtyping(0);
if(!t->lock && a < l->origin || a > l->origin+l->f.nchars)
center(l, a, -(l->f.maxlines/3));
}
switch(c){
case Kdown:
flushtyping(0);
center(l, l->origin, 1);
break;
case Kpgdown:
flushtyping(0);
center(l, l->origin, l->f.maxlines);
break;
case Kup:
flushtyping(0);
center(l, l->origin, -1);
break;
case Kpgup:
flushtyping(0);
center(l, l->origin, -l->f.maxlines);
break;
case Kleft:
flushtyping(0);
a0 = l->p0;
a0 -= (l->p0 > 0);
flsetselect(l, a0, a0);
if(a0 == l->origin-1)
center(l, l->origin, -1);
else if(a0 < l->origin-1 || a0 > l->origin+l->f.nchars)
center(l, a0, -1);
break;
case Kright:
flushtyping(0);
a0 = l->p1;
a0 += a < t->rasp.nrunes;
flsetselect(l, a0, a0);
if(a0 == l->origin+l->f.nchars)
center(l, l->origin, 1);
else if(a0 < l->origin || a0 > l->origin+l->f.nchars)
center(l, a0, -1);
break;
case Khome:
flushtyping(0);
center(l, 0, 0);
break;
case Kend:
flushtyping(0);
center(l, t->rasp.nrunes, 0);
break;
case Ksoh: /* ctrl+a */
flushtyping(1);
for(; a > 0; a--)
if(raspc(&t->rasp, a-1) == '\n')
break;
l->p0 = l->p1 = a;
goto Setsel;
case Kenq: /* ctrl+e */
flushtyping(1);
for(; a < t->rasp.nrunes; a++)
if(raspc(&t->rasp, a) == '\n')
break;
l->p0 = l->p1 = a;
goto Setsel;
case Kbs:
case Knack:
case Ketb:
case Kdel:
if(!hostlock){
/* backspacing immediately after outcmd(): sorry */
if(l->f.p0>0 && a>0){
switch(c){
case Kbs:
case Kdel: /* del */
l->p0 = del(&t->rasp, l->origin, a);
break;
case Knack: /* ctrl-u */
l->p0 = ctlu(&t->rasp, l->origin, a);
break;
case Ketb: /* ctrl-w */
l->p0 = ctlw(&t->rasp, l->origin, a);
break;
}
l->p1 = a;
if(l->p1 != l->p0){
/* cut locally if possible */
if(typestart<=l->p0 && l->p1<=typeend){
t->lock++; /* to call hcut */
hcut(t->tag, l->p0, l->p1-l->p0);
/* hcheck is local because we know rasp is contiguous */
hcheck(t->tag);
}else{
flushtyping(0);
cut(t, t->front, 0, 1);
}
}
if(typeesc >= l->p0)
typeesc = l->p0;
if(typestart >= 0){
if(typestart >= l->p0)
typestart = l->p0;
typeend = l->p0;
if(typestart == typeend){
typestart = -1;
typeend = -1;
modified = 0;
}
}
}
}
break;
case Kstx:
t = &cmd;
for(l=t->l; l->textfn==0; l++)
;
current(l);
flushtyping(0);
a = t->rasp.nrunes;
flsetselect(l, a, a);
center(l, a, 0);
break;
case Kbel:
if(work == nil)
return;
if(which != work){
current(work);
return;
}
t = (Text*)work->user1;
l = &t->l[t->front];
for(i=t->front; t->nwin>1 && (i = (i+1)%NL) != t->front; )
if(t->l[i].textfn){
l = &t->l[i];
break;
}
current(l);
break;
case Kesc:
if(typeesc >= 0){
l->p0 = typeesc;
l->p1 = a;
flushtyping(1);
} /* wet floor */
Setsel:
default:
for(l=t->l; l<&t->l[NL]; l++)
if(l->textfn)
flsetselect(l, l->p0, l->p1);
}
}
void
outcmd(void)
{
if(work)
outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
}
void
panic(char *s)
{
panic1(display, s);
}
void
panic1(Display*, char *s)
{
fprint(2, "samterm: panic: ");
perror(s);
abort();
}
Rune*
gettext(Flayer *l, long n, ulong *np)
{
Text *t;
t = l->user1;
rload(&t->rasp, l->origin, l->origin+n, np);
return scratch;
}
long
scrtotal(Flayer *l)
{
return ((Text *)l->user1)->rasp.nrunes;
}
void*
alloc(ulong n)
{
void *p;
p = mallocz(n, 1);
if(!p)
panic("alloc");
return p;
}