| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <thread.h> |
| #include <cursor.h> |
| #include <mouse.h> |
| #include <keyboard.h> |
| #include <frame.h> |
| #include <fcall.h> |
| #include <plumb.h> |
| #include "dat.h" |
| #include "fns.h" |
| /* for generating syms in mkfile only: */ |
| #include <bio.h> |
| #include "edit.h" |
| |
| void mousethread(void*); |
| void keyboardthread(void*); |
| void waitthread(void*); |
| void xfidallocthread(void*); |
| void newwindowthread(void*); |
| void plumbproc(void*); |
| int timefmt(Fmt*); |
| |
| Reffont **fontcache; |
| int nfontcache; |
| char wdir[512] = "."; |
| Reffont *reffonts[2]; |
| int snarffd = -1; |
| int mainpid; |
| int swapscrollbuttons = FALSE; |
| char *mtpt; |
| |
| enum{ |
| NSnarf = 1000 /* less than 1024, I/O buffer size */ |
| }; |
| Rune snarfrune[NSnarf+1]; |
| |
| char *fontnames[2] = |
| { |
| "/lib/font/bit/lucsans/euro.8.font", |
| "/lib/font/bit/lucm/unicode.9.font" |
| }; |
| |
| Command *command; |
| |
| void shutdownthread(void*); |
| void acmeerrorinit(void); |
| void readfile(Column*, char*); |
| static int shutdown(void*, char*); |
| |
| void |
| derror(Display *d, char *errorstr) |
| { |
| USED(d); |
| error(errorstr); |
| } |
| |
| void |
| threadmain(int argc, char *argv[]) |
| { |
| int i; |
| char *p, *loadfile; |
| Column *c; |
| int ncol; |
| Display *d; |
| |
| rfork(RFENVG|RFNAMEG); |
| |
| ncol = -1; |
| |
| loadfile = nil; |
| ARGBEGIN{ |
| case 'D': |
| {extern int _threaddebuglevel; |
| _threaddebuglevel = ~0; |
| } |
| break; |
| case 'a': |
| globalautoindent = TRUE; |
| break; |
| case 'b': |
| bartflag = TRUE; |
| break; |
| case 'c': |
| p = ARGF(); |
| if(p == nil) |
| goto Usage; |
| ncol = atoi(p); |
| if(ncol <= 0) |
| goto Usage; |
| break; |
| case 'f': |
| fontnames[0] = ARGF(); |
| if(fontnames[0] == nil) |
| goto Usage; |
| break; |
| case 'F': |
| fontnames[1] = ARGF(); |
| if(fontnames[1] == nil) |
| goto Usage; |
| break; |
| case 'l': |
| loadfile = ARGF(); |
| if(loadfile == nil) |
| goto Usage; |
| break; |
| case 'm': |
| mtpt = ARGF(); |
| if(mtpt == nil) |
| goto Usage; |
| break; |
| case 'r': |
| swapscrollbuttons = TRUE; |
| break; |
| case 'W': |
| winsize = ARGF(); |
| if(winsize == nil) |
| goto Usage; |
| break; |
| default: |
| Usage: |
| fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n"); |
| threadexitsall("usage"); |
| }ARGEND |
| |
| fontnames[0] = estrdup(fontnames[0]); |
| fontnames[1] = estrdup(fontnames[1]); |
| |
| quotefmtinstall(); |
| fmtinstall('t', timefmt); |
| |
| cputype = getenv("cputype"); |
| objtype = getenv("objtype"); |
| home = getenv("HOME"); |
| acmeshell = getenv("acmeshell"); |
| if(acmeshell && *acmeshell == '\0') |
| acmeshell = nil; |
| p = getenv("tabstop"); |
| if(p != nil){ |
| maxtab = strtoul(p, nil, 0); |
| free(p); |
| } |
| if(maxtab == 0) |
| maxtab = 4; |
| if(loadfile) |
| rowloadfonts(loadfile); |
| putenv("font", fontnames[0]); |
| snarffd = open("/dev/snarf", OREAD|OCEXEC); |
| /* |
| if(cputype){ |
| sprint(buf, "/acme/bin/%s", cputype); |
| bind(buf, "/bin", MBEFORE); |
| } |
| bind("/acme/bin", "/bin", MBEFORE); |
| */ |
| getwd(wdir, sizeof wdir); |
| |
| /* |
| if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){ |
| fprint(2, "acme: can't open display: %r\n"); |
| threadexitsall("geninitdraw"); |
| } |
| */ |
| if(initdraw(derror, fontnames[0], "acme") < 0){ |
| fprint(2, "acme: can't open display: %r\n"); |
| threadexitsall("initdraw"); |
| } |
| |
| d = display; |
| font = d->defaultfont; |
| /*assert(font); */ |
| |
| reffont.f = font; |
| reffonts[0] = &reffont; |
| incref(&reffont.ref); /* one to hold up 'font' variable */ |
| incref(&reffont.ref); /* one to hold up reffonts[0] */ |
| fontcache = emalloc(sizeof(Reffont*)); |
| nfontcache = 1; |
| fontcache[0] = &reffont; |
| |
| iconinit(); |
| timerinit(); |
| rxinit(); |
| |
| cwait = threadwaitchan(); |
| ccommand = chancreate(sizeof(Command**), 0); |
| ckill = chancreate(sizeof(Rune*), 0); |
| cxfidalloc = chancreate(sizeof(Xfid*), 0); |
| cxfidfree = chancreate(sizeof(Xfid*), 0); |
| cnewwindow = chancreate(sizeof(Channel*), 0); |
| cerr = chancreate(sizeof(char*), 0); |
| cedit = chancreate(sizeof(int), 0); |
| cexit = chancreate(sizeof(int), 0); |
| cwarn = chancreate(sizeof(void*), 1); |
| if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){ |
| fprint(2, "acme: can't create initial channels: %r\n"); |
| threadexitsall("channels"); |
| } |
| chansetname(ccommand, "ccommand"); |
| chansetname(ckill, "ckill"); |
| chansetname(cxfidalloc, "cxfidalloc"); |
| chansetname(cxfidfree, "cxfidfree"); |
| chansetname(cnewwindow, "cnewwindow"); |
| chansetname(cerr, "cerr"); |
| chansetname(cedit, "cedit"); |
| chansetname(cexit, "cexit"); |
| chansetname(cwarn, "cwarn"); |
| |
| mousectl = initmouse(nil, screen); |
| if(mousectl == nil){ |
| fprint(2, "acme: can't initialize mouse: %r\n"); |
| threadexitsall("mouse"); |
| } |
| mouse = &mousectl->m; |
| keyboardctl = initkeyboard(nil); |
| if(keyboardctl == nil){ |
| fprint(2, "acme: can't initialize keyboard: %r\n"); |
| threadexitsall("keyboard"); |
| } |
| mainpid = getpid(); |
| startplumbing(); |
| /* |
| plumbeditfd = plumbopen("edit", OREAD|OCEXEC); |
| if(plumbeditfd < 0) |
| fprint(2, "acme: can't initialize plumber: %r\n"); |
| else{ |
| cplumb = chancreate(sizeof(Plumbmsg*), 0); |
| threadcreate(plumbproc, nil, STACK); |
| } |
| plumbsendfd = plumbopen("send", OWRITE|OCEXEC); |
| */ |
| |
| fsysinit(); |
| |
| #define WPERCOL 8 |
| disk = diskinit(); |
| if(!loadfile || !rowload(&row, loadfile, TRUE)){ |
| rowinit(&row, screen->clipr); |
| if(ncol < 0){ |
| if(argc == 0) |
| ncol = 2; |
| else{ |
| ncol = (argc+(WPERCOL-1))/WPERCOL; |
| if(ncol < 2) |
| ncol = 2; |
| } |
| } |
| if(ncol == 0) |
| ncol = 2; |
| for(i=0; i<ncol; i++){ |
| c = rowadd(&row, nil, -1); |
| if(c==nil && i==0) |
| error("initializing columns"); |
| } |
| c = row.col[row.ncol-1]; |
| if(argc == 0) |
| readfile(c, wdir); |
| else |
| for(i=0; i<argc; i++){ |
| p = utfrrune(argv[i], '/'); |
| if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol) |
| readfile(c, argv[i]); |
| else |
| readfile(row.col[i/WPERCOL], argv[i]); |
| } |
| } |
| flushimage(display, 1); |
| |
| acmeerrorinit(); |
| threadcreate(keyboardthread, nil, STACK); |
| threadcreate(mousethread, nil, STACK); |
| threadcreate(waitthread, nil, STACK); |
| threadcreate(xfidallocthread, nil, STACK); |
| threadcreate(newwindowthread, nil, STACK); |
| /* threadcreate(shutdownthread, nil, STACK); */ |
| threadnotify(shutdown, 1); |
| recvul(cexit); |
| killprocs(); |
| threadexitsall(nil); |
| } |
| |
| void |
| readfile(Column *c, char *s) |
| { |
| Window *w; |
| Rune rb[256]; |
| int nr; |
| Runestr rs; |
| |
| w = coladd(c, nil, nil, -1); |
| if(s[0] != '/') |
| runesnprint(rb, sizeof rb, "%s/%s", wdir, s); |
| else |
| runesnprint(rb, sizeof rb, "%s", s); |
| nr = runestrlen(rb); |
| rs = cleanrname(runestr(rb, nr)); |
| winsetname(w, rs.r, rs.nr); |
| textload(&w->body, 0, s, 1); |
| w->body.file->mod = FALSE; |
| w->dirty = FALSE; |
| winsettag(w); |
| winresize(w, w->r, FALSE, TRUE); |
| textscrdraw(&w->body); |
| textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc); |
| xfidlog(w, "new"); |
| } |
| |
| char *ignotes[] = { |
| "sys: write on closed pipe", |
| "sys: ttin", |
| "sys: ttou", |
| "sys: tstp", |
| nil |
| }; |
| |
| char *oknotes[] ={ |
| "delete", |
| "hangup", |
| "kill", |
| "exit", |
| nil |
| }; |
| |
| int dumping; |
| |
| static int |
| shutdown(void *v, char *msg) |
| { |
| int i; |
| |
| USED(v); |
| |
| for(i=0; ignotes[i]; i++) |
| if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0) |
| return 1; |
| |
| killprocs(); |
| if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){ |
| dumping = TRUE; |
| rowdump(&row, nil); |
| } |
| for(i=0; oknotes[i]; i++) |
| if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0) |
| threadexitsall(msg); |
| print("acme: %s\n", msg); |
| return 0; |
| } |
| |
| /* |
| void |
| shutdownthread(void *v) |
| { |
| char *msg; |
| Channel *c; |
| |
| USED(v); |
| |
| threadsetname("shutdown"); |
| c = threadnotechan(); |
| while((msg = recvp(c)) != nil) |
| shutdown(nil, msg); |
| } |
| */ |
| |
| void |
| killprocs(void) |
| { |
| Command *c; |
| |
| fsysclose(); |
| /* if(display) */ |
| /* flushimage(display, 1); */ |
| |
| for(c=command; c; c=c->next) |
| postnote(PNGROUP, c->pid, "hangup"); |
| } |
| |
| static int errorfd; |
| int erroutfd; |
| |
| void |
| acmeerrorproc(void *v) |
| { |
| char *buf; |
| int n; |
| |
| USED(v); |
| threadsetname("acmeerrorproc"); |
| buf = emalloc(8192+1); |
| while((n=read(errorfd, buf, 8192)) >= 0){ |
| buf[n] = '\0'; |
| sendp(cerr, estrdup(buf)); |
| } |
| } |
| |
| void |
| acmeerrorinit(void) |
| { |
| int pfd[2]; |
| |
| if(pipe(pfd) < 0) |
| error("can't create pipe"); |
| #if 0 |
| sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid); |
| fd = create(acmeerrorfile, OWRITE, 0666); |
| if(fd < 0){ |
| remove(acmeerrorfile); |
| fd = create(acmeerrorfile, OWRITE, 0666); |
| if(fd < 0) |
| error("can't create acmeerror file"); |
| } |
| sprint(buf, "%d", pfd[0]); |
| write(fd, buf, strlen(buf)); |
| close(fd); |
| /* reopen pfd[1] close on exec */ |
| sprint(buf, "/fd/%d", pfd[1]); |
| errorfd = open(buf, OREAD|OCEXEC); |
| #endif |
| fcntl(pfd[0], F_SETFD, FD_CLOEXEC); |
| fcntl(pfd[1], F_SETFD, FD_CLOEXEC); |
| erroutfd = pfd[0]; |
| errorfd = pfd[1]; |
| if(errorfd < 0) |
| error("can't re-open acmeerror file"); |
| proccreate(acmeerrorproc, nil, STACK); |
| } |
| |
| /* |
| void |
| plumbproc(void *v) |
| { |
| Plumbmsg *m; |
| |
| USED(v); |
| threadsetname("plumbproc"); |
| for(;;){ |
| m = threadplumbrecv(plumbeditfd); |
| if(m == nil) |
| threadexits(nil); |
| sendp(cplumb, m); |
| } |
| } |
| */ |
| |
| void |
| keyboardthread(void *v) |
| { |
| Rune r; |
| Timer *timer; |
| Text *t; |
| enum { KTimer, KKey, NKALT }; |
| static Alt alts[NKALT+1]; |
| |
| USED(v); |
| alts[KTimer].c = nil; |
| alts[KTimer].v = nil; |
| alts[KTimer].op = CHANNOP; |
| alts[KKey].c = keyboardctl->c; |
| alts[KKey].v = &r; |
| alts[KKey].op = CHANRCV; |
| alts[NKALT].op = CHANEND; |
| |
| timer = nil; |
| typetext = nil; |
| threadsetname("keyboardthread"); |
| for(;;){ |
| switch(alt(alts)){ |
| case KTimer: |
| timerstop(timer); |
| t = typetext; |
| if(t!=nil && t->what==Tag){ |
| winlock(t->w, 'K'); |
| wincommit(t->w, t); |
| winunlock(t->w); |
| flushimage(display, 1); |
| } |
| alts[KTimer].c = nil; |
| alts[KTimer].op = CHANNOP; |
| break; |
| case KKey: |
| casekeyboard: |
| typetext = rowtype(&row, r, mouse->xy); |
| t = typetext; |
| if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */ |
| activecol = t->col; |
| if(t!=nil && t->w!=nil) |
| t->w->body.file->curtext = &t->w->body; |
| if(timer != nil) |
| timercancel(timer); |
| if(t!=nil && t->what==Tag) { |
| timer = timerstart(500); |
| alts[KTimer].c = timer->c; |
| alts[KTimer].op = CHANRCV; |
| }else{ |
| timer = nil; |
| alts[KTimer].c = nil; |
| alts[KTimer].op = CHANNOP; |
| } |
| if(nbrecv(keyboardctl->c, &r) > 0) |
| goto casekeyboard; |
| flushimage(display, 1); |
| break; |
| } |
| } |
| } |
| |
| void |
| mousethread(void *v) |
| { |
| Text *t, *argt; |
| int but; |
| uint q0, q1; |
| Window *w; |
| Plumbmsg *pm; |
| Mouse m; |
| char *act; |
| enum { MResize, MMouse, MPlumb, MWarnings, NMALT }; |
| static Alt alts[NMALT+1]; |
| |
| USED(v); |
| threadsetname("mousethread"); |
| alts[MResize].c = mousectl->resizec; |
| alts[MResize].v = nil; |
| alts[MResize].op = CHANRCV; |
| alts[MMouse].c = mousectl->c; |
| alts[MMouse].v = &mousectl->m; |
| alts[MMouse].op = CHANRCV; |
| alts[MPlumb].c = cplumb; |
| alts[MPlumb].v = ± |
| alts[MPlumb].op = CHANRCV; |
| alts[MWarnings].c = cwarn; |
| alts[MWarnings].v = nil; |
| alts[MWarnings].op = CHANRCV; |
| if(cplumb == nil) |
| alts[MPlumb].op = CHANNOP; |
| alts[NMALT].op = CHANEND; |
| |
| for(;;){ |
| qlock(&row.lk); |
| flushwarnings(); |
| qunlock(&row.lk); |
| flushimage(display, 1); |
| switch(alt(alts)){ |
| case MResize: |
| if(getwindow(display, Refnone) < 0) |
| error("attach to window"); |
| draw(screen, screen->r, display->white, nil, ZP); |
| iconinit(); |
| scrlresize(); |
| rowresize(&row, screen->clipr); |
| break; |
| case MPlumb: |
| if(strcmp(pm->type, "text") == 0){ |
| act = plumblookup(pm->attr, "action"); |
| if(act==nil || strcmp(act, "showfile")==0) |
| plumblook(pm); |
| else if(strcmp(act, "showdata")==0) |
| plumbshow(pm); |
| } |
| plumbfree(pm); |
| break; |
| case MWarnings: |
| break; |
| case MMouse: |
| /* |
| * Make a copy so decisions are consistent; mousectl changes |
| * underfoot. Can't just receive into m because this introduces |
| * another race; see /sys/src/libdraw/mouse.c. |
| */ |
| m = mousectl->m; |
| qlock(&row.lk); |
| t = rowwhich(&row, m.xy); |
| |
| if((t!=mousetext && t!=nil && t->w!=nil) && |
| (mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) { |
| xfidlog(t->w, "focus"); |
| } |
| |
| if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){ |
| winlock(mousetext->w, 'M'); |
| mousetext->eq0 = ~0; |
| wincommit(mousetext->w, mousetext); |
| winunlock(mousetext->w); |
| } |
| mousetext = t; |
| if(t == nil) |
| goto Continue; |
| w = t->w; |
| if(t==nil || m.buttons==0) |
| goto Continue; |
| but = 0; |
| if(m.buttons == 1) |
| but = 1; |
| else if(m.buttons == 2) |
| but = 2; |
| else if(m.buttons == 4) |
| but = 3; |
| barttext = t; |
| if(t->what==Body && ptinrect(m.xy, t->scrollr)){ |
| if(but){ |
| if(swapscrollbuttons){ |
| if(but == 1) |
| but = 3; |
| else if(but == 3) |
| but = 1; |
| } |
| winlock(w, 'M'); |
| t->eq0 = ~0; |
| textscroll(t, but); |
| winunlock(w); |
| } |
| goto Continue; |
| } |
| /* scroll buttons, wheels, etc. */ |
| if(w != nil && (m.buttons & (8|16))){ |
| if(m.buttons & 8) |
| but = Kscrolloneup; |
| else |
| but = Kscrollonedown; |
| winlock(w, 'M'); |
| t->eq0 = ~0; |
| texttype(t, but); |
| winunlock(w); |
| goto Continue; |
| } |
| if(ptinrect(m.xy, t->scrollr)){ |
| if(but){ |
| if(t->what == Columntag) |
| rowdragcol(&row, t->col, but); |
| else if(t->what == Tag){ |
| coldragwin(t->col, t->w, but); |
| if(t->w) |
| barttext = &t->w->body; |
| } |
| if(t->col) |
| activecol = t->col; |
| } |
| goto Continue; |
| } |
| if(m.buttons){ |
| if(w) |
| winlock(w, 'M'); |
| t->eq0 = ~0; |
| if(w) |
| wincommit(w, t); |
| else |
| textcommit(t, TRUE); |
| if(m.buttons & 1){ |
| textselect(t); |
| if(w) |
| winsettag(w); |
| argtext = t; |
| seltext = t; |
| if(t->col) |
| activecol = t->col; /* button 1 only */ |
| if(t->w!=nil && t==&t->w->body) |
| activewin = t->w; |
| }else if(m.buttons & 2){ |
| if(textselect2(t, &q0, &q1, &argt)) |
| execute(t, q0, q1, FALSE, argt); |
| }else if(m.buttons & 4){ |
| if(textselect3(t, &q0, &q1)) |
| look3(t, q0, q1, FALSE); |
| } |
| if(w) |
| winunlock(w); |
| goto Continue; |
| } |
| Continue: |
| qunlock(&row.lk); |
| break; |
| } |
| } |
| } |
| |
| /* |
| * There is a race between process exiting and our finding out it was ever created. |
| * This structure keeps a list of processes that have exited we haven't heard of. |
| */ |
| typedef struct Pid Pid; |
| struct Pid |
| { |
| int pid; |
| char msg[ERRMAX]; |
| Pid *next; |
| }; |
| |
| void |
| waitthread(void *v) |
| { |
| Waitmsg *w; |
| Command *c, *lc; |
| uint pid; |
| int found, ncmd; |
| Rune *cmd; |
| char *err; |
| Text *t; |
| Pid *pids, *p, *lastp; |
| enum { WErr, WKill, WWait, WCmd, NWALT }; |
| Alt alts[NWALT+1]; |
| |
| USED(v); |
| threadsetname("waitthread"); |
| pids = nil; |
| alts[WErr].c = cerr; |
| alts[WErr].v = &err; |
| alts[WErr].op = CHANRCV; |
| alts[WKill].c = ckill; |
| alts[WKill].v = &cmd; |
| alts[WKill].op = CHANRCV; |
| alts[WWait].c = cwait; |
| alts[WWait].v = &w; |
| alts[WWait].op = CHANRCV; |
| alts[WCmd].c = ccommand; |
| alts[WCmd].v = &c; |
| alts[WCmd].op = CHANRCV; |
| alts[NWALT].op = CHANEND; |
| |
| command = nil; |
| for(;;){ |
| switch(alt(alts)){ |
| case WErr: |
| qlock(&row.lk); |
| warning(nil, "%s", err); |
| free(err); |
| flushimage(display, 1); |
| qunlock(&row.lk); |
| break; |
| case WKill: |
| found = FALSE; |
| ncmd = runestrlen(cmd); |
| for(c=command; c; c=c->next){ |
| /* -1 for blank */ |
| if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){ |
| if(postnote(PNGROUP, c->pid, "kill") < 0) |
| warning(nil, "kill %S: %r\n", cmd); |
| found = TRUE; |
| } |
| } |
| if(!found) |
| warning(nil, "Kill: no process %S\n", cmd); |
| free(cmd); |
| break; |
| case WWait: |
| pid = w->pid; |
| lc = nil; |
| for(c=command; c; c=c->next){ |
| if(c->pid == pid){ |
| if(lc) |
| lc->next = c->next; |
| else |
| command = c->next; |
| break; |
| } |
| lc = c; |
| } |
| qlock(&row.lk); |
| t = &row.tag; |
| textcommit(t, TRUE); |
| if(c == nil){ |
| /* helper processes use this exit status */ |
| if(strncmp(w->msg, "libthread", 9) != 0){ |
| p = emalloc(sizeof(Pid)); |
| p->pid = pid; |
| strncpy(p->msg, w->msg, sizeof(p->msg)); |
| p->next = pids; |
| pids = p; |
| } |
| }else{ |
| if(search(t, c->name, c->nname)){ |
| textdelete(t, t->q0, t->q1, TRUE); |
| textsetselect(t, 0, 0); |
| } |
| if(w->msg[0]) |
| warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg); |
| flushimage(display, 1); |
| } |
| qunlock(&row.lk); |
| free(w); |
| Freecmd: |
| if(c){ |
| if(c->iseditcmd) |
| sendul(cedit, 0); |
| free(c->text); |
| free(c->name); |
| fsysdelid(c->md); |
| free(c); |
| } |
| break; |
| case WCmd: |
| /* has this command already exited? */ |
| lastp = nil; |
| for(p=pids; p!=nil; p=p->next){ |
| if(p->pid == c->pid){ |
| if(p->msg[0]) |
| warning(c->md, "%s\n", p->msg); |
| if(lastp == nil) |
| pids = p->next; |
| else |
| lastp->next = p->next; |
| free(p); |
| goto Freecmd; |
| } |
| lastp = p; |
| } |
| c->next = command; |
| command = c; |
| qlock(&row.lk); |
| t = &row.tag; |
| textcommit(t, TRUE); |
| textinsert(t, 0, c->name, c->nname, TRUE); |
| textsetselect(t, 0, 0); |
| flushimage(display, 1); |
| qunlock(&row.lk); |
| break; |
| } |
| } |
| } |
| |
| void |
| xfidallocthread(void *v) |
| { |
| Xfid *xfree, *x; |
| enum { Alloc, Free, N }; |
| static Alt alts[N+1]; |
| |
| USED(v); |
| threadsetname("xfidallocthread"); |
| alts[Alloc].c = cxfidalloc; |
| alts[Alloc].v = nil; |
| alts[Alloc].op = CHANRCV; |
| alts[Free].c = cxfidfree; |
| alts[Free].v = &x; |
| alts[Free].op = CHANRCV; |
| alts[N].op = CHANEND; |
| |
| xfree = nil; |
| for(;;){ |
| switch(alt(alts)){ |
| case Alloc: |
| x = xfree; |
| if(x) |
| xfree = x->next; |
| else{ |
| x = emalloc(sizeof(Xfid)); |
| x->c = chancreate(sizeof(void(*)(Xfid*)), 0); |
| chansetname(x->c, "xc%p", x->c); |
| x->arg = x; |
| threadcreate(xfidctl, x->arg, STACK); |
| } |
| sendp(cxfidalloc, x); |
| break; |
| case Free: |
| x->next = xfree; |
| xfree = x; |
| break; |
| } |
| } |
| } |
| |
| /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */ |
| void |
| newwindowthread(void *v) |
| { |
| Window *w; |
| |
| USED(v); |
| threadsetname("newwindowthread"); |
| |
| for(;;){ |
| /* only fsysproc is talking to us, so synchronization is trivial */ |
| recvp(cnewwindow); |
| w = makenewwindow(nil); |
| winsettag(w); |
| xfidlog(w, "new"); |
| sendp(cnewwindow, w); |
| } |
| } |
| |
| Reffont* |
| rfget(int fix, int save, int setfont, char *name) |
| { |
| Reffont *r; |
| Font *f; |
| int i; |
| |
| r = nil; |
| if(name == nil){ |
| name = fontnames[fix]; |
| r = reffonts[fix]; |
| } |
| if(r == nil){ |
| for(i=0; i<nfontcache; i++) |
| if(strcmp(name, fontcache[i]->f->name) == 0){ |
| r = fontcache[i]; |
| goto Found; |
| } |
| f = openfont(display, name); |
| if(f == nil){ |
| warning(nil, "can't open font file %s: %r\n", name); |
| return nil; |
| } |
| r = emalloc(sizeof(Reffont)); |
| r->f = f; |
| fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*)); |
| fontcache[nfontcache++] = r; |
| } |
| Found: |
| if(save){ |
| incref(&r->ref); |
| if(reffonts[fix]) |
| rfclose(reffonts[fix]); |
| reffonts[fix] = r; |
| if(name != fontnames[fix]){ |
| free(fontnames[fix]); |
| fontnames[fix] = estrdup(name); |
| } |
| } |
| if(setfont){ |
| reffont.f = r->f; |
| incref(&r->ref); |
| rfclose(reffonts[0]); |
| font = r->f; |
| reffonts[0] = r; |
| incref(&r->ref); |
| iconinit(); |
| } |
| incref(&r->ref); |
| return r; |
| } |
| |
| void |
| rfclose(Reffont *r) |
| { |
| int i; |
| |
| if(decref(&r->ref) == 0){ |
| for(i=0; i<nfontcache; i++) |
| if(r == fontcache[i]) |
| break; |
| if(i >= nfontcache) |
| warning(nil, "internal error: can't find font in cache\n"); |
| else{ |
| nfontcache--; |
| memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*)); |
| } |
| freefont(r->f); |
| free(r); |
| } |
| } |
| |
| Cursor boxcursor = { |
| {-7, -7}, |
| {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, |
| 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, |
| {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, |
| 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, |
| 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, |
| 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} |
| }; |
| |
| void |
| iconinit(void) |
| { |
| Rectangle r; |
| Image *tmp; |
| |
| if(tagcols[BACK] == nil) { |
| /* Blue */ |
| tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite); |
| tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen); |
| tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); |
| tagcols[TEXT] = display->black; |
| tagcols[HTEXT] = display->black; |
| |
| /* Yellow */ |
| textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite); |
| textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow); |
| textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen); |
| textcols[TEXT] = display->black; |
| textcols[HTEXT] = display->black; |
| } |
| |
| r = Rect(0, 0, Scrollwid+ButtonBorder, font->height+1); |
| if(button && eqrect(r, button->r)) |
| return; |
| |
| if(button){ |
| freeimage(button); |
| freeimage(modbutton); |
| freeimage(colbutton); |
| } |
| |
| button = allocimage(display, r, screen->chan, 0, DNofill); |
| draw(button, r, tagcols[BACK], nil, r.min); |
| r.max.x -= ButtonBorder; |
| border(button, r, ButtonBorder, tagcols[BORD], ZP); |
| |
| r = button->r; |
| modbutton = allocimage(display, r, screen->chan, 0, DNofill); |
| draw(modbutton, r, tagcols[BACK], nil, r.min); |
| r.max.x -= ButtonBorder; |
| border(modbutton, r, ButtonBorder, tagcols[BORD], ZP); |
| r = insetrect(r, ButtonBorder); |
| tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue); |
| draw(modbutton, r, tmp, nil, ZP); |
| freeimage(tmp); |
| |
| r = button->r; |
| colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue); |
| |
| but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF); |
| but3col = allocimage(display, r, screen->chan, 1, 0x006600FF); |
| } |
| |
| /* |
| * /dev/snarf updates when the file is closed, so we must open our own |
| * fd here rather than use snarffd |
| */ |
| |
| /* rio truncates larges snarf buffers, so this avoids using the |
| * service if the string is huge */ |
| |
| #define MAXSNARF 100*1024 |
| |
| void |
| acmeputsnarf(void) |
| { |
| int i, n; |
| Fmt f; |
| char *s; |
| |
| if(snarfbuf.nc==0) |
| return; |
| if(snarfbuf.nc > MAXSNARF) |
| return; |
| |
| fmtstrinit(&f); |
| for(i=0; i<snarfbuf.nc; i+=n){ |
| n = snarfbuf.nc-i; |
| if(n >= NSnarf) |
| n = NSnarf; |
| bufread(&snarfbuf, i, snarfrune, n); |
| if(fmtprint(&f, "%.*S", n, snarfrune) < 0) |
| break; |
| } |
| s = fmtstrflush(&f); |
| if(s && s[0]) |
| putsnarf(s); |
| free(s); |
| } |
| |
| void |
| acmegetsnarf(void) |
| { |
| char *s; |
| int nb, nr, nulls, len; |
| Rune *r; |
| |
| s = getsnarf(); |
| if(s == nil || s[0]==0){ |
| free(s); |
| return; |
| } |
| |
| len = strlen(s); |
| r = runemalloc(len+1); |
| cvttorunes(s, len, r, &nb, &nr, &nulls); |
| bufreset(&snarfbuf); |
| bufinsert(&snarfbuf, 0, r, nr); |
| free(r); |
| free(s); |
| } |
| |
| int |
| ismtpt(char *file) |
| { |
| int n; |
| |
| if(mtpt == nil) |
| return 0; |
| |
| /* This is not foolproof, but it will stop a lot of them. */ |
| n = strlen(mtpt); |
| return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0); |
| } |
| |
| int |
| timefmt(Fmt *f) |
| { |
| Tm *tm; |
| |
| tm = localtime(va_arg(f->args, ulong)); |
| return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d", |
| tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec); |
| } |
| |