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;
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 sendp(cerr, estrdup(buf));
398 void
399 acmeerrorinit(void)
401 int pfd[2];
403 if(pipe(pfd) < 0)
404 error("can't create pipe");
405 #if 0
406 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
407 fd = create(acmeerrorfile, OWRITE, 0666);
408 if(fd < 0){
409 remove(acmeerrorfile);
410 fd = create(acmeerrorfile, OWRITE, 0666);
411 if(fd < 0)
412 error("can't create acmeerror file");
414 sprint(buf, "%d", pfd[0]);
415 write(fd, buf, strlen(buf));
416 close(fd);
417 /* reopen pfd[1] close on exec */
418 sprint(buf, "/fd/%d", pfd[1]);
419 errorfd = open(buf, OREAD|OCEXEC);
420 #endif
421 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
422 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
423 erroutfd = pfd[0];
424 errorfd = pfd[1];
425 if(errorfd < 0)
426 error("can't re-open acmeerror file");
427 proccreate(acmeerrorproc, nil, STACK);
430 /*
431 void
432 plumbproc(void *v)
434 Plumbmsg *m;
436 USED(v);
437 threadsetname("plumbproc");
438 for(;;){
439 m = threadplumbrecv(plumbeditfd);
440 if(m == nil)
441 threadexits(nil);
442 sendp(cplumb, m);
445 */
447 void
448 keyboardthread(void *v)
450 Rune r;
451 Timer *timer;
452 Text *t;
453 enum { KTimer, KKey, NKALT };
454 static Alt alts[NKALT+1];
456 USED(v);
457 alts[KTimer].c = nil;
458 alts[KTimer].v = nil;
459 alts[KTimer].op = CHANNOP;
460 alts[KKey].c = keyboardctl->c;
461 alts[KKey].v = &r;
462 alts[KKey].op = CHANRCV;
463 alts[NKALT].op = CHANEND;
465 timer = nil;
466 typetext = nil;
467 threadsetname("keyboardthread");
468 for(;;){
469 switch(alt(alts)){
470 case KTimer:
471 timerstop(timer);
472 t = typetext;
473 if(t!=nil && t->what==Tag){
474 winlock(t->w, 'K');
475 wincommit(t->w, t);
476 winunlock(t->w);
477 flushimage(display, 1);
479 alts[KTimer].c = nil;
480 alts[KTimer].op = CHANNOP;
481 break;
482 case KKey:
483 casekeyboard:
484 typetext = rowtype(&row, r, mouse->xy);
485 t = typetext;
486 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
487 activecol = t->col;
488 if(t!=nil && t->w!=nil)
489 t->w->body.file->curtext = &t->w->body;
490 if(timer != nil)
491 timercancel(timer);
492 if(t!=nil && t->what==Tag) {
493 timer = timerstart(500);
494 alts[KTimer].c = timer->c;
495 alts[KTimer].op = CHANRCV;
496 }else{
497 timer = nil;
498 alts[KTimer].c = nil;
499 alts[KTimer].op = CHANNOP;
501 if(nbrecv(keyboardctl->c, &r) > 0)
502 goto casekeyboard;
503 flushimage(display, 1);
504 break;
509 void
510 mousethread(void *v)
512 Text *t, *argt;
513 int but;
514 uint q0, q1;
515 Window *w;
516 Plumbmsg *pm;
517 Mouse m;
518 char *act;
519 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
520 static Alt alts[NMALT+1];
522 USED(v);
523 threadsetname("mousethread");
524 alts[MResize].c = mousectl->resizec;
525 alts[MResize].v = nil;
526 alts[MResize].op = CHANRCV;
527 alts[MMouse].c = mousectl->c;
528 alts[MMouse].v = &mousectl->m;
529 alts[MMouse].op = CHANRCV;
530 alts[MPlumb].c = cplumb;
531 alts[MPlumb].v = &pm;
532 alts[MPlumb].op = CHANRCV;
533 alts[MWarnings].c = cwarn;
534 alts[MWarnings].v = nil;
535 alts[MWarnings].op = CHANRCV;
536 if(cplumb == nil)
537 alts[MPlumb].op = CHANNOP;
538 alts[NMALT].op = CHANEND;
540 for(;;){
541 qlock(&row.lk);
542 flushwarnings();
543 qunlock(&row.lk);
544 flushimage(display, 1);
545 switch(alt(alts)){
546 case MResize:
547 if(getwindow(display, Refnone) < 0)
548 error("attach to window");
549 draw(screen, screen->r, display->white, nil, ZP);
550 iconinit();
551 scrlresize();
552 rowresize(&row, screen->clipr);
553 break;
554 case MPlumb:
555 if(strcmp(pm->type, "text") == 0){
556 act = plumblookup(pm->attr, "action");
557 if(act==nil || strcmp(act, "showfile")==0)
558 plumblook(pm);
559 else if(strcmp(act, "showdata")==0)
560 plumbshow(pm);
562 plumbfree(pm);
563 break;
564 case MWarnings:
565 break;
566 case MMouse:
567 /*
568 * Make a copy so decisions are consistent; mousectl changes
569 * underfoot. Can't just receive into m because this introduces
570 * another race; see /sys/src/libdraw/mouse.c.
571 */
572 m = mousectl->m;
573 qlock(&row.lk);
574 t = rowwhich(&row, m.xy);
576 if((t!=mousetext && t!=nil && t->w!=nil) &&
577 (mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
578 xfidlog(t->w, "focus");
581 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
582 winlock(mousetext->w, 'M');
583 mousetext->eq0 = ~0;
584 wincommit(mousetext->w, mousetext);
585 winunlock(mousetext->w);
587 mousetext = t;
588 if(t == nil)
589 goto Continue;
590 w = t->w;
591 if(t==nil || m.buttons==0)
592 goto Continue;
593 but = 0;
594 if(m.buttons == 1)
595 but = 1;
596 else if(m.buttons == 2)
597 but = 2;
598 else if(m.buttons == 4)
599 but = 3;
600 barttext = t;
601 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
602 if(but){
603 if(swapscrollbuttons){
604 if(but == 1)
605 but = 3;
606 else if(but == 3)
607 but = 1;
609 winlock(w, 'M');
610 t->eq0 = ~0;
611 textscroll(t, but);
612 winunlock(w);
614 goto Continue;
616 /* scroll buttons, wheels, etc. */
617 if(w != nil && (m.buttons & (8|16))){
618 if(m.buttons & 8)
619 but = Kscrolloneup;
620 else
621 but = Kscrollonedown;
622 winlock(w, 'M');
623 t->eq0 = ~0;
624 texttype(t, but);
625 winunlock(w);
626 goto Continue;
628 if(ptinrect(m.xy, t->scrollr)){
629 if(but){
630 if(t->what == Columntag)
631 rowdragcol(&row, t->col, but);
632 else if(t->what == Tag){
633 coldragwin(t->col, t->w, but);
634 if(t->w)
635 barttext = &t->w->body;
637 if(t->col)
638 activecol = t->col;
640 goto Continue;
642 if(m.buttons){
643 if(w)
644 winlock(w, 'M');
645 t->eq0 = ~0;
646 if(w)
647 wincommit(w, t);
648 else
649 textcommit(t, TRUE);
650 if(m.buttons & 1){
651 textselect(t);
652 if(w)
653 winsettag(w);
654 argtext = t;
655 seltext = t;
656 if(t->col)
657 activecol = t->col; /* button 1 only */
658 if(t->w!=nil && t==&t->w->body)
659 activewin = t->w;
660 }else if(m.buttons & 2){
661 if(textselect2(t, &q0, &q1, &argt))
662 execute(t, q0, q1, FALSE, argt);
663 }else if(m.buttons & 4){
664 if(textselect3(t, &q0, &q1))
665 look3(t, q0, q1, FALSE);
667 if(w)
668 winunlock(w);
669 goto Continue;
671 Continue:
672 qunlock(&row.lk);
673 break;
678 /*
679 * There is a race between process exiting and our finding out it was ever created.
680 * This structure keeps a list of processes that have exited we haven't heard of.
681 */
682 typedef struct Pid Pid;
683 struct Pid
685 int pid;
686 char msg[ERRMAX];
687 Pid *next;
688 };
690 void
691 waitthread(void *v)
693 Waitmsg *w;
694 Command *c, *lc;
695 uint pid;
696 int found, ncmd;
697 Rune *cmd;
698 char *err;
699 Text *t;
700 Pid *pids, *p, *lastp;
701 enum { WErr, WKill, WWait, WCmd, NWALT };
702 Alt alts[NWALT+1];
704 USED(v);
705 threadsetname("waitthread");
706 pids = nil;
707 alts[WErr].c = cerr;
708 alts[WErr].v = &err;
709 alts[WErr].op = CHANRCV;
710 alts[WKill].c = ckill;
711 alts[WKill].v = &cmd;
712 alts[WKill].op = CHANRCV;
713 alts[WWait].c = cwait;
714 alts[WWait].v = &w;
715 alts[WWait].op = CHANRCV;
716 alts[WCmd].c = ccommand;
717 alts[WCmd].v = &c;
718 alts[WCmd].op = CHANRCV;
719 alts[NWALT].op = CHANEND;
721 command = nil;
722 for(;;){
723 switch(alt(alts)){
724 case WErr:
725 qlock(&row.lk);
726 warning(nil, "%s", err);
727 free(err);
728 flushimage(display, 1);
729 qunlock(&row.lk);
730 break;
731 case WKill:
732 found = FALSE;
733 ncmd = runestrlen(cmd);
734 for(c=command; c; c=c->next){
735 /* -1 for blank */
736 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
737 if(postnote(PNGROUP, c->pid, "kill") < 0)
738 warning(nil, "kill %S: %r\n", cmd);
739 found = TRUE;
742 if(!found)
743 warning(nil, "Kill: no process %S\n", cmd);
744 free(cmd);
745 break;
746 case WWait:
747 pid = w->pid;
748 lc = nil;
749 for(c=command; c; c=c->next){
750 if(c->pid == pid){
751 if(lc)
752 lc->next = c->next;
753 else
754 command = c->next;
755 break;
757 lc = c;
759 qlock(&row.lk);
760 t = &row.tag;
761 textcommit(t, TRUE);
762 if(c == nil){
763 /* helper processes use this exit status */
764 if(strncmp(w->msg, "libthread", 9) != 0){
765 p = emalloc(sizeof(Pid));
766 p->pid = pid;
767 strncpy(p->msg, w->msg, sizeof(p->msg));
768 p->next = pids;
769 pids = p;
771 }else{
772 if(search(t, c->name, c->nname)){
773 textdelete(t, t->q0, t->q1, TRUE);
774 textsetselect(t, 0, 0);
776 if(w->msg[0])
777 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
778 flushimage(display, 1);
780 qunlock(&row.lk);
781 free(w);
782 Freecmd:
783 if(c){
784 if(c->iseditcmd)
785 sendul(cedit, 0);
786 free(c->text);
787 free(c->name);
788 fsysdelid(c->md);
789 free(c);
791 break;
792 case WCmd:
793 /* has this command already exited? */
794 lastp = nil;
795 for(p=pids; p!=nil; p=p->next){
796 if(p->pid == c->pid){
797 if(p->msg[0])
798 warning(c->md, "%s\n", p->msg);
799 if(lastp == nil)
800 pids = p->next;
801 else
802 lastp->next = p->next;
803 free(p);
804 goto Freecmd;
806 lastp = p;
808 c->next = command;
809 command = c;
810 qlock(&row.lk);
811 t = &row.tag;
812 textcommit(t, TRUE);
813 textinsert(t, 0, c->name, c->nname, TRUE);
814 textsetselect(t, 0, 0);
815 flushimage(display, 1);
816 qunlock(&row.lk);
817 break;
822 void
823 xfidallocthread(void *v)
825 Xfid *xfree, *x;
826 enum { Alloc, Free, N };
827 static Alt alts[N+1];
829 USED(v);
830 threadsetname("xfidallocthread");
831 alts[Alloc].c = cxfidalloc;
832 alts[Alloc].v = nil;
833 alts[Alloc].op = CHANRCV;
834 alts[Free].c = cxfidfree;
835 alts[Free].v = &x;
836 alts[Free].op = CHANRCV;
837 alts[N].op = CHANEND;
839 xfree = nil;
840 for(;;){
841 switch(alt(alts)){
842 case Alloc:
843 x = xfree;
844 if(x)
845 xfree = x->next;
846 else{
847 x = emalloc(sizeof(Xfid));
848 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
849 chansetname(x->c, "xc%p", x->c);
850 x->arg = x;
851 threadcreate(xfidctl, x->arg, STACK);
853 sendp(cxfidalloc, x);
854 break;
855 case Free:
856 x->next = xfree;
857 xfree = x;
858 break;
863 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
864 void
865 newwindowthread(void *v)
867 Window *w;
869 USED(v);
870 threadsetname("newwindowthread");
872 for(;;){
873 /* only fsysproc is talking to us, so synchronization is trivial */
874 recvp(cnewwindow);
875 w = makenewwindow(nil);
876 winsettag(w);
877 xfidlog(w, "new");
878 sendp(cnewwindow, w);
882 Reffont*
883 rfget(int fix, int save, int setfont, char *name)
885 Reffont *r;
886 Font *f;
887 int i;
889 r = nil;
890 if(name == nil){
891 name = fontnames[fix];
892 r = reffonts[fix];
894 if(r == nil){
895 for(i=0; i<nfontcache; i++)
896 if(strcmp(name, fontcache[i]->f->name) == 0){
897 r = fontcache[i];
898 goto Found;
900 f = openfont(display, name);
901 if(f == nil){
902 warning(nil, "can't open font file %s: %r\n", name);
903 return nil;
905 r = emalloc(sizeof(Reffont));
906 r->f = f;
907 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
908 fontcache[nfontcache++] = r;
910 Found:
911 if(save){
912 incref(&r->ref);
913 if(reffonts[fix])
914 rfclose(reffonts[fix]);
915 reffonts[fix] = r;
916 if(name != fontnames[fix]){
917 free(fontnames[fix]);
918 fontnames[fix] = estrdup(name);
921 if(setfont){
922 reffont.f = r->f;
923 incref(&r->ref);
924 rfclose(reffonts[0]);
925 font = r->f;
926 reffonts[0] = r;
927 incref(&r->ref);
928 iconinit();
930 incref(&r->ref);
931 return r;
934 void
935 rfclose(Reffont *r)
937 int i;
939 if(decref(&r->ref) == 0){
940 for(i=0; i<nfontcache; i++)
941 if(r == fontcache[i])
942 break;
943 if(i >= nfontcache)
944 warning(nil, "internal error: can't find font in cache\n");
945 else{
946 nfontcache--;
947 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
949 freefont(r->f);
950 free(r);
954 Cursor boxcursor = {
955 {-7, -7},
956 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
957 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
958 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
959 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
960 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
961 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
962 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
963 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
964 };
966 void
967 iconinit(void)
969 Rectangle r;
970 Image *tmp;
972 if(tagcols[BACK] == nil) {
973 /* Blue */
974 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
975 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
976 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
977 tagcols[TEXT] = display->black;
978 tagcols[HTEXT] = display->black;
980 /* Yellow */
981 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
982 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
983 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
984 textcols[TEXT] = display->black;
985 textcols[HTEXT] = display->black;
988 r = Rect(0, 0, Scrollwid+ButtonBorder, font->height+1);
989 if(button && eqrect(r, button->r))
990 return;
992 if(button){
993 freeimage(button);
994 freeimage(modbutton);
995 freeimage(colbutton);
998 button = allocimage(display, r, screen->chan, 0, DNofill);
999 draw(button, r, tagcols[BACK], nil, r.min);
1000 r.max.x -= ButtonBorder;
1001 border(button, r, ButtonBorder, tagcols[BORD], ZP);
1003 r = button->r;
1004 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
1005 draw(modbutton, r, tagcols[BACK], nil, r.min);
1006 r.max.x -= ButtonBorder;
1007 border(modbutton, r, ButtonBorder, tagcols[BORD], ZP);
1008 r = insetrect(r, ButtonBorder);
1009 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
1010 draw(modbutton, r, tmp, nil, ZP);
1011 freeimage(tmp);
1013 r = button->r;
1014 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
1016 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
1017 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
1021 * /dev/snarf updates when the file is closed, so we must open our own
1022 * fd here rather than use snarffd
1025 /* rio truncates larges snarf buffers, so this avoids using the
1026 * service if the string is huge */
1028 #define MAXSNARF 100*1024
1030 void
1031 acmeputsnarf(void)
1033 int i, n;
1034 Fmt f;
1035 char *s;
1037 if(snarfbuf.nc==0)
1038 return;
1039 if(snarfbuf.nc > MAXSNARF)
1040 return;
1042 fmtstrinit(&f);
1043 for(i=0; i<snarfbuf.nc; i+=n){
1044 n = snarfbuf.nc-i;
1045 if(n >= NSnarf)
1046 n = NSnarf;
1047 bufread(&snarfbuf, i, snarfrune, n);
1048 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1049 break;
1051 s = fmtstrflush(&f);
1052 if(s && s[0])
1053 putsnarf(s);
1054 free(s);
1057 void
1058 acmegetsnarf(void)
1060 char *s;
1061 int nb, nr, nulls, len;
1062 Rune *r;
1064 s = getsnarf();
1065 if(s == nil || s[0]==0){
1066 free(s);
1067 return;
1070 len = strlen(s);
1071 r = runemalloc(len+1);
1072 cvttorunes(s, len, r, &nb, &nr, &nulls);
1073 bufreset(&snarfbuf);
1074 bufinsert(&snarfbuf, 0, r, nr);
1075 free(r);
1076 free(s);
1079 int
1080 ismtpt(char *file)
1082 int n;
1084 if(mtpt == nil)
1085 return 0;
1087 /* This is not foolproof, but it will stop a lot of them. */
1088 n = strlen(mtpt);
1089 return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0);
1092 int
1093 timefmt(Fmt *f)
1095 Tm *tm;
1097 tm = localtime(va_arg(f->args, ulong));
1098 return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d",
1099 tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);