13 /* for generating syms in mkfile only: */
17 void mousethread(void*);
18 void keyboardthread(void*);
19 void waitthread(void*);
20 void xfidallocthread(void*);
21 void newwindowthread(void*);
22 void plumbproc(void*);
34 NSnarf = 1000 /* less than 1024, I/O buffer size */
36 Rune snarfrune[NSnarf+1];
42 void acmeerrorinit(void);
43 void readfile(Column*, char*);
44 int shutdown(void*, char*);
47 derror(Display*, char *errorstr)
53 threadmain(int argc, char *argv[])
62 rfork(RFENVG|RFNAMEG);
69 globalautoindent = TRUE;
83 fontnames[0] = ARGF();
84 if(fontnames[0] == nil)
88 fontnames[1] = ARGF();
89 if(fontnames[1] == nil)
99 fprint(2, "usage: acme [-ab] [-c ncol] [-f font] [-F fixedfont] [-l loadfile | file...]\n");
103 if(fontnames[0] == nil)
104 fontnames[0] = getenv("font");
105 if(fontnames[0] == nil)
106 fontnames[0] = "/lib/font/bit/vga/unicode.font";
107 if(access(fontnames[0], 0) < 0){
108 fprint(2, "acme: can't access %s: %r\n", fontnames[0]);
111 if(fontnames[1] == nil)
112 fontnames[1] = fontnames[0];
113 fontnames[0] = estrdup(fontnames[0]);
114 fontnames[1] = estrdup(fontnames[1]);
117 cputype = getenv("cputype");
118 objtype = getenv("objtype");
119 home = getenv("home");
120 p = getenv("tabstop");
122 maxtab = strtoul(p, nil, 0);
128 rowloadfonts(loadfile);
129 putenv("font", fontnames[0]);
130 snarffd = open("/dev/snarf", OREAD|OCEXEC);
132 sprint(buf, "/acme/bin/%s", cputype);
133 bind(buf, "/bin", MBEFORE);
135 bind("/acme/bin", "/bin", MBEFORE);
136 getwd(wdir, sizeof wdir);
138 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
139 fprint(2, "acme: can't open display: %r\n");
140 exits("geninitdraw");
143 font = d->defaultfont;
146 reffonts[0] = &reffont;
147 incref(&reffont); /* one to hold up 'font' variable */
148 incref(&reffont); /* one to hold up reffonts[0] */
149 fontcache = emalloc(sizeof(Reffont*));
151 fontcache[0] = &reffont;
157 cwait = threadwaitchan();
158 ccommand = chancreate(sizeof(Command**), 0);
159 ckill = chancreate(sizeof(Rune*), 0);
160 cxfidalloc = chancreate(sizeof(Xfid*), 0);
161 cxfidfree = chancreate(sizeof(Xfid*), 0);
162 cnewwindow = chancreate(sizeof(Channel*), 0);
163 cerr = chancreate(sizeof(char*), 0);
164 cedit = chancreate(sizeof(int), 0);
165 cexit = chancreate(sizeof(int), 0);
166 cwarn = chancreate(sizeof(void*), 1);
167 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
168 fprint(2, "acme: can't create initial channels: %r\n");
169 threadexitsall("channels");
172 mousectl = initmouse(nil, screen);
174 fprint(2, "acme: can't initialize mouse: %r\n");
175 threadexitsall("mouse");
178 keyboardctl = initkeyboard(nil);
179 if(keyboardctl == nil){
180 fprint(2, "acme: can't initialize keyboard: %r\n");
181 threadexitsall("keyboard");
184 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
185 if(plumbeditfd >= 0){
186 cplumb = chancreate(sizeof(Plumbmsg*), 0);
187 proccreate(plumbproc, nil, STACK);
189 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
195 if(!loadfile || !rowload(&row, loadfile, TRUE)){
196 rowinit(&row, screen->clipr);
201 ncol = (argc+(WPERCOL-1))/WPERCOL;
208 for(i=0; i<ncol; i++){
209 c = rowadd(&row, nil, -1);
211 error("initializing columns");
213 c = row.col[row.ncol-1];
217 for(i=0; i<argc; i++){
218 p = utfrrune(argv[i], '/');
219 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
220 readfile(c, argv[i]);
222 readfile(row.col[i/WPERCOL], argv[i]);
225 flushimage(display, 1);
228 threadcreate(keyboardthread, nil, STACK);
229 threadcreate(mousethread, nil, STACK);
230 threadcreate(waitthread, nil, STACK);
231 threadcreate(xfidallocthread, nil, STACK);
232 threadcreate(newwindowthread, nil, STACK);
234 threadnotify(shutdown, 1);
241 readfile(Column *c, char *s)
248 w = coladd(c, nil, nil, -1);
249 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
250 rs = cleanrname((Runestr){rb, nr});
251 winsetname(w, rs.r, rs.nr);
252 textload(&w->body, 0, s, 1);
253 w->body.file->mod = FALSE;
256 textscrdraw(&w->body);
257 textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc);
271 shutdown(void*, char *msg)
276 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
280 for(i=0; oknotes[i]; i++)
281 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
283 print("acme: %s\n", msg);
295 // flushimage(display, 1);
297 for(c=command; c; c=c->next)
298 postnote(PNGROUP, c->pid, "hangup");
299 remove(acmeerrorfile);
305 acmeerrorproc(void *)
310 threadsetname("acmeerrorproc");
311 buf = emalloc(8192+1);
312 while((n=read(errorfd, buf, 8192)) >= 0){
314 sendp(cerr, estrdup(buf));
325 error("can't create pipe");
326 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
327 fd = create(acmeerrorfile, OWRITE, 0666);
329 remove(acmeerrorfile);
330 fd = create(acmeerrorfile, OWRITE, 0666);
332 error("can't create acmeerror file");
334 sprint(buf, "%d", pfd[0]);
335 write(fd, buf, strlen(buf));
337 /* reopen pfd[1] close on exec */
338 sprint(buf, "/fd/%d", pfd[1]);
339 errorfd = open(buf, OREAD|OCEXEC);
341 error("can't re-open acmeerror file");
344 proccreate(acmeerrorproc, nil, STACK);
352 threadsetname("plumbproc");
354 m = plumbrecv(plumbeditfd);
362 keyboardthread(void *)
367 enum { KTimer, KKey, NKALT };
368 static Alt alts[NKALT+1];
370 alts[KTimer].c = nil;
371 alts[KTimer].v = nil;
372 alts[KTimer].op = CHANNOP;
373 alts[KKey].c = keyboardctl->c;
375 alts[KKey].op = CHANRCV;
376 alts[NKALT].op = CHANEND;
380 threadsetname("keyboardthread");
386 if(t!=nil && t->what==Tag){
390 flushimage(display, 1);
392 alts[KTimer].c = nil;
393 alts[KTimer].op = CHANNOP;
397 typetext = rowtype(&row, r, mouse->xy);
399 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
401 if(t!=nil && t->w!=nil)
402 t->w->body.file->curtext = &t->w->body;
405 if(t!=nil && t->what==Tag) {
406 timer = timerstart(500);
407 alts[KTimer].c = timer->c;
408 alts[KTimer].op = CHANRCV;
411 alts[KTimer].c = nil;
412 alts[KTimer].op = CHANNOP;
414 if(nbrecv(keyboardctl->c, &r) > 0)
416 flushimage(display, 1);
432 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
433 static Alt alts[NMALT+1];
435 threadsetname("mousethread");
436 alts[MResize].c = mousectl->resizec;
437 alts[MResize].v = nil;
438 alts[MResize].op = CHANRCV;
439 alts[MMouse].c = mousectl->c;
440 alts[MMouse].v = &mousectl->Mouse;
441 alts[MMouse].op = CHANRCV;
442 alts[MPlumb].c = cplumb;
443 alts[MPlumb].v = ±
444 alts[MPlumb].op = CHANRCV;
445 alts[MWarnings].c = cwarn;
446 alts[MWarnings].v = nil;
447 alts[MWarnings].op = CHANRCV;
449 alts[MPlumb].op = CHANNOP;
450 alts[NMALT].op = CHANEND;
456 flushimage(display, 1);
459 if(getwindow(display, Refnone) < 0)
460 error("attach to window");
462 rowresize(&row, screen->clipr);
465 if(strcmp(pm->type, "text") == 0){
466 act = plumblookup(pm->attr, "action");
467 if(act==nil || strcmp(act, "showfile")==0)
469 else if(strcmp(act, "showdata")==0)
478 * Make a copy so decisions are consistent; mousectl changes
479 * underfoot. Can't just receive into m because this introduces
480 * another race; see /sys/src/libdraw/mouse.c.
484 t = rowwhich(&row, m.xy);
485 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
486 winlock(mousetext->w, 'M');
488 wincommit(mousetext->w, mousetext);
489 winunlock(mousetext->w);
495 if(t==nil || m.buttons==0)
500 else if(m.buttons == 2)
502 else if(m.buttons == 4)
505 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
514 /* scroll buttons, wheels, etc. */
515 if(t->what==Body && w != nil && (m.buttons & (8|16))){
519 but = Kscrollonedown;
526 if(ptinrect(m.xy, t->scrollr)){
528 if(t->what == Columntag)
529 rowdragcol(&row, t->col, but);
530 else if(t->what == Tag){
531 coldragwin(t->col, t->w, but);
533 barttext = &t->w->body;
555 activecol = t->col; /* button 1 only */
556 if(t->w!=nil && t==&t->w->body)
558 }else if(m.buttons & 2){
559 if(textselect2(t, &q0, &q1, &argt))
560 execute(t, q0, q1, FALSE, argt);
561 }else if(m.buttons & 4){
562 if(textselect3(t, &q0, &q1))
563 look3(t, q0, q1, FALSE);
577 * There is a race between process exiting and our finding out it was ever created.
578 * This structure keeps a list of processes that have exited we haven't heard of.
580 typedef struct Pid Pid;
598 Pid *pids, *p, *lastp;
599 enum { WErr, WKill, WWait, WCmd, NWALT };
602 threadsetname("waitthread");
606 alts[WErr].op = CHANRCV;
607 alts[WKill].c = ckill;
608 alts[WKill].v = &cmd;
609 alts[WKill].op = CHANRCV;
610 alts[WWait].c = cwait;
612 alts[WWait].op = CHANRCV;
613 alts[WCmd].c = ccommand;
615 alts[WCmd].op = CHANRCV;
616 alts[NWALT].op = CHANEND;
623 warning(nil, "%s", err);
625 flushimage(display, 1);
630 ncmd = runestrlen(cmd);
631 for(c=command; c; c=c->next){
633 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
634 if(postnote(PNGROUP, c->pid, "kill") < 0)
635 warning(nil, "kill %S: %r\n", cmd);
640 warning(nil, "Kill: no process %S\n", cmd);
646 for(c=command; c; c=c->next){
660 /* helper processes use this exit status */
661 if(strncmp(w->msg, "libthread", 9) != 0){
662 p = emalloc(sizeof(Pid));
664 strncpy(p->msg, w->msg, sizeof(p->msg));
669 if(search(t, c->name, c->nname)){
670 textdelete(t, t->q0, t->q1, TRUE);
671 textsetselect(t, 0, 0);
674 warning(c->md, "%s\n", w->msg);
675 flushimage(display, 1);
690 /* has this command already exited? */
692 for(p=pids; p!=nil; p=p->next){
693 if(p->pid == c->pid){
695 warning(c->md, "%s\n", p->msg);
699 lastp->next = p->next;
710 textinsert(t, 0, c->name, c->nname, TRUE);
711 textsetselect(t, 0, 0);
712 flushimage(display, 1);
720 xfidallocthread(void*)
723 enum { Alloc, Free, N };
724 static Alt alts[N+1];
726 threadsetname("xfidallocthread");
727 alts[Alloc].c = cxfidalloc;
729 alts[Alloc].op = CHANRCV;
730 alts[Free].c = cxfidfree;
732 alts[Free].op = CHANRCV;
733 alts[N].op = CHANEND;
743 x = emalloc(sizeof(Xfid));
744 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
746 threadcreate(xfidctl, x->arg, STACK);
748 sendp(cxfidalloc, x);
758 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
760 newwindowthread(void*)
764 threadsetname("newwindowthread");
767 /* only fsysproc is talking to us, so synchronization is trivial */
769 w = makenewwindow(nil);
771 sendp(cnewwindow, w);
776 rfget(int fix, int save, int setfont, char *name)
784 name = fontnames[fix];
788 for(i=0; i<nfontcache; i++)
789 if(strcmp(name, fontcache[i]->f->name) == 0){
793 f = openfont(display, name);
795 warning(nil, "can't open font file %s: %r\n", name);
798 r = emalloc(sizeof(Reffont));
800 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
801 fontcache[nfontcache++] = r;
807 rfclose(reffonts[fix]);
809 if(name != fontnames[fix]){
810 free(fontnames[fix]);
811 fontnames[fix] = estrdup(name);
817 rfclose(reffonts[0]);
833 for(i=0; i<nfontcache; i++)
834 if(r == fontcache[i])
837 warning(nil, "internal error: can't find font in cache\n");
840 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
849 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
850 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
851 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
852 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
853 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
854 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
855 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
856 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
866 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
867 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
868 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
869 tagcols[TEXT] = display->black;
870 tagcols[HTEXT] = display->black;
873 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
874 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
875 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
876 textcols[TEXT] = display->black;
877 textcols[HTEXT] = display->black;
881 freeimage(modbutton);
882 freeimage(colbutton);
885 r = Rect(0, 0, Scrollwid+2, font->height+1);
886 button = allocimage(display, r, screen->chan, 0, DNofill);
887 draw(button, r, tagcols[BACK], nil, r.min);
889 border(button, r, 2, tagcols[BORD], ZP);
892 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
893 draw(modbutton, r, tagcols[BACK], nil, r.min);
895 border(modbutton, r, 2, tagcols[BORD], ZP);
897 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
898 draw(modbutton, r, tmp, nil, ZP);
902 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
904 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
905 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
909 * /dev/snarf updates when the file is closed, so we must open our own
910 * fd here rather than use snarffd
913 /* rio truncates larges snarf buffers, so this avoids using the
914 * service if the string is huge */
916 #define MAXSNARF 100*1024
923 if(snarffd<0 || snarfbuf.nc==0)
925 if(snarfbuf.nc > MAXSNARF)
927 fd = open("/dev/snarf", OWRITE);
930 for(i=0; i<snarfbuf.nc; i+=n){
934 bufread(&snarfbuf, i, snarfrune, n);
935 if(fprint(fd, "%.*S", n, snarfrune) < 0)
946 if(snarfbuf.nc > MAXSNARF)
952 bufload(&snarfbuf, 0, snarffd, &nulls);