Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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