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*);
30 int swapscrollbuttons = FALSE;
33 NSnarf = 1000 /* less than 1024, I/O buffer size */
35 Rune snarfrune[NSnarf+1];
39 "/lib/font/bit/lucidasans/euro.8.font",
40 "/lib/font/bit/lucm/unicode.9.font",
45 void shutdownthread(void*);
46 void acmeerrorinit(void);
47 void readfile(Column*, char*);
48 static int shutdown(void*, char*);
51 derror(Display *d, char *errorstr)
58 threadmain(int argc, char *argv[])
66 rfork(RFENVG|RFNAMEG);
76 {extern int _threaddebuglevel;
77 _threaddebuglevel = ~0;
81 globalautoindent = TRUE;
95 fontnames[0] = ARGF();
96 if(fontnames[0] == nil)
100 fontnames[1] = ARGF();
101 if(fontnames[1] == nil)
110 swapscrollbuttons = TRUE;
119 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
120 threadexitsall("usage");
123 fontnames[0] = estrdup(fontnames[0]);
124 fontnames[1] = estrdup(fontnames[1]);
127 cputype = getenv("cputype");
128 objtype = getenv("objtype");
129 home = getenv("HOME");
130 p = getenv("tabstop");
132 maxtab = strtoul(p, nil, 0);
138 rowloadfonts(loadfile);
139 putenv("font", fontnames[0]);
140 snarffd = open("/dev/snarf", OREAD|OCEXEC);
143 sprint(buf, "/acme/bin/%s", cputype);
144 bind(buf, "/bin", MBEFORE);
146 bind("/acme/bin", "/bin", MBEFORE);
148 getwd(wdir, sizeof wdir);
151 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
152 fprint(2, "acme: can't open display: %r\n");
153 threadexitsall("geninitdraw");
156 if(initdraw(derror, fontnames[0], "acme") < 0){
157 fprint(2, "acme: can't open display: %r\n");
158 threadexitsall("initdraw");
162 font = d->defaultfont;
166 reffonts[0] = &reffont;
167 incref(&reffont.ref); /* one to hold up 'font' variable */
168 incref(&reffont.ref); /* one to hold up reffonts[0] */
169 fontcache = emalloc(sizeof(Reffont*));
171 fontcache[0] = &reffont;
177 cwait = threadwaitchan();
178 ccommand = chancreate(sizeof(Command**), 0);
179 chansetname(ccommand, "ccommand");
180 ckill = chancreate(sizeof(Rune*), 0);
181 chansetname(ckill, "ckill");
182 cxfidalloc = chancreate(sizeof(Xfid*), 0);
183 chansetname(cxfidalloc, "cxfidalloc");
184 cxfidfree = chancreate(sizeof(Xfid*), 0);
185 chansetname(cxfidfree, "cxfidfree");
186 cnewwindow = chancreate(sizeof(Channel*), 0);
187 chansetname(cnewwindow, "cnewwindow");
188 cerr = chancreate(sizeof(char*), 0);
189 chansetname(cerr, "cerr");
190 cedit = chancreate(sizeof(int), 0);
191 chansetname(cedit, "cedit");
192 cexit = chancreate(sizeof(int), 0);
193 chansetname(cexit, "cexit");
194 cwarn = chancreate(sizeof(void*), 1);
195 chansetname(cwarn, "cwarn");
196 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
197 fprint(2, "acme: can't create initial channels: %r\n");
198 threadexitsall("channels");
201 mousectl = initmouse(nil, screen);
203 fprint(2, "acme: can't initialize mouse: %r\n");
204 threadexitsall("mouse");
206 mouse = &mousectl->m;
207 keyboardctl = initkeyboard(nil);
208 if(keyboardctl == nil){
209 fprint(2, "acme: can't initialize keyboard: %r\n");
210 threadexitsall("keyboard");
215 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
217 fprint(2, "acme: can't initialize plumber: %r\n");
219 cplumb = chancreate(sizeof(Plumbmsg*), 0);
220 threadcreate(plumbproc, nil, STACK);
222 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
229 if(!loadfile || !rowload(&row, loadfile, TRUE)){
230 rowinit(&row, screen->clipr);
235 ncol = (argc+(WPERCOL-1))/WPERCOL;
242 for(i=0; i<ncol; i++){
243 c = rowadd(&row, nil, -1);
245 error("initializing columns");
247 c = row.col[row.ncol-1];
251 for(i=0; i<argc; i++){
252 p = utfrrune(argv[i], '/');
253 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
254 readfile(c, argv[i]);
256 readfile(row.col[i/WPERCOL], argv[i]);
259 flushimage(display, 1);
262 threadcreate(keyboardthread, nil, STACK);
263 threadcreate(mousethread, nil, STACK);
264 threadcreate(waitthread, nil, STACK);
265 threadcreate(xfidallocthread, nil, STACK);
266 threadcreate(newwindowthread, nil, STACK);
267 /* threadcreate(shutdownthread, nil, STACK); */
268 threadnotify(shutdown, 1);
275 readfile(Column *c, char *s)
282 w = coladd(c, nil, nil, -1);
283 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
284 rs = cleanrname(runestr(rb, nr));
285 winsetname(w, rs.r, rs.nr);
286 textload(&w->body, 0, s, 1);
287 w->body.file->mod = FALSE;
290 winresize(w, w->r, FALSE, TRUE);
291 textscrdraw(&w->body);
292 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
296 "sys: write on closed pipe",
314 shutdown(void *v, char *msg)
320 for(i=0; ignotes[i]; i++)
321 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
325 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
329 for(i=0; oknotes[i]; i++)
330 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
332 print("acme: %s\n", msg);
338 shutdownthread(void *v)
345 threadsetname("shutdown");
346 c = threadnotechan();
347 while((msg = recvp(c)) != nil)
359 // flushimage(display, 1);
361 for(c=command; c; c=c->next)
362 postnote(PNGROUP, c->pid, "hangup");
369 acmeerrorproc(void *v)
375 threadsetname("acmeerrorproc");
376 buf = emalloc(8192+1);
377 while((n=read(errorfd, buf, 8192)) >= 0){
379 sendp(cerr, estrdup(buf));
389 error("can't create pipe");
391 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
392 fd = create(acmeerrorfile, OWRITE, 0666);
394 remove(acmeerrorfile);
395 fd = create(acmeerrorfile, OWRITE, 0666);
397 error("can't create acmeerror file");
399 sprint(buf, "%d", pfd[0]);
400 write(fd, buf, strlen(buf));
402 /* reopen pfd[1] close on exec */
403 sprint(buf, "/fd/%d", pfd[1]);
404 errorfd = open(buf, OREAD|OCEXEC);
406 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
407 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
411 error("can't re-open acmeerror file");
412 proccreate(acmeerrorproc, nil, STACK);
422 threadsetname("plumbproc");
424 m = threadplumbrecv(plumbeditfd);
433 keyboardthread(void *v)
438 enum { KTimer, KKey, NKALT };
439 static Alt alts[NKALT+1];
442 alts[KTimer].c = nil;
443 alts[KTimer].v = nil;
444 alts[KTimer].op = CHANNOP;
445 alts[KKey].c = keyboardctl->c;
447 alts[KKey].op = CHANRCV;
448 alts[NKALT].op = CHANEND;
452 threadsetname("keyboardthread");
458 if(t!=nil && t->what==Tag){
462 flushimage(display, 1);
464 alts[KTimer].c = nil;
465 alts[KTimer].op = CHANNOP;
469 typetext = rowtype(&row, r, mouse->xy);
471 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
473 if(t!=nil && t->w!=nil)
474 t->w->body.file->curtext = &t->w->body;
477 if(t!=nil && t->what==Tag) {
478 timer = timerstart(500);
479 alts[KTimer].c = timer->c;
480 alts[KTimer].op = CHANRCV;
483 alts[KTimer].c = nil;
484 alts[KTimer].op = CHANNOP;
486 if(nbrecv(keyboardctl->c, &r) > 0)
488 flushimage(display, 1);
504 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
505 static Alt alts[NMALT+1];
508 threadsetname("mousethread");
509 alts[MResize].c = mousectl->resizec;
510 alts[MResize].v = nil;
511 alts[MResize].op = CHANRCV;
512 alts[MMouse].c = mousectl->c;
513 alts[MMouse].v = &mousectl->m;
514 alts[MMouse].op = CHANRCV;
515 alts[MPlumb].c = cplumb;
516 alts[MPlumb].v = ±
517 alts[MPlumb].op = CHANRCV;
518 alts[MWarnings].c = cwarn;
519 alts[MWarnings].v = nil;
520 alts[MWarnings].op = CHANRCV;
522 alts[MPlumb].op = CHANNOP;
523 alts[NMALT].op = CHANEND;
529 flushimage(display, 1);
532 if(getwindow(display, Refnone) < 0)
533 error("attach to window");
534 draw(screen, screen->r, display->white, nil, ZP);
536 rowresize(&row, screen->clipr);
539 if(strcmp(pm->type, "text") == 0){
540 act = plumblookup(pm->attr, "action");
541 if(act==nil || strcmp(act, "showfile")==0)
543 else if(strcmp(act, "showdata")==0)
552 * Make a copy so decisions are consistent; mousectl changes
553 * underfoot. Can't just receive into m because this introduces
554 * another race; see /sys/src/libdraw/mouse.c.
558 t = rowwhich(&row, m.xy);
559 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
560 winlock(mousetext->w, 'M');
562 wincommit(mousetext->w, mousetext);
563 winunlock(mousetext->w);
569 if(t==nil || m.buttons==0)
574 else if(m.buttons == 2)
576 else if(m.buttons == 4)
579 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
581 if(swapscrollbuttons){
594 /* scroll buttons, wheels, etc. */
596 * TAG used to require t->what==Body but now allow
597 * scroll wheel in tag too.
599 if(w != nil && (m.buttons & (8|16))){
603 but = Kscrollonedown;
610 if(ptinrect(m.xy, t->scrollr)){
612 if(t->what == Columntag)
613 rowdragcol(&row, t->col, but);
614 else if(t->what == Tag){
615 coldragwin(t->col, t->w, but);
617 barttext = &t->w->body;
639 activecol = t->col; /* button 1 only */
640 if(t->w!=nil && t==&t->w->body)
642 }else if(m.buttons & 2){
643 if(textselect2(t, &q0, &q1, &argt))
644 execute(t, q0, q1, FALSE, argt);
645 }else if(m.buttons & 4){
646 if(textselect3(t, &q0, &q1))
647 look3(t, q0, q1, FALSE);
661 * There is a race between process exiting and our finding out it was ever created.
662 * This structure keeps a list of processes that have exited we haven't heard of.
664 typedef struct Pid Pid;
682 Pid *pids, *p, *lastp;
683 enum { WErr, WKill, WWait, WCmd, NWALT };
687 threadsetname("waitthread");
691 alts[WErr].op = CHANRCV;
692 alts[WKill].c = ckill;
693 alts[WKill].v = &cmd;
694 alts[WKill].op = CHANRCV;
695 alts[WWait].c = cwait;
697 alts[WWait].op = CHANRCV;
698 alts[WCmd].c = ccommand;
700 alts[WCmd].op = CHANRCV;
701 alts[NWALT].op = CHANEND;
708 warning(nil, "%s", err);
710 flushimage(display, 1);
715 ncmd = runestrlen(cmd);
716 for(c=command; c; c=c->next){
718 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
719 if(postnote(PNGROUP, c->pid, "kill") < 0)
720 warning(nil, "kill %S: %r\n", cmd);
725 warning(nil, "Kill: no process %S\n", cmd);
731 for(c=command; c; c=c->next){
745 /* helper processes use this exit status */
746 if(strncmp(w->msg, "libthread", 9) != 0){
747 p = emalloc(sizeof(Pid));
749 strncpy(p->msg, w->msg, sizeof(p->msg));
754 if(search(t, c->name, c->nname)){
755 textdelete(t, t->q0, t->q1, TRUE);
756 textsetselect(t, 0, 0);
759 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
760 flushimage(display, 1);
775 /* has this command already exited? */
777 for(p=pids; p!=nil; p=p->next){
778 if(p->pid == c->pid){
780 warning(c->md, "%s\n", p->msg);
784 lastp->next = p->next;
795 textinsert(t, 0, c->name, c->nname, TRUE);
796 textsetselect(t, 0, 0);
797 flushimage(display, 1);
805 xfidallocthread(void *v)
808 enum { Alloc, Free, N };
809 static Alt alts[N+1];
812 threadsetname("xfidallocthread");
813 alts[Alloc].c = cxfidalloc;
815 alts[Alloc].op = CHANRCV;
816 alts[Free].c = cxfidfree;
818 alts[Free].op = CHANRCV;
819 alts[N].op = CHANEND;
829 x = emalloc(sizeof(Xfid));
830 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
831 chansetname(x->c, "xc%p", x->c);
833 threadcreate(xfidctl, x->arg, STACK);
835 sendp(cxfidalloc, x);
845 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
847 newwindowthread(void *v)
852 threadsetname("newwindowthread");
855 /* only fsysproc is talking to us, so synchronization is trivial */
857 w = makenewwindow(nil);
859 sendp(cnewwindow, w);
864 rfget(int fix, int save, int setfont, char *name)
872 name = fontnames[fix];
876 for(i=0; i<nfontcache; i++)
877 if(strcmp(name, fontcache[i]->f->name) == 0){
881 f = openfont(display, name);
883 fprint(2, "can't open font file %s: %r\n", name);
884 warning(nil, "can't open font file %s: %r\n", name);
887 r = emalloc(sizeof(Reffont));
889 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
890 fontcache[nfontcache++] = r;
896 rfclose(reffonts[fix]);
898 if(fontnames[fix] != name){
899 free(fontnames[fix]);
900 fontnames[fix] = estrdup(name);
906 rfclose(reffonts[0]);
921 if(decref(&r->ref) == 0){
922 for(i=0; i<nfontcache; i++)
923 if(r == fontcache[i])
926 warning(nil, "internal error: can't find font in cache\n");
929 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
938 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
939 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
940 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
941 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
942 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
943 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
944 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
945 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
955 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
956 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
957 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
958 tagcols[TEXT] = display->black;
959 tagcols[HTEXT] = display->black;
962 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
963 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
964 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
965 textcols[TEXT] = display->black;
966 textcols[HTEXT] = display->black;
970 freeimage(modbutton);
971 freeimage(colbutton);
974 r = Rect(0, 0, Scrollwid+2, font->height+1);
975 button = allocimage(display, r, screen->chan, 0, DNofill);
976 draw(button, r, tagcols[BACK], nil, r.min);
978 border(button, r, 2, tagcols[BORD], ZP);
981 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
982 draw(modbutton, r, tagcols[BACK], nil, r.min);
984 border(modbutton, r, 2, tagcols[BORD], ZP);
986 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
987 draw(modbutton, r, tmp, nil, ZP);
991 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
993 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
994 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
998 * /dev/snarf updates when the file is closed, so we must open our own
999 * fd here rather than use snarffd
1002 /* rio truncates larges snarf buffers, so this avoids using the
1003 * service if the string is huge */
1005 #define MAXSNARF 100*1024
1016 if(snarfbuf.nc > MAXSNARF)
1020 for(i=0; i<snarfbuf.nc; i+=n){
1024 bufread(&snarfbuf, i, snarfrune, n);
1025 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1028 s = fmtstrflush(&f);
1038 int nb, nr, nulls, len;
1042 if(s == nil || s[0]==0){
1048 r = runemalloc(len+1);
1049 cvttorunes(s, len, r, &nb, &nr, &nulls);
1050 bufreset(&snarfbuf);
1051 bufinsert(&snarfbuf, 0, r, nr);