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