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 <libsec.h>
12 #include "dat.h"
13 #include "fns.h"
14 /* for generating syms in mkfile only: */
15 #include <bio.h>
16 #include "edit.h"
18 void mousethread(void*);
19 void keyboardthread(void*);
20 void waitthread(void*);
21 void xfidallocthread(void*);
22 void newwindowthread(void*);
23 void plumbproc(void*);
24 int timefmt(Fmt*);
26 Reffont **fontcache;
27 int nfontcache;
28 char wdir[512] = ".";
29 Reffont *reffonts[2];
30 int snarffd = -1;
31 int mainpid;
32 int swapscrollbuttons = FALSE;
33 char *mtpt;
35 enum{
36 NSnarf = 1000 /* less than 1024, I/O buffer size */
37 };
38 Rune snarfrune[NSnarf+1];
40 char *fontnames[2] =
41 {
42 "/lib/font/bit/lucsans/euro.8.font",
43 "/lib/font/bit/lucm/unicode.9.font"
44 };
46 Command *command;
48 void shutdownthread(void*);
49 void acmeerrorinit(void);
50 void readfile(Column*, char*);
51 static int shutdown(void*, char*);
53 void
54 derror(Display *d, char *errorstr)
55 {
56 USED(d);
57 error(errorstr);
58 }
60 void
61 threadmain(int argc, char *argv[])
62 {
63 int i;
64 char *p, *loadfile;
65 Column *c;
66 int ncol;
67 Display *d;
69 rfork(RFENVG|RFNAMEG);
71 ncol = -1;
73 loadfile = nil;
74 ARGBEGIN{
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 'm':
110 mtpt = ARGF();
111 if(mtpt == nil)
112 goto Usage;
113 break;
114 case 'r':
115 swapscrollbuttons = TRUE;
116 break;
117 case 'W':
118 winsize = ARGF();
119 if(winsize == nil)
120 goto Usage;
121 break;
122 default:
123 Usage:
124 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
125 threadexitsall("usage");
126 }ARGEND
128 fontnames[0] = estrdup(fontnames[0]);
129 fontnames[1] = estrdup(fontnames[1]);
131 quotefmtinstall();
132 fmtinstall('t', timefmt);
134 cputype = getenv("cputype");
135 objtype = getenv("objtype");
136 home = getenv("HOME");
137 acmeshell = getenv("acmeshell");
138 if(acmeshell && *acmeshell == '\0')
139 acmeshell = nil;
140 p = getenv("tabstop");
141 if(p != nil){
142 maxtab = strtoul(p, nil, 0);
143 free(p);
145 if(maxtab == 0)
146 maxtab = 4;
147 if(loadfile)
148 rowloadfonts(loadfile);
149 putenv("font", fontnames[0]);
150 snarffd = open("/dev/snarf", OREAD|OCEXEC);
151 /*
152 if(cputype){
153 sprint(buf, "/acme/bin/%s", cputype);
154 bind(buf, "/bin", MBEFORE);
156 bind("/acme/bin", "/bin", MBEFORE);
157 */
158 getwd(wdir, sizeof wdir);
160 /*
161 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
162 fprint(2, "acme: can't open display: %r\n");
163 threadexitsall("geninitdraw");
165 */
166 if(initdraw(derror, fontnames[0], "acme") < 0){
167 fprint(2, "acme: can't open display: %r\n");
168 threadexitsall("initdraw");
171 d = display;
172 font = d->defaultfont;
173 /*assert(font); */
175 reffont.f = font;
176 reffonts[0] = &reffont;
177 incref(&reffont.ref); /* one to hold up 'font' variable */
178 incref(&reffont.ref); /* one to hold up reffonts[0] */
179 fontcache = emalloc(sizeof(Reffont*));
180 nfontcache = 1;
181 fontcache[0] = &reffont;
183 iconinit();
184 timerinit();
185 rxinit();
187 cwait = threadwaitchan();
188 ccommand = chancreate(sizeof(Command**), 0);
189 ckill = chancreate(sizeof(Rune*), 0);
190 cxfidalloc = chancreate(sizeof(Xfid*), 0);
191 cxfidfree = chancreate(sizeof(Xfid*), 0);
192 cnewwindow = chancreate(sizeof(Channel*), 0);
193 cerr = chancreate(sizeof(char*), 0);
194 cedit = chancreate(sizeof(int), 0);
195 cexit = chancreate(sizeof(int), 0);
196 cwarn = chancreate(sizeof(void*), 1);
197 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
198 fprint(2, "acme: can't create initial channels: %r\n");
199 threadexitsall("channels");
201 chansetname(ccommand, "ccommand");
202 chansetname(ckill, "ckill");
203 chansetname(cxfidalloc, "cxfidalloc");
204 chansetname(cxfidfree, "cxfidfree");
205 chansetname(cnewwindow, "cnewwindow");
206 chansetname(cerr, "cerr");
207 chansetname(cedit, "cedit");
208 chansetname(cexit, "cexit");
209 chansetname(cwarn, "cwarn");
211 mousectl = initmouse(nil, screen);
212 if(mousectl == nil){
213 fprint(2, "acme: can't initialize mouse: %r\n");
214 threadexitsall("mouse");
216 mouse = &mousectl->m;
217 keyboardctl = initkeyboard(nil);
218 if(keyboardctl == nil){
219 fprint(2, "acme: can't initialize keyboard: %r\n");
220 threadexitsall("keyboard");
222 mainpid = getpid();
223 startplumbing();
224 /*
225 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
226 if(plumbeditfd < 0)
227 fprint(2, "acme: can't initialize plumber: %r\n");
228 else{
229 cplumb = chancreate(sizeof(Plumbmsg*), 0);
230 threadcreate(plumbproc, nil, STACK);
232 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
233 */
235 fsysinit();
237 #define WPERCOL 8
238 disk = diskinit();
239 if(!loadfile || !rowload(&row, loadfile, TRUE)){
240 rowinit(&row, screen->clipr);
241 if(ncol < 0){
242 if(argc == 0)
243 ncol = 2;
244 else{
245 ncol = (argc+(WPERCOL-1))/WPERCOL;
246 if(ncol < 2)
247 ncol = 2;
250 if(ncol == 0)
251 ncol = 2;
252 for(i=0; i<ncol; i++){
253 c = rowadd(&row, nil, -1);
254 if(c==nil && i==0)
255 error("initializing columns");
257 c = row.col[row.ncol-1];
258 if(argc == 0)
259 readfile(c, wdir);
260 else
261 for(i=0; i<argc; i++){
262 p = utfrrune(argv[i], '/');
263 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
264 readfile(c, argv[i]);
265 else
266 readfile(row.col[i/WPERCOL], argv[i]);
269 flushimage(display, 1);
271 acmeerrorinit();
272 threadcreate(keyboardthread, nil, STACK);
273 threadcreate(mousethread, nil, STACK);
274 threadcreate(waitthread, nil, STACK);
275 threadcreate(xfidallocthread, nil, STACK);
276 threadcreate(newwindowthread, nil, STACK);
277 /* threadcreate(shutdownthread, nil, STACK); */
278 threadnotify(shutdown, 1);
279 recvul(cexit);
280 killprocs();
281 threadexitsall(nil);
284 void
285 readfile(Column *c, char *s)
287 Window *w;
288 Rune rb[256];
289 int nr;
290 Runestr rs;
292 w = coladd(c, nil, nil, -1);
293 if(s[0] != '/')
294 runesnprint(rb, sizeof rb, "%s/%s", wdir, s);
295 else
296 runesnprint(rb, sizeof rb, "%s", s);
297 nr = runestrlen(rb);
298 rs = cleanrname(runestr(rb, nr));
299 winsetname(w, rs.r, rs.nr);
300 textload(&w->body, 0, s, 1);
301 w->body.file->mod = FALSE;
302 w->dirty = FALSE;
303 winsettag(w);
304 winresize(w, w->r, FALSE, TRUE);
305 textscrdraw(&w->body);
306 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
307 xfidlog(w, "new");
310 char *ignotes[] = {
311 "sys: write on closed pipe",
312 "sys: ttin",
313 "sys: ttou",
314 "sys: tstp",
315 nil
316 };
318 char *oknotes[] ={
319 "delete",
320 "hangup",
321 "kill",
322 "exit",
323 nil
324 };
326 int dumping;
328 static int
329 shutdown(void *v, char *msg)
331 int i;
333 USED(v);
335 for(i=0; ignotes[i]; i++)
336 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
337 return 1;
339 killprocs();
340 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
341 dumping = TRUE;
342 rowdump(&row, nil);
344 for(i=0; oknotes[i]; i++)
345 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
346 threadexitsall(msg);
347 print("acme: %s\n", msg);
348 return 0;
351 /*
352 void
353 shutdownthread(void *v)
355 char *msg;
356 Channel *c;
358 USED(v);
360 threadsetname("shutdown");
361 c = threadnotechan();
362 while((msg = recvp(c)) != nil)
363 shutdown(nil, msg);
365 */
367 void
368 killprocs(void)
370 Command *c;
372 fsysclose();
373 /* if(display) */
374 /* flushimage(display, 1); */
376 for(c=command; c; c=c->next)
377 postnote(PNGROUP, c->pid, "hangup");
380 static int errorfd;
381 int erroutfd;
383 void
384 acmeerrorproc(void *v)
386 char *buf, *s;
387 int n;
389 USED(v);
390 threadsetname("acmeerrorproc");
391 buf = emalloc(8192+1);
392 while((n=read(errorfd, buf, 8192)) >= 0){
393 buf[n] = '\0';
394 s = estrdup(buf);
395 sendp(cerr, s);
396 free(s);
398 free(buf);
401 void
402 acmeerrorinit(void)
404 int pfd[2];
406 if(pipe(pfd) < 0)
407 error("can't create pipe");
408 #if 0
409 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
410 fd = create(acmeerrorfile, OWRITE, 0666);
411 if(fd < 0){
412 remove(acmeerrorfile);
413 fd = create(acmeerrorfile, OWRITE, 0666);
414 if(fd < 0)
415 error("can't create acmeerror file");
417 sprint(buf, "%d", pfd[0]);
418 write(fd, buf, strlen(buf));
419 close(fd);
420 /* reopen pfd[1] close on exec */
421 sprint(buf, "/fd/%d", pfd[1]);
422 errorfd = open(buf, OREAD|OCEXEC);
423 #endif
424 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
425 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
426 erroutfd = pfd[0];
427 errorfd = pfd[1];
428 if(errorfd < 0)
429 error("can't re-open acmeerror file");
430 proccreate(acmeerrorproc, nil, STACK);
433 /*
434 void
435 plumbproc(void *v)
437 Plumbmsg *m;
439 USED(v);
440 threadsetname("plumbproc");
441 for(;;){
442 m = threadplumbrecv(plumbeditfd);
443 if(m == nil)
444 threadexits(nil);
445 sendp(cplumb, m);
448 */
450 void
451 keyboardthread(void *v)
453 Rune r;
454 Timer *timer;
455 Text *t;
456 enum { KTimer, KKey, NKALT };
457 static Alt alts[NKALT+1];
459 USED(v);
460 alts[KTimer].c = nil;
461 alts[KTimer].v = nil;
462 alts[KTimer].op = CHANNOP;
463 alts[KKey].c = keyboardctl->c;
464 alts[KKey].v = &r;
465 alts[KKey].op = CHANRCV;
466 alts[NKALT].op = CHANEND;
468 timer = nil;
469 typetext = nil;
470 threadsetname("keyboardthread");
471 for(;;){
472 switch(alt(alts)){
473 case KTimer:
474 timerstop(timer);
475 t = typetext;
476 if(t!=nil && t->what==Tag){
477 winlock(t->w, 'K');
478 wincommit(t->w, t);
479 winunlock(t->w);
480 flushimage(display, 1);
482 alts[KTimer].c = nil;
483 alts[KTimer].op = CHANNOP;
484 break;
485 case KKey:
486 casekeyboard:
487 typetext = rowtype(&row, r, mouse->xy);
488 t = typetext;
489 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
490 activecol = t->col;
491 if(t!=nil && t->w!=nil)
492 t->w->body.file->curtext = &t->w->body;
493 if(timer != nil)
494 timercancel(timer);
495 if(t!=nil && t->what==Tag) {
496 timer = timerstart(500);
497 alts[KTimer].c = timer->c;
498 alts[KTimer].op = CHANRCV;
499 }else{
500 timer = nil;
501 alts[KTimer].c = nil;
502 alts[KTimer].op = CHANNOP;
504 if(nbrecv(keyboardctl->c, &r) > 0)
505 goto casekeyboard;
506 flushimage(display, 1);
507 break;
512 void
513 mousethread(void *v)
515 Text *t, *argt;
516 int but;
517 uint q0, q1;
518 Window *w;
519 Plumbmsg *pm;
520 Mouse m;
521 char *act;
522 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
523 static Alt alts[NMALT+1];
525 USED(v);
526 threadsetname("mousethread");
527 alts[MResize].c = mousectl->resizec;
528 alts[MResize].v = nil;
529 alts[MResize].op = CHANRCV;
530 alts[MMouse].c = mousectl->c;
531 alts[MMouse].v = &mousectl->m;
532 alts[MMouse].op = CHANRCV;
533 alts[MPlumb].c = cplumb;
534 alts[MPlumb].v = &pm;
535 alts[MPlumb].op = CHANRCV;
536 alts[MWarnings].c = cwarn;
537 alts[MWarnings].v = nil;
538 alts[MWarnings].op = CHANRCV;
539 if(cplumb == nil)
540 alts[MPlumb].op = CHANNOP;
541 alts[NMALT].op = CHANEND;
543 for(;;){
544 qlock(&row.lk);
545 flushwarnings();
546 qunlock(&row.lk);
547 flushimage(display, 1);
548 switch(alt(alts)){
549 case MResize:
550 if(getwindow(display, Refnone) < 0)
551 error("attach to window");
552 draw(screen, screen->r, display->white, nil, ZP);
553 iconinit();
554 scrlresize();
555 rowresize(&row, screen->clipr);
556 break;
557 case MPlumb:
558 if(strcmp(pm->type, "text") == 0){
559 act = plumblookup(pm->attr, "action");
560 if(act==nil || strcmp(act, "showfile")==0)
561 plumblook(pm);
562 else if(strcmp(act, "showdata")==0)
563 plumbshow(pm);
565 plumbfree(pm);
566 break;
567 case MWarnings:
568 break;
569 case MMouse:
570 /*
571 * Make a copy so decisions are consistent; mousectl changes
572 * underfoot. Can't just receive into m because this introduces
573 * another race; see /sys/src/libdraw/mouse.c.
574 */
575 m = mousectl->m;
576 qlock(&row.lk);
577 t = rowwhich(&row, m.xy);
579 if((t!=mousetext && t!=nil && t->w!=nil) &&
580 (mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
581 xfidlog(t->w, "focus");
584 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
585 winlock(mousetext->w, 'M');
586 mousetext->eq0 = ~0;
587 wincommit(mousetext->w, mousetext);
588 winunlock(mousetext->w);
590 mousetext = t;
591 if(t == nil)
592 goto Continue;
593 w = t->w;
594 if(t==nil || m.buttons==0)
595 goto Continue;
596 but = 0;
597 if(m.buttons == 1)
598 but = 1;
599 else if(m.buttons == 2)
600 but = 2;
601 else if(m.buttons == 4)
602 but = 3;
603 barttext = t;
604 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
605 if(but){
606 if(swapscrollbuttons){
607 if(but == 1)
608 but = 3;
609 else if(but == 3)
610 but = 1;
612 winlock(w, 'M');
613 t->eq0 = ~0;
614 textscroll(t, but);
615 winunlock(w);
617 goto Continue;
619 /* scroll buttons, wheels, etc. */
620 if(w != nil && (m.buttons & (8|16))){
621 if(m.buttons & 8)
622 but = Kscrolloneup;
623 else
624 but = Kscrollonedown;
625 winlock(w, 'M');
626 t->eq0 = ~0;
627 texttype(t, but);
628 winunlock(w);
629 goto Continue;
631 if(ptinrect(m.xy, t->scrollr)){
632 if(but){
633 if(t->what == Columntag)
634 rowdragcol(&row, t->col, but);
635 else if(t->what == Tag){
636 coldragwin(t->col, t->w, but);
637 if(t->w)
638 barttext = &t->w->body;
640 if(t->col)
641 activecol = t->col;
643 goto Continue;
645 if(m.buttons){
646 if(w)
647 winlock(w, 'M');
648 t->eq0 = ~0;
649 if(w)
650 wincommit(w, t);
651 else
652 textcommit(t, TRUE);
653 if(m.buttons & 1){
654 textselect(t);
655 if(w)
656 winsettag(w);
657 argtext = t;
658 seltext = t;
659 if(t->col)
660 activecol = t->col; /* button 1 only */
661 if(t->w!=nil && t==&t->w->body)
662 activewin = t->w;
663 }else if(m.buttons & 2){
664 if(textselect2(t, &q0, &q1, &argt))
665 execute(t, q0, q1, FALSE, argt);
666 }else if(m.buttons & 4){
667 if(textselect3(t, &q0, &q1))
668 look3(t, q0, q1, FALSE);
670 if(w)
671 winunlock(w);
672 goto Continue;
674 Continue:
675 qunlock(&row.lk);
676 break;
681 /*
682 * There is a race between process exiting and our finding out it was ever created.
683 * This structure keeps a list of processes that have exited we haven't heard of.
684 */
685 typedef struct Pid Pid;
686 struct Pid
688 int pid;
689 char msg[ERRMAX];
690 Pid *next;
691 };
693 void
694 waitthread(void *v)
696 Waitmsg *w;
697 Command *c, *lc;
698 uint pid;
699 int found, ncmd;
700 Rune *cmd;
701 char *err;
702 Text *t;
703 Pid *pids, *p, *lastp;
704 enum { WErr, WKill, WWait, WCmd, NWALT };
705 Alt alts[NWALT+1];
707 USED(v);
708 threadsetname("waitthread");
709 pids = nil;
710 alts[WErr].c = cerr;
711 alts[WErr].v = &err;
712 alts[WErr].op = CHANRCV;
713 alts[WKill].c = ckill;
714 alts[WKill].v = &cmd;
715 alts[WKill].op = CHANRCV;
716 alts[WWait].c = cwait;
717 alts[WWait].v = &w;
718 alts[WWait].op = CHANRCV;
719 alts[WCmd].c = ccommand;
720 alts[WCmd].v = &c;
721 alts[WCmd].op = CHANRCV;
722 alts[NWALT].op = CHANEND;
724 command = nil;
725 for(;;){
726 switch(alt(alts)){
727 case WErr:
728 qlock(&row.lk);
729 warning(nil, "%s", err);
730 free(err);
731 flushimage(display, 1);
732 qunlock(&row.lk);
733 break;
734 case WKill:
735 found = FALSE;
736 ncmd = runestrlen(cmd);
737 for(c=command; c; c=c->next){
738 /* -1 for blank */
739 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
740 if(postnote(PNGROUP, c->pid, "kill") < 0)
741 warning(nil, "kill %S: %r\n", cmd);
742 found = TRUE;
745 if(!found)
746 warning(nil, "Kill: no process %S\n", cmd);
747 free(cmd);
748 break;
749 case WWait:
750 pid = w->pid;
751 lc = nil;
752 for(c=command; c; c=c->next){
753 if(c->pid == pid){
754 if(lc)
755 lc->next = c->next;
756 else
757 command = c->next;
758 break;
760 lc = c;
762 qlock(&row.lk);
763 t = &row.tag;
764 textcommit(t, TRUE);
765 if(c == nil){
766 /* helper processes use this exit status */
767 if(strncmp(w->msg, "libthread", 9) != 0){
768 p = emalloc(sizeof(Pid));
769 p->pid = pid;
770 strncpy(p->msg, w->msg, sizeof(p->msg));
771 p->next = pids;
772 pids = p;
774 }else{
775 if(search(t, c->name, c->nname)){
776 textdelete(t, t->q0, t->q1, TRUE);
777 textsetselect(t, 0, 0);
779 if(w->msg[0])
780 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
781 flushimage(display, 1);
783 qunlock(&row.lk);
784 free(w);
785 Freecmd:
786 if(c){
787 if(c->iseditcmd)
788 sendul(cedit, 0);
789 free(c->text);
790 free(c->name);
791 fsysdelid(c->md);
792 free(c);
794 break;
795 case WCmd:
796 /* has this command already exited? */
797 lastp = nil;
798 for(p=pids; p!=nil; p=p->next){
799 if(p->pid == c->pid){
800 if(p->msg[0])
801 warning(c->md, "%s\n", p->msg);
802 if(lastp == nil)
803 pids = p->next;
804 else
805 lastp->next = p->next;
806 free(p);
807 goto Freecmd;
809 lastp = p;
811 c->next = command;
812 command = c;
813 qlock(&row.lk);
814 t = &row.tag;
815 textcommit(t, TRUE);
816 textinsert(t, 0, c->name, c->nname, TRUE);
817 textsetselect(t, 0, 0);
818 flushimage(display, 1);
819 qunlock(&row.lk);
820 break;
825 void
826 xfidallocthread(void *v)
828 Xfid *xfree, *x;
829 enum { Alloc, Free, N };
830 static Alt alts[N+1];
832 USED(v);
833 threadsetname("xfidallocthread");
834 alts[Alloc].c = cxfidalloc;
835 alts[Alloc].v = nil;
836 alts[Alloc].op = CHANRCV;
837 alts[Free].c = cxfidfree;
838 alts[Free].v = &x;
839 alts[Free].op = CHANRCV;
840 alts[N].op = CHANEND;
842 xfree = nil;
843 for(;;){
844 switch(alt(alts)){
845 case Alloc:
846 x = xfree;
847 if(x)
848 xfree = x->next;
849 else{
850 x = emalloc(sizeof(Xfid));
851 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
852 chansetname(x->c, "xc%p", x->c);
853 x->arg = x;
854 threadcreate(xfidctl, x->arg, STACK);
856 sendp(cxfidalloc, x);
857 break;
858 case Free:
859 x->next = xfree;
860 xfree = x;
861 break;
866 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
867 void
868 newwindowthread(void *v)
870 Window *w;
872 USED(v);
873 threadsetname("newwindowthread");
875 for(;;){
876 /* only fsysproc is talking to us, so synchronization is trivial */
877 recvp(cnewwindow);
878 w = makenewwindow(nil);
879 winsettag(w);
880 xfidlog(w, "new");
881 sendp(cnewwindow, w);
885 Reffont*
886 rfget(int fix, int save, int setfont, char *name)
888 Reffont *r;
889 Font *f;
890 int i;
892 r = nil;
893 if(name == nil){
894 name = fontnames[fix];
895 r = reffonts[fix];
897 if(r == nil){
898 for(i=0; i<nfontcache; i++)
899 if(strcmp(name, fontcache[i]->f->name) == 0){
900 r = fontcache[i];
901 goto Found;
903 f = openfont(display, name);
904 if(f == nil){
905 warning(nil, "can't open font file %s: %r\n", name);
906 return nil;
908 r = emalloc(sizeof(Reffont));
909 r->f = f;
910 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
911 fontcache[nfontcache++] = r;
913 Found:
914 if(save){
915 incref(&r->ref);
916 if(reffonts[fix])
917 rfclose(reffonts[fix]);
918 reffonts[fix] = r;
919 if(name != fontnames[fix]){
920 free(fontnames[fix]);
921 fontnames[fix] = estrdup(name);
924 if(setfont){
925 reffont.f = r->f;
926 incref(&r->ref);
927 rfclose(reffonts[0]);
928 font = r->f;
929 reffonts[0] = r;
930 incref(&r->ref);
931 iconinit();
933 incref(&r->ref);
934 return r;
937 void
938 rfclose(Reffont *r)
940 int i;
942 if(decref(&r->ref) == 0){
943 for(i=0; i<nfontcache; i++)
944 if(r == fontcache[i])
945 break;
946 if(i >= nfontcache)
947 warning(nil, "internal error: can't find font in cache\n");
948 else{
949 nfontcache--;
950 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
952 freefont(r->f);
953 free(r);
957 Cursor boxcursor = {
958 {-7, -7},
959 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
960 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
961 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
962 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
963 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
964 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
965 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
966 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
967 };
969 Cursor2 boxcursor2 = {
970 {-15, -15},
971 {0xFF, 0xFF, 0xFF, 0xFF,
972 0xFF, 0xFF, 0xFF, 0xFF,
973 0xFF, 0xFF, 0xFF, 0xFF,
974 0xFF, 0xFF, 0xFF, 0xFF,
975 0xFF, 0xFF, 0xFF, 0xFF,
976 0xFF, 0xFF, 0xFF, 0xFF,
977 0xFF, 0xFF, 0xFF, 0xFF,
978 0xFF, 0xFF, 0xFF, 0xFF,
979 0xFF, 0xFF, 0xFF, 0xFF,
980 0xFF, 0xFF, 0xFF, 0xFF,
981 0xFF, 0xC0, 0x03, 0xFF,
982 0xFF, 0xC0, 0x03, 0xFF,
983 0xFF, 0xC0, 0x03, 0xFF,
984 0xFF, 0xC0, 0x03, 0xFF,
985 0xFF, 0xC0, 0x03, 0xFF,
986 0xFF, 0xC0, 0x03, 0xFF,
987 0xFF, 0xC0, 0x03, 0xFF,
988 0xFF, 0xC0, 0x03, 0xFF,
989 0xFF, 0xC0, 0x03, 0xFF,
990 0xFF, 0xC0, 0x03, 0xFF,
991 0xFF, 0xC0, 0x03, 0xFF,
992 0xFF, 0xC0, 0x03, 0xFF,
993 0xFF, 0xFF, 0xFF, 0xFF,
994 0xFF, 0xFF, 0xFF, 0xFF,
995 0xFF, 0xFF, 0xFF, 0xFF,
996 0xFF, 0xFF, 0xFF, 0xFF,
997 0xFF, 0xFF, 0xFF, 0xFF,
998 0xFF, 0xFF, 0xFF, 0xFF,
999 0xFF, 0xFF, 0xFF, 0xFF,
1000 0xFF, 0xFF, 0xFF, 0xFF,
1001 0xFF, 0xFF, 0xFF, 0xFF,
1002 0xFF, 0xFF, 0xFF, 0xFF},
1003 {0x00, 0x00, 0x00, 0x00,
1004 0x00, 0x00, 0x00, 0x00,
1005 0x3F, 0xFF, 0xFF, 0xFC,
1006 0x3F, 0xFF, 0xFF, 0xFC,
1007 0x3F, 0xFF, 0xFF, 0xFC,
1008 0x3F, 0xFF, 0xFF, 0xFC,
1009 0x3F, 0xFF, 0xFF, 0xFC,
1010 0x3F, 0xFF, 0xFF, 0xFC,
1011 0x3F, 0x00, 0x00, 0xFC,
1012 0x3F, 0x00, 0x00, 0xFC,
1013 0x3F, 0x00, 0x00, 0xFC,
1014 0x3F, 0x00, 0x00, 0xFC,
1015 0x3F, 0x00, 0x00, 0xFC,
1016 0x3F, 0x00, 0x00, 0xFC,
1017 0x3F, 0x00, 0x00, 0xFC,
1018 0x3F, 0x00, 0x00, 0xFC,
1019 0x3F, 0x00, 0x00, 0xFC,
1020 0x3F, 0x00, 0x00, 0xFC,
1021 0x3F, 0x00, 0x00, 0xFC,
1022 0x3F, 0x00, 0x00, 0xFC,
1023 0x3F, 0x00, 0x00, 0xFC,
1024 0x3F, 0x00, 0x00, 0xFC,
1025 0x3F, 0x00, 0x00, 0xFC,
1026 0x3F, 0x00, 0x00, 0xFC,
1027 0x3F, 0xFF, 0xFF, 0xFC,
1028 0x3F, 0xFF, 0xFF, 0xFC,
1029 0x3F, 0xFF, 0xFF, 0xFC,
1030 0x3F, 0xFF, 0xFF, 0xFC,
1031 0x3F, 0xFF, 0xFF, 0xFC,
1032 0x3F, 0xFF, 0xFF, 0xFC,
1033 0x00, 0x00, 0x00, 0x00,
1034 0x00, 0x00, 0x00, 0x00}
1037 void
1038 iconinit(void)
1040 Rectangle r;
1041 Image *tmp;
1043 if(tagcols[BACK] == nil) {
1044 /* Blue */
1045 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
1046 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
1047 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
1048 tagcols[TEXT] = display->black;
1049 tagcols[HTEXT] = display->black;
1051 /* Yellow */
1052 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
1053 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
1054 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
1055 textcols[TEXT] = display->black;
1056 textcols[HTEXT] = display->black;
1059 r = Rect(0, 0, Scrollwid+ButtonBorder, font->height+1);
1060 if(button && eqrect(r, button->r))
1061 return;
1063 if(button){
1064 freeimage(button);
1065 freeimage(modbutton);
1066 freeimage(colbutton);
1069 button = allocimage(display, r, screen->chan, 0, DNofill);
1070 draw(button, r, tagcols[BACK], nil, r.min);
1071 r.max.x -= ButtonBorder;
1072 border(button, r, ButtonBorder, tagcols[BORD], ZP);
1074 r = button->r;
1075 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
1076 draw(modbutton, r, tagcols[BACK], nil, r.min);
1077 r.max.x -= ButtonBorder;
1078 border(modbutton, r, ButtonBorder, tagcols[BORD], ZP);
1079 r = insetrect(r, ButtonBorder);
1080 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
1081 draw(modbutton, r, tmp, nil, ZP);
1082 freeimage(tmp);
1084 r = button->r;
1085 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
1087 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
1088 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
1092 * /dev/snarf updates when the file is closed, so we must open our own
1093 * fd here rather than use snarffd
1096 /* rio truncates larges snarf buffers, so this avoids using the
1097 * service if the string is huge */
1099 #define MAXSNARF 100*1024
1101 void
1102 acmeputsnarf(void)
1104 int i, n;
1105 Fmt f;
1106 char *s;
1108 if(snarfbuf.nc==0)
1109 return;
1110 if(snarfbuf.nc > MAXSNARF)
1111 return;
1113 fmtstrinit(&f);
1114 for(i=0; i<snarfbuf.nc; i+=n){
1115 n = snarfbuf.nc-i;
1116 if(n >= NSnarf)
1117 n = NSnarf;
1118 bufread(&snarfbuf, i, snarfrune, n);
1119 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1120 break;
1122 s = fmtstrflush(&f);
1123 if(s && s[0])
1124 putsnarf(s);
1125 free(s);
1128 void
1129 acmegetsnarf(void)
1131 char *s;
1132 int nb, nr, nulls, len;
1133 Rune *r;
1135 s = getsnarf();
1136 if(s == nil || s[0]==0){
1137 free(s);
1138 return;
1141 len = strlen(s);
1142 r = runemalloc(len+1);
1143 cvttorunes(s, len, r, &nb, &nr, &nulls);
1144 bufreset(&snarfbuf);
1145 bufinsert(&snarfbuf, 0, r, nr);
1146 free(r);
1147 free(s);
1150 int
1151 ismtpt(char *file)
1153 int n;
1155 if(mtpt == nil)
1156 return 0;
1158 /* This is not foolproof, but it will stop a lot of them. */
1159 n = strlen(mtpt);
1160 return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0);
1163 int
1164 timefmt(Fmt *f)
1166 Tm *tm;
1168 tm = localtime(va_arg(f->args, ulong));
1169 return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d",
1170 tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);