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 winresize(w, w->r, FALSE, TRUE);
291 textscrdraw(&w->body);
292 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
295 char *ignotes[] = {
296 "sys: write on closed pipe",
297 "sys: ttin",
298 "sys: ttou",
299 "sys: tstp",
300 nil
301 };
303 char *oknotes[] ={
304 "delete",
305 "hangup",
306 "kill",
307 "exit",
308 nil
309 };
311 int dumping;
313 static int
314 shutdown(void *v, char *msg)
316 int i;
318 USED(v);
320 for(i=0; ignotes[i]; i++)
321 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
322 return 1;
324 killprocs();
325 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
326 dumping = TRUE;
327 rowdump(&row, nil);
329 for(i=0; oknotes[i]; i++)
330 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
331 threadexitsall(msg);
332 print("acme: %s\n", msg);
333 return 0;
336 /*
337 void
338 shutdownthread(void *v)
340 char *msg;
341 Channel *c;
343 USED(v);
345 threadsetname("shutdown");
346 c = threadnotechan();
347 while((msg = recvp(c)) != nil)
348 shutdown(nil, msg);
350 */
352 void
353 killprocs(void)
355 Command *c;
357 fsysclose();
358 // if(display)
359 // flushimage(display, 1);
361 for(c=command; c; c=c->next)
362 postnote(PNGROUP, c->pid, "hangup");
365 static int errorfd;
366 int erroutfd;
368 void
369 acmeerrorproc(void *v)
371 char *buf;
372 int n;
374 USED(v);
375 threadsetname("acmeerrorproc");
376 buf = emalloc(8192+1);
377 while((n=read(errorfd, buf, 8192)) >= 0){
378 buf[n] = '\0';
379 sendp(cerr, estrdup(buf));
383 void
384 acmeerrorinit(void)
386 int pfd[2];
388 if(pipe(pfd) < 0)
389 error("can't create pipe");
390 #if 0
391 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
392 fd = create(acmeerrorfile, OWRITE, 0666);
393 if(fd < 0){
394 remove(acmeerrorfile);
395 fd = create(acmeerrorfile, OWRITE, 0666);
396 if(fd < 0)
397 error("can't create acmeerror file");
399 sprint(buf, "%d", pfd[0]);
400 write(fd, buf, strlen(buf));
401 close(fd);
402 /* reopen pfd[1] close on exec */
403 sprint(buf, "/fd/%d", pfd[1]);
404 errorfd = open(buf, OREAD|OCEXEC);
405 #endif
406 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
407 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
408 erroutfd = pfd[0];
409 errorfd = pfd[1];
410 if(errorfd < 0)
411 error("can't re-open acmeerror file");
412 proccreate(acmeerrorproc, nil, STACK);
415 /*
416 void
417 plumbproc(void *v)
419 Plumbmsg *m;
421 USED(v);
422 threadsetname("plumbproc");
423 for(;;){
424 m = threadplumbrecv(plumbeditfd);
425 if(m == nil)
426 threadexits(nil);
427 sendp(cplumb, m);
430 */
432 void
433 keyboardthread(void *v)
435 Rune r;
436 Timer *timer;
437 Text *t;
438 enum { KTimer, KKey, NKALT };
439 static Alt alts[NKALT+1];
441 USED(v);
442 alts[KTimer].c = nil;
443 alts[KTimer].v = nil;
444 alts[KTimer].op = CHANNOP;
445 alts[KKey].c = keyboardctl->c;
446 alts[KKey].v = &r;
447 alts[KKey].op = CHANRCV;
448 alts[NKALT].op = CHANEND;
450 timer = nil;
451 typetext = nil;
452 threadsetname("keyboardthread");
453 for(;;){
454 switch(alt(alts)){
455 case KTimer:
456 timerstop(timer);
457 t = typetext;
458 if(t!=nil && t->what==Tag){
459 winlock(t->w, 'K');
460 wincommit(t->w, t);
461 winunlock(t->w);
462 flushimage(display, 1);
464 alts[KTimer].c = nil;
465 alts[KTimer].op = CHANNOP;
466 break;
467 case KKey:
468 casekeyboard:
469 typetext = rowtype(&row, r, mouse->xy);
470 t = typetext;
471 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
472 activecol = t->col;
473 if(t!=nil && t->w!=nil)
474 t->w->body.file->curtext = &t->w->body;
475 if(timer != nil)
476 timercancel(timer);
477 if(t!=nil && t->what==Tag) {
478 timer = timerstart(500);
479 alts[KTimer].c = timer->c;
480 alts[KTimer].op = CHANRCV;
481 }else{
482 timer = nil;
483 alts[KTimer].c = nil;
484 alts[KTimer].op = CHANNOP;
486 if(nbrecv(keyboardctl->c, &r) > 0)
487 goto casekeyboard;
488 flushimage(display, 1);
489 break;
494 void
495 mousethread(void *v)
497 Text *t, *argt;
498 int but;
499 uint q0, q1;
500 Window *w;
501 Plumbmsg *pm;
502 Mouse m;
503 char *act;
504 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
505 static Alt alts[NMALT+1];
507 USED(v);
508 threadsetname("mousethread");
509 alts[MResize].c = mousectl->resizec;
510 alts[MResize].v = nil;
511 alts[MResize].op = CHANRCV;
512 alts[MMouse].c = mousectl->c;
513 alts[MMouse].v = &mousectl->m;
514 alts[MMouse].op = CHANRCV;
515 alts[MPlumb].c = cplumb;
516 alts[MPlumb].v = &pm;
517 alts[MPlumb].op = CHANRCV;
518 alts[MWarnings].c = cwarn;
519 alts[MWarnings].v = nil;
520 alts[MWarnings].op = CHANRCV;
521 if(cplumb == nil)
522 alts[MPlumb].op = CHANNOP;
523 alts[NMALT].op = CHANEND;
525 for(;;){
526 qlock(&row.lk);
527 flushwarnings();
528 qunlock(&row.lk);
529 flushimage(display, 1);
530 switch(alt(alts)){
531 case MResize:
532 if(getwindow(display, Refnone) < 0)
533 error("attach to window");
534 draw(screen, screen->r, display->white, nil, ZP);
535 scrlresize();
536 rowresize(&row, screen->clipr);
537 break;
538 case MPlumb:
539 if(strcmp(pm->type, "text") == 0){
540 act = plumblookup(pm->attr, "action");
541 if(act==nil || strcmp(act, "showfile")==0)
542 plumblook(pm);
543 else if(strcmp(act, "showdata")==0)
544 plumbshow(pm);
546 plumbfree(pm);
547 break;
548 case MWarnings:
549 break;
550 case MMouse:
551 /*
552 * Make a copy so decisions are consistent; mousectl changes
553 * underfoot. Can't just receive into m because this introduces
554 * another race; see /sys/src/libdraw/mouse.c.
555 */
556 m = mousectl->m;
557 qlock(&row.lk);
558 t = rowwhich(&row, m.xy);
559 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
560 winlock(mousetext->w, 'M');
561 mousetext->eq0 = ~0;
562 wincommit(mousetext->w, mousetext);
563 winunlock(mousetext->w);
565 mousetext = t;
566 if(t == nil)
567 goto Continue;
568 w = t->w;
569 if(t==nil || m.buttons==0)
570 goto Continue;
571 but = 0;
572 if(m.buttons == 1)
573 but = 1;
574 else if(m.buttons == 2)
575 but = 2;
576 else if(m.buttons == 4)
577 but = 3;
578 barttext = t;
579 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
580 if(but){
581 if(swapscrollbuttons){
582 if(but == 1)
583 but = 3;
584 else if(but == 3)
585 but = 1;
587 winlock(w, 'M');
588 t->eq0 = ~0;
589 textscroll(t, but);
590 winunlock(w);
592 goto Continue;
594 /* scroll buttons, wheels, etc. */
595 /*
596 * TAG used to require t->what==Body but now allow
597 * scroll wheel in tag too.
598 */
599 if(w != nil && (m.buttons & (8|16))){
600 if(m.buttons & 8)
601 but = Kscrolloneup;
602 else
603 but = Kscrollonedown;
604 winlock(w, 'M');
605 t->eq0 = ~0;
606 texttype(t, but);
607 winunlock(w);
608 goto Continue;
610 if(ptinrect(m.xy, t->scrollr)){
611 if(but){
612 if(t->what == Columntag)
613 rowdragcol(&row, t->col, but);
614 else if(t->what == Tag){
615 coldragwin(t->col, t->w, but);
616 if(t->w)
617 barttext = &t->w->body;
619 if(t->col)
620 activecol = t->col;
622 goto Continue;
624 if(m.buttons){
625 if(w)
626 winlock(w, 'M');
627 t->eq0 = ~0;
628 if(w)
629 wincommit(w, t);
630 else
631 textcommit(t, TRUE);
632 if(m.buttons & 1){
633 textselect(t);
634 if(w)
635 winsettag(w);
636 argtext = t;
637 seltext = t;
638 if(t->col)
639 activecol = t->col; /* button 1 only */
640 if(t->w!=nil && t==&t->w->body)
641 activewin = t->w;
642 }else if(m.buttons & 2){
643 if(textselect2(t, &q0, &q1, &argt))
644 execute(t, q0, q1, FALSE, argt);
645 }else if(m.buttons & 4){
646 if(textselect3(t, &q0, &q1))
647 look3(t, q0, q1, FALSE);
649 if(w)
650 winunlock(w);
651 goto Continue;
653 Continue:
654 qunlock(&row.lk);
655 break;
660 /*
661 * There is a race between process exiting and our finding out it was ever created.
662 * This structure keeps a list of processes that have exited we haven't heard of.
663 */
664 typedef struct Pid Pid;
665 struct Pid
667 int pid;
668 char msg[ERRMAX];
669 Pid *next;
670 };
672 void
673 waitthread(void *v)
675 Waitmsg *w;
676 Command *c, *lc;
677 uint pid;
678 int found, ncmd;
679 Rune *cmd;
680 char *err;
681 Text *t;
682 Pid *pids, *p, *lastp;
683 enum { WErr, WKill, WWait, WCmd, NWALT };
684 Alt alts[NWALT+1];
686 USED(v);
687 threadsetname("waitthread");
688 pids = nil;
689 alts[WErr].c = cerr;
690 alts[WErr].v = &err;
691 alts[WErr].op = CHANRCV;
692 alts[WKill].c = ckill;
693 alts[WKill].v = &cmd;
694 alts[WKill].op = CHANRCV;
695 alts[WWait].c = cwait;
696 alts[WWait].v = &w;
697 alts[WWait].op = CHANRCV;
698 alts[WCmd].c = ccommand;
699 alts[WCmd].v = &c;
700 alts[WCmd].op = CHANRCV;
701 alts[NWALT].op = CHANEND;
703 command = nil;
704 for(;;){
705 switch(alt(alts)){
706 case WErr:
707 qlock(&row.lk);
708 warning(nil, "%s", err);
709 free(err);
710 flushimage(display, 1);
711 qunlock(&row.lk);
712 break;
713 case WKill:
714 found = FALSE;
715 ncmd = runestrlen(cmd);
716 for(c=command; c; c=c->next){
717 /* -1 for blank */
718 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
719 if(postnote(PNGROUP, c->pid, "kill") < 0)
720 warning(nil, "kill %S: %r\n", cmd);
721 found = TRUE;
724 if(!found)
725 warning(nil, "Kill: no process %S\n", cmd);
726 free(cmd);
727 break;
728 case WWait:
729 pid = w->pid;
730 lc = nil;
731 for(c=command; c; c=c->next){
732 if(c->pid == pid){
733 if(lc)
734 lc->next = c->next;
735 else
736 command = c->next;
737 break;
739 lc = c;
741 qlock(&row.lk);
742 t = &row.tag;
743 textcommit(t, TRUE);
744 if(c == nil){
745 /* helper processes use this exit status */
746 if(strncmp(w->msg, "libthread", 9) != 0){
747 p = emalloc(sizeof(Pid));
748 p->pid = pid;
749 strncpy(p->msg, w->msg, sizeof(p->msg));
750 p->next = pids;
751 pids = p;
753 }else{
754 if(search(t, c->name, c->nname)){
755 textdelete(t, t->q0, t->q1, TRUE);
756 textsetselect(t, 0, 0);
758 if(w->msg[0])
759 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
760 flushimage(display, 1);
762 qunlock(&row.lk);
763 free(w);
764 Freecmd:
765 if(c){
766 if(c->iseditcmd)
767 sendul(cedit, 0);
768 free(c->text);
769 free(c->name);
770 fsysdelid(c->md);
771 free(c);
773 break;
774 case WCmd:
775 /* has this command already exited? */
776 lastp = nil;
777 for(p=pids; p!=nil; p=p->next){
778 if(p->pid == c->pid){
779 if(p->msg[0])
780 warning(c->md, "%s\n", p->msg);
781 if(lastp == nil)
782 pids = p->next;
783 else
784 lastp->next = p->next;
785 free(p);
786 goto Freecmd;
788 lastp = p;
790 c->next = command;
791 command = c;
792 qlock(&row.lk);
793 t = &row.tag;
794 textcommit(t, TRUE);
795 textinsert(t, 0, c->name, c->nname, TRUE);
796 textsetselect(t, 0, 0);
797 flushimage(display, 1);
798 qunlock(&row.lk);
799 break;
804 void
805 xfidallocthread(void *v)
807 Xfid *xfree, *x;
808 enum { Alloc, Free, N };
809 static Alt alts[N+1];
811 USED(v);
812 threadsetname("xfidallocthread");
813 alts[Alloc].c = cxfidalloc;
814 alts[Alloc].v = nil;
815 alts[Alloc].op = CHANRCV;
816 alts[Free].c = cxfidfree;
817 alts[Free].v = &x;
818 alts[Free].op = CHANRCV;
819 alts[N].op = CHANEND;
821 xfree = nil;
822 for(;;){
823 switch(alt(alts)){
824 case Alloc:
825 x = xfree;
826 if(x)
827 xfree = x->next;
828 else{
829 x = emalloc(sizeof(Xfid));
830 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
831 chansetname(x->c, "xc%p", x->c);
832 x->arg = x;
833 threadcreate(xfidctl, x->arg, STACK);
835 sendp(cxfidalloc, x);
836 break;
837 case Free:
838 x->next = xfree;
839 xfree = x;
840 break;
845 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
846 void
847 newwindowthread(void *v)
849 Window *w;
851 USED(v);
852 threadsetname("newwindowthread");
854 for(;;){
855 /* only fsysproc is talking to us, so synchronization is trivial */
856 recvp(cnewwindow);
857 w = makenewwindow(nil);
858 winsettag(w);
859 sendp(cnewwindow, w);
863 Reffont*
864 rfget(int fix, int save, int setfont, char *name)
866 Reffont *r;
867 Font *f;
868 int i;
870 r = nil;
871 if(name == nil){
872 name = fontnames[fix];
873 r = reffonts[fix];
875 if(r == nil){
876 for(i=0; i<nfontcache; i++)
877 if(strcmp(name, fontcache[i]->f->name) == 0){
878 r = fontcache[i];
879 goto Found;
881 f = openfont(display, name);
882 if(f == nil){
883 fprint(2, "can't open font file %s: %r\n", name);
884 warning(nil, "can't open font file %s: %r\n", name);
885 return nil;
887 r = emalloc(sizeof(Reffont));
888 r->f = f;
889 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
890 fontcache[nfontcache++] = r;
892 Found:
893 if(save){
894 incref(&r->ref);
895 if(reffonts[fix])
896 rfclose(reffonts[fix]);
897 reffonts[fix] = r;
898 if(fontnames[fix] != name){
899 free(fontnames[fix]);
900 fontnames[fix] = estrdup(name);
903 if(setfont){
904 reffont.f = r->f;
905 incref(&r->ref);
906 rfclose(reffonts[0]);
907 font = r->f;
908 reffonts[0] = r;
909 incref(&r->ref);
910 iconinit();
912 incref(&r->ref);
913 return r;
916 void
917 rfclose(Reffont *r)
919 int i;
921 if(decref(&r->ref) == 0){
922 for(i=0; i<nfontcache; i++)
923 if(r == fontcache[i])
924 break;
925 if(i >= nfontcache)
926 warning(nil, "internal error: can't find font in cache\n");
927 else{
928 nfontcache--;
929 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
931 freefont(r->f);
932 free(r);
936 Cursor boxcursor = {
937 {-7, -7},
938 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
939 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
940 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
941 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
942 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
943 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
944 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
945 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
946 };
948 void
949 iconinit(void)
951 Rectangle r;
952 Image *tmp;
954 /* Blue */
955 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
956 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
957 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
958 tagcols[TEXT] = display->black;
959 tagcols[HTEXT] = display->black;
961 /* Yellow */
962 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
963 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
964 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
965 textcols[TEXT] = display->black;
966 textcols[HTEXT] = display->black;
968 if(button){
969 freeimage(button);
970 freeimage(modbutton);
971 freeimage(colbutton);
974 r = Rect(0, 0, Scrollwid+2, font->height+1);
975 button = allocimage(display, r, screen->chan, 0, DNofill);
976 draw(button, r, tagcols[BACK], nil, r.min);
977 r.max.x -= 2;
978 border(button, r, 2, tagcols[BORD], ZP);
980 r = button->r;
981 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
982 draw(modbutton, r, tagcols[BACK], nil, r.min);
983 r.max.x -= 2;
984 border(modbutton, r, 2, tagcols[BORD], ZP);
985 r = insetrect(r, 2);
986 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
987 draw(modbutton, r, tmp, nil, ZP);
988 freeimage(tmp);
990 r = button->r;
991 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
993 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
994 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
997 /*
998 * /dev/snarf updates when the file is closed, so we must open our own
999 * fd here rather than use snarffd
1002 /* rio truncates larges snarf buffers, so this avoids using the
1003 * service if the string is huge */
1005 #define MAXSNARF 100*1024
1007 void
1008 acmeputsnarf(void)
1010 int i, n;
1011 Fmt f;
1012 char *s;
1014 if(snarfbuf.nc==0)
1015 return;
1016 if(snarfbuf.nc > MAXSNARF)
1017 return;
1019 fmtstrinit(&f);
1020 for(i=0; i<snarfbuf.nc; i+=n){
1021 n = snarfbuf.nc-i;
1022 if(n >= NSnarf)
1023 n = NSnarf;
1024 bufread(&snarfbuf, i, snarfrune, n);
1025 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1026 break;
1028 s = fmtstrflush(&f);
1029 if(s && s[0])
1030 putsnarf(s);
1031 free(s);
1034 void
1035 acmegetsnarf(void)
1037 char *s;
1038 int nb, nr, nulls, len;
1039 Rune *r;
1041 s = getsnarf();
1042 if(s == nil || s[0]==0){
1043 free(s);
1044 return;
1047 len = strlen(s);
1048 r = runemalloc(len+1);
1049 cvttorunes(s, len, r, &nb, &nr, &nulls);
1050 bufreset(&snarfbuf);
1051 bufinsert(&snarfbuf, 0, r, nr);
1052 free(r);
1053 free(s);