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