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 "dat.h"
12 #include "fns.h"
13 /* for generating syms in mkfile only: */
14 #include <bio.h>
15 #include "edit.h"
17 void mousethread(void*);
18 void keyboardthread(void*);
19 void waitthread(void*);
20 void xfidallocthread(void*);
21 void newwindowthread(void*);
22 void plumbproc(void*);
24 Reffont **fontcache;
25 int nfontcache;
26 char wdir[512] = ".";
27 Reffont *reffonts[2];
28 int snarffd = -1;
29 int mainpid;
31 enum{
32 NSnarf = 1000 /* less than 1024, I/O buffer size */
33 };
34 Rune snarfrune[NSnarf+1];
36 char *fontnames[2] =
37 {
38 "/lib/font/bit/lucidasans/euro.8.font",
39 "/lib/font/bit/lucm/unicode.9.font",
40 };
42 Command *command;
44 void acmeerrorinit(void);
45 void readfile(Column*, char*);
46 static int shutdown(void*, char*);
48 void
49 derror(Display *d, char *errorstr)
50 {
51 USED(d);
52 error(errorstr);
53 }
55 void
56 threadmain(int argc, char *argv[])
57 {
58 int i;
59 char *p, *loadfile;
60 Column *c;
61 int ncol;
62 Display *d;
64 rfork(RFENVG|RFNAMEG);
66 ncol = -1;
68 loadfile = nil;
69 ARGBEGIN{
70 case 'a':
71 globalautoindent = TRUE;
72 break;
73 case 'b':
74 bartflag = TRUE;
75 break;
76 case 'c':
77 p = ARGF();
78 if(p == nil)
79 goto Usage;
80 ncol = atoi(p);
81 if(ncol <= 0)
82 goto Usage;
83 break;
84 case 'f':
85 fontnames[0] = ARGF();
86 if(fontnames[0] == nil)
87 goto Usage;
88 break;
89 case 'F':
90 fontnames[1] = ARGF();
91 if(fontnames[1] == nil)
92 goto Usage;
93 break;
94 case 'l':
95 loadfile = ARGF();
96 if(loadfile == nil)
97 goto Usage;
98 break;
99 case 'W':
100 winsize = ARGF();
101 if(winsize == nil)
102 goto Usage;
103 break;
104 default:
105 Usage:
106 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
107 exits("usage");
108 }ARGEND
110 quotefmtinstall();
111 cputype = getenv("cputype");
112 objtype = getenv("objtype");
113 home = getenv("home");
114 if(home == nil)
115 home = getenv("HOME");
116 p = getenv("tabstop");
117 if(p != nil){
118 maxtab = strtoul(p, nil, 0);
119 free(p);
121 if(maxtab == 0)
122 maxtab = 4;
123 if(loadfile)
124 rowloadfonts(loadfile);
125 putenv("font", fontnames[0]);
126 snarffd = open("/dev/snarf", OREAD|OCEXEC);
127 /*
128 if(cputype){
129 sprint(buf, "/acme/bin/%s", cputype);
130 bind(buf, "/bin", MBEFORE);
132 bind("/acme/bin", "/bin", MBEFORE);
133 */
134 getwd(wdir, sizeof wdir);
136 /*
137 if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
138 fprint(2, "acme: can't open display: %r\n");
139 exits("geninitdraw");
141 */
142 if(initdraw(derror, fontnames[0], "acme") < 0){
143 fprint(2, "acme: can't open display: %r\n");
144 exits("initdraw");
147 d = display;
148 font = d->defaultfont;
149 //assert(font);
151 reffont.f = font;
152 reffonts[0] = &reffont;
153 incref(&reffont.ref); /* one to hold up 'font' variable */
154 incref(&reffont.ref); /* one to hold up reffonts[0] */
155 fontcache = emalloc(sizeof(Reffont*));
156 nfontcache = 1;
157 fontcache[0] = &reffont;
159 iconinit();
160 timerinit();
161 rxinit();
163 cwait = threadwaitchan();
164 ccommand = chancreate(sizeof(Command**), 0);
165 ckill = chancreate(sizeof(Rune*), 0);
166 cxfidalloc = chancreate(sizeof(Xfid*), 0);
167 cxfidfree = chancreate(sizeof(Xfid*), 0);
168 cnewwindow = chancreate(sizeof(Channel*), 0);
169 cerr = chancreate(sizeof(char*), 0);
170 cedit = chancreate(sizeof(int), 0);
171 cexit = chancreate(sizeof(int), 0);
172 cwarn = chancreate(sizeof(void*), 1);
173 if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
174 fprint(2, "acme: can't create initial channels: %r\n");
175 exits("channels");
178 mousectl = initmouse(nil, screen);
179 if(mousectl == nil){
180 fprint(2, "acme: can't initialize mouse: %r\n");
181 exits("mouse");
183 mouse = &mousectl->m;
184 keyboardctl = initkeyboard(nil);
185 if(keyboardctl == nil){
186 fprint(2, "acme: can't initialize keyboard: %r\n");
187 exits("keyboard");
189 mainpid = getpid();
190 startplumbing();
191 /*
192 plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
193 if(plumbeditfd < 0)
194 fprint(2, "acme: can't initialize plumber: %r\n");
195 else{
196 cplumb = chancreate(sizeof(Plumbmsg*), 0);
197 threadcreate(plumbproc, nil, STACK);
199 plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
200 */
202 fsysinit();
204 #define WPERCOL 8
205 disk = diskinit();
206 if(loadfile)
207 rowload(&row, loadfile, TRUE);
208 else{
209 rowinit(&row, screen->clipr);
210 if(ncol < 0){
211 if(argc == 0)
212 ncol = 2;
213 else{
214 ncol = (argc+(WPERCOL-1))/WPERCOL;
215 if(ncol < 2)
216 ncol = 2;
219 if(ncol == 0)
220 ncol = 2;
221 for(i=0; i<ncol; i++){
222 c = rowadd(&row, nil, -1);
223 if(c==nil && i==0)
224 error("initializing columns");
226 c = row.col[row.ncol-1];
227 if(argc == 0)
228 readfile(c, wdir);
229 else
230 for(i=0; i<argc; i++){
231 p = utfrrune(argv[i], '/');
232 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
233 readfile(c, argv[i]);
234 else
235 readfile(row.col[i/WPERCOL], argv[i]);
238 flushimage(display, 1);
240 acmeerrorinit();
241 threadcreate(keyboardthread, nil, STACK);
242 threadcreate(mousethread, nil, STACK);
243 threadcreate(waitthread, nil, STACK);
244 threadcreate(xfidallocthread, nil, STACK);
245 threadcreate(newwindowthread, nil, STACK);
247 threadnotify(shutdown, 1);
248 recvul(cexit);
249 killprocs();
250 threadexitsall(nil);
253 void
254 readfile(Column *c, char *s)
256 Window *w;
257 Rune rb[256];
258 int nb, nr;
259 Runestr rs;
261 w = coladd(c, nil, nil, -1);
262 cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
263 rs = cleanrname(runestr(rb, nr));
264 winsetname(w, rs.r, rs.nr);
265 textload(&w->body, 0, s, 1);
266 w->body.file->mod = FALSE;
267 w->dirty = FALSE;
268 winsettag(w);
269 textscrdraw(&w->body);
270 textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
273 char *oknotes[] ={
274 "delete",
275 "hangup",
276 "kill",
277 "exit",
278 nil
279 };
281 int dumping;
283 static int
284 shutdown(void *v, char *msg)
286 int i;
288 if(strcmp(msg, "sys: write on closed pipe") == 0)
289 return 1;
291 USED(v);
292 killprocs();
293 if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
294 dumping = TRUE;
295 rowdump(&row, nil);
297 for(i=0; oknotes[i]; i++)
298 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
299 threadexitsall(msg);
300 print("acme: %s\n", msg);
301 abort();
302 return 0;
305 void
306 killprocs(void)
308 Command *c;
310 fsysclose();
311 // if(display)
312 // flushimage(display, 1);
314 for(c=command; c; c=c->next)
315 postnote(PNGROUP, c->pid, "hangup");
318 static int errorfd;
319 int erroutfd;
321 void
322 acmeerrorproc(void *v)
324 char *buf;
325 int n;
327 USED(v);
328 threadsetname("acmeerrorproc");
329 buf = emalloc(8192+1);
330 while((n=threadread(errorfd, buf, 8192)) >= 0){
331 buf[n] = '\0';
332 sendp(cerr, estrdup(buf));
336 void
337 acmeerrorinit(void)
339 int pfd[2];
341 if(pipe(pfd) < 0)
342 error("can't create pipe");
343 #if 0
344 sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
345 fd = create(acmeerrorfile, OWRITE, 0666);
346 if(fd < 0){
347 remove(acmeerrorfile);
348 fd = create(acmeerrorfile, OWRITE, 0666);
349 if(fd < 0)
350 error("can't create acmeerror file");
352 sprint(buf, "%d", pfd[0]);
353 write(fd, buf, strlen(buf));
354 close(fd);
355 /* reopen pfd[1] close on exec */
356 sprint(buf, "/fd/%d", pfd[1]);
357 errorfd = open(buf, OREAD|OCEXEC);
358 #endif
359 fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
360 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
361 erroutfd = pfd[0];
362 errorfd = pfd[1];
363 if(errorfd < 0)
364 error("can't re-open acmeerror file");
365 threadcreate(acmeerrorproc, nil, STACK);
368 /*
369 void
370 plumbproc(void *v)
372 Plumbmsg *m;
374 USED(v);
375 threadsetname("plumbproc");
376 for(;;){
377 m = threadplumbrecv(plumbeditfd);
378 if(m == nil)
379 threadexits(nil);
380 sendp(cplumb, m);
383 */
385 void
386 keyboardthread(void *v)
388 Rune r;
389 Timer *timer;
390 Text *t;
391 enum { KTimer, KKey, NKALT };
392 static Alt alts[NKALT+1];
394 USED(v);
395 alts[KTimer].c = nil;
396 alts[KTimer].v = nil;
397 alts[KTimer].op = CHANNOP;
398 alts[KKey].c = keyboardctl->c;
399 alts[KKey].v = &r;
400 alts[KKey].op = CHANRCV;
401 alts[NKALT].op = CHANEND;
403 timer = nil;
404 typetext = nil;
405 threadsetname("keyboardthread");
406 for(;;){
407 switch(alt(alts)){
408 case KTimer:
409 timerstop(timer);
410 t = typetext;
411 if(t!=nil && t->what==Tag){
412 winlock(t->w, 'K');
413 wincommit(t->w, t);
414 winunlock(t->w);
415 flushimage(display, 1);
417 alts[KTimer].c = nil;
418 alts[KTimer].op = CHANNOP;
419 break;
420 case KKey:
421 casekeyboard:
422 typetext = rowtype(&row, r, mouse->xy);
423 t = typetext;
424 if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */
425 activecol = t->col;
426 if(t!=nil && t->w!=nil)
427 t->w->body.file->curtext = &t->w->body;
428 if(timer != nil)
429 timercancel(timer);
430 if(t!=nil && t->what==Tag) {
431 timer = timerstart(500);
432 alts[KTimer].c = timer->c;
433 alts[KTimer].op = CHANRCV;
434 }else{
435 timer = nil;
436 alts[KTimer].c = nil;
437 alts[KTimer].op = CHANNOP;
439 if(nbrecv(keyboardctl->c, &r) > 0)
440 goto casekeyboard;
441 flushimage(display, 1);
442 break;
447 void
448 mousethread(void *v)
450 Text *t, *argt;
451 int but;
452 uint q0, q1;
453 Window *w;
454 Plumbmsg *pm;
455 Mouse m;
456 char *act;
457 enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
458 static Alt alts[NMALT+1];
460 USED(v);
461 threadsetname("mousethread");
462 alts[MResize].c = mousectl->resizec;
463 alts[MResize].v = nil;
464 alts[MResize].op = CHANRCV;
465 alts[MMouse].c = mousectl->c;
466 alts[MMouse].v = &mousectl->m;
467 alts[MMouse].op = CHANRCV;
468 alts[MPlumb].c = cplumb;
469 alts[MPlumb].v = &pm;
470 alts[MPlumb].op = CHANRCV;
471 alts[MWarnings].c = cwarn;
472 alts[MWarnings].v = nil;
473 alts[MWarnings].op = CHANRCV;
474 if(cplumb == nil)
475 alts[MPlumb].op = CHANNOP;
476 alts[NMALT].op = CHANEND;
478 for(;;){
479 qlock(&row.lk);
480 flushwarnings();
481 qunlock(&row.lk);
482 flushimage(display, 1);
483 switch(alt(alts)){
484 case MResize:
485 if(getwindow(display, Refnone) < 0)
486 error("attach to window");
487 draw(screen, screen->r, display->white, nil, ZP);
488 scrlresize();
489 rowresize(&row, screen->clipr);
490 break;
491 case MPlumb:
492 if(strcmp(pm->type, "text") == 0){
493 act = plumblookup(pm->attr, "action");
494 if(act==nil || strcmp(act, "showfile")==0)
495 plumblook(pm);
496 else if(strcmp(act, "showdata")==0)
497 plumbshow(pm);
499 plumbfree(pm);
500 break;
501 case MWarnings:
502 break;
503 case MMouse:
504 /*
505 * Make a copy so decisions are consistent; mousectl changes
506 * underfoot. Can't just receive into m because this introduces
507 * another race; see /sys/src/libdraw/mouse.c.
508 */
509 m = mousectl->m;
510 qlock(&row.lk);
511 t = rowwhich(&row, m.xy);
512 if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
513 winlock(mousetext->w, 'M');
514 mousetext->eq0 = ~0;
515 wincommit(mousetext->w, mousetext);
516 winunlock(mousetext->w);
518 mousetext = t;
519 if(t == nil)
520 goto Continue;
521 w = t->w;
522 if(t==nil || m.buttons==0)
523 goto Continue;
524 but = 0;
525 if(m.buttons == 1)
526 but = 1;
527 else if(m.buttons == 2)
528 but = 2;
529 else if(m.buttons == 4)
530 but = 3;
531 barttext = t;
532 if(t->what==Body && ptinrect(m.xy, t->scrollr)){
533 if(but){
534 winlock(w, 'M');
535 t->eq0 = ~0;
536 textscroll(t, but);
537 winunlock(w);
539 goto Continue;
541 /* scroll buttons, wheels, etc. */
542 if(t->what==Body && w != nil && (m.buttons & (8|16))){
543 if(m.buttons & 8)
544 but = Kscrolloneup;
545 else
546 but = Kscrollonedown;
547 winlock(w, 'M');
548 t->eq0 = ~0;
549 texttype(t, but);
550 winunlock(w);
551 goto Continue;
553 if(ptinrect(m.xy, t->scrollr)){
554 if(but){
555 if(t->what == Columntag)
556 rowdragcol(&row, t->col, but);
557 else if(t->what == Tag){
558 coldragwin(t->col, t->w, but);
559 if(t->w)
560 barttext = &t->w->body;
562 if(t->col)
563 activecol = t->col;
565 goto Continue;
567 if(m.buttons){
568 if(w)
569 winlock(w, 'M');
570 t->eq0 = ~0;
571 if(w)
572 wincommit(w, t);
573 else
574 textcommit(t, TRUE);
575 if(m.buttons & 1){
576 textselect(t);
577 if(w)
578 winsettag(w);
579 argtext = t;
580 seltext = t;
581 if(t->col)
582 activecol = t->col; /* button 1 only */
583 if(t->w!=nil && t==&t->w->body)
584 activewin = t->w;
585 }else if(m.buttons & 2){
586 if(textselect2(t, &q0, &q1, &argt))
587 execute(t, q0, q1, FALSE, argt);
588 }else if(m.buttons & 4){
589 if(textselect3(t, &q0, &q1))
590 look3(t, q0, q1, FALSE);
592 if(w)
593 winunlock(w);
594 goto Continue;
596 Continue:
597 qunlock(&row.lk);
598 break;
603 /*
604 * There is a race between process exiting and our finding out it was ever created.
605 * This structure keeps a list of processes that have exited we haven't heard of.
606 */
607 typedef struct Pid Pid;
608 struct Pid
610 int pid;
611 char msg[ERRMAX];
612 Pid *next;
613 };
615 void
616 waitthread(void *v)
618 Waitmsg *w;
619 Command *c, *lc;
620 uint pid;
621 int found, ncmd;
622 Rune *cmd;
623 char *err;
624 Text *t;
625 Pid *pids, *p, *lastp;
626 enum { WErr, WKill, WWait, WCmd, NWALT };
627 Alt alts[NWALT+1];
629 USED(v);
630 threadsetname("waitthread");
631 pids = nil;
632 alts[WErr].c = cerr;
633 alts[WErr].v = &err;
634 alts[WErr].op = CHANRCV;
635 alts[WKill].c = ckill;
636 alts[WKill].v = &cmd;
637 alts[WKill].op = CHANRCV;
638 alts[WWait].c = cwait;
639 alts[WWait].v = &w;
640 alts[WWait].op = CHANRCV;
641 alts[WCmd].c = ccommand;
642 alts[WCmd].v = &c;
643 alts[WCmd].op = CHANRCV;
644 alts[NWALT].op = CHANEND;
646 command = nil;
647 for(;;){
648 switch(alt(alts)){
649 case WErr:
650 qlock(&row.lk);
651 warning(nil, "%s", err);
652 free(err);
653 flushimage(display, 1);
654 qunlock(&row.lk);
655 break;
656 case WKill:
657 found = FALSE;
658 ncmd = runestrlen(cmd);
659 for(c=command; c; c=c->next){
660 /* -1 for blank */
661 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
662 if(postnote(PNGROUP, c->pid, "kill") < 0)
663 warning(nil, "kill %S: %r\n", cmd);
664 found = TRUE;
667 if(!found)
668 warning(nil, "Kill: no process %S\n", cmd);
669 free(cmd);
670 break;
671 case WWait:
672 pid = w->pid;
673 lc = nil;
674 for(c=command; c; c=c->next){
675 if(c->pid == pid){
676 if(lc)
677 lc->next = c->next;
678 else
679 command = c->next;
680 break;
682 lc = c;
684 qlock(&row.lk);
685 t = &row.tag;
686 textcommit(t, TRUE);
687 if(c == nil){
688 /* helper processes use this exit status */
689 if(strncmp(w->msg, "libthread", 9) != 0){
690 p = emalloc(sizeof(Pid));
691 p->pid = pid;
692 strncpy(p->msg, w->msg, sizeof(p->msg));
693 p->next = pids;
694 pids = p;
696 }else{
697 if(search(t, c->name, c->nname)){
698 textdelete(t, t->q0, t->q1, TRUE);
699 textsetselect(t, 0, 0);
701 if(w->msg[0])
702 warning(c->md, "%S: %s\n", c->name, w->msg);
703 flushimage(display, 1);
705 qunlock(&row.lk);
706 free(w);
707 Freecmd:
708 if(c){
709 if(c->iseditcmd)
710 sendul(cedit, 0);
711 free(c->text);
712 free(c->name);
713 fsysdelid(c->md);
714 free(c);
716 break;
717 case WCmd:
718 /* has this command already exited? */
719 lastp = nil;
720 for(p=pids; p!=nil; p=p->next){
721 if(p->pid == c->pid){
722 if(p->msg[0])
723 warning(c->md, "%s\n", p->msg);
724 if(lastp == nil)
725 pids = p->next;
726 else
727 lastp->next = p->next;
728 free(p);
729 goto Freecmd;
731 lastp = p;
733 c->next = command;
734 command = c;
735 qlock(&row.lk);
736 t = &row.tag;
737 textcommit(t, TRUE);
738 textinsert(t, 0, c->name, c->nname, TRUE);
739 textsetselect(t, 0, 0);
740 flushimage(display, 1);
741 qunlock(&row.lk);
742 break;
747 void
748 xfidallocthread(void *v)
750 Xfid *xfree, *x;
751 enum { Alloc, Free, N };
752 static Alt alts[N+1];
754 USED(v);
755 threadsetname("xfidallocthread");
756 alts[Alloc].c = cxfidalloc;
757 alts[Alloc].v = nil;
758 alts[Alloc].op = CHANRCV;
759 alts[Free].c = cxfidfree;
760 alts[Free].v = &x;
761 alts[Free].op = CHANRCV;
762 alts[N].op = CHANEND;
764 xfree = nil;
765 for(;;){
766 switch(alt(alts)){
767 case Alloc:
768 x = xfree;
769 if(x)
770 xfree = x->next;
771 else{
772 x = emalloc(sizeof(Xfid));
773 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
774 x->arg = x;
775 threadcreate(xfidctl, x->arg, STACK);
777 sendp(cxfidalloc, x);
778 break;
779 case Free:
780 x->next = xfree;
781 xfree = x;
782 break;
787 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
788 void
789 newwindowthread(void *v)
791 Window *w;
793 USED(v);
794 threadsetname("newwindowthread");
796 for(;;){
797 /* only fsysproc is talking to us, so synchronization is trivial */
798 recvp(cnewwindow);
799 w = makenewwindow(nil);
800 winsettag(w);
801 sendp(cnewwindow, w);
805 Reffont*
806 rfget(int fix, int save, int setfont, char *name)
808 Reffont *r;
809 Font *f;
810 int i;
812 r = nil;
813 if(name == nil){
814 name = fontnames[fix];
815 r = reffonts[fix];
817 if(r == nil){
818 for(i=0; i<nfontcache; i++)
819 if(strcmp(name, fontcache[i]->f->name) == 0){
820 r = fontcache[i];
821 goto Found;
823 f = openfont(display, name);
824 if(f == nil){
825 fprint(2, "can't open font file %s: %r\n", name);
826 warning(nil, "can't open font file %s: %r\n", name);
827 return nil;
829 r = emalloc(sizeof(Reffont));
830 r->f = f;
831 fontcache = realloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
832 fontcache[nfontcache++] = r;
834 Found:
835 if(save){
836 incref(&r->ref);
837 if(reffonts[fix])
838 rfclose(reffonts[fix]);
839 reffonts[fix] = r;
840 free(fontnames[fix]);
841 fontnames[fix] = name;
843 if(setfont){
844 reffont.f = r->f;
845 incref(&r->ref);
846 rfclose(reffonts[0]);
847 font = r->f;
848 reffonts[0] = r;
849 incref(&r->ref);
850 iconinit();
852 incref(&r->ref);
853 return r;
856 void
857 rfclose(Reffont *r)
859 int i;
861 if(decref(&r->ref) == 0){
862 for(i=0; i<nfontcache; i++)
863 if(r == fontcache[i])
864 break;
865 if(i >= nfontcache)
866 warning(nil, "internal error: can't find font in cache\n");
867 else{
868 nfontcache--;
869 memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
871 freefont(r->f);
872 free(r);
876 Cursor boxcursor = {
877 {-7, -7},
878 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
879 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
880 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
881 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
882 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
883 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
884 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
885 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
886 };
888 void
889 iconinit(void)
891 Rectangle r;
892 Image *tmp;
894 /* Blue */
895 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
896 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
897 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
898 tagcols[TEXT] = display->black;
899 tagcols[HTEXT] = display->black;
901 /* Yellow */
902 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
903 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
904 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
905 textcols[TEXT] = display->black;
906 textcols[HTEXT] = display->black;
908 if(button){
909 freeimage(button);
910 freeimage(modbutton);
911 freeimage(colbutton);
914 r = Rect(0, 0, Scrollwid+2, font->height+1);
915 button = allocimage(display, r, screen->chan, 0, DNofill);
916 draw(button, r, tagcols[BACK], nil, r.min);
917 r.max.x -= 2;
918 border(button, r, 2, tagcols[BORD], ZP);
920 r = button->r;
921 modbutton = allocimage(display, r, screen->chan, 0, DNofill);
922 draw(modbutton, r, tagcols[BACK], nil, r.min);
923 r.max.x -= 2;
924 border(modbutton, r, 2, tagcols[BORD], ZP);
925 r = insetrect(r, 2);
926 tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
927 draw(modbutton, r, tmp, nil, ZP);
928 freeimage(tmp);
930 r = button->r;
931 colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
933 but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
934 but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
937 /*
938 * /dev/snarf updates when the file is closed, so we must open our own
939 * fd here rather than use snarffd
940 */
942 /* rio truncates larges snarf buffers, so this avoids using the
943 * service if the string is huge */
945 #define MAXSNARF 100*1024
947 void
948 acmeputsnarf(void)
950 int i, n;
951 Fmt f;
952 char *s;
954 if(snarfbuf.nc==0)
955 return;
956 if(snarfbuf.nc > MAXSNARF)
957 return;
959 fmtstrinit(&f);
960 for(i=0; i<snarfbuf.nc; i+=n){
961 n = snarfbuf.nc-i;
962 if(n >= NSnarf)
963 n = NSnarf;
964 bufread(&snarfbuf, i, snarfrune, n);
965 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
966 break;
968 s = fmtstrflush(&f);
969 if(s && s[0])
970 putsnarf(s);
971 free(s);
974 void
975 acmegetsnarf(void)
977 char *s;
978 int nb, nr, nulls, len;
979 Rune *r;
981 s = getsnarf();
982 if(s == nil || s[0]==0){
983 free(s);
984 return;
987 len = strlen(s);
988 r = runemalloc(len+1);
989 cvttorunes(s, len, r, &nb, &nr, &nulls);
990 bufreset(&snarfbuf);
991 bufinsert(&snarfbuf, 0, r, nr);
992 free(r);
993 free(s);