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