ref: b5783b1e39122f466c1a577720496284223e2217
parent: ea347ee7f181208be5a2ace0e35d268ccfefa1e4
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Mar 7 14:26:30 EST 2021
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.
--- a/sys/src/cmd/rio/dat.h
+++ b/sys/src/cmd/rio/dat.h
@@ -183,34 +183,16 @@
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);
@@ -217,11 +199,9 @@
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
{
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -530,8 +530,10 @@
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 @@
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 @@
Window *w;
w = pointto(TRUE);
- if(w)
+ if(w!=nil)
wsendctlmesg(w, Deleted, ZR, nil);
}
@@ -1016,8 +1019,10 @@
return;
incref(w);
i = sweep();
- if(i)
+ if(i!=nil){
+ wcurrent(w);
wsendctlmesg(w, Reshaped, i->r, i);
+ }
wclose(w);
}
@@ -1032,8 +1037,10 @@
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 @@
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 @@
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 @@
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;
}
--- a/sys/src/cmd/rio/wctl.c
+++ b/sys/src/cmd/rio/wctl.c
@@ -380,6 +380,7 @@
} 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 @@
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)){
--- 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,51 +155,177 @@
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
wsetname(Window *w)
{
@@ -84,12 +346,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
}
}
+/*
+ * 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 @@
send(w->cctl, &wcm);
}
-int
+static int
wctlmesg(Window *w, int m, Rectangle r, void *p)
{
Image *i = p;
@@ -1121,8 +1357,6 @@
error("unknown control message");
break;
case Wakeup:
- if(p!=nil)
- sendp((Channel*)p, w);
break;
case Reshaped:
if(w->deleted){
@@ -1130,54 +1364,23 @@
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,178 +1449,310 @@
return m;
}
-/*
- * Convert back to physical coordinates
- */
-void
-wmovemouse(Window *w, Point p)
+static void
+wmousectl(Window *w)
{
- 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);
-}
+ int but;
-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;
+ for(but=1;; but++){
+ if(but > 5)
+ return;
+ if(w->mc.buttons == 1<<(but-1))
+ break;
}
- 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;
+ 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);
}
- return w;
+ wclose(w);
}
void
-wcurrent(Window *w)
+winctl(void *arg)
{
- if(w!=nil && w!=input)
- wsendctlmesg(w, Topped, ZR, nil);
-}
-
-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);
-}
-
-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);
- }
-}
-
-void
-wbottomme(Window *w)
-{
- if(w!=nil && w->i!=nil){
- w->topped = - ++topped;
- bottomwindow(w->i);
- flushimage(display, 1);
- }
-}
-
-Window*
-wtop(Point pt)
-{
+ 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;
}
+ if(w->i!=nil && Dx(w->screenr) > 0 && display->bufp > display->buf)
+ flushimage(display, 1);
+ }
}
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);
-}
-
-void
wsetpid(Window *w, int pid, int dolabel)
{
char buf[32];
@@ -1480,345 +1815,4 @@
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
-};
-
-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);
}