Blob


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