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