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 "dat.h"
12 #include "fns.h"
13 /* for generating syms in mkfile only: */
14 #include <bio.h>
15 #include "edit.h"
16
17 void mousethread(void*);
18 void keyboardthread(void*);
19 void waitthread(void*);
20 void xfidallocthread(void*);
21 void newwindowthread(void*);
22 void plumbproc(void*);
23
24 Reffont **fontcache;
25 int nfontcache;
26 char wdir[512] = ".";
27 Reffont *reffonts[2];
28 int snarffd = -1;
29 int mainpid;
30 int plumbsendfd;
31 int plumbeditfd;
32
33 enum{
34 NSnarf = 1000 /* less than 1024, I/O buffer size */
35 };
36 Rune snarfrune[NSnarf+1];
37
38 char *fontnames[2] =
39 {
40 "/lib/font/bit/lucidasans/euro.8.font",
41 "/lib/font/bit/lucm/unicode.9.font"
42 };
43
44 Command *command;
45
46 void acmeerrorinit(void);
47 void readfile(Column*, char*);
48 int shutdown(void*, char*);
49
50 void
derror(Display *,char * errorstr)51 derror(Display*, char *errorstr)
52 {
53 error(errorstr);
54 }
55
56 void
threadmain(int argc,char * argv[])57 threadmain(int argc, char *argv[])
58 {
59 int i;
60 char *p, *loadfile;
61 char buf[256];
62 Column *c;
63 int ncol;
64 Display *d;
65 static void *arg[1];
66
67 rfork(RFENVG|RFNAMEG);
68
69 ncol = -1;
70
71 loadfile = nil;
72 ARGBEGIN{
73 case 'a':
74 globalautoindent = TRUE;
75 break;
76 case 'b':
77 bartflag = TRUE;
78 break;
79 case 'c':
80 p = ARGF();
81 if(p == nil)
82 goto Usage;
83 ncol = atoi(p);
84 if(ncol <= 0)
85 goto Usage;
86 break;
87 case 'f':
88 fontnames[0] = ARGF();
89 if(fontnames[0] == nil)
90 goto Usage;
91 break;
92 case 'F':
93 fontnames[1] = ARGF();
94 if(fontnames[1] == nil)
95 goto Usage;
96 break;
97 case 'l':
98 loadfile = ARGF();
99 if(loadfile == nil)
100 goto Usage;
101 break;
102 default:
103 Usage:
104 fprint(2, "usage: acme [-ab] [-c ncol] [-f font] [-F fixedfont] [-l loadfile | file...]\n");
105 exits("usage");
106 }ARGEND
107
108 fontnames[0] = estrdup(fontnames[0]);
109 fontnames[1] = estrdup(fontnames[1]);
110
111 quotefmtinstall();
112 cputype = getenv("cputype");
113 objtype = getenv("objtype");
114 home = getenv("home");
115 p = getenv("tabstop");
116 if(p != nil){
117 maxtab = strtoul(p, nil, 0);
118 free(p);
119 }
120 if(maxtab == 0)
121 maxtab = 4;
122 if(loadfile)
123 rowloadfonts(loadfile);
124 putenv("font", fontnames[0]);
125 snarffd = open("/dev/snarf", OREAD|OCEXEC);
126 if(cputype){
127 sprint(buf, "/acme/bin/%s", cputype);
128 bind(buf, "/bin", MBEFORE);
129 }
130 bind("/acme/bin", "/bin", MBEFORE);
131 getwd(wdir, sizeof wdir);
132
133 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
134 fprint(2, "acme: can't open display: %r\n");
135 exits("geninitdraw");
136 }
137 d = display;
138 font = d->defaultfont;
139
140 reffont.f = font;
141 reffonts[0] = &reffont;
142 incref(&reffont); /* one to hold up 'font' variable */
143 incref(&reffont); /* one to hold up reffonts[0] */
144 fontcache = emalloc(sizeof(Reffont*));
145 nfontcache = 1;
146 fontcache[0] = &reffont;
147
148 iconinit();
149 timerinit();
150 rxinit();
151
152 cwait = threadwaitchan();
153 ccommand = chancreate(sizeof(Command**), 0);
154 ckill = chancreate(sizeof(Rune*), 0);
155 cxfidalloc = chancreate(sizeof(Xfid*), 0);
156 cxfidfree = chancreate(sizeof(Xfid*), 0);
157 cnewwindow = chancreate(sizeof(Channel*), 0);
158 cerr = chancreate(sizeof(char*), 0);
159 cedit = chancreate(sizeof(int), 0);
160 cexit = chancreate(sizeof(int), 0);
161 cwarn = chancreate(sizeof(void*), 1);
162 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
163 fprint(2, "acme: can't create initial channels: %r\n");
164 exits("channels");
165 }
166
167 mousectl = initmouse(nil, screen);
168 if(mousectl == nil){
169 fprint(2, "acme: can't initialize mouse: %r\n");
170 exits("mouse");
171 }
172 mouse = mousectl;
173 keyboardctl = initkeyboard(nil);
174 if(keyboardctl == nil){
175 fprint(2, "acme: can't initialize keyboard: %r\n");
176 exits("keyboard");
177 }
178 mainpid = getpid();
179 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
180 if(plumbeditfd >= 0){
181 cplumb = chancreate(sizeof(Plumbmsg*), 0);
182 proccreate(plumbproc, nil, STACK);
183 }
184 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
185
186 fsysinit();
187
188 #define WPERCOL 8
189 disk = diskinit();
190 if(!loadfile || !rowload(&row, loadfile, TRUE)){
191 rowinit(&row, screen->clipr);
192 if(ncol < 0){
193 if(argc == 0)
194 ncol = 2;
195 else{
196 ncol = (argc+(WPERCOL-1))/WPERCOL;
197 if(ncol < 2)
198 ncol = 2;
199 }
200 }
201 if(ncol == 0)
202 ncol = 2;
203 for(i=0; i<ncol; i++){
204 c = rowadd(&row, nil, -1);
205 if(c==nil && i==0)
206 error("initializing columns");
207 }
208 c = row.col[row.ncol-1];
209 if(argc == 0)
210 readfile(c, wdir);
211 else
212 for(i=0; i<argc; i++){
213 p = utfrrune(argv[i], '/');
214 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
215 readfile(c, argv[i]);
216 else
217 readfile(row.col[i/WPERCOL], argv[i]);
218 }
219 }
220 flushimage(display, 1);
221
222 acmeerrorinit();
223 threadcreate(keyboardthread, nil, STACK);
224 threadcreate(mousethread, nil, STACK);
225 threadcreate(waitthread, nil, STACK);
226 threadcreate(xfidallocthread, nil, STACK);
227 threadcreate(newwindowthread, nil, STACK);
228
229 threadnotify(shutdown, 1);
230 recvul(cexit);
231 killprocs();
232 threadexitsall(nil);
233 }
234
235 void
readfile(Column * c,char * s)236 readfile(Column *c, char *s)
237 {
238 Window *w;
239 Rune rb[256];
240 int nb, nr;
241 Runestr rs;
242
243 w = coladd(c, nil, nil, -1);
244 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
245 rs = cleanrname((Runestr){rb, nr});
246 winsetname(w, rs.r, rs.nr);
247 textload(&w->body, 0, s, 1);
248 w->body.file->mod = FALSE;
249 w->dirty = FALSE;
250 winsettag(w);
251 textscrdraw(&w->body);
252 textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc);
253 }
254
255 char *oknotes[] ={
256 "delete",
257 "hangup",
258 "kill",
259 "exit",
260 nil
261 };
262
263 int dumping;
264
265 int
shutdown(void *,char * msg)266 shutdown(void*, char *msg)
267 {
268 int i;
269
270 killprocs();
271 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
272 dumping = TRUE;
273 rowdump(&row, nil);
274 }
275 for(i=0; oknotes[i]; i++)
276 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
277 threadexitsall(msg);
278 print("acme: %s\n", msg);
279 abort();
280 return 0;
281 }
282
283 void
killprocs(void)284 killprocs(void)
285 {
286 Command *c;
287
288 fsysclose();
289 // if(display)
290 // flushimage(display, 1);
291
292 for(c=command; c; c=c->next)
293 postnote(PNGROUP, c->pid, "hangup");
294 remove(acmeerrorfile);
295 }
296
297 static int errorfd;
298
299 void
acmeerrorproc(void *)300 acmeerrorproc(void *)
301 {
302 char *buf;
303 int n;
304
305 threadsetname("acmeerrorproc");
306 buf = emalloc(8192+1);
307 while((n=read(errorfd, buf, 8192)) >= 0){
308 buf[n] = '\0';
309 sendp(cerr, estrdup(buf));
310 }
311 }
312
313 void
acmeerrorinit(void)314 acmeerrorinit(void)
315 {
316 int fd, pfd[2];
317 char buf[64];
318
319 if(pipe(pfd) < 0)
320 error("can't create pipe");
321 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
322 fd = create(acmeerrorfile, OWRITE, 0666);
323 if(fd < 0){
324 remove(acmeerrorfile);
325 fd = create(acmeerrorfile, OWRITE, 0666);
326 if(fd < 0)
327 error("can't create acmeerror file");
328 }
329 sprint(buf, "%d", pfd[0]);
330 write(fd, buf, strlen(buf));
331 close(fd);
332 /* reopen pfd[1] close on exec */
333 sprint(buf, "/fd/%d", pfd[1]);
334 errorfd = open(buf, OREAD|OCEXEC);
335 if(errorfd < 0)
336 error("can't re-open acmeerror file");
337 close(pfd[1]);
338 close(pfd[0]);
339 proccreate(acmeerrorproc, nil, STACK);
340 }
341
342 void
plumbproc(void *)343 plumbproc(void *)
344 {
345 Plumbmsg *m;
346
347 threadsetname("plumbproc");
348 for(;;){
349 m = plumbrecv(plumbeditfd);
350 if(m == nil)
351 threadexits(nil);
352 sendp(cplumb, m);
353 }
354 }
355
356 void
keyboardthread(void *)357 keyboardthread(void *)
358 {
359 Rune r;
360 Timer *timer;
361 Text *t;
362 enum { KTimer, KKey, NKALT };
363 static Alt alts[NKALT+1];
364
365 alts[KTimer].c = nil;
366 alts[KTimer].v = nil;
367 alts[KTimer].op = CHANNOP;
368 alts[KKey].c = keyboardctl->c;
369 alts[KKey].v = &r;
370 alts[KKey].op = CHANRCV;
371 alts[NKALT].op = CHANEND;
372
373 timer = nil;
374 typetext = nil;
375 threadsetname("keyboardthread");
376 for(;;){
377 switch(alt(alts)){
378 case KTimer:
379 timerstop(timer);
380 t = typetext;
381 if(t!=nil && t->what==Tag){
382 winlock(t->w, 'K');
383 wincommit(t->w, t);
384 winunlock(t->w);
385 flushimage(display, 1);
386 }
387 alts[KTimer].c = nil;
388 alts[KTimer].op = CHANNOP;
389 break;
390 case KKey:
391 casekeyboard:
392 typetext = rowtype(&row, r, mouse->xy);
393 t = typetext;
394 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
395 activecol = t->col;
396 if(t!=nil && t->w!=nil)
397 t->w->body.file->curtext = &t->w->body;
398 if(timer != nil)
399 timercancel(timer);
400 if(t!=nil && t->what==Tag) {
401 timer = timerstart(500);
402 alts[KTimer].c = timer->c;
403 alts[KTimer].op = CHANRCV;
404 }else{
405 timer = nil;
406 alts[KTimer].c = nil;
407 alts[KTimer].op = CHANNOP;
408 }
409 if(nbrecv(keyboardctl->c, &r) > 0)
410 goto casekeyboard;
411 flushimage(display, 1);
412 break;
413 }
414 }
415 }
416
417 void
mousethread(void *)418 mousethread(void *)
419 {
420 Text *t, *argt;
421 int but;
422 uint q0, q1;
423 Window *w;
424 Plumbmsg *pm;
425 Mouse m;
426 char *act;
427 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
428 static Alt alts[NMALT+1];
429
430 threadsetname("mousethread");
431 alts[MResize].c = mousectl->resizec;
432 alts[MResize].v = nil;
433 alts[MResize].op = CHANRCV;
434 alts[MMouse].c = mousectl->c;
435 alts[MMouse].v = &mousectl->Mouse;
436 alts[MMouse].op = CHANRCV;
437 alts[MPlumb].c = cplumb;
438 alts[MPlumb].v = ±
439 alts[MPlumb].op = CHANRCV;
440 alts[MWarnings].c = cwarn;
441 alts[MWarnings].v = nil;
442 alts[MWarnings].op = CHANRCV;
443 if(cplumb == nil)
444 alts[MPlumb].op = CHANNOP;
445 alts[NMALT].op = CHANEND;
446
447 for(;;){
448 qlock(&row);
449 flushwarnings();
450 qunlock(&row);
451 flushimage(display, 1);
452 switch(alt(alts)){
453 case MResize:
454 if(getwindow(display, Refnone) < 0)
455 error("attach to window");
456 scrlresize();
457 rowresize(&row, screen->clipr);
458 break;
459 case MPlumb:
460 if(strcmp(pm->type, "text") == 0){
461 act = plumblookup(pm->attr, "action");
462 if(act==nil || strcmp(act, "showfile")==0)
463 plumblook(pm);
464 else if(strcmp(act, "showdata")==0)
465 plumbshow(pm);
466 }
467 plumbfree(pm);
468 break;
469 case MWarnings:
470 break;
471 case MMouse:
472 /*
473 * Make a copy so decisions are consistent; mousectl changes
474 * underfoot. Can't just receive into m because this introduces
475 * another race; see /sys/src/libdraw/mouse.c.
476 */
477 m = mousectl->Mouse;
478 qlock(&row);
479 t = rowwhich(&row, m.xy);
480 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
481 winlock(mousetext->w, 'M');
482 mousetext->eq0 = ~0;
483 wincommit(mousetext->w, mousetext);
484 winunlock(mousetext->w);
485 }
486 mousetext = t;
487 if(t == nil)
488 goto Continue;
489 w = t->w;
490 if(t==nil || m.buttons==0)
491 goto Continue;
492 but = 0;
493 if(m.buttons == 1)
494 but = 1;
495 else if(m.buttons == 2)
496 but = 2;
497 else if(m.buttons == 4)
498 but = 3;
499 barttext = t;
500 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
501 if(but){
502 winlock(w, 'M');
503 t->eq0 = ~0;
504 textscroll(t, but);
505 winunlock(w);
506 }
507 goto Continue;
508 }
509 /* scroll buttons, wheels, etc. */
510 if(t->what==Body && w != nil && (m.buttons & (8|16))){
511 if(m.buttons & 8)
512 but = Kscrolloneup;
513 else
514 but = Kscrollonedown;
515 winlock(w, 'M');
516 t->eq0 = ~0;
517 texttype(t, but);
518 winunlock(w);
519 goto Continue;
520 }
521 if(ptinrect(m.xy, t->scrollr)){
522 if(but){
523 if(t->what == Columntag)
524 rowdragcol(&row, t->col, but);
525 else if(t->what == Tag){
526 coldragwin(t->col, t->w, but);
527 if(t->w)
528 barttext = &t->w->body;
529 }
530 if(t->col)
531 activecol = t->col;
532 }
533 goto Continue;
534 }
535 if(m.buttons){
536 if(w)
537 winlock(w, 'M');
538 t->eq0 = ~0;
539 if(w)
540 wincommit(w, t);
541 else
542 textcommit(t, TRUE);
543 if(m.buttons & 1){
544 textselect(t);
545 if(w)
546 winsettag(w);
547 argtext = t;
548 seltext = t;
549 if(t->col)
550 activecol = t->col; /* button 1 only */
551 if(t->w!=nil && t==&t->w->body)
552 activewin = t->w;
553 }else if(m.buttons & 2){
554 if(textselect2(t, &q0, &q1, &argt))
555 execute(t, q0, q1, FALSE, argt);
556 }else if(m.buttons & 4){
557 if(textselect3(t, &q0, &q1))
558 look3(t, q0, q1, FALSE);
559 }
560 if(w)
561 winunlock(w);
562 goto Continue;
563 }
564 Continue:
565 qunlock(&row);
566 break;
567 }
568 }
569 }
570
571 /*
572 * There is a race between process exiting and our finding out it was ever created.
573 * This structure keeps a list of processes that have exited we haven't heard of.
574 */
575 typedef struct Pid Pid;
576 struct Pid
577 {
578 int pid;
579 char msg[ERRMAX];
580 Pid *next;
581 };
582
583 void
waitthread(void *)584 waitthread(void *)
585 {
586 Waitmsg *w;
587 Command *c, *lc;
588 uint pid;
589 int found, ncmd;
590 Rune *cmd;
591 char *err;
592 Text *t;
593 Pid *pids, *p, *lastp;
594 enum { WErr, WKill, WWait, WCmd, NWALT };
595 Alt alts[NWALT+1];
596
597 threadsetname("waitthread");
598 pids = nil;
599 alts[WErr].c = cerr;
600 alts[WErr].v = &err;
601 alts[WErr].op = CHANRCV;
602 alts[WKill].c = ckill;
603 alts[WKill].v = &cmd;
604 alts[WKill].op = CHANRCV;
605 alts[WWait].c = cwait;
606 alts[WWait].v = &w;
607 alts[WWait].op = CHANRCV;
608 alts[WCmd].c = ccommand;
609 alts[WCmd].v = &c;
610 alts[WCmd].op = CHANRCV;
611 alts[NWALT].op = CHANEND;
612
613 command = nil;
614 for(;;){
615 switch(alt(alts)){
616 case WErr:
617 qlock(&row);
618 warning(nil, "%s", err);
619 free(err);
620 flushimage(display, 1);
621 qunlock(&row);
622 break;
623 case WKill:
624 found = FALSE;
625 ncmd = runestrlen(cmd);
626 for(c=command; c; c=c->next){
627 /* -1 for blank */
628 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
629 if(postnote(PNGROUP, c->pid, "kill") < 0)
630 warning(nil, "kill %S: %r\n", cmd);
631 found = TRUE;
632 }
633 }
634 if(!found)
635 warning(nil, "Kill: no process %S\n", cmd);
636 free(cmd);
637 break;
638 case WWait:
639 pid = w->pid;
640 lc = nil;
641 for(c=command; c; c=c->next){
642 if(c->pid == pid){
643 if(lc)
644 lc->next = c->next;
645 else
646 command = c->next;
647 break;
648 }
649 lc = c;
650 }
651 qlock(&row);
652 t = &row.tag;
653 textcommit(t, TRUE);
654 if(c == nil){
655 /* helper processes use this exit status */
656 if(strncmp(w->msg, "libthread", 9) != 0){
657 p = emalloc(sizeof(Pid));
658 p->pid = pid;
659 strncpy(p->msg, w->msg, sizeof(p->msg));
660 p->next = pids;
661 pids = p;
662 }
663 }else{
664 if(search(t, c->name, c->nname)){
665 textdelete(t, t->q0, t->q1, TRUE);
666 textsetselect(t, 0, 0);
667 }
668 if(w->msg[0])
669 warning(c->md, "%s\n", w->msg);
670 flushimage(display, 1);
671 }
672 qunlock(&row);
673 free(w);
674 Freecmd:
675 if(c){
676 if(c->iseditcmd)
677 sendul(cedit, 0);
678 free(c->text);
679 free(c->name);
680 fsysdelid(c->md);
681 free(c);
682 }
683 break;
684 case WCmd:
685 /* has this command already exited? */
686 lastp = nil;
687 for(p=pids; p!=nil; p=p->next){
688 if(p->pid == c->pid){
689 if(p->msg[0])
690 warning(c->md, "%s\n", p->msg);
691 if(lastp == nil)
692 pids = p->next;
693 else
694 lastp->next = p->next;
695 free(p);
696 goto Freecmd;
697 }
698 lastp = p;
699 }
700 c->next = command;
701 command = c;
702 qlock(&row);
703 t = &row.tag;
704 textcommit(t, TRUE);
705 textinsert(t, 0, c->name, c->nname, TRUE);
706 textsetselect(t, 0, 0);
707 flushimage(display, 1);
708 qunlock(&row);
709 break;
710 }
711 }
712 }
713
714 void
xfidallocthread(void *)715 xfidallocthread(void*)
716 {
717 Xfid *xfree, *x;
718 enum { Alloc, Free, N };
719 static Alt alts[N+1];
720
721 threadsetname("xfidallocthread");
722 alts[Alloc].c = cxfidalloc;
723 alts[Alloc].v = nil;
724 alts[Alloc].op = CHANRCV;
725 alts[Free].c = cxfidfree;
726 alts[Free].v = &x;
727 alts[Free].op = CHANRCV;
728 alts[N].op = CHANEND;
729
730 xfree = nil;
731 for(;;){
732 switch(alt(alts)){
733 case Alloc:
734 x = xfree;
735 if(x)
736 xfree = x->next;
737 else{
738 x = emalloc(sizeof(Xfid));
739 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
740 x->arg = x;
741 threadcreate(xfidctl, x->arg, STACK);
742 }
743 sendp(cxfidalloc, x);
744 break;
745 case Free:
746 x->next = xfree;
747 xfree = x;
748 break;
749 }
750 }
751 }
752
753 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
754 void
newwindowthread(void *)755 newwindowthread(void*)
756 {
757 Window *w;
758
759 threadsetname("newwindowthread");
760
761 for(;;){
762 /* only fsysproc is talking to us, so synchronization is trivial */
763 recvp(cnewwindow);
764 w = makenewwindow(nil);
765 winsettag(w);
766 sendp(cnewwindow, w);
767 }
768 }
769
770 Reffont*
rfget(int fix,int save,int setfont,char * name)771 rfget(int fix, int save, int setfont, char *name)
772 {
773 Reffont *r;
774 Font *f;
775 int i;
776
777 r = nil;
778 if(name == nil){
779 name = fontnames[fix];
780 r = reffonts[fix];
781 }
782 if(r == nil){
783 for(i=0; i<nfontcache; i++)
784 if(strcmp(name, fontcache[i]->f->name) == 0){
785 r = fontcache[i];
786 goto Found;
787 }
788 f = openfont(display, name);
789 if(f == nil){
790 warning(nil, "can't open font file %s: %r\n", name);
791 return nil;
792 }
793 r = emalloc(sizeof(Reffont));
794 r->f = f;
795 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
796 fontcache[nfontcache++] = r;
797 }
798 Found:
799 if(save){
800 incref(r);
801 if(reffonts[fix])
802 rfclose(reffonts[fix]);
803 reffonts[fix] = r;
804 if(name != fontnames[fix]){
805 free(fontnames[fix]);
806 fontnames[fix] = estrdup(name);
807 }
808 }
809 if(setfont){
810 reffont.f = r->f;
811 incref(r);
812 rfclose(reffonts[0]);
813 font = r->f;
814 reffonts[0] = r;
815 incref(r);
816 iconinit();
817 }
818 incref(r);
819 return r;
820 }
821
822 void
rfclose(Reffont * r)823 rfclose(Reffont *r)
824 {
825 int i;
826
827 if(decref(r) == 0){
828 for(i=0; i<nfontcache; i++)
829 if(r == fontcache[i])
830 break;
831 if(i >= nfontcache)
832 warning(nil, "internal error: can't find font in cache\n");
833 else{
834 nfontcache--;
835 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
836 }
837 freefont(r->f);
838 free(r);
839 }
840 }
841
842 Cursor boxcursor = {
843 {-7, -7},
844 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
845 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
846 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
847 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
848 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
849 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
850 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
851 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
852 };
853
854 void
iconinit(void)855 iconinit(void)
856 {
857 Rectangle r;
858 Image *tmp;
859
860 /* Blue */
861 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
862 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
863 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
864 tagcols[TEXT] = display->black;
865 tagcols[HTEXT] = display->black;
866
867 /* Yellow */
868 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
869 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
870 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
871 textcols[TEXT] = display->black;
872 textcols[HTEXT] = display->black;
873
874 if(button){
875 freeimage(button);
876 freeimage(modbutton);
877 freeimage(colbutton);
878 }
879
880 r = Rect(0, 0, Scrollwid+2, font->height+1);
881 button = allocimage(display, r, screen->chan, 0, DNofill);
882 draw(button, r, tagcols[BACK], nil, r.min);
883 r.max.x -= 2;
884 border(button, r, 2, tagcols[BORD], ZP);
885
886 r = button->r;
887 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
888 draw(modbutton, r, tagcols[BACK], nil, r.min);
889 r.max.x -= 2;
890 border(modbutton, r, 2, tagcols[BORD], ZP);
891 r = insetrect(r, 2);
892 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
893 draw(modbutton, r, tmp, nil, ZP);
894 freeimage(tmp);
895
896 r = button->r;
897 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
898
899 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
900 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
901 }
902
903 /*
904 * /dev/snarf updates when the file is closed, so we must open our own
905 * fd here rather than use snarffd
906 */
907
908 /* rio truncates larges snarf buffers, so this avoids using the
909 * service if the string is huge */
910
911 #define MAXSNARF 100*1024
912
913 void
putsnarf(void)914 putsnarf(void)
915 {
916 int fd, i, n;
917
918 if(snarffd<0 || snarfbuf.nc==0)
919 return;
920 if(snarfbuf.nc > MAXSNARF)
921 return;
922 fd = open("/dev/snarf", OWRITE);
923 if(fd < 0)
924 return;
925 for(i=0; i<snarfbuf.nc; i+=n){
926 n = snarfbuf.nc-i;
927 if(n >= NSnarf)
928 n = NSnarf;
929 bufread(&snarfbuf, i, snarfrune, n);
930 if(fprint(fd, "%.*S", n, snarfrune) < 0)
931 break;
932 }
933 close(fd);
934 }
935
936 void
getsnarf()937 getsnarf()
938 {
939 int nulls;
940
941 if(snarfbuf.nc > MAXSNARF)
942 return;
943 if(snarffd < 0)
944 return;
945 seek(snarffd, 0, 0);
946 bufreset(&snarfbuf);
947 bufload(&snarfbuf, 0, snarffd, &nulls);
948 }
949