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 'D':
73 {extern int _threaddebuglevel;
74 _threaddebuglevel = ~0;
75 }
76 break;
77 case 'a':
78 globalautoindent = TRUE;
79 break;
80 case 'b':
81 bartflag = TRUE;
82 break;
83 case 'c':
84 p = ARGF();
85 if(p == nil)
86 goto Usage;
87 ncol = atoi(p);
88 if(ncol <= 0)
89 goto Usage;
90 break;
91 case 'f':
92 fontnames[0] = ARGF();
93 if(fontnames[0] == nil)
94 goto Usage;
95 break;
96 case 'F':
97 fontnames[1] = ARGF();
98 if(fontnames[1] == nil)
99 goto Usage;
100 break;
101 case 'l':
102 loadfile = ARGF();
103 if(loadfile == nil)
104 goto Usage;
105 break;
106 case 'r':
107 swapscrollbuttons = TRUE;
108 break;
109 case 'W':
110 winsize = ARGF();
111 if(winsize == nil)
112 goto Usage;
113 break;
114 default:
115 Usage:
116 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
117 threadexitsall("usage");
118 }ARGEND
120 fontnames[0] = estrdup(fontnames[0]);
121 fontnames[1] = estrdup(fontnames[1]);
123 quotefmtinstall();
124 cputype = getenv("cputype");
125 objtype = getenv("objtype");
126 home = getenv("HOME");
127 p = getenv("tabstop");
128 if(p != nil){
129 maxtab = strtoul(p, nil, 0);
130 free(p);
132 if(maxtab == 0)
133 maxtab = 4;
134 if(loadfile)
135 rowloadfonts(loadfile);
136 putenv("font", fontnames[0]);
137 snarffd = open("/dev/snarf", OREAD|OCEXEC);
138 /*
139 if(cputype){
140 sprint(buf, "/acme/bin/%s", cputype);
141 bind(buf, "/bin", MBEFORE);
143 bind("/acme/bin", "/bin", MBEFORE);
144 */
145 getwd(wdir, sizeof wdir);
147 /*
148 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
149 fprint(2, "acme: can't open display: %r\n");
150 threadexitsall("geninitdraw");
152 */
153 if(initdraw(derror, fontnames[0], "acme") < 0){
154 fprint(2, "acme: can't open display: %r\n");
155 threadexitsall("initdraw");
158 d = display;
159 font = d->defaultfont;
160 //assert(font);
162 reffont.f = font;
163 reffonts[0] = &reffont;
164 incref(&reffont.ref); /* one to hold up 'font' variable */
165 incref(&reffont.ref); /* one to hold up reffonts[0] */
166 fontcache = emalloc(sizeof(Reffont*));
167 nfontcache = 1;
168 fontcache[0] = &reffont;
170 iconinit();
171 timerinit();
172 rxinit();
174 cwait = threadwaitchan();
175 ccommand = chancreate(sizeof(Command**), 0);
176 chansetname(ccommand, "ccommand");
177 ckill = chancreate(sizeof(Rune*), 0);
178 chansetname(ckill, "ckill");
179 cxfidalloc = chancreate(sizeof(Xfid*), 0);
180 chansetname(cxfidalloc, "cxfidalloc");
181 cxfidfree = chancreate(sizeof(Xfid*), 0);
182 chansetname(cxfidfree, "cxfidfree");
183 cnewwindow = chancreate(sizeof(Channel*), 0);
184 chansetname(cnewwindow, "cnewwindow");
185 cerr = chancreate(sizeof(char*), 0);
186 chansetname(cerr, "cerr");
187 cedit = chancreate(sizeof(int), 0);
188 chansetname(cedit, "cedit");
189 cexit = chancreate(sizeof(int), 0);
190 chansetname(cexit, "cexit");
191 cwarn = chancreate(sizeof(void*), 1);
192 chansetname(cwarn, "cwarn");
193 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
194 fprint(2, "acme: can't create initial channels: %r\n");
195 threadexitsall("channels");
198 mousectl = initmouse(nil, screen);
199 if(mousectl == nil){
200 fprint(2, "acme: can't initialize mouse: %r\n");
201 threadexitsall("mouse");
203 mouse = &mousectl->m;
204 keyboardctl = initkeyboard(nil);
205 if(keyboardctl == nil){
206 fprint(2, "acme: can't initialize keyboard: %r\n");
207 threadexitsall("keyboard");
209 mainpid = getpid();
210 startplumbing();
211 /*
212 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
213 if(plumbeditfd < 0)
214 fprint(2, "acme: can't initialize plumber: %r\n");
215 else{
216 cplumb = chancreate(sizeof(Plumbmsg*), 0);
217 threadcreate(plumbproc, nil, STACK);
219 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
220 */
222 fsysinit();
224 #define WPERCOL 8
225 disk = diskinit();
226 if(!loadfile || !rowload(&row, loadfile, TRUE)){
227 rowinit(&row, screen->clipr);
228 if(ncol < 0){
229 if(argc == 0)
230 ncol = 2;
231 else{
232 ncol = (argc+(WPERCOL-1))/WPERCOL;
233 if(ncol < 2)
234 ncol = 2;
237 if(ncol == 0)
238 ncol = 2;
239 for(i=0; i<ncol; i++){
240 c = rowadd(&row, nil, -1);
241 if(c==nil && i==0)
242 error("initializing columns");
244 c = row.col[row.ncol-1];
245 if(argc == 0)
246 readfile(c, wdir);
247 else
248 for(i=0; i<argc; i++){
249 p = utfrrune(argv[i], '/');
250 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
251 readfile(c, argv[i]);
252 else
253 readfile(row.col[i/WPERCOL], argv[i]);
256 flushimage(display, 1);
258 acmeerrorinit();
259 threadcreate(keyboardthread, nil, STACK);
260 threadcreate(mousethread, nil, STACK);
261 threadcreate(waitthread, nil, STACK);
262 threadcreate(xfidallocthread, nil, STACK);
263 threadcreate(newwindowthread, nil, STACK);
264 /* threadcreate(shutdownthread, nil, STACK); */
265 threadnotify(shutdown, 1);
266 recvul(cexit);
267 killprocs();
268 threadexitsall(nil);
271 void
272 readfile(Column *c, char *s)
274 Window *w;
275 Rune rb[256];
276 int nb, nr;
277 Runestr rs;
279 w = coladd(c, nil, nil, -1);
280 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
281 rs = cleanrname(runestr(rb, nr));
282 winsetname(w, rs.r, rs.nr);
283 textload(&w->body, 0, s, 1);
284 w->body.file->mod = FALSE;
285 w->dirty = FALSE;
286 winsettag(w);
287 textscrdraw(&w->body);
288 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
291 char *oknotes[] ={
292 "delete",
293 "hangup",
294 "kill",
295 "exit",
296 nil
297 };
299 int dumping;
301 static int
302 shutdown(void *v, char *msg)
304 int i;
306 if(strcmp(msg, "sys: write on closed pipe") == 0)
307 return 1;
309 USED(v);
310 killprocs();
311 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
312 dumping = TRUE;
313 rowdump(&row, nil);
315 for(i=0; oknotes[i]; i++)
316 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
317 threadexitsall(msg);
318 print("acme: %s\n", msg);
319 abort();
320 return 0;
323 /*
324 void
325 shutdownthread(void *v)
327 char *msg;
328 Channel *c;
330 USED(v);
332 c = threadnotechan();
333 while((msg = recvp(c)) != nil)
334 shutdown(nil, msg);
336 */
338 void
339 killprocs(void)
341 Command *c;
343 fsysclose();
344 // if(display)
345 // flushimage(display, 1);
347 for(c=command; c; c=c->next)
348 postnote(PNGROUP, c->pid, "hangup");
351 static int errorfd;
352 int erroutfd;
354 void
355 acmeerrorproc(void *v)
357 char *buf;
358 int n;
360 USED(v);
361 threadsetname("acmeerrorproc");
362 buf = emalloc(8192+1);
363 while((n=read(errorfd, buf, 8192)) >= 0){
364 buf[n] = '\0';
365 sendp(cerr, estrdup(buf));
369 void
370 acmeerrorinit(void)
372 int pfd[2];
374 if(pipe(pfd) < 0)
375 error("can't create pipe");
376 #if 0
377 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
378 fd = create(acmeerrorfile, OWRITE, 0666);
379 if(fd < 0){
380 remove(acmeerrorfile);
381 fd = create(acmeerrorfile, OWRITE, 0666);
382 if(fd < 0)
383 error("can't create acmeerror file");
385 sprint(buf, "%d", pfd[0]);
386 write(fd, buf, strlen(buf));
387 close(fd);
388 /* reopen pfd[1] close on exec */
389 sprint(buf, "/fd/%d", pfd[1]);
390 errorfd = open(buf, OREAD|OCEXEC);
391 #endif
392 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
393 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
394 erroutfd = pfd[0];
395 errorfd = pfd[1];
396 if(errorfd < 0)
397 error("can't re-open acmeerror file");
398 proccreate(acmeerrorproc, nil, STACK);
401 /*
402 void
403 plumbproc(void *v)
405 Plumbmsg *m;
407 USED(v);
408 threadsetname("plumbproc");
409 for(;;){
410 m = threadplumbrecv(plumbeditfd);
411 if(m == nil)
412 threadexits(nil);
413 sendp(cplumb, m);
416 */
418 void
419 keyboardthread(void *v)
421 Rune r;
422 Timer *timer;
423 Text *t;
424 enum { KTimer, KKey, NKALT };
425 static Alt alts[NKALT+1];
427 USED(v);
428 alts[KTimer].c = nil;
429 alts[KTimer].v = nil;
430 alts[KTimer].op = CHANNOP;
431 alts[KKey].c = keyboardctl->c;
432 alts[KKey].v = &r;
433 alts[KKey].op = CHANRCV;
434 alts[NKALT].op = CHANEND;
436 timer = nil;
437 typetext = nil;
438 threadsetname("keyboardthread");
439 for(;;){
440 switch(alt(alts)){
441 case KTimer:
442 timerstop(timer);
443 t = typetext;
444 if(t!=nil && t->what==Tag){
445 winlock(t->w, 'K');
446 wincommit(t->w, t);
447 winunlock(t->w);
448 flushimage(display, 1);
450 alts[KTimer].c = nil;
451 alts[KTimer].op = CHANNOP;
452 break;
453 case KKey:
454 casekeyboard:
455 typetext = rowtype(&row, r, mouse->xy);
456 t = typetext;
457 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
458 activecol = t->col;
459 if(t!=nil && t->w!=nil)
460 t->w->body.file->curtext = &t->w->body;
461 if(timer != nil)
462 timercancel(timer);
463 if(t!=nil && t->what==Tag) {
464 timer = timerstart(500);
465 alts[KTimer].c = timer->c;
466 alts[KTimer].op = CHANRCV;
467 }else{
468 timer = nil;
469 alts[KTimer].c = nil;
470 alts[KTimer].op = CHANNOP;
472 if(nbrecv(keyboardctl->c, &r) > 0)
473 goto casekeyboard;
474 flushimage(display, 1);
475 break;
480 void
481 mousethread(void *v)
483 Text *t, *argt;
484 int but;
485 uint q0, q1;
486 Window *w;
487 Plumbmsg *pm;
488 Mouse m;
489 char *act;
490 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
491 static Alt alts[NMALT+1];
493 USED(v);
494 threadsetname("mousethread");
495 alts[MResize].c = mousectl->resizec;
496 alts[MResize].v = nil;
497 alts[MResize].op = CHANRCV;
498 alts[MMouse].c = mousectl->c;
499 alts[MMouse].v = &mousectl->m;
500 alts[MMouse].op = CHANRCV;
501 alts[MPlumb].c = cplumb;
502 alts[MPlumb].v = &pm;
503 alts[MPlumb].op = CHANRCV;
504 alts[MWarnings].c = cwarn;
505 alts[MWarnings].v = nil;
506 alts[MWarnings].op = CHANRCV;
507 if(cplumb == nil)
508 alts[MPlumb].op = CHANNOP;
509 alts[NMALT].op = CHANEND;
511 for(;;){
512 qlock(&row.lk);
513 flushwarnings();
514 qunlock(&row.lk);
515 flushimage(display, 1);
516 switch(alt(alts)){
517 case MResize:
518 if(getwindow(display, Refnone) < 0)
519 error("attach to window");
520 draw(screen, screen->r, display->white, nil, ZP);
521 scrlresize();
522 rowresize(&row, screen->clipr);
523 break;
524 case MPlumb:
525 if(strcmp(pm->type, "text") == 0){
526 act = plumblookup(pm->attr, "action");
527 if(act==nil || strcmp(act, "showfile")==0)
528 plumblook(pm);
529 else if(strcmp(act, "showdata")==0)
530 plumbshow(pm);
532 plumbfree(pm);
533 break;
534 case MWarnings:
535 break;
536 case MMouse:
537 /*
538 * Make a copy so decisions are consistent; mousectl changes
539 * underfoot. Can't just receive into m because this introduces
540 * another race; see /sys/src/libdraw/mouse.c.
541 */
542 m = mousectl->m;
543 qlock(&row.lk);
544 t = rowwhich(&row, m.xy);
545 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
546 winlock(mousetext->w, 'M');
547 mousetext->eq0 = ~0;
548 wincommit(mousetext->w, mousetext);
549 winunlock(mousetext->w);
551 mousetext = t;
552 if(t == nil)
553 goto Continue;
554 w = t->w;
555 if(t==nil || m.buttons==0)
556 goto Continue;
557 but = 0;
558 if(m.buttons == 1)
559 but = 1;
560 else if(m.buttons == 2)
561 but = 2;
562 else if(m.buttons == 4)
563 but = 3;
564 barttext = t;
565 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
566 if(but){
567 if(swapscrollbuttons){
568 if(but == 1)
569 but = 3;
570 else if(but == 3)
571 but = 1;
573 winlock(w, 'M');
574 t->eq0 = ~0;
575 textscroll(t, but);
576 winunlock(w);
578 goto Continue;
580 /* scroll buttons, wheels, etc. */
581 if(t->what==Body && w != nil && (m.buttons & (8|16))){
582 if(m.buttons & 8)
583 but = Kscrolloneup;
584 else
585 but = Kscrollonedown;
586 winlock(w, 'M');
587 t->eq0 = ~0;
588 texttype(t, but);
589 winunlock(w);
590 goto Continue;
592 if(ptinrect(m.xy, t->scrollr)){
593 if(but){
594 if(t->what == Columntag)
595 rowdragcol(&row, t->col, but);
596 else if(t->what == Tag){
597 coldragwin(t->col, t->w, but);
598 if(t->w)
599 barttext = &t->w->body;
601 if(t->col)
602 activecol = t->col;
604 goto Continue;
606 if(m.buttons){
607 if(w)
608 winlock(w, 'M');
609 t->eq0 = ~0;
610 if(w)
611 wincommit(w, t);
612 else
613 textcommit(t, TRUE);
614 if(m.buttons & 1){
615 textselect(t);
616 if(w)
617 winsettag(w);
618 argtext = t;
619 seltext = t;
620 if(t->col)
621 activecol = t->col; /* button 1 only */
622 if(t->w!=nil && t==&t->w->body)
623 activewin = t->w;
624 }else if(m.buttons & 2){
625 if(textselect2(t, &q0, &q1, &argt))
626 execute(t, q0, q1, FALSE, argt);
627 }else if(m.buttons & 4){
628 if(textselect3(t, &q0, &q1))
629 look3(t, q0, q1, FALSE);
631 if(w)
632 winunlock(w);
633 goto Continue;
635 Continue:
636 qunlock(&row.lk);
637 break;
642 /*
643 * There is a race between process exiting and our finding out it was ever created.
644 * This structure keeps a list of processes that have exited we haven't heard of.
645 */
646 typedef struct Pid Pid;
647 struct Pid
649 int pid;
650 char msg[ERRMAX];
651 Pid *next;
652 };
654 void
655 waitthread(void *v)
657 Waitmsg *w;
658 Command *c, *lc;
659 uint pid;
660 int found, ncmd;
661 Rune *cmd;
662 char *err;
663 Text *t;
664 Pid *pids, *p, *lastp;
665 enum { WErr, WKill, WWait, WCmd, NWALT };
666 Alt alts[NWALT+1];
668 USED(v);
669 threadsetname("waitthread");
670 pids = nil;
671 alts[WErr].c = cerr;
672 alts[WErr].v = &err;
673 alts[WErr].op = CHANRCV;
674 alts[WKill].c = ckill;
675 alts[WKill].v = &cmd;
676 alts[WKill].op = CHANRCV;
677 alts[WWait].c = cwait;
678 alts[WWait].v = &w;
679 alts[WWait].op = CHANRCV;
680 alts[WCmd].c = ccommand;
681 alts[WCmd].v = &c;
682 alts[WCmd].op = CHANRCV;
683 alts[NWALT].op = CHANEND;
685 command = nil;
686 for(;;){
687 switch(alt(alts)){
688 case WErr:
689 qlock(&row.lk);
690 warning(nil, "%s", err);
691 free(err);
692 flushimage(display, 1);
693 qunlock(&row.lk);
694 break;
695 case WKill:
696 found = FALSE;
697 ncmd = runestrlen(cmd);
698 for(c=command; c; c=c->next){
699 /* -1 for blank */
700 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
701 if(postnote(PNGROUP, c->pid, "kill") < 0)
702 warning(nil, "kill %S: %r\n", cmd);
703 found = TRUE;
706 if(!found)
707 warning(nil, "Kill: no process %S\n", cmd);
708 free(cmd);
709 break;
710 case WWait:
711 pid = w->pid;
712 lc = nil;
713 for(c=command; c; c=c->next){
714 if(c->pid == pid){
715 if(lc)
716 lc->next = c->next;
717 else
718 command = c->next;
719 break;
721 lc = c;
723 qlock(&row.lk);
724 t = &row.tag;
725 textcommit(t, TRUE);
726 if(c == nil){
727 /* helper processes use this exit status */
728 if(strncmp(w->msg, "libthread", 9) != 0){
729 p = emalloc(sizeof(Pid));
730 p->pid = pid;
731 strncpy(p->msg, w->msg, sizeof(p->msg));
732 p->next = pids;
733 pids = p;
735 }else{
736 if(search(t, c->name, c->nname)){
737 textdelete(t, t->q0, t->q1, TRUE);
738 textsetselect(t, 0, 0);
740 if(w->msg[0])
741 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
742 flushimage(display, 1);
744 qunlock(&row.lk);
745 free(w);
746 Freecmd:
747 if(c){
748 if(c->iseditcmd)
749 sendul(cedit, 0);
750 free(c->text);
751 free(c->name);
752 fsysdelid(c->md);
753 free(c);
755 break;
756 case WCmd:
757 /* has this command already exited? */
758 lastp = nil;
759 for(p=pids; p!=nil; p=p->next){
760 if(p->pid == c->pid){
761 if(p->msg[0])
762 warning(c->md, "%s\n", p->msg);
763 if(lastp == nil)
764 pids = p->next;
765 else
766 lastp->next = p->next;
767 free(p);
768 goto Freecmd;
770 lastp = p;
772 c->next = command;
773 command = c;
774 qlock(&row.lk);
775 t = &row.tag;
776 textcommit(t, TRUE);
777 textinsert(t, 0, c->name, c->nname, TRUE);
778 textsetselect(t, 0, 0);
779 flushimage(display, 1);
780 qunlock(&row.lk);
781 break;
786 void
787 xfidallocthread(void *v)
789 Xfid *xfree, *x;
790 enum { Alloc, Free, N };
791 static Alt alts[N+1];
793 USED(v);
794 threadsetname("xfidallocthread");
795 alts[Alloc].c = cxfidalloc;
796 alts[Alloc].v = nil;
797 alts[Alloc].op = CHANRCV;
798 alts[Free].c = cxfidfree;
799 alts[Free].v = &x;
800 alts[Free].op = CHANRCV;
801 alts[N].op = CHANEND;
803 xfree = nil;
804 for(;;){
805 switch(alt(alts)){
806 case Alloc:
807 x = xfree;
808 if(x)
809 xfree = x->next;
810 else{
811 x = emalloc(sizeof(Xfid));
812 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
813 chansetname(x->c, "xc%p", x->c);
814 x->arg = x;
815 threadcreate(xfidctl, x->arg, STACK);
817 sendp(cxfidalloc, x);
818 break;
819 case Free:
820 x->next = xfree;
821 xfree = x;
822 break;
827 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
828 void
829 newwindowthread(void *v)
831 Window *w;
833 USED(v);
834 threadsetname("newwindowthread");
836 for(;;){
837 /* only fsysproc is talking to us, so synchronization is trivial */
838 recvp(cnewwindow);
839 w = makenewwindow(nil);
840 winsettag(w);
841 sendp(cnewwindow, w);
845 Reffont*
846 rfget(int fix, int save, int setfont, char *name)
848 Reffont *r;
849 Font *f;
850 int i;
852 r = nil;
853 if(name == nil){
854 name = fontnames[fix];
855 r = reffonts[fix];
857 if(r == nil){
858 for(i=0; i<nfontcache; i++)
859 if(strcmp(name, fontcache[i]->f->name) == 0){
860 r = fontcache[i];
861 goto Found;
863 f = openfont(display, name);
864 if(f == nil){
865 fprint(2, "can't open font file %s: %r\n", name);
866 warning(nil, "can't open font file %s: %r\n", name);
867 return nil;
869 r = emalloc(sizeof(Reffont));
870 r->f = f;
871 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
872 fontcache[nfontcache++] = r;
874 Found:
875 if(save){
876 incref(&r->ref);
877 if(reffonts[fix])
878 rfclose(reffonts[fix]);
879 reffonts[fix] = r;
880 if(fontnames[fix] != name){
881 free(fontnames[fix]);
882 fontnames[fix] = estrdup(name);
885 if(setfont){
886 reffont.f = r->f;
887 incref(&r->ref);
888 rfclose(reffonts[0]);
889 font = r->f;
890 reffonts[0] = r;
891 incref(&r->ref);
892 iconinit();
894 incref(&r->ref);
895 return r;
898 void
899 rfclose(Reffont *r)
901 int i;
903 if(decref(&r->ref) == 0){
904 for(i=0; i<nfontcache; i++)
905 if(r == fontcache[i])
906 break;
907 if(i >= nfontcache)
908 warning(nil, "internal error: can't find font in cache\n");
909 else{
910 nfontcache--;
911 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
913 freefont(r->f);
914 free(r);
918 Cursor boxcursor = {
919 {-7, -7},
920 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
921 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
922 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
923 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
924 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
925 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
926 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
927 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
928 };
930 void
931 iconinit(void)
933 Rectangle r;
934 Image *tmp;
936 /* Blue */
937 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
938 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
939 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
940 tagcols[TEXT] = display->black;
941 tagcols[HTEXT] = display->black;
943 /* Yellow */
944 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
945 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
946 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
947 textcols[TEXT] = display->black;
948 textcols[HTEXT] = display->black;
950 if(button){
951 freeimage(button);
952 freeimage(modbutton);
953 freeimage(colbutton);
956 r = Rect(0, 0, Scrollwid+2, font->height+1);
957 button = allocimage(display, r, screen->chan, 0, DNofill);
958 draw(button, r, tagcols[BACK], nil, r.min);
959 r.max.x -= 2;
960 border(button, r, 2, tagcols[BORD], ZP);
962 r = button->r;
963 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
964 draw(modbutton, r, tagcols[BACK], nil, r.min);
965 r.max.x -= 2;
966 border(modbutton, r, 2, tagcols[BORD], ZP);
967 r = insetrect(r, 2);
968 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
969 draw(modbutton, r, tmp, nil, ZP);
970 freeimage(tmp);
972 r = button->r;
973 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
975 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
976 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
979 /*
980 * /dev/snarf updates when the file is closed, so we must open our own
981 * fd here rather than use snarffd
982 */
984 /* rio truncates larges snarf buffers, so this avoids using the
985 * service if the string is huge */
987 #define MAXSNARF 100*1024
989 void
990 acmeputsnarf(void)
992 int i, n;
993 Fmt f;
994 char *s;
996 if(snarfbuf.nc==0)
997 return;
998 if(snarfbuf.nc > MAXSNARF)
999 return;
1001 fmtstrinit(&f);
1002 for(i=0; i<snarfbuf.nc; i+=n){
1003 n = snarfbuf.nc-i;
1004 if(n >= NSnarf)
1005 n = NSnarf;
1006 bufread(&snarfbuf, i, snarfrune, n);
1007 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1008 break;
1010 s = fmtstrflush(&f);
1011 if(s && s[0])
1012 putsnarf(s);
1013 free(s);
1016 void
1017 acmegetsnarf(void)
1019 char *s;
1020 int nb, nr, nulls, len;
1021 Rune *r;
1023 s = getsnarf();
1024 if(s == nil || s[0]==0){
1025 free(s);
1026 return;
1029 len = strlen(s);
1030 r = runemalloc(len+1);
1031 cvttorunes(s, len, r, &nb, &nr, &nulls);
1032 bufreset(&snarfbuf);
1033 bufinsert(&snarfbuf, 0, r, nr);
1034 free(r);
1035 free(s);