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