URI:
       twind.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       twind.c (34335B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <draw.h>
            4 #include <thread.h>
            5 #include <cursor.h>
            6 #include <mouse.h>
            7 #include <keyboard.h>
            8 #include <frame.h>
            9 #include <fcall.h>
           10 #include <9pclient.h>
           11 #include <plumb.h>
           12 #include <complete.h>
           13 #include "dat.h"
           14 #include "fns.h"
           15 
           16 #define MOVEIT if(0)
           17 
           18 enum
           19 {
           20         HiWater        = 64000000,        /* max size of history */
           21         LoWater        = 400000,        /* min size of history after max'ed */
           22         MinWater        = 20000        /* room to leave available when reallocating */
           23 };
           24 
           25 static        int                topped;
           26 static        int                id;
           27 
           28 static        Image        *cols[NCOL];
           29 static        Image        *grey;
           30 static        Image        *darkgrey;
           31 static        Cursor        *lastcursor;
           32 static        Image        *titlecol;
           33 static        Image        *lighttitlecol;
           34 static        Image        *holdcol;
           35 static        Image        *lightholdcol;
           36 static        Image        *paleholdcol;
           37 
           38 static int
           39 wscale(Window *w, int n)
           40 {
           41         if(w == nil || w->i == nil)
           42                 return n;
           43         return scalesize(w->i->display, n);
           44 }
           45 
           46 Window*
           47 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
           48 {
           49         Window *w;
           50         Rectangle r;
           51 
           52         if(cols[0] == nil){
           53                 /* greys are multiples of 0x11111100+0xFF, 14* being palest */
           54                 grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
           55                 darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
           56                 cols[BACK] = display->white;
           57                 cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
           58                 cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
           59                 cols[TEXT] = display->black;
           60                 cols[HTEXT] = display->black;
           61                 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
           62                 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
           63                 holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
           64                 lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
           65                 paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
           66         }
           67         w = emalloc(sizeof(Window));
           68         w->screenr = i->r;
           69         r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1));
           70         w->i = i;
           71         w->mc = *mc;
           72         w->ck = ck;
           73         w->cctl = cctl;
           74         w->cursorp = nil;
           75         w->conswrite = chancreate(sizeof(Conswritemesg), 0);
           76         w->consread =  chancreate(sizeof(Consreadmesg), 0);
           77         w->mouseread =  chancreate(sizeof(Mousereadmesg), 0);
           78         w->wctlread =  chancreate(sizeof(Consreadmesg), 0);
           79         w->scrollr = r;
           80         w->scrollr.max.x = r.min.x+wscale(w, Scrollwid);
           81         w->lastsr = ZR;
           82         r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap);
           83         frinit(&w->f, r, font, i, cols);
           84         w->f.maxtab = maxtab*stringwidth(font, "0");
           85         w->topped = ++topped;
           86         w->id = ++id;
           87         w->notefd = -1;
           88         w->scrolling = scrolling;
           89         w->dir = estrdup(startdir);
           90         w->label = estrdup("<unnamed>");
           91         r = insetrect(w->i->r, wscale(w, Selborder));
           92         draw(w->i, r, cols[BACK], nil, w->f.entire.min);
           93         wborder(w, wscale(w, Selborder));
           94         wscrdraw(w);
           95         incref(&w->ref);        /* ref will be removed after mounting; avoids delete before ready to be deleted */
           96         return w;
           97 }
           98 
           99 void
          100 wsetname(Window *w)
          101 {
          102         int i, n;
          103         char err[ERRMAX];
          104 
          105         n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
          106         for(i='A'; i<='Z'; i++){
          107                 if(nameimage(w->i, w->name, 1) > 0)
          108                         return;
          109                 errstr(err, sizeof err);
          110                 if(strcmp(err, "image name in use") != 0)
          111                         break;
          112                 w->name[n] = i;
          113                 w->name[n+1] = 0;
          114         }
          115         w->name[0] = 0;
          116         fprint(2, "rio: setname failed: %s\n", err);
          117 }
          118 
          119 void
          120 wresize(Window *w, Image *i, int move)
          121 {
          122         Rectangle r, or;
          123 
          124         or = w->i->r;
          125         if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
          126                 draw(i, i->r, w->i, nil, w->i->r.min);
          127         if(w->i != i){
          128 fprint(2, "res %p %p\n", w->i, i);
          129                 freeimage(w->i);
          130                 w->i = i;
          131         }
          132 /*        wsetname(w); */
          133 /*XXX        w->mc.image = i; */
          134         r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1));
          135         w->scrollr = r;
          136         w->scrollr.max.x = r.min.x+wscale(w, Scrollwid);
          137         w->lastsr = ZR;
          138         r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap);
          139         if(move)
          140                 frsetrects(&w->f, r, w->i);
          141         else{
          142                 frclear(&w->f, FALSE);
          143                 frinit(&w->f, r, w->f.font, w->i, cols);
          144                 wsetcols(w);
          145                 w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
          146                 r = insetrect(w->i->r, wscale(w, Selborder));
          147                 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
          148                 wfill(w);
          149                 wsetselect(w, w->q0, w->q1);
          150                 wscrdraw(w);
          151         }
          152         wborder(w, wscale(w, Selborder));
          153         w->topped = ++topped;
          154         w->resized = TRUE;
          155         w->mouse.counter++;
          156 }
          157 
          158 void
          159 wrefresh(Window *w, Rectangle r)
          160 {
          161         /* USED(r); */
          162 
          163         /* BUG: rectangle is ignored */
          164         if(w == input)
          165                 wborder(w, wscale(w, Selborder));
          166         else
          167                 wborder(w, wscale(w, Unselborder));
          168         if(w->mouseopen)
          169                 return;
          170         draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
          171         w->f.ticked = 0;
          172         if(w->f.p0 > 0)
          173                 frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
          174         if(w->f.p1 < w->f.nchars)
          175                 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
          176         frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
          177         w->lastsr = ZR;
          178         wscrdraw(w);
          179 }
          180 
          181 int
          182 wclose(Window *w)
          183 {
          184         int i;
          185 
          186         i = decref(&w->ref);
          187         if(i > 0)
          188                 return 0;
          189         if(i < 0)
          190                 error("negative ref count");
          191         if(!w->deleted)
          192                 wclosewin(w);
          193         wsendctlmesg(w, Exited, ZR, nil);
          194         return 1;
          195 }
          196 
          197 
          198 void
          199 winctl(void *arg)
          200 {
          201         Rune *rp, *bp, *up, *kbdr;
          202         uint qh;
          203         int nr, nb, c, wid, i, npart, initial, lastb, scrolling;
          204         char *s, *t, part[UTFmax];
          205         Window *w;
          206         Mousestate *mp, m;
          207         enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
          208         Alt alts[NWALT+1];
          209         Mousereadmesg mrm;
          210         Conswritemesg cwm;
          211         Consreadmesg crm;
          212         Consreadmesg cwrm;
          213         Stringpair pair;
          214         Wctlmesg wcm;
          215         char buf[4*12+1];
          216 
          217         w = arg;
          218         snprint(buf, sizeof buf, "winctl-id%d", w->id);
          219         threadsetname(buf);
          220 
          221         mrm.cm = chancreate(sizeof(Mouse), 0);
          222         cwm.cw = chancreate(sizeof(Stringpair), 0);
          223         crm.c1 = chancreate(sizeof(Stringpair), 0);
          224         crm.c2 = chancreate(sizeof(Stringpair), 0);
          225         cwrm.c1 = chancreate(sizeof(Stringpair), 0);
          226         cwrm.c2 = chancreate(sizeof(Stringpair), 0);
          227 
          228 
          229         alts[WKey].c = w->ck;
          230         alts[WKey].v = &kbdr;
          231         alts[WKey].op = CHANRCV;
          232         alts[WMouse].c = w->mc.c;
          233         alts[WMouse].v = &w->mc.m;
          234         alts[WMouse].op = CHANRCV;
          235         alts[WMouseread].c = w->mouseread;
          236         alts[WMouseread].v = &mrm;
          237         alts[WMouseread].op = CHANSND;
          238         alts[WCtl].c = w->cctl;
          239         alts[WCtl].v = &wcm;
          240         alts[WCtl].op = CHANRCV;
          241         alts[WCwrite].c = w->conswrite;
          242         alts[WCwrite].v = &cwm;
          243         alts[WCwrite].op = CHANSND;
          244         alts[WCread].c = w->consread;
          245         alts[WCread].v = &crm;
          246         alts[WCread].op = CHANSND;
          247         alts[WWread].c = w->wctlread;
          248         alts[WWread].v = &cwrm;
          249         alts[WWread].op = CHANSND;
          250         alts[NWALT].op = CHANEND;
          251 
          252         npart = 0;
          253         lastb = -1;
          254         for(;;){
          255                 if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
          256                         alts[WMouseread].op = CHANSND;
          257                 else
          258                         alts[WMouseread].op = CHANNOP;
          259                 //        if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars)
          260                 //                alts[WCwrite].op = CHANNOP;
          261                 //        else
          262                 alts[WCwrite].op = CHANSND;
          263                 if(w->deleted || !w->wctlready)
          264                         alts[WWread].op = CHANNOP;
          265                 else
          266                         alts[WWread].op = CHANSND;
          267                 /* this code depends on NL and EOT fitting in a single byte */
          268                 /* kind of expensive for each loop; worth precomputing? */
          269                 if(w->holding)
          270                         alts[WCread].op = CHANNOP;
          271                 else if(npart || (w->rawing && w->nraw>0))
          272                         alts[WCread].op = CHANSND;
          273                 else{
          274                         alts[WCread].op = CHANNOP;
          275                         for(i=w->qh; i<w->nr; i++){
          276                                 c = w->r[i];
          277                                 if(c=='\n' || c=='\004'){
          278                                         alts[WCread].op = CHANSND;
          279                                         break;
          280                                 }
          281                         }
          282                 }
          283                 switch(alt(alts)){
          284                 case WKey:
          285                         for(i=0; kbdr[i]!=L'\0'; i++)
          286                                 wkeyctl(w, kbdr[i]);
          287 /*                        wkeyctl(w, r); */
          288 /*                        while(nbrecv(w->ck, &r)) */
          289 /*                                wkeyctl(w, r); */
          290                         break;
          291                 case WMouse:
          292                         if(w->mouseopen) {
          293                                 w->mouse.counter++;
          294 
          295                                 /* queue click events */
          296                                 if(!w->mouse.qfull && lastb != w->mc.m.buttons) {        /* add to ring */
          297                                         mp = &w->mouse.queue[w->mouse.wi];
          298                                         if(++w->mouse.wi == nelem(w->mouse.queue))
          299                                                 w->mouse.wi = 0;
          300                                         if(w->mouse.wi == w->mouse.ri)
          301                                                 w->mouse.qfull = TRUE;
          302                                         mp->m = w->mc.m;
          303                                         mp->counter = w->mouse.counter;
          304                                         lastb = w->mc.m.buttons;
          305                                 }
          306                         } else
          307                                 wmousectl(w);
          308                         break;
          309                 case WMouseread:
          310                         /* send a queued event or, if the queue is empty, the current state */
          311                         /* if the queue has filled, we discard all the events it contained. */
          312                         /* the intent is to discard frantic clicking by the user during long latencies. */
          313                         w->mouse.qfull = FALSE;
          314                         if(w->mouse.wi != w->mouse.ri) {
          315                                 m = w->mouse.queue[w->mouse.ri];
          316                                 if(++w->mouse.ri == nelem(w->mouse.queue))
          317                                         w->mouse.ri = 0;
          318                         } else {
          319                                 m.m = w->mc.m;
          320                                 m.counter = w->mouse.counter;
          321                         }
          322                         w->mouse.lastcounter = m.counter;
          323                         send(mrm.cm, &m.m);
          324                         continue;
          325                 case WCtl:
          326                         if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
          327                                 chanfree(crm.c1);
          328                                 chanfree(crm.c2);
          329                                 chanfree(mrm.cm);
          330                                 chanfree(cwm.cw);
          331                                 chanfree(cwrm.c1);
          332                                 chanfree(cwrm.c2);
          333                                 threadexits(nil);
          334                         }
          335                         continue;
          336                 case WCwrite:
          337                         recv(cwm.cw, &pair);
          338                         rp = pair.s;
          339                         nr = pair.ns;
          340                         bp = rp;
          341                         up = rp;
          342                         initial = 0;
          343                         for(i=0; i<nr; i++){
          344                                 switch(*bp){
          345                                 case 0:
          346                                         break;
          347                                 case '\b':
          348                                         if(up == rp)
          349                                                 initial++;
          350                                         else
          351                                                 --up;
          352                                         break;
          353                                 case '\r':
          354                                         while(i<nr-1 && *(bp+1) == '\r'){
          355                                                 bp++;
          356                                                 i++;
          357                                         }
          358                                         if(i<nr-1 && *(bp+1) != '\n'){
          359                                                 while(up > rp && *(up-1) != '\n')
          360                                                         up--;
          361                                                 if(up == rp)
          362                                                         initial = wbswidth(w, '\r');
          363                                         }else if(i == nr-1)
          364                                                 *up++ = '\n';
          365                                         break;
          366                                 default:
          367                                         *up++ = *bp;
          368                                         break;
          369                                 }
          370                                 bp++;
          371                         }
          372                         if(initial){
          373                                 if(initial > w->qh)
          374                                         initial = w->qh;
          375                                 qh = w->qh - initial;
          376                                 wdelete(w, qh, qh+initial);
          377                                 w->qh = qh;
          378                         }
          379                         nr = up - rp;
          380                         scrolling = w->scrolling && w->org <= w->qh && w->qh <= w->org + w->f.nchars;
          381                         w->qh = winsert(w, rp, nr, w->qh)+nr;
          382                         if(scrolling)
          383                                 wshow(w, w->qh);
          384                         wsetselect(w, w->q0, w->q1);
          385                         wscrdraw(w);
          386                         free(rp);
          387                         break;
          388                 case WCread:
          389                         recv(crm.c1, &pair);
          390                         t = pair.s;
          391                         nb = pair.ns;
          392                         i = npart;
          393                         npart = 0;
          394                         if(i)
          395                                 memmove(t, part, i);
          396                         while(i<nb && (w->qh<w->nr || w->nraw>0)){
          397                                 if(w->qh == w->nr){
          398                                         wid = runetochar(t+i, &w->raw[0]);
          399                                         w->nraw--;
          400                                         runemove(w->raw, w->raw+1, w->nraw);
          401                                 }else
          402                                         wid = runetochar(t+i, &w->r[w->qh++]);
          403                                 c = t[i];        /* knows break characters fit in a byte */
          404                                 i += wid;
          405                                 if(!w->rawing && (c == '\n' || c=='\004')){
          406                                 /*        if(c == '\004') */
          407                                 /*                i--; */
          408                                         break;
          409                                 }
          410                         }
          411                 /*        if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') */
          412                 /*                w->qh++; */
          413                         if(i > nb){
          414                                 npart = i-nb;
          415                                 memmove(part, t+nb, npart);
          416                                 i = nb;
          417                         }
          418                         pair.s = t;
          419                         pair.ns = i;
          420                         send(crm.c2, &pair);
          421                         continue;
          422                 case WWread:
          423                         w->wctlready = 0;
          424                         recv(cwrm.c1, &pair);
          425                         if(w->deleted || w->i==nil)
          426                                 pair.ns = sprint(pair.s, "");
          427                         else{
          428                                 s = "visible";
          429                                 for(i=0; i<nhidden; i++)
          430                                         if(hidden[i] == w){
          431                                                 s = "hidden";
          432                                                 break;
          433                                         }
          434                                 t = "notcurrent";
          435                                 if(w == input)
          436                                         t = "current";
          437                                 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
          438                                         w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
          439                         }
          440                         send(cwrm.c2, &pair);
          441                         continue;
          442                 }
          443                 if(!w->deleted)
          444                         flushimage(display, 1);
          445         }
          446 }
          447 
          448 void
          449 waddraw(Window *w, Rune *r, int nr)
          450 {
          451         w->raw = runerealloc(w->raw, w->nraw+nr);
          452         runemove(w->raw+w->nraw, r, nr);
          453         w->nraw += nr;
          454 }
          455 
          456 /*
          457  * Need to do this in a separate proc because if process we're interrupting
          458  * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
          459  */
          460 void
          461 interruptproc(void *v)
          462 {
          463         int *notefd;
          464 
          465         notefd = v;
          466         write(*notefd, "interrupt", 9);
          467         free(notefd);
          468 }
          469 
          470 int
          471 windfilewidth(Window *w, uint q0, int oneelement)
          472 {
          473         uint q;
          474         Rune r;
          475 
          476         q = q0;
          477         while(q > 0){
          478                 r = w->r[q-1];
          479                 if(r<=' ')
          480                         break;
          481                 if(oneelement && r=='/')
          482                         break;
          483                 --q;
          484         }
          485         return q0-q;
          486 }
          487 
          488 void
          489 showcandidates(Window *w, Completion *c)
          490 {
          491         int i;
          492         Fmt f;
          493         Rune *rp;
          494         uint nr, qline, q0;
          495         char *s;
          496 
          497         runefmtstrinit(&f);
          498         if (c->nmatch == 0)
          499                 s = "[no matches in ";
          500         else
          501                 s = "[";
          502         if(c->nfile > 32)
          503                 fmtprint(&f, "%s%d files]\n", s, c->nfile);
          504         else{
          505                 fmtprint(&f, "%s", s);
          506                 for(i=0; i<c->nfile; i++){
          507                         if(i > 0)
          508                                 fmtprint(&f, " ");
          509                         fmtprint(&f, "%s", c->filename[i]);
          510                 }
          511                 fmtprint(&f, "]\n");
          512         }
          513         /* place text at beginning of line before host point */
          514         qline = w->qh;
          515         while(qline>0 && w->r[qline-1] != '\n')
          516                 qline--;
          517 
          518         rp = runefmtstrflush(&f);
          519         nr = runestrlen(rp);
          520 
          521         q0 = w->q0;
          522         q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
          523         free(rp);
          524         wsetselect(w, q0+nr, q0+nr);
          525 }
          526 
          527 Rune*
          528 namecomplete(Window *w)
          529 {
          530         int nstr, npath;
          531         Rune *rp, *path, *str;
          532         Completion *c;
          533         char *s, *dir, *root;
          534 
          535         /* control-f: filename completion; works back to white space or / */
          536         if(w->q0<w->nr && w->r[w->q0]>' ')        /* must be at end of word */
          537                 return nil;
          538         nstr = windfilewidth(w, w->q0, TRUE);
          539         str = runemalloc(nstr);
          540         runemove(str, w->r+(w->q0-nstr), nstr);
          541         npath = windfilewidth(w, w->q0-nstr, FALSE);
          542         path = runemalloc(npath);
          543         runemove(path, w->r+(w->q0-nstr-npath), npath);
          544         rp = nil;
          545 
          546         /* is path rooted? if not, we need to make it relative to window path */
          547         if(npath>0 && path[0]=='/'){
          548                 dir = malloc(UTFmax*npath+1);
          549                 sprint(dir, "%.*S", npath, path);
          550         }else{
          551                 if(strcmp(w->dir, "") == 0)
          552                         root = ".";
          553                 else
          554                         root = w->dir;
          555                 dir = malloc(strlen(root)+1+UTFmax*npath+1);
          556                 sprint(dir, "%s/%.*S", root, npath, path);
          557         }
          558         dir = cleanname(dir);
          559 
          560         s = smprint("%.*S", nstr, str);
          561         c = complete(dir, s);
          562         free(s);
          563         if(c == nil)
          564                 goto Return;
          565 
          566         if(!c->advance)
          567                 showcandidates(w, c);
          568 
          569         if(c->advance)
          570                 rp = runesmprint("%s", c->string);
          571 
          572   Return:
          573         freecompletion(c);
          574         free(dir);
          575         free(path);
          576         free(str);
          577         return rp;
          578 }
          579 
          580 void
          581 wkeyctl(Window *w, Rune r)
          582 {
          583         uint q0 ,q1;
          584         int n, nb, nr;
          585         Rune *rp;
          586 
          587         if(r == 0)
          588                 return;
          589         if(w->deleted)
          590                 return;
          591         w->rawing = rawon();
          592         /* navigation keys work only when mouse is not open */
          593         if(!w->mouseopen)
          594                 switch(r){
          595                 case Kdown:
          596                         n = w->f.maxlines/3;
          597                         goto case_Down;
          598                 case Kscrollonedown:
          599                         n = mousescrollsize(w->f.maxlines);
          600                         if(n <= 0)
          601                                 n = 1;
          602                         goto case_Down;
          603                 case Kpgdown:
          604                         n = 2*w->f.maxlines/3;
          605                 case_Down:
          606                         q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
          607                         wsetorigin(w, q0, TRUE);
          608                         return;
          609                 case Kup:
          610                         n = w->f.maxlines/3;
          611                         goto case_Up;
          612                 case Kscrolloneup:
          613                         n = mousescrollsize(w->f.maxlines);
          614                         if(n <= 0)
          615                                 n = 1;
          616                         goto case_Up;
          617                 case Kpgup:
          618                         n = 2*w->f.maxlines/3;
          619                 case_Up:
          620                         q0 = wbacknl(w, w->org, n);
          621                         wsetorigin(w, q0, TRUE);
          622                         return;
          623                 case Kleft:
          624                         if(w->q0 > 0){
          625                                 q0 = w->q0-1;
          626                                 wsetselect(w, q0, q0);
          627                                 wshow(w, q0);
          628                         }
          629                         return;
          630                 case Kright:
          631                         if(w->q1 < w->nr){
          632                                 q1 = w->q1+1;
          633                                 wsetselect(w, q1, q1);
          634                                 wshow(w, q1);
          635                         }
          636                         return;
          637                 case Khome:
          638                         if(w->org > w->iq1) {
          639                                 q0 = wbacknl(w, w->iq1, 1);
          640                                 wsetorigin(w, q0, TRUE);
          641                         } else
          642                                 wshow(w, 0);
          643                         return;
          644                 case Kend:
          645                         if(w->iq1 > w->org+w->f.nchars) {
          646                                 q0 = wbacknl(w, w->iq1, 1);
          647                                 wsetorigin(w, q0, TRUE);
          648                         } else
          649                                 wshow(w, w->nr);
          650                         return;
          651                 case 0x01:        /* ^A: beginning of line */
          652                         if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
          653                                 return;
          654                         nb = wbswidth(w, 0x15 /* ^U */);
          655                         wsetselect(w, w->q0-nb, w->q0-nb);
          656                         wshow(w, w->q0);
          657                         return;
          658                 case 0x05:        /* ^E: end of line */
          659                         q0 = w->q0;
          660                         while(q0 < w->nr && w->r[q0]!='\n')
          661                                 q0++;
          662                         wsetselect(w, q0, q0);
          663                         wshow(w, w->q0);
          664                         return;
          665                 }
          666         /*
          667          * This if used to be below the if(w->rawing ...),
          668          * but let's try putting it here.  This will allow ESC-processing
          669          * to toggle hold mode even in remote SSH connections.
          670          * The drawback is that vi-style processing gets harder.
          671          * If you find yourself in some weird readline mode, good
          672          * luck getting out without ESC.  Let's see who complains.
          673          */
          674         if(r==0x1B || (w->holding && r==0x7F)){        /* toggle hold */
          675                 if(w->holding)
          676                         --w->holding;
          677                 else
          678                         w->holding++;
          679                 wrepaint(w);
          680                 if(r == 0x1B)
          681                         return;
          682         }
          683         if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
          684                 waddraw(w, &r, 1);
          685                 return;
          686         }
          687         if(r == Kcmd+'x'){
          688                 wsnarf(w);
          689                 wcut(w);
          690                 wscrdraw(w);
          691                 return;
          692         }
          693         if(r == Kcmd+'c'){
          694                 wsnarf(w);
          695                 return;
          696         }
          697         if(r == Kcmd+'v'){
          698                 riogetsnarf();
          699                 wpaste(w);
          700                 wscrdraw(w);
          701                 return;
          702         }
          703         if(r != 0x7F){
          704                 wsnarf(w);
          705                 wcut(w);
          706         }
          707         switch(r){
          708         case 0x03:                /* maybe send interrupt */
          709                 /* since ^C is so commonly used as interrupt, special case it */
          710                 if (intrc() != 0x03)
          711                         break;
          712                 /* fall through */
          713         case 0x7F:                /* send interrupt */
          714                 w->qh = w->nr;
          715                 wshow(w, w->qh);
          716                 winterrupt(w);
          717                 w->iq1 = w->q0;
          718                 return;
          719         case 0x06:        /* ^F: file name completion */
          720         case Kins:                /* Insert: file name completion */
          721                 rp = namecomplete(w);
          722                 if(rp == nil)
          723                         return;
          724                 nr = runestrlen(rp);
          725                 q0 = w->q0;
          726                 q0 = winsert(w, rp, nr, q0);
          727                 wshow(w, q0+nr);
          728                 w->iq1 = w->q0;
          729                 free(rp);
          730                 return;
          731         case 0x08:        /* ^H: erase character */
          732         case 0x15:        /* ^U: erase line */
          733         case 0x17:        /* ^W: erase word */
          734                 if(w->q0==0 || w->q0==w->qh)
          735                         return;
          736                 nb = wbswidth(w, r);
          737                 q1 = w->q0;
          738                 q0 = q1-nb;
          739                 if(q0 < w->org){
          740                         q0 = w->org;
          741                         nb = q1-q0;
          742                 }
          743                 if(nb > 0){
          744                         wdelete(w, q0, q0+nb);
          745                         wsetselect(w, q0, q0);
          746                 }
          747                 w->iq1 = w->q0;
          748                 return;
          749         }
          750         /* otherwise ordinary character; just insert */
          751         q0 = w->q0;
          752         q0 = winsert(w, &r, 1, q0);
          753         wshow(w, q0+1);
          754         w->iq1 = w->q0;
          755 }
          756 
          757 void
          758 wsetcols(Window *w)
          759 {
          760         if(w->holding)
          761                 if(w == input)
          762                         w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
          763                 else
          764                         w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
          765         else
          766                 if(w == input)
          767                         w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
          768                 else
          769                         w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
          770 }
          771 
          772 void
          773 wrepaint(Window *w)
          774 {
          775         wsetcols(w);
          776         if(!w->mouseopen){
          777                 frredraw(&w->f);
          778         }
          779         if(w == input){
          780                 wborder(w, wscale(w, Selborder));
          781                 wsetcursor(w, 0);
          782         }else
          783                 wborder(w, wscale(w, Unselborder));
          784 }
          785 
          786 int
          787 wbswidth(Window *w, Rune c)
          788 {
          789         uint q, eq, stop;
          790         Rune r;
          791         int skipping;
          792 
          793         /* there is known to be at least one character to erase */
          794         if(c == 0x08)        /* ^H: erase character */
          795                 return 1;
          796         q = w->q0;
          797         stop = 0;
          798         if(q > w->qh)
          799                 stop = w->qh;
          800         skipping = TRUE;
          801         while(q > stop){
          802                 r = w->r[q-1];
          803                 if(r == '\n'){                /* eat at most one more character */
          804                         if(q == w->q0 && c != '\r')        /* eat the newline */
          805                                 --q;
          806                         break;
          807                 }
          808                 if(c == 0x17){
          809                         eq = isalnum(r);
          810                         if(eq && skipping)        /* found one; stop skipping */
          811                                 skipping = FALSE;
          812                         else if(!eq && !skipping)
          813                                 break;
          814                 }
          815                 --q;
          816         }
          817         return w->q0-q;
          818 }
          819 
          820 void
          821 wsnarf(Window *w)
          822 {
          823         if(w->q1 == w->q0)
          824                 return;
          825         nsnarf = w->q1-w->q0;
          826         snarf = runerealloc(snarf, nsnarf);
          827         snarfversion++;        /* maybe modified by parent */
          828         runemove(snarf, w->r+w->q0, nsnarf);
          829         rioputsnarf();
          830 }
          831 
          832 void
          833 wcut(Window *w)
          834 {
          835         if(w->q1 == w->q0)
          836                 return;
          837         wdelete(w, w->q0, w->q1);
          838         wsetselect(w, w->q0, w->q0);
          839 }
          840 
          841 void
          842 wpaste(Window *w)
          843 {
          844         uint q0;
          845 
          846         if(nsnarf == 0)
          847                 return;
          848         wcut(w);
          849         q0 = w->q0;
          850         if(w->rawing && !w->holding && q0==w->nr){
          851                 waddraw(w, snarf, nsnarf);
          852                 wsetselect(w, q0, q0);
          853         }else{
          854                 q0 = winsert(w, snarf, nsnarf, w->q0);
          855                 wsetselect(w, q0, q0+nsnarf);
          856         }
          857 }
          858 
          859 void
          860 wplumb(Window *w)
          861 {
          862         Plumbmsg *m;
          863         static CFid *fd;
          864         char buf[32];
          865         uint p0, p1;
          866         Cursor *c;
          867 
          868         if(fd == nil)
          869                 fd = plumbopenfid("send", OWRITE);
          870         if(fd == nil)
          871                 return;
          872         m = emalloc(sizeof(Plumbmsg));
          873         m->src = estrdup("rio");
          874         m->dst = nil;
          875         m->wdir = estrdup(w->dir);
          876         m->type = estrdup("text");
          877         p0 = w->q0;
          878         p1 = w->q1;
          879         if(w->q1 > w->q0)
          880                 m->attr = nil;
          881         else{
          882                 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
          883                         p0--;
          884                 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
          885                         p1++;
          886                 sprint(buf, "click=%d", w->q0-p0);
          887                 m->attr = plumbunpackattr(buf);
          888         }
          889         if(p1-p0 > messagesize-1024){
          890                 plumbfree(m);
          891                 return;        /* too large for 9P */
          892         }
          893         m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
          894         if(plumbsendtofid(fd, m) < 0){
          895                 c = lastcursor;
          896                 riosetcursor(&query, 1);
          897                 sleep(300);
          898                 riosetcursor(c, 1);
          899         }
          900         plumbfree(m);
          901 }
          902 
          903 int
          904 winborder(Window *w, Point xy)
          905 {
          906         return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, wscale(w, Selborder)));
          907 }
          908 
          909 void
          910 wlook(Window *w)
          911 {
          912         int i, n, e;
          913 
          914         i = w->q1;
          915         n = i - w->q0;
          916         e = w->nr - n;
          917         if(n <= 0 || e < n)
          918                 return;
          919 
          920         if(i > e)
          921                 i = 0;
          922 
          923         while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
          924                 if(i < e)
          925                         i++;
          926                 else
          927                         i = 0;
          928         }
          929 
          930         wsetselect(w, i, i+n);
          931         wshow(w, i);
          932 }
          933 
          934 void
          935 wmousectl(Window *w)
          936 {
          937         int but;
          938 
          939         if(w->mc.m.buttons == 1)
          940                 but = 1;
          941         else if(w->mc.m.buttons == 2)
          942                 but = 2;
          943         else if(w->mc.m.buttons == 4)
          944                 but = 3;
          945         else{
          946                 if(w->mc.m.buttons == 8)
          947                         wkeyctl(w, Kscrolloneup);
          948                 if(w->mc.m.buttons == 16)
          949                         wkeyctl(w, Kscrollonedown);
          950                 return;
          951         }
          952 
          953         incref(&w->ref);                /* hold up window while we track */
          954         if(w->deleted)
          955                 goto Return;
          956         if(ptinrect(w->mc.m.xy, w->scrollr)){
          957                 if(but)
          958                         wscroll(w, but);
          959                 goto Return;
          960         }
          961         if(but == 1)
          962                 wselect(w);
          963         /* else all is handled by main process */
          964    Return:
          965         wclose(w);
          966 }
          967 
          968 void
          969 wdelete(Window *w, uint q0, uint q1)
          970 {
          971         uint n, p0, p1;
          972 
          973         n = q1-q0;
          974         if(n == 0)
          975                 return;
          976         runemove(w->r+q0, w->r+q1, w->nr-q1);
          977         w->nr -= n;
          978         if(q0 < w->iq1)
          979                 w->iq1 -= min(n, w->iq1-q0);
          980         if(q0 < w->q0)
          981                 w->q0 -= min(n, w->q0-q0);
          982         if(q0 < w->q1)
          983                 w->q1 -= min(n, w->q1-q0);
          984         if(q1 < w->qh)
          985                 w->qh -= n;
          986         else if(q0 < w->qh)
          987                 w->qh = q0;
          988         if(q1 <= w->org)
          989                 w->org -= n;
          990         else if(q0 < w->org+w->f.nchars){
          991                 p1 = q1 - w->org;
          992                 if(p1 > w->f.nchars)
          993                         p1 = w->f.nchars;
          994                 if(q0 < w->org){
          995                         w->org = q0;
          996                         p0 = 0;
          997                 }else
          998                         p0 = q0 - w->org;
          999                 frdelete(&w->f, p0, p1);
         1000                 wfill(w);
         1001         }
         1002 }
         1003 
         1004 
         1005 static Window        *clickwin;
         1006 static uint        clickmsec;
         1007 static Window        *selectwin;
         1008 static uint        selectq;
         1009 
         1010 /*
         1011  * called from frame library
         1012  */
         1013 void
         1014 framescroll(Frame *f, int dl)
         1015 {
         1016         if(f != &selectwin->f)
         1017                 error("frameselect not right frame");
         1018         wframescroll(selectwin, dl);
         1019 }
         1020 
         1021 void
         1022 wframescroll(Window *w, int dl)
         1023 {
         1024         uint q0;
         1025 
         1026         if(dl == 0){
         1027                 wscrsleep(w, 100);
         1028                 return;
         1029         }
         1030         if(dl < 0){
         1031                 q0 = wbacknl(w, w->org, -dl);
         1032                 if(selectq > w->org+w->f.p0)
         1033                         wsetselect(w, w->org+w->f.p0, selectq);
         1034                 else
         1035                         wsetselect(w, selectq, w->org+w->f.p0);
         1036         }else{
         1037                 if(w->org+w->f.nchars == w->nr)
         1038                         return;
         1039                 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
         1040                 if(selectq >= w->org+w->f.p1)
         1041                         wsetselect(w, w->org+w->f.p1, selectq);
         1042                 else
         1043                         wsetselect(w, selectq, w->org+w->f.p1);
         1044         }
         1045         wsetorigin(w, q0, TRUE);
         1046 }
         1047 
         1048 void
         1049 wselect(Window *w)
         1050 {
         1051         uint q0, q1;
         1052         int b, x, y, first;
         1053 
         1054         first = 1;
         1055         selectwin = w;
         1056         /*
         1057          * Double-click immediately if it might make sense.
         1058          */
         1059         b = w->mc.m.buttons;
         1060         q0 = w->q0;
         1061         q1 = w->q1;
         1062         selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
         1063         if(clickwin==w && w->mc.m.msec-clickmsec<500)
         1064         if(q0==q1 && selectq==w->q0){
         1065                 wdoubleclick(w, &q0, &q1);
         1066                 wsetselect(w, q0, q1);
         1067                 flushimage(display, 1);
         1068                 x = w->mc.m.xy.x;
         1069                 y = w->mc.m.xy.y;
         1070                 /* stay here until something interesting happens */
         1071                 do
         1072                         readmouse(&w->mc);
         1073                 while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
         1074                 w->mc.m.xy.x = x;        /* in case we're calling frselect */
         1075                 w->mc.m.xy.y = y;
         1076                 q0 = w->q0;        /* may have changed */
         1077                 q1 = w->q1;
         1078                 selectq = q0;
         1079         }
         1080         if(w->mc.m.buttons == b){
         1081                 w->f.scroll = framescroll;
         1082                 frselect(&w->f, &w->mc);
         1083                 /* horrible botch: while asleep, may have lost selection altogether */
         1084                 if(selectq > w->nr)
         1085                         selectq = w->org + w->f.p0;
         1086                 w->f.scroll = nil;
         1087                 if(selectq < w->org)
         1088                         q0 = selectq;
         1089                 else
         1090                         q0 = w->org + w->f.p0;
         1091                 if(selectq > w->org+w->f.nchars)
         1092                         q1 = selectq;
         1093                 else
         1094                         q1 = w->org+w->f.p1;
         1095         }
         1096         if(q0 == q1){
         1097                 if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
         1098                         wdoubleclick(w, &q0, &q1);
         1099                         clickwin = nil;
         1100                 }else{
         1101                         clickwin = w;
         1102                         clickmsec = w->mc.m.msec;
         1103                 }
         1104         }else
         1105                 clickwin = nil;
         1106         wsetselect(w, q0, q1);
         1107         flushimage(display, 1);
         1108         while(w->mc.m.buttons){
         1109                 w->mc.m.msec = 0;
         1110                 b = w->mc.m.buttons;
         1111                 if(b & 6){
         1112                         if(b & 2){
         1113                                 wsnarf(w);
         1114                                 wcut(w);
         1115                         }else{
         1116                                 if(first){
         1117                                         first = 0;
         1118                                         riogetsnarf();
         1119                                 }
         1120                                 wpaste(w);
         1121                         }
         1122                 }
         1123                 wscrdraw(w);
         1124                 flushimage(display, 1);
         1125                 while(w->mc.m.buttons == b)
         1126                         readmouse(&w->mc);
         1127                 clickwin = nil;
         1128         }
         1129 }
         1130 
         1131 void
         1132 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
         1133 {
         1134         Wctlmesg wcm;
         1135 
         1136         wcm.type = type;
         1137         wcm.r = r;
         1138         wcm.image = image;
         1139         send(w->cctl, &wcm);
         1140 }
         1141 
         1142 int
         1143 wctlmesg(Window *w, int m, Rectangle r, Image *i)
         1144 {
         1145         char buf[64];
         1146 
         1147         switch(m){
         1148         default:
         1149                 error("unknown control message");
         1150                 break;
         1151         case Wakeup:
         1152                 break;
         1153         case Moved:
         1154         case Reshaped:
         1155                 if(w->deleted){
         1156                         freeimage(i);
         1157                         break;
         1158                 }
         1159                 w->screenr = r;
         1160                 strcpy(buf, w->name);
         1161                 wresize(w, i, m==Moved);
         1162                 w->wctlready = 1;
         1163                 if(Dx(r) > 0){
         1164                         if(w != input)
         1165                                 wcurrent(w);
         1166                 }else if(w == input)
         1167                         wcurrent(nil);
         1168                 flushimage(display, 1);
         1169                 break;
         1170         case Refresh:
         1171                 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
         1172                         break;
         1173                 if(!w->mouseopen)
         1174                         wrefresh(w, r);
         1175                 flushimage(display, 1);
         1176                 break;
         1177         case Movemouse:
         1178                 if(sweeping || !ptinrect(r.min, w->i->r))
         1179                         break;
         1180                 wmovemouse(w, r.min);
         1181         case Rawon:
         1182                 break;
         1183         case Rawoff:
         1184                 if(w->deleted)
         1185                         break;
         1186                 while(w->nraw > 0){
         1187                         wkeyctl(w, w->raw[0]);
         1188                         --w->nraw;
         1189                         runemove(w->raw, w->raw+1, w->nraw);
         1190                 }
         1191                 break;
         1192         case Holdon:
         1193         case Holdoff:
         1194                 if(w->deleted)
         1195                         break;
         1196                 wrepaint(w);
         1197                 flushimage(display, 1);
         1198                 break;
         1199         case Deleted:
         1200                 if(w->deleted)
         1201                         break;
         1202                 write(w->notefd, "hangup", 6);
         1203                 wclosewin(w);
         1204                 break;
         1205         case Exited:
         1206                 frclear(&w->f, TRUE);
         1207                 close(w->notefd);
         1208                 chanfree(w->mc.c);
         1209                 chanfree(w->ck);
         1210                 chanfree(w->cctl);
         1211                 chanfree(w->conswrite);
         1212                 chanfree(w->consread);
         1213                 chanfree(w->mouseread);
         1214                 chanfree(w->wctlread);
         1215                 free(w->raw);
         1216                 free(w->r);
         1217                 free(w->dir);
         1218                 free(w->label);
         1219                 free(w);
         1220                 break;
         1221         }
         1222         return m;
         1223 }
         1224 
         1225 /*
         1226  * Convert back to physical coordinates
         1227  */
         1228 void
         1229 wmovemouse(Window *w, Point p)
         1230 {
         1231         p.x += w->screenr.min.x-w->i->r.min.x;
         1232         p.y += w->screenr.min.y-w->i->r.min.y;
         1233         moveto(mousectl, p);
         1234 }
         1235 
         1236 void
         1237 wcurrent(Window *w)
         1238 {
         1239         Window *oi;
         1240 
         1241         if(wkeyboard!=nil && w==wkeyboard)
         1242                 return;
         1243         oi = input;
         1244         input = w;
         1245         if(oi!=w && oi!=nil)
         1246                 wrepaint(oi);
         1247         if(w !=nil){
         1248                 wrepaint(w);
         1249                 wsetcursor(w, 0);
         1250         }
         1251         if(w != oi){
         1252                 if(oi){
         1253                         oi->wctlready = 1;
         1254                         wsendctlmesg(oi, Wakeup, ZR, nil);
         1255                 }
         1256                 if(w){
         1257                         w->wctlready = 1;
         1258                         wsendctlmesg(w, Wakeup, ZR, nil);
         1259                 }
         1260         }
         1261 }
         1262 
         1263 void
         1264 wsetcursor(Window *w, int force)
         1265 {
         1266         Cursor *p;
         1267 
         1268         if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
         1269                 p = nil;
         1270         else if(wpointto(mouse->xy) == w){
         1271                 p = w->cursorp;
         1272                 if(p==nil && w->holding)
         1273                         p = &whitearrow;
         1274         }else
         1275                 p = nil;
         1276         if(!menuing)
         1277                 riosetcursor(p, force && !menuing);
         1278 }
         1279 
         1280 void
         1281 riosetcursor(Cursor *p, int force)
         1282 {
         1283         if(!force && p==lastcursor)
         1284                 return;
         1285         setcursor(mousectl, p);
         1286         lastcursor = p;
         1287 }
         1288 
         1289 Window*
         1290 wtop(Point pt)
         1291 {
         1292         Window *w;
         1293 
         1294         w = wpointto(pt);
         1295         if(w){
         1296                 if(w->topped == topped)
         1297                         return nil;
         1298                 topwindow(w->i);
         1299                 wcurrent(w);
         1300                 flushimage(display, 1);
         1301                 w->topped = ++topped;
         1302         }
         1303         return w;
         1304 }
         1305 
         1306 void
         1307 wtopme(Window *w)
         1308 {
         1309         if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
         1310                 topwindow(w->i);
         1311                 flushimage(display, 1);
         1312                 w->topped = ++ topped;
         1313         }
         1314 }
         1315 
         1316 void
         1317 wbottomme(Window *w)
         1318 {
         1319         if(w!=nil && w->i!=nil && !w->deleted){
         1320                 bottomwindow(w->i);
         1321                 flushimage(display, 1);
         1322                 w->topped = 0;
         1323         }
         1324 }
         1325 
         1326 Window*
         1327 wlookid(int id)
         1328 {
         1329         int i;
         1330 
         1331         for(i=0; i<nwindow; i++)
         1332                 if(window[i]->id == id)
         1333                         return window[i];
         1334         return nil;
         1335 }
         1336 
         1337 void
         1338 wclosewin(Window *w)
         1339 {
         1340         Rectangle r;
         1341         int i;
         1342 
         1343         w->deleted = TRUE;
         1344         if(w == input){
         1345                 input = nil;
         1346                 wsetcursor(w, 0);
         1347         }
         1348         if(w == wkeyboard)
         1349                 wkeyboard = nil;
         1350         for(i=0; i<nhidden; i++)
         1351                 if(hidden[i] == w){
         1352                         --nhidden;
         1353                         memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
         1354                         break;
         1355                 }
         1356         for(i=0; i<nwindow; i++)
         1357                 if(window[i] == w){
         1358                         --nwindow;
         1359                         memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
         1360                         w->deleted = TRUE;
         1361                         r = w->i->r;
         1362                         /* move it off-screen to hide it, in case client is slow in letting it go */
         1363                         MOVEIT originwindow(w->i, r.min, view->r.max);
         1364                         freeimage(w->i);
         1365                         w->i = nil;
         1366                         return;
         1367                 }
         1368         error("unknown window in closewin");
         1369 }
         1370 
         1371 void
         1372 wsetpid(Window *w, int pid, int dolabel)
         1373 {
         1374         char buf[128];
         1375 
         1376         w->pid = pid;
         1377         if(dolabel){
         1378                 sprint(buf, "rc %d", pid);
         1379                 free(w->label);
         1380                 w->label = estrdup(buf);
         1381                 drawsetlabel(w->label);
         1382         }
         1383 }
         1384 
         1385 static Rune left1[] =  {
         1386         '{', '[', '(', '<', 0xAB,
         1387         0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea,
         1388         0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b,
         1389         0
         1390 };
         1391 static Rune right1[] = {
         1392         '}', ']', ')', '>', 0xBB,
         1393         0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb,
         1394         0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d,
         1395         0
         1396 };
         1397 static Rune left2[] =  { '\n', 0 };
         1398 static Rune left3[] =  { '\'', '"', '`', 0 };
         1399 
         1400 Rune *left[] = {
         1401         left1,
         1402         left2,
         1403         left3,
         1404         nil
         1405 };
         1406 Rune *right[] = {
         1407         right1,
         1408         left2,
         1409         left3,
         1410         nil
         1411 };
         1412 
         1413 void
         1414 wdoubleclick(Window *w, uint *q0, uint *q1)
         1415 {
         1416         int c, i;
         1417         Rune *r, *l, *p;
         1418         uint q;
         1419 
         1420         for(i=0; left[i]!=nil; i++){
         1421                 q = *q0;
         1422                 l = left[i];
         1423                 r = right[i];
         1424                 /* try matching character to left, looking right */
         1425                 if(q == 0)
         1426                         c = '\n';
         1427                 else
         1428                         c = w->r[q-1];
         1429                 p = strrune(l, c);
         1430                 if(p != nil){
         1431                         if(wclickmatch(w, c, r[p-l], 1, &q))
         1432                                 *q1 = q-(c!='\n');
         1433                         return;
         1434                 }
         1435                 /* try matching character to right, looking left */
         1436                 if(q == w->nr)
         1437                         c = '\n';
         1438                 else
         1439                         c = w->r[q];
         1440                 p = strrune(r, c);
         1441                 if(p != nil){
         1442                         if(wclickmatch(w, c, l[p-r], -1, &q)){
         1443                                 *q1 = *q0+(*q0<w->nr && c=='\n');
         1444                                 *q0 = q;
         1445                                 if(c!='\n' || q!=0 || w->r[0]=='\n')
         1446                                         (*q0)++;
         1447                         }
         1448                         return;
         1449                 }
         1450         }
         1451         /* try filling out word to right */
         1452         while(*q1<w->nr && isalnum(w->r[*q1]))
         1453                 (*q1)++;
         1454         /* try filling out word to left */
         1455         while(*q0>0 && isalnum(w->r[*q0-1]))
         1456                 (*q0)--;
         1457 }
         1458 
         1459 int
         1460 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
         1461 {
         1462         Rune c;
         1463         int nest;
         1464 
         1465         nest = 1;
         1466         for(;;){
         1467                 if(dir > 0){
         1468                         if(*q == w->nr)
         1469                                 break;
         1470                         c = w->r[*q];
         1471                         (*q)++;
         1472                 }else{
         1473                         if(*q == 0)
         1474                                 break;
         1475                         (*q)--;
         1476                         c = w->r[*q];
         1477                 }
         1478                 if(c == cr){
         1479                         if(--nest==0)
         1480                                 return 1;
         1481                 }else if(c == cl)
         1482                         nest++;
         1483         }
         1484         return cl=='\n' && nest==1;
         1485 }
         1486 
         1487 
         1488 uint
         1489 wbacknl(Window *w, uint p, uint n)
         1490 {
         1491         int i, j;
         1492 
         1493         /* look for start of this line if n==0 */
         1494         if(n==0 && p>0 && w->r[p-1]!='\n')
         1495                 n = 1;
         1496         i = n;
         1497         while(i-->0 && p>0){
         1498                 --p;        /* it's at a newline now; back over it */
         1499                 if(p == 0)
         1500                         break;
         1501                 /* at 128 chars, call it a line anyway */
         1502                 for(j=128; --j>0 && p>0; p--)
         1503                         if(w->r[p-1]=='\n')
         1504                                 break;
         1505         }
         1506         return p;
         1507 }
         1508 
         1509 void
         1510 wshow(Window *w, uint q0)
         1511 {
         1512         int qe;
         1513         int nl;
         1514         uint q;
         1515 
         1516         qe = w->org+w->f.nchars;
         1517         if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
         1518                 wscrdraw(w);
         1519         else{
         1520                 nl = 4*w->f.maxlines/5;
         1521                 q = wbacknl(w, q0, nl);
         1522                 /* avoid going backwards if trying to go forwards - long lines! */
         1523                 if(!(q0>w->org && q<w->org))
         1524                         wsetorigin(w, q, TRUE);
         1525                 while(q0 > w->org+w->f.nchars)
         1526                         wsetorigin(w, w->org+1, FALSE);
         1527         }
         1528 }
         1529 
         1530 void
         1531 wsetorigin(Window *w, uint org, int exact)
         1532 {
         1533         int i, a, fixup;
         1534         Rune *r;
         1535         uint n;
         1536 
         1537         if(org>0 && !exact){
         1538                 /* org is an estimate of the char posn; find a newline */
         1539                 /* don't try harder than 256 chars */
         1540                 for(i=0; i<256 && org<w->nr; i++){
         1541                         if(w->r[org] == '\n'){
         1542                                 org++;
         1543                                 break;
         1544                         }
         1545                         org++;
         1546                 }
         1547         }
         1548         a = org-w->org;
         1549         fixup = 0;
         1550         if(a>=0 && a<w->f.nchars){
         1551                 frdelete(&w->f, 0, a);
         1552                 fixup = 1;        /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
         1553         }else if(a<0 && -a<w->f.nchars){
         1554                 n = w->org - org;
         1555                 r = runemalloc(n);
         1556                 runemove(r, w->r+org, n);
         1557                 frinsert(&w->f, r, r+n, 0);
         1558                 free(r);
         1559         }else
         1560                 frdelete(&w->f, 0, w->f.nchars);
         1561         w->org = org;
         1562         wfill(w);
         1563         wscrdraw(w);
         1564         wsetselect(w, w->q0, w->q1);
         1565         if(fixup && w->f.p1 > w->f.p0)
         1566                 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
         1567 }
         1568 
         1569 void
         1570 wsetselect(Window *w, uint q0, uint q1)
         1571 {
         1572         int p0, p1;
         1573 
         1574         /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
         1575         w->q0 = q0;
         1576         w->q1 = q1;
         1577         /* compute desired p0,p1 from q0,q1 */
         1578         p0 = q0-w->org;
         1579         p1 = q1-w->org;
         1580         if(p0 < 0)
         1581                 p0 = 0;
         1582         if(p1 < 0)
         1583                 p1 = 0;
         1584         if(p0 > w->f.nchars)
         1585                 p0 = w->f.nchars;
         1586         if(p1 > w->f.nchars)
         1587                 p1 = w->f.nchars;
         1588         if(p0==w->f.p0 && p1==w->f.p1)
         1589                 return;
         1590         /* screen disagrees with desired selection */
         1591         if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
         1592                 /* no overlap or too easy to bother trying */
         1593                 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
         1594                 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
         1595                 goto Return;
         1596         }
         1597         /* overlap; avoid unnecessary painting */
         1598         if(p0 < w->f.p0){
         1599                 /* extend selection backwards */
         1600                 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
         1601         }else if(p0 > w->f.p0){
         1602                 /* trim first part of selection */
         1603                 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
         1604         }
         1605         if(p1 > w->f.p1){
         1606                 /* extend selection forwards */
         1607                 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
         1608         }else if(p1 < w->f.p1){
         1609                 /* trim last part of selection */
         1610                 frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
         1611         }
         1612 
         1613     Return:
         1614         w->f.p0 = p0;
         1615         w->f.p1 = p1;
         1616 }
         1617 
         1618 uint
         1619 winsert(Window *w, Rune *r, int n, uint q0)
         1620 {
         1621         uint m;
         1622 
         1623         if(n == 0)
         1624                 return q0;
         1625         if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
         1626                 m = min(HiWater-LoWater, min(w->org, w->qh));
         1627                 w->org -= m;
         1628                 w->qh -= m;
         1629                 if(w->q0 > m)
         1630                         w->q0 -= m;
         1631                 else
         1632                         w->q0 = 0;
         1633                 if(w->q1 > m)
         1634                         w->q1 -= m;
         1635                 else
         1636                         w->q1 = 0;
         1637                 w->nr -= m;
         1638                 runemove(w->r, w->r+m, w->nr);
         1639                 q0 -= m;
         1640         }
         1641         if(w->nr+n > w->maxr){
         1642                 /*
         1643                  * Minimize realloc breakage:
         1644                  *        Allocate at least MinWater
         1645                  *         Double allocation size each time
         1646                  *        But don't go much above HiWater
         1647                  */
         1648                 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
         1649                 if(m > HiWater)
         1650                         m = max(HiWater+MinWater, w->nr+n);
         1651                 if(m > w->maxr){
         1652                         w->r = runerealloc(w->r, m);
         1653                         w->maxr = m;
         1654                 }
         1655         }
         1656         runemove(w->r+q0+n, w->r+q0, w->nr-q0);
         1657         runemove(w->r+q0, r, n);
         1658         w->nr += n;
         1659         /* if output touches, advance selection, not qh; works best for keyboard and output */
         1660         if(q0 <= w->q1)
         1661                 w->q1 += n;
         1662         if(q0 <= w->q0)
         1663                 w->q0 += n;
         1664         if(q0 < w->qh)
         1665                 w->qh += n;
         1666         if(q0 < w->iq1)
         1667                 w->iq1 += n;
         1668         if(q0 < w->org)
         1669                 w->org += n;
         1670         else if(q0 <= w->org+w->f.nchars)
         1671                 frinsert(&w->f, r, r+n, q0-w->org);
         1672         return q0;
         1673 }
         1674 
         1675 void
         1676 wfill(Window *w)
         1677 {
         1678         Rune *rp;
         1679         int i, n, m, nl;
         1680 
         1681         if(w->f.lastlinefull)
         1682                 return;
         1683         rp = malloc(messagesize);
         1684         do{
         1685                 n = w->nr-(w->org+w->f.nchars);
         1686                 if(n == 0)
         1687                         break;
         1688                 if(n > 2000)        /* educated guess at reasonable amount */
         1689                         n = 2000;
         1690                 runemove(rp, w->r+(w->org+w->f.nchars), n);
         1691                 /*
         1692                  * it's expensive to frinsert more than we need, so
         1693                  * count newlines.
         1694                  */
         1695                 nl = w->f.maxlines-w->f.nlines;
         1696                 m = 0;
         1697                 for(i=0; i<n; ){
         1698                         if(rp[i++] == '\n'){
         1699                                 m++;
         1700                                 if(m >= nl)
         1701                                         break;
         1702                         }
         1703                 }
         1704                 frinsert(&w->f, rp, rp+i, w->f.nchars);
         1705         }while(w->f.lastlinefull == FALSE);
         1706         free(rp);
         1707 }
         1708 
         1709 char*
         1710 wcontents(Window *w, int *ip)
         1711 {
         1712         return runetobyte(w->r, w->nr, ip);
         1713 }