summaryrefslogtreecommitdiff
path: root/sys/src/cmd/rio
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2021-03-07 20:26:30 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2021-03-07 20:26:30 +0100
commitb5783b1e39122f466c1a577720496284223e2217 (patch)
tree1f56ab52e2968a14fba725c3d5ec15b9114e1dd0 /sys/src/cmd/rio
parentea347ee7f181208be5a2ace0e35d268ccfefa1e4 (diff)
rio: make window focus changes deterministic, cleanup wind.c
Switching window focus used to be non deterministic as the current window in focus (Window *input) was set concurrently while processing window messages such as Resized and Topped. This implements a new approach where wcurrent() and wuncurrent() are responsible for the synchronization and switch of the input. It is implemented by sending a Repaint message to the old input window first, neccesarily waiting until that window releases the focus and then input is updated and then a Topped or Reshaped message is send to the new input window. Note, that when the whole screen is resized that no input changes need to happening anymore.
Diffstat (limited to 'sys/src/cmd/rio')
-rw-r--r--sys/src/cmd/rio/dat.h22
-rw-r--r--sys/src/cmd/rio/rio.c28
-rw-r--r--sys/src/cmd/rio/wctl.c4
-rw-r--r--sys/src/cmd/rio/wind.c2348
4 files changed, 1194 insertions, 1208 deletions
diff --git a/sys/src/cmd/rio/dat.h b/sys/src/cmd/rio/dat.h
index edc855595..dac32da5f 100644
--- a/sys/src/cmd/rio/dat.h
+++ b/sys/src/cmd/rio/dat.h
@@ -183,45 +183,25 @@ Window* wtop(Point);
void wtopme(Window*);
void wbottomme(Window*);
char* wcontents(Window*, int*);
-int wbswidth(Window*, Rune);
-int wclickmatch(Window*, int, int, int, uint*);
int wclose(Window*);
-int wctlmesg(Window*, int, Rectangle, void*);
uint wbacknl(Window*, uint, uint);
-uint winsert(Window*, Rune*, int, uint);
-void waddraw(Window*, Rune*, int);
-void wborder(Window*, int);
-void wclunk(Window*);
-void wclosewin(Window*);
void wcurrent(Window*);
+void wuncurrent(Window*);
void wcut(Window*);
-void wdelete(Window*, uint, uint);
-void wstretchsel(Window*, uint, uint*, uint*, int);
-void wfill(Window*);
-void wframescroll(Window*, int);
-void wkeyctl(Window*, Rune);
-void wmousectl(Window*);
-void wmovemouse(Window*, Point);
void wpaste(Window*);
void wplumb(Window*);
void wlook(Window*);
-void wrefresh(Window*);
-void wrepaint(Window*);
-void wresize(Window*, Image*);
void wscrdraw(Window*);
void wscroll(Window*, int);
-void wselect(Window*);
void wsend(Window*);
void wsendctlmesg(Window*, int, Rectangle, void*);
void wsetcursor(Window*, int);
void wsetname(Window*);
void wsetorigin(Window*, uint, int);
void wsetpid(Window*, int, int);
-void wsetselect(Window*, uint, uint);
void wshow(Window*, uint);
void wsnarf(Window*);
void wscrsleep(Window*, uint);
-void wsetcols(Window*, int);
struct Dirtab
{
diff --git a/sys/src/cmd/rio/rio.c b/sys/src/cmd/rio/rio.c
index a28fb2ccc..9c18a6955 100644
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -530,8 +530,10 @@ mousethread(void*)
else
i = drag(winput);
sweeping = FALSE;
- if(i != nil)
+ if(i != nil){
+ wcurrent(winput);
wsendctlmesg(winput, Reshaped, i->r, i);
+ }
wclose(winput);
continue;
}
@@ -616,9 +618,10 @@ resized(void)
if(j < nhidden){
im = allocimage(display, r, screen->chan, 0, DNofill);
r = ZR;
- } else
+ } else {
im = allocwindow(wscreen, r, Refbackup, DNofill);
- if(im)
+ }
+ if(im!=nil)
wsendctlmesg(w, Reshaped, r, im);
wclose(w);
}
@@ -1001,7 +1004,7 @@ delete(void)
Window *w;
w = pointto(TRUE);
- if(w)
+ if(w!=nil)
wsendctlmesg(w, Deleted, ZR, nil);
}
@@ -1016,8 +1019,10 @@ resize(void)
return;
incref(w);
i = sweep();
- if(i)
+ if(i!=nil){
+ wcurrent(w);
wsendctlmesg(w, Reshaped, i->r, i);
+ }
wclose(w);
}
@@ -1032,8 +1037,10 @@ move(void)
return;
incref(w);
i = drag(w);
- if(i)
+ if(i!=nil){
+ wcurrent(w);
wsendctlmesg(w, Reshaped, i->r, i);
+ }
wclose(w);
}
@@ -1049,8 +1056,9 @@ whide(Window *w)
if(nhidden >= nelem(hidden))
return 0;
incref(w);
+ wuncurrent(w);
i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
- if(i){
+ if(i!=nil){
hidden[nhidden++] = w;
wsendctlmesg(w, Reshaped, ZR, i);
}
@@ -1070,8 +1078,9 @@ wunhide(Window *w)
if(j == nhidden)
return -1; /* not hidden */
incref(w);
+ wcurrent(w);
i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
- if(i){
+ if(i!=nil){
--nhidden;
memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
wsendctlmesg(w, Reshaped, w->i->r, i);
@@ -1109,8 +1118,9 @@ unhide(int j)
for(j=0; j<nwindow; j++)
if(window[j] == w){
incref(w);
- wtopme(w);
wcurrent(w);
+ wtopme(w);
+ wsendctlmesg(w, Topped, ZR, nil);
wclose(w);
return;
}
diff --git a/sys/src/cmd/rio/wctl.c b/sys/src/cmd/rio/wctl.c
index bef98fda9..b2de73e86 100644
--- a/sys/src/cmd/rio/wctl.c
+++ b/sys/src/cmd/rio/wctl.c
@@ -380,6 +380,7 @@ wctlcmd(Window *w, Rectangle r, int cmd, char *err)
} else { /* hidden */
if(eqrect(r, w->i->r))
return 1;
+ wuncurrent(w);
i = allocimage(display, r, w->i->chan, 0, DNofill);
r = ZR;
}
@@ -409,8 +410,9 @@ wctlcmd(Window *w, Rectangle r, int cmd, char *err)
strcpy(err, "window is hidden");
return -1;
}
- wtopme(w);
wcurrent(w);
+ wtopme(w);
+ wsendctlmesg(w, Topped, ZR, nil);
return 1;
case Hide:
switch(whide(w)){
diff --git a/sys/src/cmd/rio/wind.c b/sys/src/cmd/rio/wind.c
index 1fd1b671c..6869cb8af 100644
--- a/sys/src/cmd/rio/wind.c
+++ b/sys/src/cmd/rio/wind.c
@@ -12,6 +12,142 @@
#include "dat.h"
#include "fns.h"
+Window*
+wlookid(int id)
+{
+ int i;
+
+ for(i=0; i<nwindow; i++)
+ if(window[i]->id == id)
+ return window[i];
+ return nil;
+}
+
+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;
+}
+
+static int topped;
+
+void
+wtopme(Window *w)
+{
+ if(w!=nil && w->i!=nil && w->topped!=topped){
+ w->topped = ++topped;
+ topwindow(w->i);
+ flushimage(display, 1);
+ }
+}
+
+void
+wbottomme(Window *w)
+{
+ if(w!=nil && w->i!=nil){
+ w->topped = - ++topped;
+ bottomwindow(w->i);
+ flushimage(display, 1);
+ }
+}
+
+Window*
+wtop(Point pt)
+{
+ Window *w;
+
+ w = wpointto(pt);
+ if(w!=nil){
+ incref(w);
+ wcurrent(w);
+ wtopme(w);
+ wsendctlmesg(w, Topped, ZR, nil);
+ wclose(w);
+ }
+ return w;
+}
+
+void
+wcurrent(Window *w)
+{
+ Channel *c;
+
+ if(input == nil){
+ input = w;
+ return;
+ }
+ if(w == input)
+ return;
+ incref(input);
+ c = chancreate(sizeof(Window*), 0);
+ wsendctlmesg(input, Repaint, ZR, c);
+ sendp(c, w); /* send the new input */
+ wclose(recvp(c)); /* release old input */
+ chanfree(c);
+}
+
+void
+wuncurrent(Window *w)
+{
+ Channel *c;
+
+ if(input == nil || w != input)
+ return;
+ c = chancreate(sizeof(Window*), 0);
+ wsendctlmesg(w, Repaint, ZR, c);
+ sendp(c, nil);
+ recvp(c);
+ chanfree(c);
+}
+
+static Cursor *lastcursor;
+
+void
+riosetcursor(Cursor *p)
+{
+ if(p==lastcursor)
+ return;
+ setcursor(mousectl, p);
+ lastcursor = p;
+}
+
+void
+wsetcursor(Window *w, int force)
+{
+ Cursor *p;
+
+ if(menuing || sweeping || (w!=input && wpointto(mouse->xy)!=w))
+ return;
+ if(w==nil)
+ p = nil;
+ else {
+ p = w->cursorp;
+ if(p==nil && w->holding)
+ p = &whitearrow;
+ }
+ if(p && force) /* force cursor reload */
+ lastcursor = nil;
+ riosetcursor(p);
+}
+
+static 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;
+}
+
enum
{
HiWater = 640000, /* max size of history */
@@ -19,49 +155,175 @@ enum
MinWater = 20000, /* room to leave available when reallocating */
};
-static int topped;
-static int id;
-static Cursor *lastcursor;
+static uint
+winsert(Window *w, Rune *r, int n, uint q0)
+{
+ uint m;
-Window*
-wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
+ 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;
+}
+
+static void
+wfill(Window *w)
{
- Window *w;
- Rectangle r;
+ Rune *rp;
+ int i, n, m, nl;
- 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(Consreadmesg), 0);
- w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
- w->wctlread = chancreate(sizeof(Consreadmesg), 0);
- w->complete = chancreate(sizeof(Completion*), 0);
- w->gone = chancreate(sizeof(char*), 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;
+ while(w->lastlinefull == FALSE){
+ n = w->nr-(w->org+w->nchars);
+ if(n == 0)
+ break;
+ if(n > 2000) /* educated guess at reasonable amount */
+ n = 2000;
+ rp = w->r+(w->org+w->nchars);
+
+ /*
+ * 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);
+ }
+}
+
+static 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;
+}
+
+static 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);
+}
+
+static void
+wsetcols(Window *w, int topped)
+{
+ if(w->holding)
+ if(topped)
+ w->cols[TEXT] = holdcol;
+ else
+ w->cols[TEXT] = lightholdcol;
+ else
+ if(topped)
+ w->cols[TEXT] = cols[TEXT];
+ else
+ w->cols[TEXT] = paletextcol;
}
void
@@ -84,12 +346,11 @@ wsetname(Window *w)
fprint(2, "rio: setname failed: %s\n", err);
}
-void
+static void
wresize(Window *w, Image *i)
{
Rectangle r;
- wclosewin(w);
w->i = i;
w->mc.image = i;
r = insetrect(i->r, Selborder+1);
@@ -99,7 +360,7 @@ wresize(Window *w, Image *i)
r.min.x += Scrollwid+Scrollgap;
frclear(w, FALSE);
frinit(w, r, w->font, w->i, cols);
- wsetcols(w, 1);
+ wsetcols(w, w == input);
w->maxtab = maxtab*stringwidth(w->font, "0");
if(!w->mouseopen || !w->winnameread){
r = insetrect(w->i->r, Selborder);
@@ -108,7 +369,10 @@ wresize(Window *w, Image *i)
wsetselect(w, w->q0, w->q1);
wscrdraw(w);
}
- wborder(w, Selborder);
+ if(w == input)
+ wborder(w, Selborder);
+ else
+ wborder(w, Unselborder);
flushimage(display, 1);
wsetname(w);
w->topped = ++topped;
@@ -118,7 +382,19 @@ wresize(Window *w, Image *i)
w->wctlready = 1;
}
-void
+static void
+wrepaint(Window *w)
+{
+ wsetcols(w, w == input);
+ if(!w->mouseopen || !w->winnameread)
+ frredraw(w);
+ if(w == input)
+ wborder(w, Selborder);
+ else
+ wborder(w, Unselborder);
+}
+
+static void
wrefresh(Window *w)
{
Rectangle r;
@@ -140,316 +416,11 @@ wrefresh(Window *w)
wscrdraw(w);
}
-int
-wclose(Window *w)
-{
- int i;
-
- i = decref(w);
- if(i > 0)
- return 0;
- if(i < 0)
- error("negative ref count");
- wclunk(w);
- wsendctlmesg(w, Exited, ZR, nil);
- return 1;
-}
-
-void
-showcandidates(Window *, Completion *);
-
-void
-winctl(void *arg)
-{
- Rune *rp, *up, r;
- uint qh, q0;
- 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, WComplete, Wgone, NWALT };
- Alt alts[NWALT+1];
- Consreadmesg crm;
- Mousereadmesg mrm;
- Conswritemesg cwm;
- Stringpair pair;
- Wctlmesg wcm;
- Completion *cr;
- char *kbdq[32], *kbds;
- uint kbdqr, kbdqw;
-
- w = arg;
- threadsetname("winctl-id%d", w->id);
-
- mrm.cm = chancreate(sizeof(Mouse), 0);
- crm.c1 = chancreate(sizeof(Stringpair), 0);
- crm.c2 = chancreate(sizeof(Stringpair), 0);
- cwm.cw = 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 = &crm;
- 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 = &crm;
- alts[WWread].op = CHANSND;
- alts[WComplete].c = w->complete;
- alts[WComplete].v = &cr;
- alts[WComplete].op = CHANRCV;
- alts[Wgone].c = w->gone;
- alts[Wgone].v = "window deleted";
- alts[Wgone].op = CHANNOP;
- alts[NWALT].op = CHANEND;
-
- kbdqr = kbdqw = 0;
- npart = 0;
- lastb = -1;
- for(;;){
- if(w->i==nil){
- /* window deleted */
- alts[Wgone].op = CHANSND;
-
- alts[WKbdread].op = CHANNOP;
- alts[WMouseread].op = CHANNOP;
- alts[WCwrite].op = CHANNOP;
- alts[WWread].op = CHANNOP;
- alts[WCread].op = CHANNOP;
- } else {
- 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) ?
- CHANSND : CHANNOP;
- alts[WWread].op = w->wctlready ?
- CHANSND : CHANNOP;
- /* 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(kbdqw - kbdqr < nelem(kbdq))
- kbdq[kbdqw++ % nelem(kbdq)] = kbds;
- else
- free(kbds);
- if(w->kbdopen)
- continue;
- while(kbdqr != kbdqw){
- kbds = kbdq[kbdqr++ % nelem(kbdq)];
- if(*kbds == 'c'){
- chartorune(&r, kbds+1);
- if(r)
- wkeyctl(w, r);
- }
- free(kbds);
- }
- break;
- case WKbdread:
- recv(crm.c1, &pair);
- nb = 0;
- while(kbdqr != kbdqw){
- kbds = kbdq[kbdqr % nelem(kbdq)];
- i = strlen(kbds)+1;
- if(nb+i > pair.ns)
- break;
- memmove((char*)pair.s + nb, kbds, i);
- free(kbds);
- nb += i;
- kbdqr++;
- }
- pair.ns = nb;
- send(crm.c2, &pair);
- 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.p) == Exited){
- while(kbdqr != kbdqw)
- free(kbdq[kbdqr++ % nelem(kbdq)]);
- chanfree(crm.c1);
- chanfree(crm.c2);
- chanfree(mrm.cm);
- chanfree(cwm.cw);
- threadexits(nil);
- }
- continue;
- case WCwrite:
- recv(cwm.cw, &pair);
- rp = pair.s;
- nr = pair.ns;
- for(i=0; i<nr; i++)
- if(rp[i] == '\b'){
- up = rp+i;
- initial = 0;
- for(; i<nr; i++){
- if(rp[i] == '\b'){
- if(up == rp)
- initial++;
- else
- up--;
- }else
- *up++ = rp[i];
- }
- if(initial){
- if(initial > w->qh)
- initial = w->qh;
- qh = w->qh-initial;
- wdelete(w, qh, qh+initial);
- w->qh = qh;
- }
- nr = up - rp;
- 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(crm.c1, &pair);
- s = Dx(w->screenr) > 0 ? "visible" : "hidden";
- t = "notcurrent";
- if(w == input)
- t = "current";
- pair.ns = snprint(pair.s, pair.ns+1, "%11d %11d %11d %11d %11s %11s ",
- w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
- send(crm.c2, &pair);
- continue;
- case WComplete:
- if(w->i!=nil){
- if(!cr->advance)
- showcandidates(w, cr);
- if(cr->advance){
- rp = runesmprint("%s", cr->string);
- if(rp){
- nr = runestrlen(rp);
- q0 = w->q0;
- q0 = winsert(w, rp, nr, q0);
- wshow(w, q0+nr);
- free(rp);
- }
- }
- }
- freecompletion(cr);
- break;
- }
- if(w->i!=nil && Dx(w->screenr) > 0 && display->bufp > display->buf)
- 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
+static void
interruptproc(void *v)
{
int *notefd;
@@ -460,7 +431,35 @@ interruptproc(void *v)
free(notefd);
}
-int
+typedef struct Completejob Completejob;
+struct Completejob
+{
+ char *dir;
+ char *str;
+ Window *win;
+};
+
+static void
+completeproc(void *arg)
+{
+ Completejob *job;
+ Completion *c;
+
+ job = arg;
+ threadsetname("namecomplete %s", job->dir);
+
+ c = complete(job->dir, job->str);
+ if(c != nil && sendp(job->win->complete, c) <= 0)
+ freecompletion(c);
+
+ wclose(job->win);
+
+ free(job->dir);
+ free(job->str);
+ free(job);
+}
+
+static int
windfilewidth(Window *w, uint q0, int oneelement)
{
uint q;
@@ -478,7 +477,45 @@ windfilewidth(Window *w, uint q0, int oneelement)
return q0-q;
}
-void
+static void
+namecomplete(Window *w)
+{
+ int nstr, npath;
+ Rune *path, *str;
+ char *dir, *root;
+ Completejob *job;
+
+ /* 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;
+ nstr = windfilewidth(w, w->q0, TRUE);
+ str = w->r+(w->q0-nstr);
+ npath = windfilewidth(w, w->q0-nstr, FALSE);
+ path = w->r+(w->q0-nstr-npath);
+
+ /* is path rooted? if not, we need to make it relative to window path */
+ if(npath>0 && path[0]=='/')
+ dir = runetobyte(path, npath, &npath);
+ else {
+ if(strcmp(w->dir, "") == 0)
+ root = ".";
+ else
+ root = w->dir;
+ dir = smprint("%s/%.*S", root, npath, path);
+ }
+ if(dir == nil)
+ return;
+
+ /* run in background, winctl will collect the result on w->complete chan */
+ job = emalloc(sizeof *job);
+ job->str = runetobyte(str, nstr, &nstr);
+ job->dir = cleanname(dir);
+ job->win = w;
+ incref(w);
+ proccreate(completeproc, job, STACK);
+}
+
+static void
showcandidates(Window *w, Completion *c)
{
int i;
@@ -520,73 +557,288 @@ showcandidates(Window *w, Completion *c)
free(rp);
}
-typedef struct Completejob Completejob;
-struct Completejob
+static int
+wbswidth(Window *w, Rune c)
{
- char *dir;
- char *str;
- Window *win;
-};
+ 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
-completeproc(void *arg)
+wsetorigin(Window *w, uint org, int exact)
{
- Completejob *job;
- Completion *c;
+ int i, a, fixup;
+ Rune *r;
+ uint n;
- job = arg;
- threadsetname("namecomplete %s", job->dir);
+ 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 = w->r+org;
+ frinsert(w, r, r+n, 0);
+ }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);
+}
- c = complete(job->dir, job->str);
- if(c != nil && sendp(job->win->complete, c) <= 0)
- freecompletion(c);
+uint
+wbacknl(Window *w, uint p, uint n)
+{
+ int i, j;
- wclose(job->win);
+ /* 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;
+}
- free(job->dir);
- free(job->str);
- free(job);
+char*
+wcontents(Window *w, int *ip)
+{
+ return runetobyte(w->r, w->nr, ip);
}
void
-namecomplete(Window *w)
+wshow(Window *w, uint q0)
{
- int nstr, npath;
- Rune *path, *str;
- char *dir, *root;
- Completejob *job;
+ int qe;
+ int nl;
+ uint q;
- /* 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 */
+ 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
+wsnarf(Window *w)
+{
+ if(w->q1 == w->q0)
return;
- nstr = windfilewidth(w, w->q0, TRUE);
- str = w->r+(w->q0-nstr);
- npath = windfilewidth(w, w->q0-nstr, FALSE);
- path = w->r+(w->q0-nstr-npath);
+ nsnarf = w->q1-w->q0;
+ snarf = runerealloc(snarf, nsnarf);
+ snarfversion++; /* maybe modified by parent */
+ runemove(snarf, w->r+w->q0, nsnarf);
+ putsnarf();
+}
- /* is path rooted? if not, we need to make it relative to window path */
- if(npath>0 && path[0]=='/')
- dir = runetobyte(path, npath, &npath);
- else {
- if(strcmp(w->dir, "") == 0)
- root = ".";
- else
- root = w->dir;
- dir = smprint("%s/%.*S", root, npath, path);
+void
+wsend(Window *w)
+{
+ getsnarf();
+ wsnarf(w);
+ if(nsnarf == 0)
+ return;
+ if(w->rawing){
+ waddraw(w, snarf, nsnarf);
+ if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
+ waddraw(w, L"\n", 1);
+ }else{
+ winsert(w, snarf, nsnarf, w->nr);
+ if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
+ winsert(w, L"\n", 1, w->nr);
}
- if(dir == nil)
+ wsetselect(w, w->nr, w->nr);
+ wshow(w, w->nr);
+}
+
+static 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);
+ }
+}
- /* run in background, winctl will collect the result on w->complete chan */
- job = emalloc(sizeof *job);
- job->str = runetobyte(str, nstr, &nstr);
- job->dir = cleanname(dir);
- job->win = w;
- incref(w);
- proccreate(completeproc, job, STACK);
+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
+wlook(Window *w)
+{
+ int i, n, e;
+
+ i = w->q1;
+ n = i - w->q0;
+ e = w->nr - n;
+ if(n <= 0 || e < n)
+ return;
+
+ if(i > e)
+ i = 0;
+
+ while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
+ if(i < e)
+ i++;
+ else
+ i = 0;
+ }
+
+ wsetselect(w, i, i+n);
+ wshow(w, i);
}
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++;
+ snprint(buf, sizeof(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);
+ sleep(300);
+ riosetcursor(c);
+ }
+ plumbfree(m);
+}
+
+static void
wkeyctl(Window *w, Rune r)
{
uint q0 ,q1;
@@ -736,234 +988,6 @@ wkeyctl(Window *w, Rune r)
wshow(w, q0+1);
}
-void
-wsetcols(Window *w, int topped)
-{
- if(w->holding)
- if(topped)
- w->cols[TEXT] = holdcol;
- else
- w->cols[TEXT] = lightholdcol;
- else
- if(topped)
- w->cols[TEXT] = cols[TEXT];
- else
- w->cols[TEXT] = paletextcol;
-}
-
-void
-wrepaint(Window *w)
-{
- wsetcols(w, w == input);
- if(!w->mouseopen || !w->winnameread)
- frredraw(w);
- if(w == input)
- wborder(w, Selborder);
- 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++;
- snprint(buf, sizeof(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);
- sleep(300);
- riosetcursor(c);
- }
- plumbfree(m);
-}
-
-void
-wlook(Window *w)
-{
- int i, n, e;
-
- i = w->q1;
- n = i - w->q0;
- e = w->nr - n;
- if(n <= 0 || e < n)
- return;
-
- if(i > e)
- i = 0;
-
- while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
- if(i < e)
- i++;
- else
- i = 0;
- }
-
- wsetselect(w, i, i+n);
- wshow(w, i);
-}
-
-void
-wmousectl(Window *w)
-{
- int but;
-
- for(but=1;; but++){
- if(but > 5)
- return;
- if(w->mc.buttons == 1<<(but-1))
- break;
- }
-
- incref(w); /* hold up window while we track */
- if(w->i != nil){
- if(shiftdown && but > 3)
- wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
- else if(ptinrect(w->mc.xy, w->scrollr) || (but > 3))
- wscroll(w, but);
- else if(but == 1)
- wselect(w);
- }
- 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 Point clickpt;
@@ -971,18 +995,7 @@ static uint clickcount;
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
+static void
wframescroll(Window *w, int dl)
{
uint q0;
@@ -1009,7 +1022,118 @@ wframescroll(Window *w, int dl)
wsetorigin(w, q0, TRUE);
}
-void
+/*
+ * called from frame library
+ */
+static void
+framescroll(Frame *f, int dl)
+{
+ if(f != &selectwin->Frame)
+ error("frameselect not right frame");
+ wframescroll(selectwin, dl);
+}
+
+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 };
+
+static Rune *left[] = {
+ left1,
+ left2,
+ left3,
+ nil
+};
+static Rune *right[] = {
+ right1,
+ left2,
+ left3,
+ nil
+};
+
+static 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;
+}
+
+static int
+inmode(Rune r, int mode)
+{
+ return (mode == 1) ? isalnum(r) : r && !isspace(r);
+}
+
+static void
+wstretchsel(Window *w, uint pt, uint *q0, uint *q1, int mode)
+{
+ int c, i;
+ Rune *r, *l, *p;
+ uint q;
+
+ *q0 = pt;
+ *q1 = pt;
+ 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 && inmode(w->r[*q1], mode))
+ (*q1)++;
+ /* try filling out word to left */
+ while(*q0>0 && inmode(w->r[*q0-1], mode))
+ (*q0)--;
+}
+
+static void
wselect(Window *w)
{
uint q0, q1;
@@ -1100,6 +1224,118 @@ wselect(Window *w)
}
}
+/*
+ * Convert back to physical coordinates
+ */
+static 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);
+}
+
+
+Window*
+wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
+{
+ static int id;
+
+ 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(Consreadmesg), 0);
+ w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
+ w->wctlread = chancreate(sizeof(Consreadmesg), 0);
+ w->complete = chancreate(sizeof(Completion*), 0);
+ w->gone = chancreate(sizeof(char*), 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;
+}
+
+static void
+wclosewin(Window *w)
+{
+ Image *i = w->i;
+ if(i == nil)
+ return;
+ w->i = nil;
+ /* move it off-screen to hide it, in case client is slow in letting it go */
+ originwindow(i, i->r.min, view->r.max);
+ freeimage(i);
+}
+
+static void
+wclunk(Window *w)
+{
+ int i;
+
+ if(w->deleted)
+ return;
+ w->deleted = TRUE;
+ if(w == input){
+ input = nil;
+ riosetcursor(nil);
+ }
+ 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;
+ }
+}
+
+int
+wclose(Window *w)
+{
+ int i;
+
+ i = decref(w);
+ if(i > 0)
+ return 0;
+ if(i < 0)
+ error("negative ref count");
+ wclunk(w);
+ wsendctlmesg(w, Exited, ZR, nil);
+ return 1;
+}
+
void
wsendctlmesg(Window *w, int type, Rectangle r, void *p)
{
@@ -1111,7 +1347,7 @@ wsendctlmesg(Window *w, int type, Rectangle r, void *p)
send(w->cctl, &wcm);
}
-int
+static int
wctlmesg(Window *w, int m, Rectangle r, void *p)
{
Image *i = p;
@@ -1121,8 +1357,6 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
error("unknown control message");
break;
case Wakeup:
- if(p!=nil)
- sendp((Channel*)p, w);
break;
case Reshaped:
if(w->deleted){
@@ -1130,54 +1364,23 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
break;
}
w->screenr = r;
+ wclosewin(w);
wresize(w, i);
- if(Dx(r)<=0){ /* window got hidden, if we had the input, drop it */
- if(w==input)
- input = nil;
- break;
- }
- /* fall through to get input if needed */
+ wsetcursor(w, FALSE);
+ break;
case Topped:
- if(w->deleted || w==input)
+ if(w->deleted)
break;
- if(input!=nil){
- Window *oi;
- Channel *c;
-
- oi = input;
- incref(oi);
-
- /*
- * have to wait until old input responds before
- * changing input to us because the window might
- * currently be mouse tracking and it is not
- * prepared for getting its input revoked.
- */
- c = chancreate(sizeof(void*), 0);
- wsendctlmesg(oi, Wakeup, ZR, c);
- recv(c, nil);
- chanfree(c);
-
- /*
- * if we are still top window and nobody else has taken
- * input from original window, take the input.
- */
- if(!w->deleted && w->topped==topped && oi==input){
- input = w;
-
- oi->wctlready = 1;
- wsendctlmesg(oi, Repaint, ZR, nil);
- }
- wclose(oi);
- } else {
- input = w;
- wsetcursor(w, FALSE);
- }
w->wctlready = 1;
- if(m!=Topped && w==input)
- break;
+ wsetcursor(w, FALSE);
/* fall thrugh for redraw after input change */
case Repaint:
+ if(p != nil){
+ /* sync with input change from wcurrent()/wuncurrent() */
+ Channel *c = p;
+ input = recvp(c);
+ sendp(c, w);
+ }
if(w->i==nil || Dx(w->screenr)<=0)
break;
wrepaint(w);
@@ -1246,175 +1449,307 @@ wctlmesg(Window *w, int m, Rectangle r, void *p)
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)
-{
- if(w!=nil && w!=input)
- wsendctlmesg(w, Topped, ZR, nil);
-}
-
-void
-wsetcursor(Window *w, int force)
+static void
+wmousectl(Window *w)
{
- Cursor *p;
+ int but;
- if(menuing || sweeping || (w!=input && wpointto(mouse->xy)!=w))
- return;
- if(w==nil)
- p = nil;
- else {
- p = w->cursorp;
- if(p==nil && w->holding)
- p = &whitearrow;
+ for(but=1;; but++){
+ if(but > 5)
+ return;
+ if(w->mc.buttons == 1<<(but-1))
+ break;
}
- if(p && force) /* force cursor reload */
- lastcursor = nil;
- riosetcursor(p);
-}
-void
-riosetcursor(Cursor *p)
-{
- if(p==lastcursor)
- return;
- setcursor(mousectl, p);
- lastcursor = p;
-}
-
-void
-wtopme(Window *w)
-{
- if(w!=nil && w->i!=nil && w->topped!=topped){
- w->topped = ++topped;
- topwindow(w->i);
- flushimage(display, 1);
+ incref(w); /* hold up window while we track */
+ if(w->i != nil){
+ if(shiftdown && but > 3)
+ wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
+ else if(ptinrect(w->mc.xy, w->scrollr) || (but > 3))
+ wscroll(w, but);
+ else if(but == 1)
+ wselect(w);
}
+ wclose(w);
}
void
-wbottomme(Window *w)
-{
- if(w!=nil && w->i!=nil){
- w->topped = - ++topped;
- bottomwindow(w->i);
- flushimage(display, 1);
- }
-}
-
-Window*
-wtop(Point pt)
+winctl(void *arg)
{
+ Rune *rp, *up, r;
+ uint qh, q0;
+ 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, WComplete, Wgone, NWALT };
+ Alt alts[NWALT+1];
+ Consreadmesg crm;
+ Mousereadmesg mrm;
+ Conswritemesg cwm;
+ Stringpair pair;
+ Wctlmesg wcm;
+ Completion *cr;
+ char *kbdq[32], *kbds;
+ uint kbdqr, kbdqw;
- w = wpointto(pt);
- if(w){
- incref(w);
- wtopme(w);
- wcurrent(w);
- wclose(w);
- }
- return w;
-}
+ w = arg;
+ threadsetname("winctl-id%d", w->id);
-Window*
-wlookid(int id)
-{
- int i;
+ mrm.cm = chancreate(sizeof(Mouse), 0);
+ crm.c1 = chancreate(sizeof(Stringpair), 0);
+ crm.c2 = chancreate(sizeof(Stringpair), 0);
+ cwm.cw = 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 = &crm;
+ 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 = &crm;
+ alts[WWread].op = CHANSND;
+ alts[WComplete].c = w->complete;
+ alts[WComplete].v = &cr;
+ alts[WComplete].op = CHANRCV;
+ alts[Wgone].c = w->gone;
+ alts[Wgone].v = "window deleted";
+ alts[Wgone].op = CHANNOP;
+ alts[NWALT].op = CHANEND;
- for(i=0; i<nwindow; i++)
- if(window[i]->id == id)
- return window[i];
- return nil;
-}
+ kbdqr = kbdqw = 0;
+ npart = 0;
+ lastb = -1;
+ for(;;){
+ if(w->i==nil){
+ /* window deleted */
+ alts[Wgone].op = CHANSND;
-void
-wclunk(Window *w)
-{
- int i;
+ alts[WKbdread].op = CHANNOP;
+ alts[WMouseread].op = CHANNOP;
+ alts[WCwrite].op = CHANNOP;
+ alts[WWread].op = CHANNOP;
+ alts[WCread].op = CHANNOP;
+ } else {
+ 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) ?
+ CHANSND : CHANNOP;
+ alts[WWread].op = w->wctlready ?
+ CHANSND : CHANNOP;
+ /* 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(kbdqw - kbdqr < nelem(kbdq))
+ kbdq[kbdqw++ % nelem(kbdq)] = kbds;
+ else
+ free(kbds);
+ if(w->kbdopen)
+ continue;
+ while(kbdqr != kbdqw){
+ kbds = kbdq[kbdqr++ % nelem(kbdq)];
+ if(*kbds == 'c'){
+ chartorune(&r, kbds+1);
+ if(r)
+ wkeyctl(w, r);
+ }
+ free(kbds);
+ }
+ break;
+ case WKbdread:
+ recv(crm.c1, &pair);
+ nb = 0;
+ while(kbdqr != kbdqw){
+ kbds = kbdq[kbdqr % nelem(kbdq)];
+ i = strlen(kbds)+1;
+ if(nb+i > pair.ns)
+ break;
+ memmove((char*)pair.s + nb, kbds, i);
+ free(kbds);
+ nb += i;
+ kbdqr++;
+ }
+ pair.ns = nb;
+ send(crm.c2, &pair);
+ continue;
+ case WMouse:
+ if(w->mouseopen) {
+ w->mouse.counter++;
- if(w->deleted)
- return;
- w->deleted = TRUE;
- if(w == input){
- input = nil;
- riosetcursor(nil);
- }
- 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]));
+ /* 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;
- }
- for(i=0; i<nwindow; i++)
- if(window[i] == w){
- --nwindow;
- memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
+ 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.p) == Exited){
+ while(kbdqr != kbdqw)
+ free(kbdq[kbdqr++ % nelem(kbdq)]);
+ chanfree(crm.c1);
+ chanfree(crm.c2);
+ chanfree(mrm.cm);
+ chanfree(cwm.cw);
+ threadexits(nil);
+ }
+ continue;
+ case WCwrite:
+ recv(cwm.cw, &pair);
+ rp = pair.s;
+ nr = pair.ns;
+ for(i=0; i<nr; i++)
+ if(rp[i] == '\b'){
+ up = rp+i;
+ initial = 0;
+ for(; i<nr; i++){
+ if(rp[i] == '\b'){
+ if(up == rp)
+ initial++;
+ else
+ up--;
+ }else
+ *up++ = rp[i];
+ }
+ if(initial){
+ if(initial > w->qh)
+ initial = w->qh;
+ qh = w->qh-initial;
+ wdelete(w, qh, qh+initial);
+ w->qh = qh;
+ }
+ nr = up - rp;
+ 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(crm.c1, &pair);
+ s = Dx(w->screenr) > 0 ? "visible" : "hidden";
+ t = "notcurrent";
+ if(w == input)
+ t = "current";
+ pair.ns = snprint(pair.s, pair.ns+1, "%11d %11d %11d %11d %11s %11s ",
+ w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
+ send(crm.c2, &pair);
+ continue;
+ case WComplete:
+ if(w->i!=nil){
+ if(!cr->advance)
+ showcandidates(w, cr);
+ if(cr->advance){
+ rp = runesmprint("%s", cr->string);
+ if(rp){
+ nr = runestrlen(rp);
+ q0 = w->q0;
+ q0 = winsert(w, rp, nr, q0);
+ wshow(w, q0+nr);
+ free(rp);
+ }
+ }
+ }
+ freecompletion(cr);
break;
}
-}
-
-void
-wclosewin(Window *w)
-{
- Image *i = w->i;
- if(i == nil)
- return;
- w->i = nil;
- /* move it off-screen to hide it, in case client is slow in letting it go */
- originwindow(i, i->r.min, view->r.max);
- freeimage(i);
+ if(w->i!=nil && Dx(w->screenr) > 0 && display->bufp > display->buf)
+ flushimage(display, 1);
+ }
}
void
@@ -1481,344 +1816,3 @@ winshell(void *args)
_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
-};
-
-int
-inmode(Rune r, int mode)
-{
- return (mode == 1) ? isalnum(r) : r && !isspace(r);
-}
-
-void
-wstretchsel(Window *w, uint pt, uint *q0, uint *q1, int mode)
-{
- int c, i;
- Rune *r, *l, *p;
- uint q;
-
- *q0 = pt;
- *q1 = pt;
- 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 && inmode(w->r[*q1], mode))
- (*q1)++;
- /* try filling out word to left */
- while(*q0>0 && inmode(w->r[*q0-1], mode))
- (*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 = w->r+org;
- frinsert(w, r, r+n, 0);
- }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;
-
- while(w->lastlinefull == FALSE){
- n = w->nr-(w->org+w->nchars);
- if(n == 0)
- break;
- if(n > 2000) /* educated guess at reasonable amount */
- n = 2000;
- rp = w->r+(w->org+w->nchars);
-
- /*
- * 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);
- }
-}
-
-char*
-wcontents(Window *w, int *ip)
-{
- return runetobyte(w->r, w->nr, ip);
-}
-
-void
-wsend(Window *w)
-{
- getsnarf();
- wsnarf(w);
- if(nsnarf == 0)
- return;
- if(w->rawing){
- waddraw(w, snarf, nsnarf);
- if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
- waddraw(w, L"\n", 1);
- }else{
- winsert(w, snarf, nsnarf, w->nr);
- if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
- winsert(w, L"\n", 1, w->nr);
- }
- wsetselect(w, w->nr, w->nr);
- wshow(w, w->nr);
-}