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