![cinap_lenrek](/assets/img/avatar_default.png)
allocate all the colors in iconinit(), remove unused ones like grey. rename darkgrey to paletextcol because thats what it is used for. new approach to window image allocation. we allocate the window with DNofill and let the window fill itself. this reduces flickering especially with (-b) option and makes rio resize feel a lot faster. wrefresh() didnt work. now fixed.
1714 lines
32 KiB
C
1714 lines
32 KiB
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 <fcall.h>
|
|
#include <plumb.h>
|
|
#include <complete.h>
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
|
|
#define MOVEIT if(0)
|
|
|
|
enum
|
|
{
|
|
HiWater = 640000, /* max size of history */
|
|
LoWater = 400000, /* min size of history after max'ed */
|
|
MinWater = 20000, /* room to leave available when reallocating */
|
|
};
|
|
|
|
static int topped;
|
|
static int id;
|
|
static Cursor *lastcursor;
|
|
|
|
Window*
|
|
wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
|
|
{
|
|
Window *w;
|
|
Rectangle r;
|
|
|
|
w = emalloc(sizeof(Window));
|
|
w->screenr = i->r;
|
|
r = insetrect(i->r, Selborder+1);
|
|
w->i = i;
|
|
w->mc = *mc;
|
|
w->ck = ck;
|
|
w->cctl = cctl;
|
|
w->cursorp = nil;
|
|
w->conswrite = chancreate(sizeof(Conswritemesg), 0);
|
|
w->consread = chancreate(sizeof(Consreadmesg), 0);
|
|
w->kbdread = chancreate(sizeof(Kbdreadmesg), 0);
|
|
w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
|
|
w->wctlread = chancreate(sizeof(Consreadmesg), 0);
|
|
w->scrollr = r;
|
|
w->scrollr.max.x = r.min.x+Scrollwid;
|
|
w->lastsr = ZR;
|
|
r.min.x += Scrollwid+Scrollgap;
|
|
frinit(w, r, font, i, cols);
|
|
w->maxtab = maxtab*stringwidth(font, "0");
|
|
w->topped = ++topped;
|
|
w->id = ++id;
|
|
w->notefd = -1;
|
|
w->scrolling = scrolling;
|
|
w->dir = estrdup(startdir);
|
|
w->label = estrdup("<unnamed>");
|
|
r = insetrect(w->i->r, Selborder);
|
|
draw(w->i, r, cols[BACK], nil, w->entire.min);
|
|
wborder(w, Selborder);
|
|
wscrdraw(w);
|
|
incref(w); /* ref will be removed after mounting; avoids delete before ready to be deleted */
|
|
return w;
|
|
}
|
|
|
|
void
|
|
wsetname(Window *w)
|
|
{
|
|
int i, n;
|
|
char err[ERRMAX];
|
|
|
|
n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
|
|
for(i='A'; i<='Z'; i++){
|
|
if(nameimage(w->i, w->name, 1) > 0)
|
|
return;
|
|
errstr(err, sizeof err);
|
|
if(strcmp(err, "image name in use") != 0)
|
|
break;
|
|
w->name[n] = i;
|
|
w->name[n+1] = 0;
|
|
}
|
|
w->name[0] = 0;
|
|
fprint(2, "rio: setname failed: %s\n", err);
|
|
}
|
|
|
|
void
|
|
wresize(Window *w, Image *i, int move)
|
|
{
|
|
Rectangle r, or;
|
|
|
|
or = w->i->r;
|
|
if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
|
|
draw(i, i->r, w->i, nil, w->i->r.min);
|
|
freeimage(w->i);
|
|
w->i = i;
|
|
w->mc.image = i;
|
|
r = insetrect(i->r, Selborder+1);
|
|
w->scrollr = r;
|
|
w->scrollr.max.x = r.min.x+Scrollwid;
|
|
w->lastsr = ZR;
|
|
r.min.x += Scrollwid+Scrollgap;
|
|
if(move)
|
|
frsetrects(w, r, w->i);
|
|
else{
|
|
frclear(w, FALSE);
|
|
frinit(w, r, w->font, w->i, cols);
|
|
wsetcols(w);
|
|
w->maxtab = maxtab*stringwidth(w->font, "0");
|
|
r = insetrect(w->i->r, Selborder);
|
|
draw(w->i, r, cols[BACK], nil, w->entire.min);
|
|
wfill(w);
|
|
wsetselect(w, w->q0, w->q1);
|
|
wscrdraw(w);
|
|
}
|
|
if(w == input)
|
|
wborder(w, Selborder);
|
|
else
|
|
wborder(w, Unselborder);
|
|
wsetname(w);
|
|
w->topped = ++topped;
|
|
w->resized = TRUE;
|
|
w->mouse.counter++;
|
|
w->wctlready = 1;
|
|
}
|
|
|
|
void
|
|
wrefresh(Window *w, Rectangle r)
|
|
{
|
|
/* BUG: rectangle is ignored */
|
|
if(w == input)
|
|
wborder(w, Selborder);
|
|
else
|
|
wborder(w, Unselborder);
|
|
r = insetrect(w->i->r, Selborder);
|
|
draw(w->i, r, w->cols[BACK], nil, w->entire.min);
|
|
w->ticked = 0;
|
|
if(w->p0 > 0)
|
|
frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0);
|
|
if(w->p1 < w->nchars)
|
|
frdrawsel(w, frptofchar(w, w->p1), w->p1, w->nchars, 0);
|
|
frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 1);
|
|
w->lastsr = ZR;
|
|
wscrdraw(w);
|
|
}
|
|
|
|
int
|
|
wclose(Window *w)
|
|
{
|
|
int i;
|
|
|
|
i = decref(w);
|
|
if(i > 0)
|
|
return 0;
|
|
if(i < 0)
|
|
error("negative ref count");
|
|
if(!w->deleted)
|
|
wclunk(w);
|
|
wsendctlmesg(w, Exited, ZR, nil);
|
|
return 1;
|
|
}
|
|
|
|
|
|
void
|
|
winctl(void *arg)
|
|
{
|
|
Rune *rp, *bp, *tp, *up;
|
|
uint qh;
|
|
int nr, nb, c, wid, i, npart, initial, lastb;
|
|
char *s, *t, part[3];
|
|
Window *w;
|
|
Mousestate *mp, m;
|
|
enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
|
|
Alt alts[NWALT+1];
|
|
Mousereadmesg mrm;
|
|
Kbdreadmesg krm;
|
|
Conswritemesg cwm;
|
|
Consreadmesg crm;
|
|
Consreadmesg cwrm;
|
|
Stringpair pair;
|
|
Wctlmesg wcm;
|
|
char buf[4*12+1], *kbdq[8], *kbds;
|
|
int kbdqr, kbdqw;
|
|
|
|
w = arg;
|
|
snprint(buf, sizeof buf, "winctl-id%d", w->id);
|
|
threadsetname(buf);
|
|
|
|
mrm.cm = chancreate(sizeof(Mouse), 0);
|
|
krm.ck = chancreate(sizeof(char*), 0);
|
|
cwm.cw = chancreate(sizeof(Stringpair), 0);
|
|
crm.c1 = chancreate(sizeof(Stringpair), 0);
|
|
crm.c2 = chancreate(sizeof(Stringpair), 0);
|
|
cwrm.c1 = chancreate(sizeof(Stringpair), 0);
|
|
cwrm.c2 = chancreate(sizeof(Stringpair), 0);
|
|
|
|
alts[WKbd].c = w->ck;
|
|
alts[WKbd].v = &kbds;
|
|
alts[WKbd].op = CHANRCV;
|
|
alts[WKbdread].c = w->kbdread;
|
|
alts[WKbdread].v = &krm;
|
|
alts[WKbdread].op = CHANSND;
|
|
alts[WMouse].c = w->mc.c;
|
|
alts[WMouse].v = &w->mc.Mouse;
|
|
alts[WMouse].op = CHANRCV;
|
|
alts[WMouseread].c = w->mouseread;
|
|
alts[WMouseread].v = &mrm;
|
|
alts[WMouseread].op = CHANSND;
|
|
alts[WCtl].c = w->cctl;
|
|
alts[WCtl].v = &wcm;
|
|
alts[WCtl].op = CHANRCV;
|
|
alts[WCwrite].c = w->conswrite;
|
|
alts[WCwrite].v = &cwm;
|
|
alts[WCwrite].op = CHANSND;
|
|
alts[WCread].c = w->consread;
|
|
alts[WCread].v = &crm;
|
|
alts[WCread].op = CHANSND;
|
|
alts[WWread].c = w->wctlread;
|
|
alts[WWread].v = &cwrm;
|
|
alts[WWread].op = CHANSND;
|
|
alts[NWALT].op = CHANEND;
|
|
|
|
memset(kbdq, 0, sizeof(kbdq));
|
|
kbdqr = kbdqw = 0;
|
|
npart = 0;
|
|
lastb = -1;
|
|
for(;;){
|
|
alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
|
|
CHANSND : CHANNOP;
|
|
alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ?
|
|
CHANSND : CHANNOP;
|
|
alts[WCwrite].op = (!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars) ?
|
|
CHANNOP : CHANSND;
|
|
alts[WWread].op = (w->deleted || !w->wctlready) ?
|
|
CHANNOP : CHANSND;
|
|
|
|
/* this code depends on NL and EOT fitting in a single byte */
|
|
/* kind of expensive for each loop; worth precomputing? */
|
|
if(w->holding)
|
|
alts[WCread].op = CHANNOP;
|
|
else if(npart || (w->rawing && w->nraw>0))
|
|
alts[WCread].op = CHANSND;
|
|
else{
|
|
alts[WCread].op = CHANNOP;
|
|
for(i=w->qh; i<w->nr; i++){
|
|
c = w->r[i];
|
|
if(c=='\n' || c=='\004'){
|
|
alts[WCread].op = CHANSND;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
switch(alt(alts)){
|
|
case WKbd:
|
|
if(w->kbdopen){
|
|
i = (kbdqw+1) % nelem(kbdq);
|
|
if(i != kbdqr)
|
|
kbdqw = i;
|
|
} else if(*kbds == 'c'){
|
|
Rune r;
|
|
|
|
chartorune(&r, kbds+1);
|
|
if(r)
|
|
wkeyctl(w, r);
|
|
}
|
|
free(kbdq[kbdqw]);
|
|
kbdq[kbdqw] = kbds;
|
|
break;
|
|
|
|
case WKbdread:
|
|
i = (kbdqr+1) % nelem(kbdq);
|
|
if(kbdqr != kbdqw)
|
|
kbdqr = i;
|
|
if(kbdq[i]){
|
|
sendp(krm.ck, kbdq[i]);
|
|
kbdq[i] = nil;
|
|
}else
|
|
sendp(krm.ck, strdup("K"));
|
|
continue;
|
|
|
|
case WMouse:
|
|
if(w->mouseopen) {
|
|
w->mouse.counter++;
|
|
|
|
/* queue click events */
|
|
if(!w->mouse.qfull && lastb != w->mc.buttons) { /* add to ring */
|
|
mp = &w->mouse.queue[w->mouse.wi];
|
|
if(++w->mouse.wi == nelem(w->mouse.queue))
|
|
w->mouse.wi = 0;
|
|
if(w->mouse.wi == w->mouse.ri)
|
|
w->mouse.qfull = TRUE;
|
|
mp->Mouse = w->mc;
|
|
mp->counter = w->mouse.counter;
|
|
lastb = w->mc.buttons;
|
|
}
|
|
} else
|
|
wmousectl(w);
|
|
break;
|
|
case WMouseread:
|
|
/* send a queued event or, if the queue is empty, the current state */
|
|
/* if the queue has filled, we discard all the events it contained. */
|
|
/* the intent is to discard frantic clicking by the user during long latencies. */
|
|
w->mouse.qfull = FALSE;
|
|
if(w->mouse.wi != w->mouse.ri) {
|
|
m = w->mouse.queue[w->mouse.ri];
|
|
if(++w->mouse.ri == nelem(w->mouse.queue))
|
|
w->mouse.ri = 0;
|
|
} else
|
|
m = (Mousestate){w->mc.Mouse, w->mouse.counter};
|
|
|
|
w->mouse.lastcounter = m.counter;
|
|
send(mrm.cm, &m.Mouse);
|
|
continue;
|
|
case WCtl:
|
|
if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
|
|
for(i=0; i<nelem(kbdq); i++)
|
|
free(kbdq[i]);
|
|
chanfree(crm.c1);
|
|
chanfree(crm.c2);
|
|
chanfree(mrm.cm);
|
|
chanfree(krm.ck);
|
|
chanfree(cwm.cw);
|
|
chanfree(cwrm.c1);
|
|
chanfree(cwrm.c2);
|
|
threadexits(nil);
|
|
}
|
|
continue;
|
|
case WCwrite:
|
|
recv(cwm.cw, &pair);
|
|
rp = pair.s;
|
|
nr = pair.ns;
|
|
bp = rp;
|
|
for(i=0; i<nr; i++)
|
|
if(*bp++ == '\b'){
|
|
--bp;
|
|
initial = 0;
|
|
tp = runemalloc(nr);
|
|
runemove(tp, rp, i);
|
|
up = tp+i;
|
|
for(; i<nr; i++){
|
|
*up = *bp++;
|
|
if(*up == '\b')
|
|
if(up == tp)
|
|
initial++;
|
|
else
|
|
--up;
|
|
else
|
|
up++;
|
|
}
|
|
if(initial){
|
|
if(initial > w->qh)
|
|
initial = w->qh;
|
|
qh = w->qh-initial;
|
|
wdelete(w, qh, qh+initial);
|
|
w->qh = qh;
|
|
}
|
|
free(rp);
|
|
rp = tp;
|
|
nr = up-tp;
|
|
rp[nr] = 0;
|
|
break;
|
|
}
|
|
w->qh = winsert(w, rp, nr, w->qh)+nr;
|
|
if(w->scrolling || w->mouseopen)
|
|
wshow(w, w->qh);
|
|
wsetselect(w, w->q0, w->q1);
|
|
wscrdraw(w);
|
|
free(rp);
|
|
break;
|
|
case WCread:
|
|
recv(crm.c1, &pair);
|
|
t = pair.s;
|
|
nb = pair.ns;
|
|
i = npart;
|
|
npart = 0;
|
|
if(i)
|
|
memmove(t, part, i);
|
|
while(i<nb && (w->qh<w->nr || w->nraw>0)){
|
|
if(w->qh == w->nr){
|
|
wid = runetochar(t+i, &w->raw[0]);
|
|
w->nraw--;
|
|
runemove(w->raw, w->raw+1, w->nraw);
|
|
}else
|
|
wid = runetochar(t+i, &w->r[w->qh++]);
|
|
c = t[i]; /* knows break characters fit in a byte */
|
|
i += wid;
|
|
if(!w->rawing && (c == '\n' || c=='\004')){
|
|
if(c == '\004')
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004')
|
|
w->qh++;
|
|
if(i > nb){
|
|
npart = i-nb;
|
|
memmove(part, t+nb, npart);
|
|
i = nb;
|
|
}
|
|
pair.s = t;
|
|
pair.ns = i;
|
|
send(crm.c2, &pair);
|
|
continue;
|
|
case WWread:
|
|
w->wctlready = 0;
|
|
recv(cwrm.c1, &pair);
|
|
if(w->deleted || w->i==nil)
|
|
pair.ns = sprint(pair.s, "");
|
|
else{
|
|
s = "visible";
|
|
for(i=0; i<nhidden; i++)
|
|
if(hidden[i] == w){
|
|
s = "hidden";
|
|
break;
|
|
}
|
|
t = "notcurrent";
|
|
if(w == input)
|
|
t = "current";
|
|
pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
|
|
w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
|
|
}
|
|
send(cwrm.c2, &pair);
|
|
continue;
|
|
}
|
|
if(!w->deleted)
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
waddraw(Window *w, Rune *r, int nr)
|
|
{
|
|
w->raw = runerealloc(w->raw, w->nraw+nr);
|
|
runemove(w->raw+w->nraw, r, nr);
|
|
w->nraw += nr;
|
|
}
|
|
|
|
/*
|
|
* Need to do this in a separate proc because if process we're interrupting
|
|
* is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
|
|
*/
|
|
void
|
|
interruptproc(void *v)
|
|
{
|
|
int *notefd;
|
|
|
|
notefd = v;
|
|
write(*notefd, "interrupt", 9);
|
|
free(notefd);
|
|
}
|
|
|
|
int
|
|
windfilewidth(Window *w, uint q0, int oneelement)
|
|
{
|
|
uint q;
|
|
Rune r;
|
|
|
|
q = q0;
|
|
while(q > 0){
|
|
r = w->r[q-1];
|
|
if(r<=' ')
|
|
break;
|
|
if(oneelement && r=='/')
|
|
break;
|
|
--q;
|
|
}
|
|
return q0-q;
|
|
}
|
|
|
|
void
|
|
showcandidates(Window *w, Completion *c)
|
|
{
|
|
int i;
|
|
Fmt f;
|
|
Rune *rp;
|
|
uint nr, qline, q0;
|
|
char *s;
|
|
|
|
runefmtstrinit(&f);
|
|
if (c->nmatch == 0)
|
|
s = "[no matches in ";
|
|
else
|
|
s = "[";
|
|
if(c->nfile > 32)
|
|
fmtprint(&f, "%s%d files]\n", s, c->nfile);
|
|
else{
|
|
fmtprint(&f, "%s", s);
|
|
for(i=0; i<c->nfile; i++){
|
|
if(i > 0)
|
|
fmtprint(&f, " ");
|
|
fmtprint(&f, "%s", c->filename[i]);
|
|
}
|
|
fmtprint(&f, "]\n");
|
|
}
|
|
/* place text at beginning of line before host point */
|
|
qline = w->qh;
|
|
while(qline>0 && w->r[qline-1] != '\n')
|
|
qline--;
|
|
|
|
rp = runefmtstrflush(&f);
|
|
nr = runestrlen(rp);
|
|
|
|
q0 = w->q0;
|
|
q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
|
|
free(rp);
|
|
wsetselect(w, q0+nr, q0+nr);
|
|
}
|
|
|
|
Rune*
|
|
namecomplete(Window *w)
|
|
{
|
|
int nstr, npath;
|
|
Rune *rp, *path, *str;
|
|
Completion *c;
|
|
char *s, *dir, *root;
|
|
|
|
/* control-f: filename completion; works back to white space or / */
|
|
if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
|
|
return nil;
|
|
nstr = windfilewidth(w, w->q0, TRUE);
|
|
str = runemalloc(nstr);
|
|
runemove(str, w->r+(w->q0-nstr), nstr);
|
|
npath = windfilewidth(w, w->q0-nstr, FALSE);
|
|
path = runemalloc(npath);
|
|
runemove(path, w->r+(w->q0-nstr-npath), npath);
|
|
rp = nil;
|
|
|
|
/* is path rooted? if not, we need to make it relative to window path */
|
|
if(npath>0 && path[0]=='/'){
|
|
dir = malloc(UTFmax*npath+1);
|
|
sprint(dir, "%.*S", npath, path);
|
|
}else{
|
|
if(strcmp(w->dir, "") == 0)
|
|
root = ".";
|
|
else
|
|
root = w->dir;
|
|
dir = malloc(strlen(root)+1+UTFmax*npath+1);
|
|
sprint(dir, "%s/%.*S", root, npath, path);
|
|
}
|
|
dir = cleanname(dir);
|
|
|
|
s = smprint("%.*S", nstr, str);
|
|
c = complete(dir, s);
|
|
free(s);
|
|
if(c == nil)
|
|
goto Return;
|
|
|
|
if(!c->advance)
|
|
showcandidates(w, c);
|
|
|
|
if(c->advance)
|
|
rp = runesmprint("%s", c->string);
|
|
|
|
Return:
|
|
freecompletion(c);
|
|
free(dir);
|
|
free(path);
|
|
free(str);
|
|
return rp;
|
|
}
|
|
|
|
void
|
|
wkeyctl(Window *w, Rune r)
|
|
{
|
|
uint q0 ,q1;
|
|
int n, nb, nr;
|
|
Rune *rp;
|
|
int *notefd;
|
|
|
|
switch(r){
|
|
case 0:
|
|
case Kcaps:
|
|
case Knum:
|
|
case Kshift:
|
|
case Kalt:
|
|
case Kctl:
|
|
case Kaltgr:
|
|
return;
|
|
}
|
|
|
|
if(w->deleted)
|
|
return;
|
|
/* navigation keys work only when mouse and kbd is not open */
|
|
if(!w->mouseopen)
|
|
switch(r){
|
|
case Kdown:
|
|
n = shiftdown ? 1 : w->maxlines/3;
|
|
goto case_Down;
|
|
case Kscrollonedown:
|
|
n = mousescrollsize(w->maxlines);
|
|
if(n <= 0)
|
|
n = 1;
|
|
goto case_Down;
|
|
case Kpgdown:
|
|
n = 2*w->maxlines/3;
|
|
case_Down:
|
|
q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+n*w->font->height));
|
|
wsetorigin(w, q0, TRUE);
|
|
return;
|
|
case Kup:
|
|
n = shiftdown ? 1 : w->maxlines/3;
|
|
goto case_Up;
|
|
case Kscrolloneup:
|
|
n = mousescrollsize(w->maxlines);
|
|
if(n <= 0)
|
|
n = 1;
|
|
goto case_Up;
|
|
case Kpgup:
|
|
n = 2*w->maxlines/3;
|
|
case_Up:
|
|
q0 = wbacknl(w, w->org, n);
|
|
wsetorigin(w, q0, TRUE);
|
|
return;
|
|
case Kleft:
|
|
if(w->q0 > 0){
|
|
q0 = w->q0-1;
|
|
wsetselect(w, q0, q0);
|
|
wshow(w, q0);
|
|
}
|
|
return;
|
|
case Kright:
|
|
if(w->q1 < w->nr){
|
|
q1 = w->q1+1;
|
|
wsetselect(w, q1, q1);
|
|
wshow(w, q1);
|
|
}
|
|
return;
|
|
case Khome:
|
|
wshow(w, 0);
|
|
return;
|
|
case Kend:
|
|
wshow(w, w->nr);
|
|
return;
|
|
case Kscroll:
|
|
w->scrolling ^= 1;
|
|
wshow(w, w->nr);
|
|
return;
|
|
case Ksoh: /* ^A: beginning of line */
|
|
if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
|
|
return;
|
|
nb = wbswidth(w, 0x15 /* ^U */);
|
|
wsetselect(w, w->q0-nb, w->q0-nb);
|
|
wshow(w, w->q0);
|
|
return;
|
|
case Kenq: /* ^E: end of line */
|
|
q0 = w->q0;
|
|
while(q0 < w->nr && w->r[q0]!='\n')
|
|
q0++;
|
|
wsetselect(w, q0, q0);
|
|
wshow(w, w->q0);
|
|
return;
|
|
}
|
|
if(w->rawing && (w->q0==w->nr || w->mouseopen)){
|
|
waddraw(w, &r, 1);
|
|
return;
|
|
}
|
|
if(r==Kesc || (w->holding && r==Kdel)){ /* toggle hold */
|
|
if(w->holding)
|
|
--w->holding;
|
|
else
|
|
w->holding++;
|
|
wrepaint(w);
|
|
if(r == Kesc)
|
|
return;
|
|
}
|
|
if(r != Kdel){
|
|
wsnarf(w);
|
|
wcut(w);
|
|
}
|
|
switch(r){
|
|
case Kdel: /* send interrupt */
|
|
w->qh = w->nr;
|
|
wshow(w, w->qh);
|
|
notefd = emalloc(sizeof(int));
|
|
*notefd = w->notefd;
|
|
proccreate(interruptproc, notefd, 4096);
|
|
return;
|
|
case Kack: /* ^F: file name completion */
|
|
case Kins: /* Insert: file name completion */
|
|
rp = namecomplete(w);
|
|
if(rp == nil)
|
|
return;
|
|
nr = runestrlen(rp);
|
|
q0 = w->q0;
|
|
q0 = winsert(w, rp, nr, q0);
|
|
wshow(w, q0+nr);
|
|
free(rp);
|
|
return;
|
|
case Kbs: /* ^H: erase character */
|
|
case Knack: /* ^U: erase line */
|
|
case Ketb: /* ^W: erase word */
|
|
if(w->q0==0 || w->q0==w->qh)
|
|
return;
|
|
nb = wbswidth(w, r);
|
|
q1 = w->q0;
|
|
q0 = q1-nb;
|
|
if(q0 < w->org){
|
|
q0 = w->org;
|
|
nb = q1-q0;
|
|
}
|
|
if(nb > 0){
|
|
wdelete(w, q0, q0+nb);
|
|
wsetselect(w, q0, q0);
|
|
}
|
|
return;
|
|
}
|
|
/* otherwise ordinary character; just insert */
|
|
q0 = w->q0;
|
|
q0 = winsert(w, &r, 1, q0);
|
|
wshow(w, q0+1);
|
|
}
|
|
|
|
void
|
|
wsetcols(Window *w)
|
|
{
|
|
if(w->holding)
|
|
if(w == input)
|
|
w->cols[TEXT] = w->cols[HTEXT] = holdcol;
|
|
else
|
|
w->cols[TEXT] = w->cols[HTEXT] = lightholdcol;
|
|
else
|
|
if(w == input)
|
|
w->cols[TEXT] = w->cols[HTEXT] = cols[TEXT];
|
|
else
|
|
w->cols[TEXT] = w->cols[HTEXT] = paletextcol;
|
|
}
|
|
|
|
void
|
|
wrepaint(Window *w)
|
|
{
|
|
wsetcols(w);
|
|
if(!w->mouseopen)
|
|
frredraw(w);
|
|
if(w == input){
|
|
wborder(w, Selborder);
|
|
wsetcursor(w, 0);
|
|
}else
|
|
wborder(w, Unselborder);
|
|
}
|
|
|
|
int
|
|
wbswidth(Window *w, Rune c)
|
|
{
|
|
uint q, eq, stop;
|
|
Rune r;
|
|
int skipping;
|
|
|
|
/* there is known to be at least one character to erase */
|
|
if(c == 0x08) /* ^H: erase character */
|
|
return 1;
|
|
q = w->q0;
|
|
stop = 0;
|
|
if(q > w->qh)
|
|
stop = w->qh;
|
|
skipping = TRUE;
|
|
while(q > stop){
|
|
r = w->r[q-1];
|
|
if(r == '\n'){ /* eat at most one more character */
|
|
if(q == w->q0) /* eat the newline */
|
|
--q;
|
|
break;
|
|
}
|
|
if(c == 0x17){
|
|
eq = isalnum(r);
|
|
if(eq && skipping) /* found one; stop skipping */
|
|
skipping = FALSE;
|
|
else if(!eq && !skipping)
|
|
break;
|
|
}
|
|
--q;
|
|
}
|
|
return w->q0-q;
|
|
}
|
|
|
|
void
|
|
wsnarf(Window *w)
|
|
{
|
|
if(w->q1 == w->q0)
|
|
return;
|
|
nsnarf = w->q1-w->q0;
|
|
snarf = runerealloc(snarf, nsnarf);
|
|
snarfversion++; /* maybe modified by parent */
|
|
runemove(snarf, w->r+w->q0, nsnarf);
|
|
putsnarf();
|
|
}
|
|
|
|
void
|
|
wcut(Window *w)
|
|
{
|
|
if(w->q1 == w->q0)
|
|
return;
|
|
wdelete(w, w->q0, w->q1);
|
|
wsetselect(w, w->q0, w->q0);
|
|
}
|
|
|
|
void
|
|
wpaste(Window *w)
|
|
{
|
|
uint q0;
|
|
|
|
if(nsnarf == 0)
|
|
return;
|
|
wcut(w);
|
|
q0 = w->q0;
|
|
if(w->rawing && q0==w->nr){
|
|
waddraw(w, snarf, nsnarf);
|
|
wsetselect(w, q0, q0);
|
|
}else{
|
|
q0 = winsert(w, snarf, nsnarf, w->q0);
|
|
wsetselect(w, q0, q0+nsnarf);
|
|
}
|
|
}
|
|
|
|
void
|
|
wplumb(Window *w)
|
|
{
|
|
Plumbmsg *m;
|
|
static int fd = -2;
|
|
char buf[32];
|
|
uint p0, p1;
|
|
Cursor *c;
|
|
|
|
if(fd == -2)
|
|
fd = plumbopen("send", OWRITE|OCEXEC);
|
|
if(fd < 0)
|
|
return;
|
|
m = emalloc(sizeof(Plumbmsg));
|
|
m->src = estrdup("rio");
|
|
m->dst = nil;
|
|
m->wdir = estrdup(w->dir);
|
|
m->type = estrdup("text");
|
|
p0 = w->q0;
|
|
p1 = w->q1;
|
|
if(w->q1 > w->q0)
|
|
m->attr = nil;
|
|
else{
|
|
while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
|
|
p0--;
|
|
while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
|
|
p1++;
|
|
sprint(buf, "click=%d", w->q0-p0);
|
|
m->attr = plumbunpackattr(buf);
|
|
}
|
|
if(p1-p0 > messagesize-1024){
|
|
plumbfree(m);
|
|
return; /* too large for 9P */
|
|
}
|
|
m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
|
|
if(plumbsend(fd, m) < 0){
|
|
c = lastcursor;
|
|
riosetcursor(&query, 1);
|
|
sleep(300);
|
|
riosetcursor(c, 1);
|
|
}
|
|
plumbfree(m);
|
|
}
|
|
|
|
int
|
|
winborder(Window *w, Point xy)
|
|
{
|
|
return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
|
|
}
|
|
|
|
void
|
|
wmousectl(Window *w)
|
|
{
|
|
int but;
|
|
|
|
if(w->mc.buttons == 1)
|
|
but = 1;
|
|
else if(w->mc.buttons == 2)
|
|
but = 2;
|
|
else if(w->mc.buttons == 4)
|
|
but = 3;
|
|
else{
|
|
if(w->mc.buttons == 8)
|
|
wkeyctl(w, Kscrolloneup);
|
|
if(w->mc.buttons == 16)
|
|
wkeyctl(w, Kscrollonedown);
|
|
return;
|
|
}
|
|
|
|
incref(w); /* hold up window while we track */
|
|
if(w->deleted)
|
|
goto Return;
|
|
if(ptinrect(w->mc.xy, w->scrollr)){
|
|
if(but)
|
|
wscroll(w, but);
|
|
goto Return;
|
|
}
|
|
if(but == 1)
|
|
wselect(w);
|
|
/* else all is handled by main process */
|
|
Return:
|
|
wclose(w);
|
|
}
|
|
|
|
void
|
|
wdelete(Window *w, uint q0, uint q1)
|
|
{
|
|
uint n, p0, p1;
|
|
|
|
n = q1-q0;
|
|
if(n == 0)
|
|
return;
|
|
runemove(w->r+q0, w->r+q1, w->nr-q1);
|
|
w->nr -= n;
|
|
if(q0 < w->q0)
|
|
w->q0 -= min(n, w->q0-q0);
|
|
if(q0 < w->q1)
|
|
w->q1 -= min(n, w->q1-q0);
|
|
if(q1 < w->qh)
|
|
w->qh -= n;
|
|
else if(q0 < w->qh)
|
|
w->qh = q0;
|
|
if(q1 <= w->org)
|
|
w->org -= n;
|
|
else if(q0 < w->org+w->nchars){
|
|
p1 = q1 - w->org;
|
|
if(p1 > w->nchars)
|
|
p1 = w->nchars;
|
|
if(q0 < w->org){
|
|
w->org = q0;
|
|
p0 = 0;
|
|
}else
|
|
p0 = q0 - w->org;
|
|
frdelete(w, p0, p1);
|
|
wfill(w);
|
|
}
|
|
}
|
|
|
|
|
|
static Window *clickwin;
|
|
static uint clickmsec;
|
|
static Window *selectwin;
|
|
static uint selectq;
|
|
|
|
/*
|
|
* called from frame library
|
|
*/
|
|
void
|
|
framescroll(Frame *f, int dl)
|
|
{
|
|
if(f != &selectwin->Frame)
|
|
error("frameselect not right frame");
|
|
wframescroll(selectwin, dl);
|
|
}
|
|
|
|
void
|
|
wframescroll(Window *w, int dl)
|
|
{
|
|
uint q0;
|
|
|
|
if(dl == 0){
|
|
wscrsleep(w, 100);
|
|
return;
|
|
}
|
|
if(dl < 0){
|
|
q0 = wbacknl(w, w->org, -dl);
|
|
if(selectq > w->org+w->p0)
|
|
wsetselect(w, w->org+w->p0, selectq);
|
|
else
|
|
wsetselect(w, selectq, w->org+w->p0);
|
|
}else{
|
|
if(w->org+w->nchars == w->nr)
|
|
return;
|
|
q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height));
|
|
if(selectq >= w->org+w->p1)
|
|
wsetselect(w, w->org+w->p1, selectq);
|
|
else
|
|
wsetselect(w, selectq, w->org+w->p1);
|
|
}
|
|
wsetorigin(w, q0, TRUE);
|
|
}
|
|
|
|
void
|
|
wselect(Window *w)
|
|
{
|
|
uint q0, q1;
|
|
int b, x, y, first;
|
|
|
|
first = 1;
|
|
selectwin = w;
|
|
/*
|
|
* Double-click immediately if it might make sense.
|
|
*/
|
|
b = w->mc.buttons;
|
|
q0 = w->q0;
|
|
q1 = w->q1;
|
|
selectq = w->org+frcharofpt(w, w->mc.xy);
|
|
if(clickwin==w && w->mc.msec-clickmsec<500)
|
|
if(q0==q1 && selectq==w->q0){
|
|
wdoubleclick(w, &q0, &q1);
|
|
wsetselect(w, q0, q1);
|
|
flushimage(display, 1);
|
|
x = w->mc.xy.x;
|
|
y = w->mc.xy.y;
|
|
/* stay here until something interesting happens */
|
|
do
|
|
readmouse(&w->mc);
|
|
while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3);
|
|
w->mc.xy.x = x; /* in case we're calling frselect */
|
|
w->mc.xy.y = y;
|
|
q0 = w->q0; /* may have changed */
|
|
q1 = w->q1;
|
|
selectq = q0;
|
|
}
|
|
if(w->mc.buttons == b){
|
|
w->scroll = framescroll;
|
|
frselect(w, &w->mc);
|
|
/* horrible botch: while asleep, may have lost selection altogether */
|
|
if(selectq > w->nr)
|
|
selectq = w->org + w->p0;
|
|
w->Frame.scroll = nil;
|
|
if(selectq < w->org)
|
|
q0 = selectq;
|
|
else
|
|
q0 = w->org + w->p0;
|
|
if(selectq > w->org+w->nchars)
|
|
q1 = selectq;
|
|
else
|
|
q1 = w->org+w->p1;
|
|
}
|
|
if(q0 == q1){
|
|
if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){
|
|
wdoubleclick(w, &q0, &q1);
|
|
clickwin = nil;
|
|
}else{
|
|
clickwin = w;
|
|
clickmsec = w->mc.msec;
|
|
}
|
|
}else
|
|
clickwin = nil;
|
|
wsetselect(w, q0, q1);
|
|
flushimage(display, 1);
|
|
while(w->mc.buttons){
|
|
w->mc.msec = 0;
|
|
b = w->mc.buttons;
|
|
if(b & 6){
|
|
if(b & 2){
|
|
wsnarf(w);
|
|
wcut(w);
|
|
}else{
|
|
if(first){
|
|
first = 0;
|
|
getsnarf();
|
|
}
|
|
wpaste(w);
|
|
}
|
|
}
|
|
wscrdraw(w);
|
|
flushimage(display, 1);
|
|
while(w->mc.buttons == b)
|
|
readmouse(&w->mc);
|
|
clickwin = nil;
|
|
}
|
|
}
|
|
|
|
void
|
|
wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
|
|
{
|
|
Wctlmesg wcm;
|
|
|
|
wcm.type = type;
|
|
wcm.r = r;
|
|
wcm.image = image;
|
|
send(w->cctl, &wcm);
|
|
}
|
|
|
|
int
|
|
wctlmesg(Window *w, int m, Rectangle r, Image *i)
|
|
{
|
|
char buf[64];
|
|
|
|
switch(m){
|
|
default:
|
|
error("unknown control message");
|
|
break;
|
|
case Wakeup:
|
|
break;
|
|
case Moved:
|
|
case Reshaped:
|
|
if(w->deleted){
|
|
freeimage(i);
|
|
break;
|
|
}
|
|
w->screenr = r;
|
|
strcpy(buf, w->name);
|
|
wresize(w, i, m==Moved);
|
|
proccreate(deletetimeoutproc, estrdup(buf), 4096);
|
|
break;
|
|
case Refresh:
|
|
if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r) || w->mouseopen)
|
|
break;
|
|
wrefresh(w, r);
|
|
flushimage(display, 1);
|
|
break;
|
|
case Movemouse:
|
|
if(w->deleted || Dx(w->screenr)<=0 || !ptinrect(r.min, w->i->r))
|
|
break;
|
|
wmovemouse(w, r.min);
|
|
case Rawon:
|
|
break;
|
|
case Rawoff:
|
|
if(w->deleted)
|
|
break;
|
|
while(w->nraw > 0){
|
|
wkeyctl(w, w->raw[0]);
|
|
--w->nraw;
|
|
runemove(w->raw, w->raw+1, w->nraw);
|
|
}
|
|
break;
|
|
case Holdon:
|
|
case Holdoff:
|
|
case Repaint:
|
|
if(w->deleted)
|
|
break;
|
|
wrepaint(w);
|
|
flushimage(display, 1);
|
|
break;
|
|
case Deleted:
|
|
if(w->deleted)
|
|
break;
|
|
wclunk(w);
|
|
write(w->notefd, "hangup", 6);
|
|
proccreate(deletetimeoutproc, estrdup(w->name), 4096);
|
|
wclosewin(w);
|
|
break;
|
|
case Exited:
|
|
wclosewin(w);
|
|
frclear(w, TRUE);
|
|
close(w->notefd);
|
|
chanfree(w->mc.c);
|
|
chanfree(w->ck);
|
|
chanfree(w->cctl);
|
|
chanfree(w->conswrite);
|
|
chanfree(w->consread);
|
|
chanfree(w->mouseread);
|
|
chanfree(w->wctlread);
|
|
chanfree(w->kbdread);
|
|
free(w->raw);
|
|
free(w->r);
|
|
free(w->dir);
|
|
free(w->label);
|
|
free(w);
|
|
break;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
/*
|
|
* Convert back to physical coordinates
|
|
*/
|
|
void
|
|
wmovemouse(Window *w, Point p)
|
|
{
|
|
if(w != input || menuing || sweeping)
|
|
return;
|
|
p.x += w->screenr.min.x-w->i->r.min.x;
|
|
p.y += w->screenr.min.y-w->i->r.min.y;
|
|
moveto(mousectl, p);
|
|
}
|
|
|
|
void
|
|
wborder(Window *w, int type)
|
|
{
|
|
Image *col;
|
|
|
|
if(w->i == nil)
|
|
return;
|
|
if(w->holding){
|
|
if(type == Selborder)
|
|
col = holdcol;
|
|
else
|
|
col = paleholdcol;
|
|
}else{
|
|
if(type == Selborder)
|
|
col = titlecol;
|
|
else
|
|
col = lighttitlecol;
|
|
}
|
|
border(w->i, w->i->r, Selborder, col, ZP);
|
|
}
|
|
|
|
Window*
|
|
wpointto(Point pt)
|
|
{
|
|
int i;
|
|
Window *v, *w;
|
|
|
|
w = nil;
|
|
for(i=0; i<nwindow; i++){
|
|
v = window[i];
|
|
if(ptinrect(pt, v->screenr))
|
|
if(w==nil || v->topped>w->topped)
|
|
w = v;
|
|
}
|
|
return w;
|
|
}
|
|
|
|
void
|
|
wcurrent(Window *w)
|
|
{
|
|
Window *oi;
|
|
|
|
if(wkeyboard!=nil && w==wkeyboard)
|
|
return;
|
|
oi = input;
|
|
input = w;
|
|
if(w != oi){
|
|
if(oi){
|
|
oi->wctlready = 1;
|
|
wsendctlmesg(oi, Repaint, ZR, nil);
|
|
}
|
|
if(w){
|
|
w->wctlready = 1;
|
|
wsendctlmesg(w, Repaint, ZR, nil);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
wsetcursor(Window *w, int force)
|
|
{
|
|
Cursor *p;
|
|
|
|
if(w==nil || w->deleted || w->i==nil || Dx(w->screenr)<=0)
|
|
p = nil;
|
|
else if(wpointto(mouse->xy) == w){
|
|
p = w->cursorp;
|
|
if(p==nil && w->holding)
|
|
p = &whitearrow;
|
|
}else
|
|
p = nil;
|
|
if(!menuing)
|
|
riosetcursor(p, force && !menuing);
|
|
}
|
|
|
|
void
|
|
riosetcursor(Cursor *p, int force)
|
|
{
|
|
if(!force && p==lastcursor)
|
|
return;
|
|
setcursor(mousectl, p);
|
|
lastcursor = p;
|
|
}
|
|
|
|
|
|
void
|
|
wtopme(Window *w)
|
|
{
|
|
if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
|
|
w->topped = ++topped;
|
|
topwindow(w->i);
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
wbottomme(Window *w)
|
|
{
|
|
if(w!=nil && w->i!=nil && !w->deleted){
|
|
w->topped = - ++topped;
|
|
bottomwindow(w->i);
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
Window*
|
|
wtop(Point pt)
|
|
{
|
|
Window *w;
|
|
|
|
w = wpointto(pt);
|
|
if(w){
|
|
incref(w);
|
|
wtopme(w);
|
|
wcurrent(w);
|
|
wclose(w);
|
|
}
|
|
return w;
|
|
}
|
|
|
|
Window*
|
|
wlookid(int id)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; i<nwindow; i++)
|
|
if(window[i]->id == id)
|
|
return window[i];
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
wclunk(Window *w)
|
|
{
|
|
int i;
|
|
|
|
if(w == input){
|
|
input = nil;
|
|
wsetcursor(w, 0);
|
|
}
|
|
if(w == wkeyboard)
|
|
wkeyboard = nil;
|
|
for(i=0; i<nhidden; i++)
|
|
if(hidden[i] == w){
|
|
--nhidden;
|
|
memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
|
|
break;
|
|
}
|
|
for(i=0; i<nwindow; i++)
|
|
if(window[i] == w){
|
|
--nwindow;
|
|
memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
|
|
break;
|
|
}
|
|
w->deleted = TRUE;
|
|
}
|
|
|
|
void
|
|
wclosewin(Window *w)
|
|
{
|
|
Image *i;
|
|
|
|
i = w->i;
|
|
if(i){
|
|
w->i = nil;
|
|
/* move it off-screen to hide it, in case client is slow in letting it go */
|
|
MOVEIT originwindow(i, i->r.min, view->r.max);
|
|
freeimage(i);
|
|
}
|
|
}
|
|
|
|
void
|
|
wsetpid(Window *w, int pid, int dolabel)
|
|
{
|
|
char buf[128];
|
|
int fd;
|
|
|
|
w->pid = pid;
|
|
if(dolabel){
|
|
sprint(buf, "rc %d", pid);
|
|
free(w->label);
|
|
w->label = estrdup(buf);
|
|
}
|
|
sprint(buf, "/proc/%d/notepg", pid);
|
|
fd = open(buf, OWRITE|OCEXEC);
|
|
if(w->notefd > 0)
|
|
close(w->notefd);
|
|
w->notefd = fd;
|
|
}
|
|
|
|
void
|
|
winshell(void *args)
|
|
{
|
|
Window *w;
|
|
Channel *pidc;
|
|
void **arg;
|
|
char *cmd, *dir;
|
|
char **argv;
|
|
|
|
arg = args;
|
|
w = arg[0];
|
|
pidc = arg[1];
|
|
cmd = arg[2];
|
|
argv = arg[3];
|
|
dir = arg[4];
|
|
rfork(RFNAMEG|RFFDG|RFENVG);
|
|
if(filsysmount(filsys, w->id) < 0){
|
|
fprint(2, "mount failed: %r\n");
|
|
sendul(pidc, 0);
|
|
threadexits("mount failed");
|
|
}
|
|
close(0);
|
|
if(open("/dev/cons", OREAD) < 0){
|
|
fprint(2, "can't open /dev/cons: %r\n");
|
|
sendul(pidc, 0);
|
|
threadexits("/dev/cons");
|
|
}
|
|
close(1);
|
|
if(open("/dev/cons", OWRITE) < 0){
|
|
fprint(2, "can't open /dev/cons: %r\n");
|
|
sendul(pidc, 0);
|
|
threadexits("open"); /* BUG? was terminate() */
|
|
}
|
|
if(wclose(w) == 0){ /* remove extra ref hanging from creation */
|
|
notify(nil);
|
|
dup(1, 2);
|
|
if(dir)
|
|
chdir(dir);
|
|
procexec(pidc, cmd, argv);
|
|
_exits("exec failed");
|
|
}
|
|
}
|
|
|
|
static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
|
|
static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
|
|
static Rune left2[] = { L'\n', 0 };
|
|
static Rune left3[] = { L'\'', L'"', L'`', 0 };
|
|
|
|
Rune *left[] = {
|
|
left1,
|
|
left2,
|
|
left3,
|
|
nil
|
|
};
|
|
Rune *right[] = {
|
|
right1,
|
|
left2,
|
|
left3,
|
|
nil
|
|
};
|
|
|
|
void
|
|
wdoubleclick(Window *w, uint *q0, uint *q1)
|
|
{
|
|
int c, i;
|
|
Rune *r, *l, *p;
|
|
uint q;
|
|
|
|
for(i=0; left[i]!=nil; i++){
|
|
q = *q0;
|
|
l = left[i];
|
|
r = right[i];
|
|
/* try matching character to left, looking right */
|
|
if(q == 0)
|
|
c = '\n';
|
|
else
|
|
c = w->r[q-1];
|
|
p = strrune(l, c);
|
|
if(p != nil){
|
|
if(wclickmatch(w, c, r[p-l], 1, &q))
|
|
*q1 = q-(c!='\n');
|
|
return;
|
|
}
|
|
/* try matching character to right, looking left */
|
|
if(q == w->nr)
|
|
c = '\n';
|
|
else
|
|
c = w->r[q];
|
|
p = strrune(r, c);
|
|
if(p != nil){
|
|
if(wclickmatch(w, c, l[p-r], -1, &q)){
|
|
*q1 = *q0+(*q0<w->nr && c=='\n');
|
|
*q0 = q;
|
|
if(c!='\n' || q!=0 || w->r[0]=='\n')
|
|
(*q0)++;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
/* try filling out word to right */
|
|
while(*q1<w->nr && isalnum(w->r[*q1]))
|
|
(*q1)++;
|
|
/* try filling out word to left */
|
|
while(*q0>0 && isalnum(w->r[*q0-1]))
|
|
(*q0)--;
|
|
}
|
|
|
|
int
|
|
wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
|
|
{
|
|
Rune c;
|
|
int nest;
|
|
|
|
nest = 1;
|
|
for(;;){
|
|
if(dir > 0){
|
|
if(*q == w->nr)
|
|
break;
|
|
c = w->r[*q];
|
|
(*q)++;
|
|
}else{
|
|
if(*q == 0)
|
|
break;
|
|
(*q)--;
|
|
c = w->r[*q];
|
|
}
|
|
if(c == cr){
|
|
if(--nest==0)
|
|
return 1;
|
|
}else if(c == cl)
|
|
nest++;
|
|
}
|
|
return cl=='\n' && nest==1;
|
|
}
|
|
|
|
|
|
uint
|
|
wbacknl(Window *w, uint p, uint n)
|
|
{
|
|
int i, j;
|
|
|
|
/* look for start of this line if n==0 */
|
|
if(n==0 && p>0 && w->r[p-1]!='\n')
|
|
n = 1;
|
|
i = n;
|
|
while(i-->0 && p>0){
|
|
--p; /* it's at a newline now; back over it */
|
|
if(p == 0)
|
|
break;
|
|
/* at 128 chars, call it a line anyway */
|
|
for(j=128; --j>0 && p>0; p--)
|
|
if(w->r[p-1]=='\n')
|
|
break;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void
|
|
wshow(Window *w, uint q0)
|
|
{
|
|
int qe;
|
|
int nl;
|
|
uint q;
|
|
|
|
qe = w->org+w->nchars;
|
|
if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
|
|
wscrdraw(w);
|
|
else{
|
|
nl = 4*w->maxlines/5;
|
|
q = wbacknl(w, q0, nl);
|
|
/* avoid going backwards if trying to go forwards - long lines! */
|
|
if(!(q0>w->org && q<w->org))
|
|
wsetorigin(w, q, TRUE);
|
|
while(q0 > w->org+w->nchars)
|
|
wsetorigin(w, w->org+1, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
wsetorigin(Window *w, uint org, int exact)
|
|
{
|
|
int i, a, fixup;
|
|
Rune *r;
|
|
uint n;
|
|
|
|
if(org>0 && !exact){
|
|
/* org is an estimate of the char posn; find a newline */
|
|
/* don't try harder than 256 chars */
|
|
for(i=0; i<256 && org<w->nr; i++){
|
|
if(w->r[org] == '\n'){
|
|
org++;
|
|
break;
|
|
}
|
|
org++;
|
|
}
|
|
}
|
|
a = org-w->org;
|
|
fixup = 0;
|
|
if(a>=0 && a<w->nchars){
|
|
frdelete(w, 0, a);
|
|
fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
|
|
}else if(a<0 && -a<w->nchars){
|
|
n = w->org - org;
|
|
r = runemalloc(n);
|
|
runemove(r, w->r+org, n);
|
|
frinsert(w, r, r+n, 0);
|
|
free(r);
|
|
}else
|
|
frdelete(w, 0, w->nchars);
|
|
w->org = org;
|
|
wfill(w);
|
|
wscrdraw(w);
|
|
wsetselect(w, w->q0, w->q1);
|
|
if(fixup && w->p1 > w->p0)
|
|
frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
|
|
}
|
|
|
|
void
|
|
wsetselect(Window *w, uint q0, uint q1)
|
|
{
|
|
int p0, p1;
|
|
|
|
/* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
|
|
w->q0 = q0;
|
|
w->q1 = q1;
|
|
/* compute desired p0,p1 from q0,q1 */
|
|
p0 = q0-w->org;
|
|
p1 = q1-w->org;
|
|
if(p0 < 0)
|
|
p0 = 0;
|
|
if(p1 < 0)
|
|
p1 = 0;
|
|
if(p0 > w->nchars)
|
|
p0 = w->nchars;
|
|
if(p1 > w->nchars)
|
|
p1 = w->nchars;
|
|
if(p0==w->p0 && p1==w->p1)
|
|
return;
|
|
/* screen disagrees with desired selection */
|
|
if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
|
|
/* no overlap or too easy to bother trying */
|
|
frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0);
|
|
frdrawsel(w, frptofchar(w, p0), p0, p1, 1);
|
|
goto Return;
|
|
}
|
|
/* overlap; avoid unnecessary painting */
|
|
if(p0 < w->p0){
|
|
/* extend selection backwards */
|
|
frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1);
|
|
}else if(p0 > w->p0){
|
|
/* trim first part of selection */
|
|
frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0);
|
|
}
|
|
if(p1 > w->p1){
|
|
/* extend selection forwards */
|
|
frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1);
|
|
}else if(p1 < w->p1){
|
|
/* trim last part of selection */
|
|
frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0);
|
|
}
|
|
|
|
Return:
|
|
w->p0 = p0;
|
|
w->p1 = p1;
|
|
}
|
|
|
|
uint
|
|
winsert(Window *w, Rune *r, int n, uint q0)
|
|
{
|
|
uint m;
|
|
|
|
if(n == 0)
|
|
return q0;
|
|
if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
|
|
m = min(HiWater-LoWater, min(w->org, w->qh));
|
|
w->org -= m;
|
|
w->qh -= m;
|
|
if(w->q0 > m)
|
|
w->q0 -= m;
|
|
else
|
|
w->q0 = 0;
|
|
if(w->q1 > m)
|
|
w->q1 -= m;
|
|
else
|
|
w->q1 = 0;
|
|
w->nr -= m;
|
|
runemove(w->r, w->r+m, w->nr);
|
|
q0 -= m;
|
|
}
|
|
if(w->nr+n > w->maxr){
|
|
/*
|
|
* Minimize realloc breakage:
|
|
* Allocate at least MinWater
|
|
* Double allocation size each time
|
|
* But don't go much above HiWater
|
|
*/
|
|
m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
|
|
if(m > HiWater)
|
|
m = max(HiWater+MinWater, w->nr+n);
|
|
if(m > w->maxr){
|
|
w->r = runerealloc(w->r, m);
|
|
w->maxr = m;
|
|
}
|
|
}
|
|
runemove(w->r+q0+n, w->r+q0, w->nr-q0);
|
|
runemove(w->r+q0, r, n);
|
|
w->nr += n;
|
|
/* if output touches, advance selection, not qh; works best for keyboard and output */
|
|
if(q0 <= w->q1)
|
|
w->q1 += n;
|
|
if(q0 <= w->q0)
|
|
w->q0 += n;
|
|
if(q0 < w->qh)
|
|
w->qh += n;
|
|
if(q0 < w->org)
|
|
w->org += n;
|
|
else if(q0 <= w->org+w->nchars)
|
|
frinsert(w, r, r+n, q0-w->org);
|
|
return q0;
|
|
}
|
|
|
|
void
|
|
wfill(Window *w)
|
|
{
|
|
Rune *rp;
|
|
int i, n, m, nl;
|
|
|
|
if(w->lastlinefull)
|
|
return;
|
|
rp = malloc(messagesize);
|
|
do{
|
|
n = w->nr-(w->org+w->nchars);
|
|
if(n == 0)
|
|
break;
|
|
if(n > 2000) /* educated guess at reasonable amount */
|
|
n = 2000;
|
|
runemove(rp, w->r+(w->org+w->nchars), n);
|
|
/*
|
|
* it's expensive to frinsert more than we need, so
|
|
* count newlines.
|
|
*/
|
|
nl = w->maxlines-w->nlines;
|
|
m = 0;
|
|
for(i=0; i<n; ){
|
|
if(rp[i++] == '\n'){
|
|
m++;
|
|
if(m >= nl)
|
|
break;
|
|
}
|
|
}
|
|
frinsert(w, rp, rp+i, w->nchars);
|
|
}while(w->lastlinefull == FALSE);
|
|
free(rp);
|
|
}
|
|
|
|
char*
|
|
wcontents(Window *w, int *ip)
|
|
{
|
|
return runetobyte(w->r, w->nr, ip);
|
|
}
|