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