tacme.c - plan9port - [fork] Plan 9 from user space
(HTM) git clone git://src.adamsgaard.dk/plan9port
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
tacme.c (24330B)
---
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"
17
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*);
25
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;
34
35 enum{
36 NSnarf = 1000 /* less than 1024, I/O buffer size */
37 };
38 Rune snarfrune[NSnarf+1];
39
40 char *fontnames[2] =
41 {
42 "/lib/font/bit/lucsans/euro.8.font",
43 "/lib/font/bit/lucm/unicode.9.font"
44 };
45
46 Command *command;
47
48 void shutdownthread(void*);
49 void acmeerrorinit(void);
50 void readfile(Column*, char*);
51 static int shutdown(void*, char*);
52
53 void
54 derror(Display *d, char *errorstr)
55 {
56 USED(d);
57 error(errorstr);
58 }
59
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;
68
69 rfork(RFENVG|RFNAMEG);
70
71 ncol = -1;
72
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
127
128 fontnames[0] = estrdup(fontnames[0]);
129 fontnames[1] = estrdup(fontnames[1]);
130
131 quotefmtinstall();
132 fmtinstall('t', timefmt);
133
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);
144 }
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);
155 }
156 bind("/acme/bin", "/bin", MBEFORE);
157 */
158 getwd(wdir, sizeof wdir);
159
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");
164 }
165 */
166 if(initdraw(derror, fontnames[0], "acme") < 0){
167 fprint(2, "acme: can't open display: %r\n");
168 threadexitsall("initdraw");
169 }
170
171 d = display;
172 font = d->defaultfont;
173 /*assert(font); */
174
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;
182
183 iconinit();
184 timerinit();
185 rxinit();
186
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");
200 }
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");
210
211 mousectl = initmouse(nil, screen);
212 if(mousectl == nil){
213 fprint(2, "acme: can't initialize mouse: %r\n");
214 threadexitsall("mouse");
215 }
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");
221 }
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);
231 }
232 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
233 */
234
235 fsysinit();
236
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;
248 }
249 }
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");
256 }
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]);
267 }
268 }
269 flushimage(display, 1);
270
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);
282 }
283
284 void
285 readfile(Column *c, char *s)
286 {
287 Window *w;
288 Rune rb[256];
289 int nr;
290 Runestr rs;
291
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");
308 }
309
310 char *ignotes[] = {
311 "sys: write on closed pipe",
312 "sys: ttin",
313 "sys: ttou",
314 "sys: tstp",
315 nil
316 };
317
318 char *oknotes[] ={
319 "delete",
320 "hangup",
321 "kill",
322 "exit",
323 nil
324 };
325
326 int dumping;
327
328 static int
329 shutdown(void *v, char *msg)
330 {
331 int i;
332
333 USED(v);
334
335 for(i=0; ignotes[i]; i++)
336 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
337 return 1;
338
339 killprocs();
340 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
341 dumping = TRUE;
342 rowdump(&row, nil);
343 }
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;
349 }
350
351 /*
352 void
353 shutdownthread(void *v)
354 {
355 char *msg;
356 Channel *c;
357
358 USED(v);
359
360 threadsetname("shutdown");
361 c = threadnotechan();
362 while((msg = recvp(c)) != nil)
363 shutdown(nil, msg);
364 }
365 */
366
367 void
368 killprocs(void)
369 {
370 Command *c;
371
372 fsysclose();
373 /* if(display) */
374 /* flushimage(display, 1); */
375
376 for(c=command; c; c=c->next)
377 postnote(PNGROUP, c->pid, "hangup");
378 }
379
380 static int errorfd;
381 int erroutfd;
382
383 void
384 acmeerrorproc(void *v)
385 {
386 char *buf;
387 int n;
388
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));
395 }
396 free(buf);
397 }
398
399 void
400 acmeerrorinit(void)
401 {
402 int pfd[2];
403
404 if(pipe(pfd) < 0)
405 error("can't create pipe");
406 #if 0
407 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
408 fd = create(acmeerrorfile, OWRITE, 0666);
409 if(fd < 0){
410 remove(acmeerrorfile);
411 fd = create(acmeerrorfile, OWRITE, 0666);
412 if(fd < 0)
413 error("can't create acmeerror file");
414 }
415 sprint(buf, "%d", pfd[0]);
416 write(fd, buf, strlen(buf));
417 close(fd);
418 /* reopen pfd[1] close on exec */
419 sprint(buf, "/fd/%d", pfd[1]);
420 errorfd = open(buf, OREAD|OCEXEC);
421 #endif
422 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
423 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
424 erroutfd = pfd[0];
425 errorfd = pfd[1];
426 if(errorfd < 0)
427 error("can't re-open acmeerror file");
428 proccreate(acmeerrorproc, nil, STACK);
429 }
430
431 /*
432 void
433 plumbproc(void *v)
434 {
435 Plumbmsg *m;
436
437 USED(v);
438 threadsetname("plumbproc");
439 for(;;){
440 m = threadplumbrecv(plumbeditfd);
441 if(m == nil)
442 threadexits(nil);
443 sendp(cplumb, m);
444 }
445 }
446 */
447
448 void
449 keyboardthread(void *v)
450 {
451 Rune r;
452 Timer *timer;
453 Text *t;
454 enum { KTimer, KKey, NKALT };
455 static Alt alts[NKALT+1];
456
457 USED(v);
458 alts[KTimer].c = nil;
459 alts[KTimer].v = nil;
460 alts[KTimer].op = CHANNOP;
461 alts[KKey].c = keyboardctl->c;
462 alts[KKey].v = &r;
463 alts[KKey].op = CHANRCV;
464 alts[NKALT].op = CHANEND;
465
466 timer = nil;
467 typetext = nil;
468 threadsetname("keyboardthread");
469 for(;;){
470 switch(alt(alts)){
471 case KTimer:
472 timerstop(timer);
473 t = typetext;
474 if(t!=nil && t->what==Tag){
475 winlock(t->w, 'K');
476 wincommit(t->w, t);
477 winunlock(t->w);
478 flushimage(display, 1);
479 }
480 alts[KTimer].c = nil;
481 alts[KTimer].op = CHANNOP;
482 break;
483 case KKey:
484 casekeyboard:
485 typetext = rowtype(&row, r, mouse->xy);
486 t = typetext;
487 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
488 activecol = t->col;
489 if(t!=nil && t->w!=nil)
490 t->w->body.file->curtext = &t->w->body;
491 if(timer != nil)
492 timercancel(timer);
493 if(t!=nil && t->what==Tag) {
494 timer = timerstart(500);
495 alts[KTimer].c = timer->c;
496 alts[KTimer].op = CHANRCV;
497 }else{
498 timer = nil;
499 alts[KTimer].c = nil;
500 alts[KTimer].op = CHANNOP;
501 }
502 if(nbrecv(keyboardctl->c, &r) > 0)
503 goto casekeyboard;
504 flushimage(display, 1);
505 break;
506 }
507 }
508 }
509
510 void
511 mousethread(void *v)
512 {
513 Text *t, *argt;
514 int but;
515 uint q0, q1;
516 Window *w;
517 Plumbmsg *pm;
518 Mouse m;
519 char *act;
520 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
521 static Alt alts[NMALT+1];
522
523 USED(v);
524 threadsetname("mousethread");
525 alts[MResize].c = mousectl->resizec;
526 alts[MResize].v = nil;
527 alts[MResize].op = CHANRCV;
528 alts[MMouse].c = mousectl->c;
529 alts[MMouse].v = &mousectl->m;
530 alts[MMouse].op = CHANRCV;
531 alts[MPlumb].c = cplumb;
532 alts[MPlumb].v = ±
533 alts[MPlumb].op = CHANRCV;
534 alts[MWarnings].c = cwarn;
535 alts[MWarnings].v = nil;
536 alts[MWarnings].op = CHANRCV;
537 if(cplumb == nil)
538 alts[MPlumb].op = CHANNOP;
539 alts[NMALT].op = CHANEND;
540
541 for(;;){
542 qlock(&row.lk);
543 flushwarnings();
544 qunlock(&row.lk);
545 flushimage(display, 1);
546 switch(alt(alts)){
547 case MResize:
548 if(getwindow(display, Refnone) < 0)
549 error("attach to window");
550 draw(screen, screen->r, display->white, nil, ZP);
551 iconinit();
552 scrlresize();
553 rowresize(&row, screen->clipr);
554 break;
555 case MPlumb:
556 if(strcmp(pm->type, "text") == 0){
557 act = plumblookup(pm->attr, "action");
558 if(act==nil || strcmp(act, "showfile")==0)
559 plumblook(pm);
560 else if(strcmp(act, "showdata")==0)
561 plumbshow(pm);
562 }
563 plumbfree(pm);
564 break;
565 case MWarnings:
566 break;
567 case MMouse:
568 /*
569 * Make a copy so decisions are consistent; mousectl changes
570 * underfoot. Can't just receive into m because this introduces
571 * another race; see /sys/src/libdraw/mouse.c.
572 */
573 m = mousectl->m;
574 qlock(&row.lk);
575 t = rowwhich(&row, m.xy);
576
577 if((t!=mousetext && t!=nil && t->w!=nil) &&
578 (mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
579 xfidlog(t->w, "focus");
580 }
581
582 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
583 winlock(mousetext->w, 'M');
584 mousetext->eq0 = ~0;
585 wincommit(mousetext->w, mousetext);
586 winunlock(mousetext->w);
587 }
588 mousetext = t;
589 if(t == nil)
590 goto Continue;
591 w = t->w;
592 if(t==nil || m.buttons==0)
593 goto Continue;
594 but = 0;
595 if(m.buttons == 1)
596 but = 1;
597 else if(m.buttons == 2)
598 but = 2;
599 else if(m.buttons == 4)
600 but = 3;
601 barttext = t;
602 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
603 if(but){
604 if(swapscrollbuttons){
605 if(but == 1)
606 but = 3;
607 else if(but == 3)
608 but = 1;
609 }
610 winlock(w, 'M');
611 t->eq0 = ~0;
612 textscroll(t, but);
613 winunlock(w);
614 }
615 goto Continue;
616 }
617 /* scroll buttons, wheels, etc. */
618 if(w != nil && (m.buttons & (8|16))){
619 if(m.buttons & 8)
620 but = Kscrolloneup;
621 else
622 but = Kscrollonedown;
623 winlock(w, 'M');
624 t->eq0 = ~0;
625 texttype(t, but);
626 winunlock(w);
627 goto Continue;
628 }
629 if(ptinrect(m.xy, t->scrollr)){
630 if(but){
631 if(t->what == Columntag)
632 rowdragcol(&row, t->col, but);
633 else if(t->what == Tag){
634 coldragwin(t->col, t->w, but);
635 if(t->w)
636 barttext = &t->w->body;
637 }
638 if(t->col)
639 activecol = t->col;
640 }
641 goto Continue;
642 }
643 if(m.buttons){
644 if(w)
645 winlock(w, 'M');
646 t->eq0 = ~0;
647 if(w)
648 wincommit(w, t);
649 else
650 textcommit(t, TRUE);
651 if(m.buttons & 1){
652 textselect(t);
653 if(w)
654 winsettag(w);
655 argtext = t;
656 seltext = t;
657 if(t->col)
658 activecol = t->col; /* button 1 only */
659 if(t->w!=nil && t==&t->w->body)
660 activewin = t->w;
661 }else if(m.buttons & 2){
662 if(textselect2(t, &q0, &q1, &argt))
663 execute(t, q0, q1, FALSE, argt);
664 }else if(m.buttons & 4){
665 if(textselect3(t, &q0, &q1))
666 look3(t, q0, q1, FALSE);
667 }
668 if(w)
669 winunlock(w);
670 goto Continue;
671 }
672 Continue:
673 qunlock(&row.lk);
674 break;
675 }
676 }
677 }
678
679 /*
680 * There is a race between process exiting and our finding out it was ever created.
681 * This structure keeps a list of processes that have exited we haven't heard of.
682 */
683 typedef struct Pid Pid;
684 struct Pid
685 {
686 int pid;
687 char msg[ERRMAX];
688 Pid *next;
689 };
690
691 void
692 waitthread(void *v)
693 {
694 Waitmsg *w;
695 Command *c, *lc;
696 uint pid;
697 int found, ncmd;
698 Rune *cmd;
699 char *err;
700 Text *t;
701 Pid *pids, *p, *lastp;
702 enum { WErr, WKill, WWait, WCmd, NWALT };
703 Alt alts[NWALT+1];
704
705 USED(v);
706 threadsetname("waitthread");
707 pids = nil;
708 alts[WErr].c = cerr;
709 alts[WErr].v = &err;
710 alts[WErr].op = CHANRCV;
711 alts[WKill].c = ckill;
712 alts[WKill].v = &cmd;
713 alts[WKill].op = CHANRCV;
714 alts[WWait].c = cwait;
715 alts[WWait].v = &w;
716 alts[WWait].op = CHANRCV;
717 alts[WCmd].c = ccommand;
718 alts[WCmd].v = &c;
719 alts[WCmd].op = CHANRCV;
720 alts[NWALT].op = CHANEND;
721
722 command = nil;
723 for(;;){
724 switch(alt(alts)){
725 case WErr:
726 qlock(&row.lk);
727 warning(nil, "%s", err);
728 free(err);
729 flushimage(display, 1);
730 qunlock(&row.lk);
731 break;
732 case WKill:
733 found = FALSE;
734 ncmd = runestrlen(cmd);
735 for(c=command; c; c=c->next){
736 /* -1 for blank */
737 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
738 if(postnote(PNGROUP, c->pid, "kill") < 0)
739 warning(nil, "kill %S: %r\n", cmd);
740 found = TRUE;
741 }
742 }
743 if(!found)
744 warning(nil, "Kill: no process %S\n", cmd);
745 free(cmd);
746 break;
747 case WWait:
748 pid = w->pid;
749 lc = nil;
750 for(c=command; c; c=c->next){
751 if(c->pid == pid){
752 if(lc)
753 lc->next = c->next;
754 else
755 command = c->next;
756 break;
757 }
758 lc = c;
759 }
760 qlock(&row.lk);
761 t = &row.tag;
762 textcommit(t, TRUE);
763 if(c == nil){
764 /* helper processes use this exit status */
765 if(strncmp(w->msg, "libthread", 9) != 0){
766 p = emalloc(sizeof(Pid));
767 p->pid = pid;
768 strncpy(p->msg, w->msg, sizeof(p->msg));
769 p->next = pids;
770 pids = p;
771 }
772 }else{
773 if(search(t, c->name, c->nname)){
774 textdelete(t, t->q0, t->q1, TRUE);
775 textsetselect(t, 0, 0);
776 }
777 if(w->msg[0])
778 warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
779 flushimage(display, 1);
780 }
781 qunlock(&row.lk);
782 free(w);
783 Freecmd:
784 if(c){
785 if(c->iseditcmd)
786 sendul(cedit, 0);
787 free(c->text);
788 free(c->name);
789 fsysdelid(c->md);
790 free(c);
791 }
792 break;
793 case WCmd:
794 /* has this command already exited? */
795 lastp = nil;
796 for(p=pids; p!=nil; p=p->next){
797 if(p->pid == c->pid){
798 if(p->msg[0])
799 warning(c->md, "%s\n", p->msg);
800 if(lastp == nil)
801 pids = p->next;
802 else
803 lastp->next = p->next;
804 free(p);
805 goto Freecmd;
806 }
807 lastp = p;
808 }
809 c->next = command;
810 command = c;
811 qlock(&row.lk);
812 t = &row.tag;
813 textcommit(t, TRUE);
814 textinsert(t, 0, c->name, c->nname, TRUE);
815 textsetselect(t, 0, 0);
816 flushimage(display, 1);
817 qunlock(&row.lk);
818 break;
819 }
820 }
821 }
822
823 void
824 xfidallocthread(void *v)
825 {
826 Xfid *xfree, *x;
827 enum { Alloc, Free, N };
828 static Alt alts[N+1];
829
830 USED(v);
831 threadsetname("xfidallocthread");
832 alts[Alloc].c = cxfidalloc;
833 alts[Alloc].v = nil;
834 alts[Alloc].op = CHANRCV;
835 alts[Free].c = cxfidfree;
836 alts[Free].v = &x;
837 alts[Free].op = CHANRCV;
838 alts[N].op = CHANEND;
839
840 xfree = nil;
841 for(;;){
842 switch(alt(alts)){
843 case Alloc:
844 x = xfree;
845 if(x)
846 xfree = x->next;
847 else{
848 x = emalloc(sizeof(Xfid));
849 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
850 chansetname(x->c, "xc%p", x->c);
851 x->arg = x;
852 threadcreate(xfidctl, x->arg, STACK);
853 }
854 sendp(cxfidalloc, x);
855 break;
856 case Free:
857 x->next = xfree;
858 xfree = x;
859 break;
860 }
861 }
862 }
863
864 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
865 void
866 newwindowthread(void *v)
867 {
868 Window *w;
869
870 USED(v);
871 threadsetname("newwindowthread");
872
873 for(;;){
874 /* only fsysproc is talking to us, so synchronization is trivial */
875 recvp(cnewwindow);
876 w = makenewwindow(nil);
877 winsettag(w);
878 xfidlog(w, "new");
879 sendp(cnewwindow, w);
880 }
881 }
882
883 Reffont*
884 rfget(int fix, int save, int setfont, char *name)
885 {
886 Reffont *r;
887 Font *f;
888 int i;
889
890 r = nil;
891 if(name == nil){
892 name = fontnames[fix];
893 r = reffonts[fix];
894 }
895 if(r == nil){
896 for(i=0; i<nfontcache; i++)
897 if(strcmp(name, fontcache[i]->f->name) == 0){
898 r = fontcache[i];
899 goto Found;
900 }
901 f = openfont(display, name);
902 if(f == nil){
903 warning(nil, "can't open font file %s: %r\n", name);
904 return nil;
905 }
906 r = emalloc(sizeof(Reffont));
907 r->f = f;
908 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
909 fontcache[nfontcache++] = r;
910 }
911 Found:
912 if(save){
913 incref(&r->ref);
914 if(reffonts[fix])
915 rfclose(reffonts[fix]);
916 reffonts[fix] = r;
917 if(name != fontnames[fix]){
918 free(fontnames[fix]);
919 fontnames[fix] = estrdup(name);
920 }
921 }
922 if(setfont){
923 reffont.f = r->f;
924 incref(&r->ref);
925 rfclose(reffonts[0]);
926 font = r->f;
927 reffonts[0] = r;
928 incref(&r->ref);
929 iconinit();
930 }
931 incref(&r->ref);
932 return r;
933 }
934
935 void
936 rfclose(Reffont *r)
937 {
938 int i;
939
940 if(decref(&r->ref) == 0){
941 for(i=0; i<nfontcache; i++)
942 if(r == fontcache[i])
943 break;
944 if(i >= nfontcache)
945 warning(nil, "internal error: can't find font in cache\n");
946 else{
947 nfontcache--;
948 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
949 }
950 freefont(r->f);
951 free(r);
952 }
953 }
954
955 Cursor boxcursor = {
956 {-7, -7},
957 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
958 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
959 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
960 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
961 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
962 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
963 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
964 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
965 };
966
967 Cursor2 boxcursor2 = {
968 {-15, -15},
969 {0xFF, 0xFF, 0xFF, 0xFF,
970 0xFF, 0xFF, 0xFF, 0xFF,
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, 0xC0, 0x03, 0xFF,
980 0xFF, 0xC0, 0x03, 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, 0xFF, 0xFF, 0xFF,
992 0xFF, 0xFF, 0xFF, 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 {0x00, 0x00, 0x00, 0x00,
1002 0x00, 0x00, 0x00, 0x00,
1003 0x3F, 0xFF, 0xFF, 0xFC,
1004 0x3F, 0xFF, 0xFF, 0xFC,
1005 0x3F, 0xFF, 0xFF, 0xFC,
1006 0x3F, 0xFF, 0xFF, 0xFC,
1007 0x3F, 0xFF, 0xFF, 0xFC,
1008 0x3F, 0xFF, 0xFF, 0xFC,
1009 0x3F, 0x00, 0x00, 0xFC,
1010 0x3F, 0x00, 0x00, 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, 0xFF, 0xFF, 0xFC,
1026 0x3F, 0xFF, 0xFF, 0xFC,
1027 0x3F, 0xFF, 0xFF, 0xFC,
1028 0x3F, 0xFF, 0xFF, 0xFC,
1029 0x3F, 0xFF, 0xFF, 0xFC,
1030 0x3F, 0xFF, 0xFF, 0xFC,
1031 0x00, 0x00, 0x00, 0x00,
1032 0x00, 0x00, 0x00, 0x00}
1033 };
1034
1035 void
1036 iconinit(void)
1037 {
1038 Rectangle r;
1039 Image *tmp;
1040
1041 if(tagcols[BACK] == nil) {
1042 /* Blue */
1043 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
1044 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
1045 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
1046 tagcols[TEXT] = display->black;
1047 tagcols[HTEXT] = display->black;
1048
1049 /* Yellow */
1050 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
1051 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
1052 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
1053 textcols[TEXT] = display->black;
1054 textcols[HTEXT] = display->black;
1055 }
1056
1057 r = Rect(0, 0, Scrollwid+ButtonBorder, font->height+1);
1058 if(button && eqrect(r, button->r))
1059 return;
1060
1061 if(button){
1062 freeimage(button);
1063 freeimage(modbutton);
1064 freeimage(colbutton);
1065 }
1066
1067 button = allocimage(display, r, screen->chan, 0, DNofill);
1068 draw(button, r, tagcols[BACK], nil, r.min);
1069 r.max.x -= ButtonBorder;
1070 border(button, r, ButtonBorder, tagcols[BORD], ZP);
1071
1072 r = button->r;
1073 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
1074 draw(modbutton, r, tagcols[BACK], nil, r.min);
1075 r.max.x -= ButtonBorder;
1076 border(modbutton, r, ButtonBorder, tagcols[BORD], ZP);
1077 r = insetrect(r, ButtonBorder);
1078 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
1079 draw(modbutton, r, tmp, nil, ZP);
1080 freeimage(tmp);
1081
1082 r = button->r;
1083 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
1084
1085 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
1086 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
1087 }
1088
1089 /*
1090 * /dev/snarf updates when the file is closed, so we must open our own
1091 * fd here rather than use snarffd
1092 */
1093
1094 /* rio truncates larges snarf buffers, so this avoids using the
1095 * service if the string is huge */
1096
1097 #define MAXSNARF 100*1024
1098
1099 void
1100 acmeputsnarf(void)
1101 {
1102 int i, n;
1103 Fmt f;
1104 char *s;
1105
1106 if(snarfbuf.nc==0)
1107 return;
1108 if(snarfbuf.nc > MAXSNARF)
1109 return;
1110
1111 fmtstrinit(&f);
1112 for(i=0; i<snarfbuf.nc; i+=n){
1113 n = snarfbuf.nc-i;
1114 if(n >= NSnarf)
1115 n = NSnarf;
1116 bufread(&snarfbuf, i, snarfrune, n);
1117 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
1118 break;
1119 }
1120 s = fmtstrflush(&f);
1121 if(s && s[0])
1122 putsnarf(s);
1123 free(s);
1124 }
1125
1126 void
1127 acmegetsnarf(void)
1128 {
1129 char *s;
1130 int nb, nr, nulls, len;
1131 Rune *r;
1132
1133 s = getsnarf();
1134 if(s == nil || s[0]==0){
1135 free(s);
1136 return;
1137 }
1138
1139 len = strlen(s);
1140 r = runemalloc(len+1);
1141 cvttorunes(s, len, r, &nb, &nr, &nulls);
1142 bufreset(&snarfbuf);
1143 bufinsert(&snarfbuf, 0, r, nr);
1144 free(r);
1145 free(s);
1146 }
1147
1148 int
1149 ismtpt(char *file)
1150 {
1151 int n;
1152
1153 if(mtpt == nil)
1154 return 0;
1155
1156 /* This is not foolproof, but it will stop a lot of them. */
1157 n = strlen(mtpt);
1158 return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0);
1159 }
1160
1161 int
1162 timefmt(Fmt *f)
1163 {
1164 Tm *tm;
1165
1166 tm = localtime(va_arg(f->args, ulong));
1167 return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d",
1168 tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);
1169 }