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 acmeerrorinit(void);
45 void readfile(Column*, char*);
46 static int shutdown(void*, char*);
48 void
49 derror(Display *d, char *errorstr)
50 {
51 USED(d);
52 error(errorstr);
53 }
55 void
56 threadmain(int argc, char *argv[])
57 {
58 int i;
59 char *p, *loadfile;
60 Column *c;
61 int ncol;
62 Display *d;
64 rfork(RFENVG|RFNAMEG);
66 ncol = -1;
68 loadfile = nil;
69 ARGBEGIN{
70 case 'D':
71 {extern int _threaddebuglevel;
72 _threaddebuglevel = ~0;
73 }
74 break;
75 case 'a':
76 globalautoindent = TRUE;
77 break;
78 case 'b':
79 bartflag = TRUE;
80 break;
81 case 'c':
82 p = ARGF();
83 if(p == nil)
84 goto Usage;
85 ncol = atoi(p);
86 if(ncol <= 0)
87 goto Usage;
88 break;
89 case 'f':
90 fontnames[0] = ARGF();
91 if(fontnames[0] == nil)
92 goto Usage;
93 break;
94 case 'F':
95 fontnames[1] = ARGF();
96 if(fontnames[1] == nil)
97 goto Usage;
98 break;
99 case 'l':
100 loadfile = ARGF();
101 if(loadfile == nil)
102 goto Usage;
103 break;
104 case 'W':
105 winsize = ARGF();
106 if(winsize == nil)
107 goto Usage;
108 break;
109 default:
110 Usage:
111 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
112 exits("usage");
113 }ARGEND
115 quotefmtinstall();
116 cputype = getenv("cputype");
117 objtype = getenv("objtype");
118 home = getenv("home");
119 if(home == nil)
120 home = getenv("HOME");
121 p = getenv("tabstop");
122 if(p != nil){
123 maxtab = strtoul(p, nil, 0);
124 free(p);
126 if(maxtab == 0)
127 maxtab = 4;
128 if(loadfile)
129 rowloadfonts(loadfile);
130 putenv("font", fontnames[0]);
131 snarffd = open("/dev/snarf", OREAD|OCEXEC);
132 /*
133 if(cputype){
134 sprint(buf, "/acme/bin/%s", cputype);
135 bind(buf, "/bin", MBEFORE);
137 bind("/acme/bin", "/bin", MBEFORE);
138 */
139 getwd(wdir, sizeof wdir);
141 /*
142 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
143 fprint(2, "acme: can't open display: %r\n");
144 exits("geninitdraw");
146 */
147 if(initdraw(derror, fontnames[0], "acme") < 0){
148 fprint(2, "acme: can't open display: %r\n");
149 exits("initdraw");
152 d = display;
153 font = d->defaultfont;
154 //assert(font);
156 reffont.f = font;
157 reffonts[0] = &reffont;
158 incref(&reffont.ref); /* one to hold up 'font' variable */
159 incref(&reffont.ref); /* one to hold up reffonts[0] */
160 fontcache = emalloc(sizeof(Reffont*));
161 nfontcache = 1;
162 fontcache[0] = &reffont;
164 iconinit();
165 timerinit();
166 rxinit();
168 cwait = threadwaitchan();
169 ccommand = chancreate(sizeof(Command**), 0);
170 ckill = chancreate(sizeof(Rune*), 0);
171 cxfidalloc = chancreate(sizeof(Xfid*), 0);
172 cxfidfree = chancreate(sizeof(Xfid*), 0);
173 cnewwindow = chancreate(sizeof(Channel*), 0);
174 cerr = chancreate(sizeof(char*), 0);
175 cedit = chancreate(sizeof(int), 0);
176 cexit = chancreate(sizeof(int), 0);
177 cwarn = chancreate(sizeof(void*), 1);
178 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
179 fprint(2, "acme: can't create initial channels: %r\n");
180 exits("channels");
183 mousectl = initmouse(nil, screen);
184 if(mousectl == nil){
185 fprint(2, "acme: can't initialize mouse: %r\n");
186 exits("mouse");
188 mouse = &mousectl->m;
189 keyboardctl = initkeyboard(nil);
190 if(keyboardctl == nil){
191 fprint(2, "acme: can't initialize keyboard: %r\n");
192 exits("keyboard");
194 mainpid = getpid();
195 startplumbing();
196 /*
197 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
198 if(plumbeditfd < 0)
199 fprint(2, "acme: can't initialize plumber: %r\n");
200 else{
201 cplumb = chancreate(sizeof(Plumbmsg*), 0);
202 threadcreate(plumbproc, nil, STACK);
204 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
205 */
207 fsysinit();
209 #define WPERCOL 8
210 disk = diskinit();
211 if(loadfile)
212 rowload(&row, loadfile, TRUE);
213 else{
214 rowinit(&row, screen->clipr);
215 if(ncol < 0){
216 if(argc == 0)
217 ncol = 2;
218 else{
219 ncol = (argc+(WPERCOL-1))/WPERCOL;
220 if(ncol < 2)
221 ncol = 2;
224 if(ncol == 0)
225 ncol = 2;
226 for(i=0; i<ncol; i++){
227 c = rowadd(&row, nil, -1);
228 if(c==nil && i==0)
229 error("initializing columns");
231 c = row.col[row.ncol-1];
232 if(argc == 0)
233 readfile(c, wdir);
234 else
235 for(i=0; i<argc; i++){
236 p = utfrrune(argv[i], '/');
237 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
238 readfile(c, argv[i]);
239 else
240 readfile(row.col[i/WPERCOL], argv[i]);
243 flushimage(display, 1);
245 acmeerrorinit();
246 threadcreate(keyboardthread, nil, STACK);
247 threadcreate(mousethread, nil, STACK);
248 threadcreate(waitthread, nil, STACK);
249 threadcreate(xfidallocthread, nil, STACK);
250 threadcreate(newwindowthread, nil, STACK);
252 threadnotify(shutdown, 1);
253 recvul(cexit);
254 killprocs();
255 threadexitsall(nil);
258 void
259 readfile(Column *c, char *s)
261 Window *w;
262 Rune rb[256];
263 int nb, nr;
264 Runestr rs;
266 w = coladd(c, nil, nil, -1);
267 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
268 rs = cleanrname(runestr(rb, nr));
269 winsetname(w, rs.r, rs.nr);
270 textload(&w->body, 0, s, 1);
271 w->body.file->mod = FALSE;
272 w->dirty = FALSE;
273 winsettag(w);
274 textscrdraw(&w->body);
275 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
278 char *oknotes[] ={
279 "delete",
280 "hangup",
281 "kill",
282 "exit",
283 nil
284 };
286 int dumping;
288 static int
289 shutdown(void *v, char *msg)
291 int i;
293 if(strcmp(msg, "sys: write on closed pipe") == 0)
294 return 1;
296 USED(v);
297 killprocs();
298 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
299 dumping = TRUE;
300 rowdump(&row, nil);
302 for(i=0; oknotes[i]; i++)
303 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
304 threadexitsall(msg);
305 print("acme: %s\n", msg);
306 abort();
307 return 0;
310 void
311 killprocs(void)
313 Command *c;
315 fsysclose();
316 // if(display)
317 // flushimage(display, 1);
319 for(c=command; c; c=c->next)
320 postnote(PNGROUP, c->pid, "hangup");
323 static int errorfd;
324 int erroutfd;
326 void
327 acmeerrorproc(void *v)
329 char *buf;
330 int n;
332 USED(v);
333 threadsetname("acmeerrorproc");
334 buf = emalloc(8192+1);
335 while((n=threadread(errorfd, buf, 8192)) >= 0){
336 buf[n] = '\0';
337 sendp(cerr, estrdup(buf));
341 void
342 acmeerrorinit(void)
344 int pfd[2];
346 if(pipe(pfd) < 0)
347 error("can't create pipe");
348 #if 0
349 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
350 fd = create(acmeerrorfile, OWRITE, 0666);
351 if(fd < 0){
352 remove(acmeerrorfile);
353 fd = create(acmeerrorfile, OWRITE, 0666);
354 if(fd < 0)
355 error("can't create acmeerror file");
357 sprint(buf, "%d", pfd[0]);
358 write(fd, buf, strlen(buf));
359 close(fd);
360 /* reopen pfd[1] close on exec */
361 sprint(buf, "/fd/%d", pfd[1]);
362 errorfd = open(buf, OREAD|OCEXEC);
363 #endif
364 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
365 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
366 erroutfd = pfd[0];
367 errorfd = pfd[1];
368 if(errorfd < 0)
369 error("can't re-open acmeerror file");
370 threadcreate(acmeerrorproc, nil, STACK);
373 /*
374 void
375 plumbproc(void *v)
377 Plumbmsg *m;
379 USED(v);
380 threadsetname("plumbproc");
381 for(;;){
382 m = threadplumbrecv(plumbeditfd);
383 if(m == nil)
384 threadexits(nil);
385 sendp(cplumb, m);
388 */
390 void
391 keyboardthread(void *v)
393 Rune r;
394 Timer *timer;
395 Text *t;
396 enum { KTimer, KKey, NKALT };
397 static Alt alts[NKALT+1];
399 USED(v);
400 alts[KTimer].c = nil;
401 alts[KTimer].v = nil;
402 alts[KTimer].op = CHANNOP;
403 alts[KKey].c = keyboardctl->c;
404 alts[KKey].v = &r;
405 alts[KKey].op = CHANRCV;
406 alts[NKALT].op = CHANEND;
408 timer = nil;
409 typetext = nil;
410 threadsetname("keyboardthread");
411 for(;;){
412 switch(alt(alts)){
413 case KTimer:
414 timerstop(timer);
415 t = typetext;
416 if(t!=nil && t->what==Tag){
417 winlock(t->w, 'K');
418 wincommit(t->w, t);
419 winunlock(t->w);
420 flushimage(display, 1);
422 alts[KTimer].c = nil;
423 alts[KTimer].op = CHANNOP;
424 break;
425 case KKey:
426 casekeyboard:
427 typetext = rowtype(&row, r, mouse->xy);
428 t = typetext;
429 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
430 activecol = t->col;
431 if(t!=nil && t->w!=nil)
432 t->w->body.file->curtext = &t->w->body;
433 if(timer != nil)
434 timercancel(timer);
435 if(t!=nil && t->what==Tag) {
436 timer = timerstart(500);
437 alts[KTimer].c = timer->c;
438 alts[KTimer].op = CHANRCV;
439 }else{
440 timer = nil;
441 alts[KTimer].c = nil;
442 alts[KTimer].op = CHANNOP;
444 if(nbrecv(keyboardctl->c, &r) > 0)
445 goto casekeyboard;
446 flushimage(display, 1);
447 break;
452 void
453 mousethread(void *v)
455 Text *t, *argt;
456 int but;
457 uint q0, q1;
458 Window *w;
459 Plumbmsg *pm;
460 Mouse m;
461 char *act;
462 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
463 static Alt alts[NMALT+1];
465 USED(v);
466 threadsetname("mousethread");
467 alts[MResize].c = mousectl->resizec;
468 alts[MResize].v = nil;
469 alts[MResize].op = CHANRCV;
470 alts[MMouse].c = mousectl->c;
471 alts[MMouse].v = &mousectl->m;
472 alts[MMouse].op = CHANRCV;
473 alts[MPlumb].c = cplumb;
474 alts[MPlumb].v = &pm;
475 alts[MPlumb].op = CHANRCV;
476 alts[MWarnings].c = cwarn;
477 alts[MWarnings].v = nil;
478 alts[MWarnings].op = CHANRCV;
479 if(cplumb == nil)
480 alts[MPlumb].op = CHANNOP;
481 alts[NMALT].op = CHANEND;
483 for(;;){
484 qlock(&row.lk);
485 flushwarnings();
486 qunlock(&row.lk);
487 flushimage(display, 1);
488 switch(alt(alts)){
489 case MResize:
490 if(getwindow(display, Refnone) < 0)
491 error("attach to window");
492 draw(screen, screen->r, display->white, nil, ZP);
493 scrlresize();
494 rowresize(&row, screen->clipr);
495 break;
496 case MPlumb:
497 if(strcmp(pm->type, "text") == 0){
498 act = plumblookup(pm->attr, "action");
499 if(act==nil || strcmp(act, "showfile")==0)
500 plumblook(pm);
501 else if(strcmp(act, "showdata")==0)
502 plumbshow(pm);
504 plumbfree(pm);
505 break;
506 case MWarnings:
507 break;
508 case MMouse:
509 /*
510 * Make a copy so decisions are consistent; mousectl changes
511 * underfoot. Can't just receive into m because this introduces
512 * another race; see /sys/src/libdraw/mouse.c.
513 */
514 m = mousectl->m;
515 qlock(&row.lk);
516 t = rowwhich(&row, m.xy);
517 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
518 winlock(mousetext->w, 'M');
519 mousetext->eq0 = ~0;
520 wincommit(mousetext->w, mousetext);
521 winunlock(mousetext->w);
523 mousetext = t;
524 if(t == nil)
525 goto Continue;
526 w = t->w;
527 if(t==nil || m.buttons==0)
528 goto Continue;
529 but = 0;
530 if(m.buttons == 1)
531 but = 1;
532 else if(m.buttons == 2)
533 but = 2;
534 else if(m.buttons == 4)
535 but = 3;
536 barttext = t;
537 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
538 if(but){
539 winlock(w, 'M');
540 t->eq0 = ~0;
541 textscroll(t, but);
542 winunlock(w);
544 goto Continue;
546 /* scroll buttons, wheels, etc. */
547 if(t->what==Body && w != nil && (m.buttons & (8|16))){
548 if(m.buttons & 8)
549 but = Kscrolloneup;
550 else
551 but = Kscrollonedown;
552 winlock(w, 'M');
553 t->eq0 = ~0;
554 texttype(t, but);
555 winunlock(w);
556 goto Continue;
558 if(ptinrect(m.xy, t->scrollr)){
559 if(but){
560 if(t->what == Columntag)
561 rowdragcol(&row, t->col, but);
562 else if(t->what == Tag){
563 coldragwin(t->col, t->w, but);
564 if(t->w)
565 barttext = &t->w->body;
567 if(t->col)
568 activecol = t->col;
570 goto Continue;
572 if(m.buttons){
573 if(w)
574 winlock(w, 'M');
575 t->eq0 = ~0;
576 if(w)
577 wincommit(w, t);
578 else
579 textcommit(t, TRUE);
580 if(m.buttons & 1){
581 textselect(t);
582 if(w)
583 winsettag(w);
584 argtext = t;
585 seltext = t;
586 if(t->col)
587 activecol = t->col; /* button 1 only */
588 if(t->w!=nil && t==&t->w->body)
589 activewin = t->w;
590 }else if(m.buttons & 2){
591 if(textselect2(t, &q0, &q1, &argt))
592 execute(t, q0, q1, FALSE, argt);
593 }else if(m.buttons & 4){
594 if(textselect3(t, &q0, &q1))
595 look3(t, q0, q1, FALSE);
597 if(w)
598 winunlock(w);
599 goto Continue;
601 Continue:
602 qunlock(&row.lk);
603 break;
608 /*
609 * There is a race between process exiting and our finding out it was ever created.
610 * This structure keeps a list of processes that have exited we haven't heard of.
611 */
612 typedef struct Pid Pid;
613 struct Pid
615 int pid;
616 char msg[ERRMAX];
617 Pid *next;
618 };
620 void
621 waitthread(void *v)
623 Waitmsg *w;
624 Command *c, *lc;
625 uint pid;
626 int found, ncmd;
627 Rune *cmd;
628 char *err;
629 Text *t;
630 Pid *pids, *p, *lastp;
631 enum { WErr, WKill, WWait, WCmd, NWALT };
632 Alt alts[NWALT+1];
634 USED(v);
635 threadsetname("waitthread");
636 pids = nil;
637 alts[WErr].c = cerr;
638 alts[WErr].v = &err;
639 alts[WErr].op = CHANRCV;
640 alts[WKill].c = ckill;
641 alts[WKill].v = &cmd;
642 alts[WKill].op = CHANRCV;
643 alts[WWait].c = cwait;
644 alts[WWait].v = &w;
645 alts[WWait].op = CHANRCV;
646 alts[WCmd].c = ccommand;
647 alts[WCmd].v = &c;
648 alts[WCmd].op = CHANRCV;
649 alts[NWALT].op = CHANEND;
651 /*
652 * BUG. Actually there's no bug here but this is the
653 * first place you'd look. When a program is run,
654 * it doesn't disappear from the main tag until the
655 * mouse is moved or keyboard is hit. This would
656 * suggest that the WWait case isn't working right,
657 * but what's actually going on is that the X11 code
658 * is running a select-based threading loop that
659 * doesn't get interrupted until there is data from X11.
660 * This was done to make acme work on Suns and
661 * other systems where our threading was sub-par.
662 * Now that we've gotten pthreads working (sort of),
663 * we might be able to fix this properly.
664 * But the bug is in libdraw and libthread, not here.
665 */
666 command = nil;
667 for(;;){
668 switch(alt(alts)){
669 case WErr:
670 qlock(&row.lk);
671 warning(nil, "%s", err);
672 free(err);
673 flushimage(display, 1);
674 qunlock(&row.lk);
675 break;
676 case WKill:
677 found = FALSE;
678 ncmd = runestrlen(cmd);
679 for(c=command; c; c=c->next){
680 /* -1 for blank */
681 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
682 if(postnote(PNGROUP, c->pid, "kill") < 0)
683 warning(nil, "kill %S: %r\n", cmd);
684 found = TRUE;
687 if(!found)
688 warning(nil, "Kill: no process %S\n", cmd);
689 free(cmd);
690 break;
691 case WWait:
692 pid = w->pid;
693 lc = nil;
694 for(c=command; c; c=c->next){
695 if(c->pid == pid){
696 if(lc)
697 lc->next = c->next;
698 else
699 command = c->next;
700 break;
702 lc = c;
704 qlock(&row.lk);
705 t = &row.tag;
706 textcommit(t, TRUE);
707 if(c == nil){
708 /* helper processes use this exit status */
709 if(strncmp(w->msg, "libthread", 9) != 0){
710 p = emalloc(sizeof(Pid));
711 p->pid = pid;
712 strncpy(p->msg, w->msg, sizeof(p->msg));
713 p->next = pids;
714 pids = p;
716 }else{
717 if(search(t, c->name, c->nname)){
718 textdelete(t, t->q0, t->q1, TRUE);
719 textsetselect(t, 0, 0);
721 if(w->msg[0])
722 warning(c->md, "%S: %s\n", c->name, w->msg);
723 flushimage(display, 1);
725 qunlock(&row.lk);
726 free(w);
727 Freecmd:
728 if(c){
729 if(c->iseditcmd)
730 sendul(cedit, 0);
731 free(c->text);
732 free(c->name);
733 fsysdelid(c->md);
734 free(c);
736 break;
737 case WCmd:
738 /* has this command already exited? */
739 lastp = nil;
740 for(p=pids; p!=nil; p=p->next){
741 if(p->pid == c->pid){
742 if(p->msg[0])
743 warning(c->md, "%s\n", p->msg);
744 if(lastp == nil)
745 pids = p->next;
746 else
747 lastp->next = p->next;
748 free(p);
749 goto Freecmd;
751 lastp = p;
753 c->next = command;
754 command = c;
755 qlock(&row.lk);
756 t = &row.tag;
757 textcommit(t, TRUE);
758 textinsert(t, 0, c->name, c->nname, TRUE);
759 textsetselect(t, 0, 0);
760 flushimage(display, 1);
761 qunlock(&row.lk);
762 break;
767 void
768 xfidallocthread(void *v)
770 Xfid *xfree, *x;
771 enum { Alloc, Free, N };
772 static Alt alts[N+1];
774 USED(v);
775 threadsetname("xfidallocthread");
776 alts[Alloc].c = cxfidalloc;
777 alts[Alloc].v = nil;
778 alts[Alloc].op = CHANRCV;
779 alts[Free].c = cxfidfree;
780 alts[Free].v = &x;
781 alts[Free].op = CHANRCV;
782 alts[N].op = CHANEND;
784 xfree = nil;
785 for(;;){
786 switch(alt(alts)){
787 case Alloc:
788 x = xfree;
789 if(x)
790 xfree = x->next;
791 else{
792 x = emalloc(sizeof(Xfid));
793 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
794 x->arg = x;
795 threadcreate(xfidctl, x->arg, STACK);
797 sendp(cxfidalloc, x);
798 break;
799 case Free:
800 x->next = xfree;
801 xfree = x;
802 break;
807 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
808 void
809 newwindowthread(void *v)
811 Window *w;
813 USED(v);
814 threadsetname("newwindowthread");
816 for(;;){
817 /* only fsysproc is talking to us, so synchronization is trivial */
818 recvp(cnewwindow);
819 w = makenewwindow(nil);
820 winsettag(w);
821 sendp(cnewwindow, w);
825 Reffont*
826 rfget(int fix, int save, int setfont, char *name)
828 Reffont *r;
829 Font *f;
830 int i;
832 r = nil;
833 if(name == nil){
834 name = fontnames[fix];
835 r = reffonts[fix];
837 if(r == nil){
838 for(i=0; i<nfontcache; i++)
839 if(strcmp(name, fontcache[i]->f->name) == 0){
840 r = fontcache[i];
841 goto Found;
843 f = openfont(display, name);
844 if(f == nil){
845 fprint(2, "can't open font file %s: %r\n", name);
846 warning(nil, "can't open font file %s: %r\n", name);
847 return nil;
849 r = emalloc(sizeof(Reffont));
850 r->f = f;
851 fontcache = realloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
852 fontcache[nfontcache++] = r;
854 Found:
855 if(save){
856 incref(&r->ref);
857 if(reffonts[fix])
858 rfclose(reffonts[fix]);
859 reffonts[fix] = r;
860 free(fontnames[fix]);
861 fontnames[fix] = name;
863 if(setfont){
864 reffont.f = r->f;
865 incref(&r->ref);
866 rfclose(reffonts[0]);
867 font = r->f;
868 reffonts[0] = r;
869 incref(&r->ref);
870 iconinit();
872 incref(&r->ref);
873 return r;
876 void
877 rfclose(Reffont *r)
879 int i;
881 if(decref(&r->ref) == 0){
882 for(i=0; i<nfontcache; i++)
883 if(r == fontcache[i])
884 break;
885 if(i >= nfontcache)
886 warning(nil, "internal error: can't find font in cache\n");
887 else{
888 nfontcache--;
889 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
891 freefont(r->f);
892 free(r);
896 Cursor boxcursor = {
897 {-7, -7},
898 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
899 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
900 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
901 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
902 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
903 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
904 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
905 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
906 };
908 void
909 iconinit(void)
911 Rectangle r;
912 Image *tmp;
914 /* Blue */
915 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
916 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
917 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
918 tagcols[TEXT] = display->black;
919 tagcols[HTEXT] = display->black;
921 /* Yellow */
922 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
923 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
924 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
925 textcols[TEXT] = display->black;
926 textcols[HTEXT] = display->black;
928 if(button){
929 freeimage(button);
930 freeimage(modbutton);
931 freeimage(colbutton);
934 r = Rect(0, 0, Scrollwid+2, font->height+1);
935 button = allocimage(display, r, screen->chan, 0, DNofill);
936 draw(button, r, tagcols[BACK], nil, r.min);
937 r.max.x -= 2;
938 border(button, r, 2, tagcols[BORD], ZP);
940 r = button->r;
941 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
942 draw(modbutton, r, tagcols[BACK], nil, r.min);
943 r.max.x -= 2;
944 border(modbutton, r, 2, tagcols[BORD], ZP);
945 r = insetrect(r, 2);
946 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
947 draw(modbutton, r, tmp, nil, ZP);
948 freeimage(tmp);
950 r = button->r;
951 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
953 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
954 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
957 /*
958 * /dev/snarf updates when the file is closed, so we must open our own
959 * fd here rather than use snarffd
960 */
962 /* rio truncates larges snarf buffers, so this avoids using the
963 * service if the string is huge */
965 #define MAXSNARF 100*1024
967 void
968 acmeputsnarf(void)
970 int i, n;
971 Fmt f;
972 char *s;
974 if(snarfbuf.nc==0)
975 return;
976 if(snarfbuf.nc > MAXSNARF)
977 return;
979 fmtstrinit(&f);
980 for(i=0; i<snarfbuf.nc; i+=n){
981 n = snarfbuf.nc-i;
982 if(n >= NSnarf)
983 n = NSnarf;
984 bufread(&snarfbuf, i, snarfrune, n);
985 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
986 break;
988 s = fmtstrflush(&f);
989 if(s && s[0])
990 putsnarf(s);
991 free(s);
994 void
995 acmegetsnarf(void)
997 char *s;
998 int nb, nr, nulls, len;
999 Rune *r;
1001 s = getsnarf();
1002 if(s == nil || s[0]==0){
1003 free(s);
1004 return;
1007 len = strlen(s);
1008 r = runemalloc(len+1);
1009 cvttorunes(s, len, r, &nb, &nr, &nulls);
1010 bufreset(&snarfbuf);
1011 bufinsert(&snarfbuf, 0, r, nr);
1012 free(r);
1013 free(s);