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