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;
31 char *mtpt;
33 enum{
34 NSnarf = 1000 /* less than 1024, I/O buffer size */
35 };
36 Rune snarfrune[NSnarf+1];
38 char *fontnames[2] =
39 {
40 "/lib/font/bit/lucsans/euro.8.font",
41 "/lib/font/bit/lucm/unicode.9.font"
42 };
44 Command *command;
46 void shutdownthread(void*);
47 void acmeerrorinit(void);
48 void readfile(Column*, char*);
49 static int shutdown(void*, char*);
51 void
52 derror(Display *d, char *errorstr)
53 {
54 USED(d);
55 error(errorstr);
56 }
58 void
59 threadmain(int argc, char *argv[])
60 {
61 int i;
62 char *p, *loadfile;
63 Column *c;
64 int ncol;
65 Display *d;
67 rfork(RFENVG|RFNAMEG);
69 ncol = -1;
71 loadfile = nil;
72 ARGBEGIN{
73 case 'D':
74 {extern int _threaddebuglevel;
75 _threaddebuglevel = ~0;
76 }
77 break;
78 case 'a':
79 globalautoindent = TRUE;
80 break;
81 case 'b':
82 bartflag = TRUE;
83 break;
84 case 'c':
85 p = ARGF();
86 if(p == nil)
87 goto Usage;
88 ncol = atoi(p);
89 if(ncol <= 0)
90 goto Usage;
91 break;
92 case 'f':
93 fontnames[0] = ARGF();
94 if(fontnames[0] == nil)
95 goto Usage;
96 break;
97 case 'F':
98 fontnames[1] = ARGF();
99 if(fontnames[1] == nil)
100 goto Usage;
101 break;
102 case 'l':
103 loadfile = ARGF();
104 if(loadfile == nil)
105 goto Usage;
106 break;
107 case 'm':
108 mtpt = ARGF();
109 if(mtpt == nil)
110 goto Usage;
111 break;
112 case 'r':
113 swapscrollbuttons = TRUE;
114 break;
115 case 'W':
116 winsize = ARGF();
117 if(winsize == nil)
118 goto Usage;
119 break;
120 default:
121 Usage:
122 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
123 threadexitsall("usage");
124 }ARGEND
126 fontnames[0] = estrdup(fontnames[0]);
127 fontnames[1] = estrdup(fontnames[1]);
129 quotefmtinstall();
130 cputype = getenv("cputype");
131 objtype = getenv("objtype");
132 home = getenv("HOME");
133 p = getenv("tabstop");
134 if(p != nil){
135 maxtab = strtoul(p, nil, 0);
136 free(p);
138 if(maxtab == 0)
139 maxtab = 4;
140 if(loadfile)
141 rowloadfonts(loadfile);
142 putenv("font", fontnames[0]);
143 snarffd = open("/dev/snarf", OREAD|OCEXEC);
144 /*
145 if(cputype){
146 sprint(buf, "/acme/bin/%s", cputype);
147 bind(buf, "/bin", MBEFORE);
149 bind("/acme/bin", "/bin", MBEFORE);
150 */
151 getwd(wdir, sizeof wdir);
153 /*
154 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
155 fprint(2, "acme: can't open display: %r\n");
156 threadexitsall("geninitdraw");
158 */
159 if(initdraw(derror, fontnames[0], "acme") < 0){
160 fprint(2, "acme: can't open display: %r\n");
161 threadexitsall("initdraw");
164 d = display;
165 font = d->defaultfont;
166 /*assert(font); */
168 reffont.f = font;
169 reffonts[0] = &reffont;
170 incref(&reffont.ref); /* one to hold up 'font' variable */
171 incref(&reffont.ref); /* one to hold up reffonts[0] */
172 fontcache = emalloc(sizeof(Reffont*));
173 nfontcache = 1;
174 fontcache[0] = &reffont;
176 iconinit();
177 timerinit();
178 rxinit();
180 cwait = threadwaitchan();
181 ccommand = chancreate(sizeof(Command**), 0);
182 ckill = chancreate(sizeof(Rune*), 0);
183 cxfidalloc = chancreate(sizeof(Xfid*), 0);
184 cxfidfree = chancreate(sizeof(Xfid*), 0);
185 cnewwindow = chancreate(sizeof(Channel*), 0);
186 cerr = chancreate(sizeof(char*), 0);
187 cedit = chancreate(sizeof(int), 0);
188 cexit = chancreate(sizeof(int), 0);
189 cwarn = chancreate(sizeof(void*), 1);
190 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
191 fprint(2, "acme: can't create initial channels: %r\n");
192 threadexitsall("channels");
194 chansetname(ccommand, "ccommand");
195 chansetname(ckill, "ckill");
196 chansetname(cxfidalloc, "cxfidalloc");
197 chansetname(cxfidfree, "cxfidfree");
198 chansetname(cnewwindow, "cnewwindow");
199 chansetname(cerr, "cerr");
200 chansetname(cedit, "cedit");
201 chansetname(cexit, "cexit");
202 chansetname(cwarn, "cwarn");
204 mousectl = initmouse(nil, screen);
205 if(mousectl == nil){
206 fprint(2, "acme: can't initialize mouse: %r\n");
207 threadexitsall("mouse");
209 mouse = &mousectl->m;
210 keyboardctl = initkeyboard(nil);
211 if(keyboardctl == nil){
212 fprint(2, "acme: can't initialize keyboard: %r\n");
213 threadexitsall("keyboard");
215 mainpid = getpid();
216 startplumbing();
217 /*
218 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
219 if(plumbeditfd < 0)
220 fprint(2, "acme: can't initialize plumber: %r\n");
221 else{
222 cplumb = chancreate(sizeof(Plumbmsg*), 0);
223 threadcreate(plumbproc, nil, STACK);
225 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
226 */
228 fsysinit();
230 #define WPERCOL 8
231 disk = diskinit();
232 if(!loadfile || !rowload(&row, loadfile, TRUE)){
233 rowinit(&row, screen->clipr);
234 if(ncol < 0){
235 if(argc == 0)
236 ncol = 2;
237 else{
238 ncol = (argc+(WPERCOL-1))/WPERCOL;
239 if(ncol < 2)
240 ncol = 2;
243 if(ncol == 0)
244 ncol = 2;
245 for(i=0; i<ncol; i++){
246 c = rowadd(&row, nil, -1);
247 if(c==nil && i==0)
248 error("initializing columns");
250 c = row.col[row.ncol-1];
251 if(argc == 0)
252 readfile(c, wdir);
253 else
254 for(i=0; i<argc; i++){
255 p = utfrrune(argv[i], '/');
256 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
257 readfile(c, argv[i]);
258 else
259 readfile(row.col[i/WPERCOL], argv[i]);
262 flushimage(display, 1);
264 acmeerrorinit();
265 threadcreate(keyboardthread, nil, STACK);
266 threadcreate(mousethread, nil, STACK);
267 threadcreate(waitthread, nil, STACK);
268 threadcreate(xfidallocthread, nil, STACK);
269 threadcreate(newwindowthread, nil, STACK);
270 /* threadcreate(shutdownthread, nil, STACK); */
271 threadnotify(shutdown, 1);
272 recvul(cexit);
273 killprocs();
274 threadexitsall(nil);
277 void
278 readfile(Column *c, char *s)
280 Window *w;
281 Rune rb[256];
282 int nr;
283 Runestr rs;
285 w = coladd(c, nil, nil, -1);
286 if(s[0] != '/')
287 runesnprint(rb, sizeof rb, "%s/%s", wdir, s);
288 else
289 runesnprint(rb, sizeof rb, "%s", s);
290 nr = runestrlen(rb);
291 rs = cleanrname(runestr(rb, nr));
292 winsetname(w, rs.r, rs.nr);
293 textload(&w->body, 0, s, 1);
294 w->body.file->mod = FALSE;
295 w->dirty = FALSE;
296 winsettag(w);
297 winresize(w, w->r, FALSE, TRUE);
298 textscrdraw(&w->body);
299 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
302 char *ignotes[] = {
303 "sys: write on closed pipe",
304 "sys: ttin",
305 "sys: ttou",
306 "sys: tstp",
307 nil
308 };
310 char *oknotes[] ={
311 "delete",
312 "hangup",
313 "kill",
314 "exit",
315 nil
316 };
318 int dumping;
320 static int
321 shutdown(void *v, char *msg)
323 int i;
325 USED(v);
327 for(i=0; ignotes[i]; i++)
328 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
329 return 1;
331 killprocs();
332 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
333 dumping = TRUE;
334 rowdump(&row, nil);
336 for(i=0; oknotes[i]; i++)
337 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
338 threadexitsall(msg);
339 print("acme: %s\n", msg);
340 return 0;
343 /*
344 void
345 shutdownthread(void *v)
347 char *msg;
348 Channel *c;
350 USED(v);
352 threadsetname("shutdown");
353 c = threadnotechan();
354 while((msg = recvp(c)) != nil)
355 shutdown(nil, msg);
357 */
359 void
360 killprocs(void)
362 Command *c;
364 fsysclose();
365 /* if(display) */
366 /* flushimage(display, 1); */
368 for(c=command; c; c=c->next)
369 postnote(PNGROUP, c->pid, "hangup");
372 static int errorfd;
373 int erroutfd;
375 void
376 acmeerrorproc(void *v)
378 char *buf;
379 int n;
381 USED(v);
382 threadsetname("acmeerrorproc");
383 buf = emalloc(8192+1);
384 while((n=read(errorfd, buf, 8192)) >= 0){
385 buf[n] = '\0';
386 sendp(cerr, estrdup(buf));
390 void
391 acmeerrorinit(void)
393 int pfd[2];
395 if(pipe(pfd) < 0)
396 error("can't create pipe");
397 #if 0
398 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
399 fd = create(acmeerrorfile, OWRITE, 0666);
400 if(fd < 0){
401 remove(acmeerrorfile);
402 fd = create(acmeerrorfile, OWRITE, 0666);
403 if(fd < 0)
404 error("can't create acmeerror file");
406 sprint(buf, "%d", pfd[0]);
407 write(fd, buf, strlen(buf));
408 close(fd);
409 /* reopen pfd[1] close on exec */
410 sprint(buf, "/fd/%d", pfd[1]);
411 errorfd = open(buf, OREAD|OCEXEC);
412 #endif
413 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
414 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
415 erroutfd = pfd[0];
416 errorfd = pfd[1];
417 if(errorfd < 0)
418 error("can't re-open acmeerror file");
419 proccreate(acmeerrorproc, nil, STACK);
422 /*
423 void
424 plumbproc(void *v)
426 Plumbmsg *m;
428 USED(v);
429 threadsetname("plumbproc");
430 for(;;){
431 m = threadplumbrecv(plumbeditfd);
432 if(m == nil)
433 threadexits(nil);
434 sendp(cplumb, m);
437 */
439 void
440 keyboardthread(void *v)
442 Rune r;
443 Timer *timer;
444 Text *t;
445 enum { KTimer, KKey, NKALT };
446 static Alt alts[NKALT+1];
448 USED(v);
449 alts[KTimer].c = nil;
450 alts[KTimer].v = nil;
451 alts[KTimer].op = CHANNOP;
452 alts[KKey].c = keyboardctl->c;
453 alts[KKey].v = &r;
454 alts[KKey].op = CHANRCV;
455 alts[NKALT].op = CHANEND;
457 timer = nil;
458 typetext = nil;
459 threadsetname("keyboardthread");
460 for(;;){
461 switch(alt(alts)){
462 case KTimer:
463 timerstop(timer);
464 t = typetext;
465 if(t!=nil && t->what==Tag){
466 winlock(t->w, 'K');
467 wincommit(t->w, t);
468 winunlock(t->w);
469 flushimage(display, 1);
471 alts[KTimer].c = nil;
472 alts[KTimer].op = CHANNOP;
473 break;
474 case KKey:
475 casekeyboard:
476 typetext = rowtype(&row, r, mouse->xy);
477 t = typetext;
478 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
479 activecol = t->col;
480 if(t!=nil && t->w!=nil)
481 t->w->body.file->curtext = &t->w->body;
482 if(timer != nil)
483 timercancel(timer);
484 if(t!=nil && t->what==Tag) {
485 timer = timerstart(500);
486 alts[KTimer].c = timer->c;
487 alts[KTimer].op = CHANRCV;
488 }else{
489 timer = nil;
490 alts[KTimer].c = nil;
491 alts[KTimer].op = CHANNOP;
493 if(nbrecv(keyboardctl->c, &r) > 0)
494 goto casekeyboard;
495 flushimage(display, 1);
496 break;
501 void
502 mousethread(void *v)
504 Text *t, *argt;
505 int but;
506 uint q0, q1;
507 Window *w;
508 Plumbmsg *pm;
509 Mouse m;
510 char *act;
511 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
512 static Alt alts[NMALT+1];
514 USED(v);
515 threadsetname("mousethread");
516 alts[MResize].c = mousectl->resizec;
517 alts[MResize].v = nil;
518 alts[MResize].op = CHANRCV;
519 alts[MMouse].c = mousectl->c;
520 alts[MMouse].v = &mousectl->m;
521 alts[MMouse].op = CHANRCV;
522 alts[MPlumb].c = cplumb;
523 alts[MPlumb].v = &pm;
524 alts[MPlumb].op = CHANRCV;
525 alts[MWarnings].c = cwarn;
526 alts[MWarnings].v = nil;
527 alts[MWarnings].op = CHANRCV;
528 if(cplumb == nil)
529 alts[MPlumb].op = CHANNOP;
530 alts[NMALT].op = CHANEND;
532 for(;;){
533 qlock(&row.lk);
534 flushwarnings();
535 qunlock(&row.lk);
536 flushimage(display, 1);
537 switch(alt(alts)){
538 case MResize:
539 if(getwindow(display, Refnone) < 0)
540 error("attach to window");
541 draw(screen, screen->r, display->white, nil, ZP);
542 scrlresize();
543 rowresize(&row, screen->clipr);
544 break;
545 case MPlumb:
546 if(strcmp(pm->type, "text") == 0){
547 act = plumblookup(pm->attr, "action");
548 if(act==nil || strcmp(act, "showfile")==0)
549 plumblook(pm);
550 else if(strcmp(act, "showdata")==0)
551 plumbshow(pm);
553 plumbfree(pm);
554 break;
555 case MWarnings:
556 break;
557 case MMouse:
558 /*
559 * Make a copy so decisions are consistent; mousectl changes
560 * underfoot. Can't just receive into m because this introduces
561 * another race; see /sys/src/libdraw/mouse.c.
562 */
563 m = mousectl->m;
564 qlock(&row.lk);
565 t = rowwhich(&row, m.xy);
566 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
567 winlock(mousetext->w, 'M');
568 mousetext->eq0 = ~0;
569 wincommit(mousetext->w, mousetext);
570 winunlock(mousetext->w);
572 mousetext = t;
573 if(t == nil)
574 goto Continue;
575 w = t->w;
576 if(t==nil || m.buttons==0)
577 goto Continue;
578 but = 0;
579 if(m.buttons == 1)
580 but = 1;
581 else if(m.buttons == 2)
582 but = 2;
583 else if(m.buttons == 4)
584 but = 3;
585 barttext = t;
586 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
587 if(but){
588 if(swapscrollbuttons){
589 if(but == 1)
590 but = 3;
591 else if(but == 3)
592 but = 1;
594 winlock(w, 'M');
595 t->eq0 = ~0;
596 textscroll(t, but);
597 winunlock(w);
599 goto Continue;
601 /* scroll buttons, wheels, etc. */
602 if(w != nil && (m.buttons & (8|16))){
603 if(m.buttons & 8)
604 but = Kscrolloneup;
605 else
606 but = Kscrollonedown;
607 winlock(w, 'M');
608 t->eq0 = ~0;
609 texttype(t, but);
610 winunlock(w);
611 goto Continue;
613 if(ptinrect(m.xy, t->scrollr)){
614 if(but){
615 if(t->what == Columntag)
616 rowdragcol(&row, t->col, but);
617 else if(t->what == Tag){
618 coldragwin(t->col, t->w, but);
619 if(t->w)
620 barttext = &t->w->body;
622 if(t->col)
623 activecol = t->col;
625 goto Continue;
627 if(m.buttons){
628 if(w)
629 winlock(w, 'M');
630 t->eq0 = ~0;
631 if(w)
632 wincommit(w, t);
633 else
634 textcommit(t, TRUE);
635 if(m.buttons & 1){
636 textselect(t);
637 if(w)
638 winsettag(w);
639 argtext = t;
640 seltext = t;
641 if(t->col)
642 activecol = t->col; /* button 1 only */
643 if(t->w!=nil && t==&t->w->body)
644 activewin = t->w;
645 }else if(m.buttons & 2){
646 if(textselect2(t, &q0, &q1, &argt))
647 execute(t, q0, q1, FALSE, argt);
648 }else if(m.buttons & 4){
649 if(textselect3(t, &q0, &q1))
650 look3(t, q0, q1, FALSE);
652 if(w)
653 winunlock(w);
654 goto Continue;
656 Continue:
657 qunlock(&row.lk);
658 break;
663 /*
664 * There is a race between process exiting and our finding out it was ever created.
665 * This structure keeps a list of processes that have exited we haven't heard of.
666 */
667 typedef struct Pid Pid;
668 struct Pid
670 int pid;
671 char msg[ERRMAX];
672 Pid *next;
673 };
675 void
676 waitthread(void *v)
678 Waitmsg *w;
679 Command *c, *lc;
680 uint pid;
681 int found, ncmd;
682 Rune *cmd;
683 char *err;
684 Text *t;
685 Pid *pids, *p, *lastp;
686 enum { WErr, WKill, WWait, WCmd, NWALT };
687 Alt alts[NWALT+1];
689 USED(v);
690 threadsetname("waitthread");
691 pids = nil;
692 alts[WErr].c = cerr;
693 alts[WErr].v = &err;
694 alts[WErr].op = CHANRCV;
695 alts[WKill].c = ckill;
696 alts[WKill].v = &cmd;
697 alts[WKill].op = CHANRCV;
698 alts[WWait].c = cwait;
699 alts[WWait].v = &w;
700 alts[WWait].op = CHANRCV;
701 alts[WCmd].c = ccommand;
702 alts[WCmd].v = &c;
703 alts[WCmd].op = CHANRCV;
704 alts[NWALT].op = CHANEND;
706 command = nil;
707 for(;;){
708 switch(alt(alts)){
709 case WErr:
710 qlock(&row.lk);
711 warning(nil, "%s", err);
712 free(err);
713 flushimage(display, 1);
714 qunlock(&row.lk);
715 break;
716 case WKill:
717 found = FALSE;
718 ncmd = runestrlen(cmd);
719 for(c=command; c; c=c->next){
720 /* -1 for blank */
721 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
722 if(postnote(PNGROUP, c->pid, "kill") < 0)
723 warning(nil, "kill %S: %r\n", cmd);
724 found = TRUE;
727 if(!found)
728 warning(nil, "Kill: no process %S\n", cmd);
729 free(cmd);
730 break;
731 case WWait:
732 pid = w->pid;
733 lc = nil;
734 for(c=command; c; c=c->next){
735 if(c->pid == pid){
736 if(lc)
737 lc->next = c->next;
738 else
739 command = c->next;
740 break;
742 lc = c;
744 qlock(&row.lk);
745 t = &row.tag;
746 textcommit(t, TRUE);
747 if(c == nil){
748 /* helper processes use this exit status */
749 if(strncmp(w->msg, "libthread", 9) != 0){
750 p = emalloc(sizeof(Pid));
751 p->pid = pid;
752 strncpy(p->msg, w->msg, sizeof(p->msg));
753 p->next = pids;
754 pids = p;
756 }else{
757 if(search(t, c->name, c->nname)){
758 textdelete(t, t->q0, t->q1, TRUE);
759 textsetselect(t, 0, 0);
761 if(w->msg[0])
762 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
763 flushimage(display, 1);
765 qunlock(&row.lk);
766 free(w);
767 Freecmd:
768 if(c){
769 if(c->iseditcmd)
770 sendul(cedit, 0);
771 free(c->text);
772 free(c->name);
773 fsysdelid(c->md);
774 free(c);
776 break;
777 case WCmd:
778 /* has this command already exited? */
779 lastp = nil;
780 for(p=pids; p!=nil; p=p->next){
781 if(p->pid == c->pid){
782 if(p->msg[0])
783 warning(c->md, "%s\n", p->msg);
784 if(lastp == nil)
785 pids = p->next;
786 else
787 lastp->next = p->next;
788 free(p);
789 goto Freecmd;
791 lastp = p;
793 c->next = command;
794 command = c;
795 qlock(&row.lk);
796 t = &row.tag;
797 textcommit(t, TRUE);
798 textinsert(t, 0, c->name, c->nname, TRUE);
799 textsetselect(t, 0, 0);
800 flushimage(display, 1);
801 qunlock(&row.lk);
802 break;
807 void
808 xfidallocthread(void *v)
810 Xfid *xfree, *x;
811 enum { Alloc, Free, N };
812 static Alt alts[N+1];
814 USED(v);
815 threadsetname("xfidallocthread");
816 alts[Alloc].c = cxfidalloc;
817 alts[Alloc].v = nil;
818 alts[Alloc].op = CHANRCV;
819 alts[Free].c = cxfidfree;
820 alts[Free].v = &x;
821 alts[Free].op = CHANRCV;
822 alts[N].op = CHANEND;
824 xfree = nil;
825 for(;;){
826 switch(alt(alts)){
827 case Alloc:
828 x = xfree;
829 if(x)
830 xfree = x->next;
831 else{
832 x = emalloc(sizeof(Xfid));
833 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
834 chansetname(x->c, "xc%p", x->c);
835 x->arg = x;
836 threadcreate(xfidctl, x->arg, STACK);
838 sendp(cxfidalloc, x);
839 break;
840 case Free:
841 x->next = xfree;
842 xfree = x;
843 break;
848 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
849 void
850 newwindowthread(void *v)
852 Window *w;
854 USED(v);
855 threadsetname("newwindowthread");
857 for(;;){
858 /* only fsysproc is talking to us, so synchronization is trivial */
859 recvp(cnewwindow);
860 w = makenewwindow(nil);
861 winsettag(w);
862 sendp(cnewwindow, w);
866 Reffont*
867 rfget(int fix, int save, int setfont, char *name)
869 Reffont *r;
870 Font *f;
871 int i;
873 r = nil;
874 if(name == nil){
875 name = fontnames[fix];
876 r = reffonts[fix];
878 if(r == nil){
879 for(i=0; i<nfontcache; i++)
880 if(strcmp(name, fontcache[i]->f->name) == 0){
881 r = fontcache[i];
882 goto Found;
884 f = openfont(display, name);
885 if(f == nil){
886 warning(nil, "can't open font file %s: %r\n", name);
887 return nil;
889 r = emalloc(sizeof(Reffont));
890 r->f = f;
891 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
892 fontcache[nfontcache++] = r;
894 Found:
895 if(save){
896 incref(&r->ref);
897 if(reffonts[fix])
898 rfclose(reffonts[fix]);
899 reffonts[fix] = r;
900 if(name != fontnames[fix]){
901 free(fontnames[fix]);
902 fontnames[fix] = estrdup(name);
905 if(setfont){
906 reffont.f = r->f;
907 incref(&r->ref);
908 rfclose(reffonts[0]);
909 font = r->f;
910 reffonts[0] = r;
911 incref(&r->ref);
912 iconinit();
914 incref(&r->ref);
915 return r;
918 void
919 rfclose(Reffont *r)
921 int i;
923 if(decref(&r->ref) == 0){
924 for(i=0; i<nfontcache; i++)
925 if(r == fontcache[i])
926 break;
927 if(i >= nfontcache)
928 warning(nil, "internal error: can't find font in cache\n");
929 else{
930 nfontcache--;
931 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
933 freefont(r->f);
934 free(r);
938 Cursor boxcursor = {
939 {-7, -7},
940 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
941 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
942 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
943 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
944 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
945 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
946 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
947 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
948 };
950 void
951 iconinit(void)
953 Rectangle r;
954 Image *tmp;
956 /* Blue */
957 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
958 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
959 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
960 tagcols[TEXT] = display->black;
961 tagcols[HTEXT] = display->black;
963 /* Yellow */
964 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
965 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
966 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
967 textcols[TEXT] = display->black;
968 textcols[HTEXT] = display->black;
970 if(button){
971 freeimage(button);
972 freeimage(modbutton);
973 freeimage(colbutton);
976 r = Rect(0, 0, Scrollwid+2, font->height+1);
977 button = allocimage(display, r, screen->chan, 0, DNofill);
978 draw(button, r, tagcols[BACK], nil, r.min);
979 r.max.x -= 2;
980 border(button, r, 2, tagcols[BORD], ZP);
982 r = button->r;
983 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
984 draw(modbutton, r, tagcols[BACK], nil, r.min);
985 r.max.x -= 2;
986 border(modbutton, r, 2, tagcols[BORD], ZP);
987 r = insetrect(r, 2);
988 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
989 draw(modbutton, r, tmp, nil, ZP);
990 freeimage(tmp);
992 r = button->r;
993 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
995 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
996 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
999 /*
1000 * /dev/snarf updates when the file is closed, so we must open our own
1001 * fd here rather than use snarffd
1004 /* rio truncates larges snarf buffers, so this avoids using the
1005 * service if the string is huge */
1007 #define MAXSNARF 100*1024
1009 void
1010 acmeputsnarf(void)
1012 int i, n;
1013 Fmt f;
1014 char *s;
1016 if(snarfbuf.nc==0)
1017 return;
1018 if(snarfbuf.nc > MAXSNARF)
1019 return;
1021 fmtstrinit(&f);
1022 for(i=0; i<snarfbuf.nc; i+=n){
1023 n = snarfbuf.nc-i;
1024 if(n >= NSnarf)
1025 n = NSnarf;
1026 bufread(&snarfbuf, i, snarfrune, n);
1027 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1028 break;
1030 s = fmtstrflush(&f);
1031 if(s && s[0])
1032 putsnarf(s);
1033 free(s);
1036 void
1037 acmegetsnarf(void)
1039 char *s;
1040 int nb, nr, nulls, len;
1041 Rune *r;
1043 s = getsnarf();
1044 if(s == nil || s[0]==0){
1045 free(s);
1046 return;
1049 len = strlen(s);
1050 r = runemalloc(len+1);
1051 cvttorunes(s, len, r, &nb, &nr, &nulls);
1052 bufreset(&snarfbuf);
1053 bufinsert(&snarfbuf, 0, r, nr);
1054 free(r);
1055 free(s);
1058 int
1059 ismtpt(char *file)
1061 int n;
1063 if(mtpt == nil)
1064 return 0;
1066 /* This is not foolproof, but it will stop a lot of them. */
1067 n = strlen(mtpt);
1068 return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0);