xref: /plan9/sys/src/cmd/abaco/main.c (revision 7ab27030036b6c877a6f81728daeda263d1ca3cf)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <thread.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <cursor.h>
9 #include <frame.h>
10 #include <regexp.h>
11 #include <plumb.h>
12 #include <html.h>
13 #include "dat.h"
14 #include "fns.h"
15 
16 enum {
17 	WPERCOL = 8,
18 };
19 void	mousethread(void *);
20 void	keyboardthread(void *);
21 void	iconinit(void);
22 void	plumbproc(void*);
23 
24 Channel	*cexit;
25 Channel	*cplumb;
26 Mousectl *mousectl;
27 
28 char *fontnames[2] = {
29 	"/lib/font/bit/lucidasans/unicode.8.font",
30 	"/lib/font/bit/lucidasans/passwd.6.font",
31 };
32 
33 int	snarffd = -1;
34 int	mainpid;
35 int	plumbwebfd;
36 int	plumbsendfd ;
37 char	*webmountpt = "/mnt/web";
38 char	*charset = "iso-8859-1";
39 int	mainstacksize = STACK;
40 
41 void	readpage(Column *, char *);
42 int	shutdown(void *, char *);
43 
44 void
derror(Display *,char * s)45 derror(Display *, char *s)
46 {
47 	error(s);
48 }
49 
50 static void
usage(void)51 usage(void)
52 {
53 	fprint(2, "usage: %s [-c ncol] [-m mtpt] [-t charset] [url...]\n",
54 		argv0);
55 	exits("usage");
56 }
57 
58 void
threadmain(int argc,char * argv[])59 threadmain(int argc, char *argv[])
60 {
61 	Column *c;
62 	char buf[256];
63 	int i, ncol;
64 
65 	rfork(RFENVG|RFNAMEG);
66 
67 	ncol = 1;
68 	ARGBEGIN{
69 	case 'c':
70 		ncol = atoi(EARGF(usage()));
71 		if(ncol <= 0)
72 			usage();
73 		break;
74 	case 'm':
75 		webmountpt = EARGF(usage());
76 		break;
77 	case 'p':
78 		procstderr++;
79 		break;
80 	case 't':
81 		charset = EARGF(usage());
82 		break;
83 	default:
84 		usage();
85 		break;
86 	}ARGEND
87 
88 	snprint(buf, sizeof(buf), "%s/ctl", webmountpt);
89 	webctlfd = open(buf, ORDWR);
90 	if(webctlfd < 0)
91 		sysfatal("can't initialize webfs: %r");
92 
93 	snarffd = open("/dev/snarf", OREAD|OCEXEC);
94 
95 	if(initdraw(derror, fontnames[0], "abaco") < 0)
96 		sysfatal("can't open display: %r");
97 	memimageinit();
98 	iconinit();
99 	timerinit();
100 	initfontpaths();
101 
102 	cexit = chancreate(sizeof(int), 0);
103 	crefresh = chancreate(sizeof(Page *), 0);
104 	if(cexit==nil || crefresh==nil)
105 		sysfatal("can't create initial channels: %r");
106 
107 	mousectl = initmouse(nil, screen);
108 	if(mousectl == nil)
109 		sysfatal("can't initialize mouse: %r");
110 	mouse = mousectl;
111 	keyboardctl = initkeyboard(nil);
112 	if(keyboardctl == nil)
113 		sysfatal("can't initialize keyboard: %r");
114 	mainpid = getpid();
115 	plumbwebfd = plumbopen("web", OREAD|OCEXEC);
116 	if(plumbwebfd >= 0){
117 		cplumb = chancreate(sizeof(Plumbmsg*), 0);
118 		proccreate(plumbproc, nil, STACK);
119 	}
120 	plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
121 
122 	rowinit(&row, screen->clipr);
123 	for(i=0; i<ncol; i++){
124 		c = rowadd(&row, nil, -1);
125 		if(c==nil && i==0)
126 			error("initializing columns");
127 	}
128 	c = row.col[row.ncol-1];
129 	for(i=0; i<argc; i++)
130 		if(i/WPERCOL >= row.ncol)
131 			readpage(c, argv[i]);
132 		else
133 			readpage(row.col[i/WPERCOL], argv[i]);
134 	flushimage(display, 1);
135 	threadcreate(keyboardthread, nil, STACK);
136 	threadcreate(mousethread, nil, STACK);
137 
138 	threadnotify(shutdown, 1);
139 	recvul(cexit);
140 	threadexitsall(nil);
141 }
142 
143 void
readpage(Column * c,char * s)144 readpage(Column *c, char *s)
145 {
146 	Window *w;
147 	Runestr rs;
148 
149 	w = coladd(c, nil, nil, -1);
150 	bytetorunestr(s, &rs);
151 	pageget(&w->page, &rs, nil, HGet, TRUE);
152 	closerunestr(&rs);
153 }
154 
155 char *oknotes[] = {
156 	"delete",
157 	"hangup",
158 	"kill",
159 	"exit",
160 	nil
161 };
162 
163 int
shutdown(void *,char * msg)164 shutdown(void*, char *msg)
165 {
166 	int i;
167 
168 	for(i=0; oknotes[i]; i++)
169 		if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
170 			threadexitsall(msg);
171 	print("abaco: %s\n", msg);
172 //	abort();
173 	return 0;
174 }
175 
176 void
plumbproc(void *)177 plumbproc(void *)
178 {
179 	Plumbmsg *m;
180 
181 	threadsetname("plumbproc");
182 	for(;;){
183 		m = plumbrecv(plumbwebfd);
184 		if(m == nil)
185 			threadexits(nil);
186 		sendp(cplumb, m);
187 	}
188 }
189 
190 enum { KTimer, KKey, NKALT, };
191 
192 void
keyboardthread(void *)193 keyboardthread(void *)
194 {
195 	Timer *timer;
196 	Text *t;
197 	Rune r;
198 
199 	static Alt alts[NKALT+1];
200 
201 	alts[KTimer].c = nil;
202 	alts[KTimer].v = nil;
203 	alts[KTimer].op = CHANNOP;
204 	alts[KKey].c = keyboardctl->c;
205 	alts[KKey].v = &r;
206 	alts[KKey].op = CHANRCV;
207 	alts[NKALT].op = CHANEND;
208 
209 	timer = nil;
210 	threadsetname("keyboardthread");
211 	for(;;){
212 		switch(alt(alts)){
213 		case KTimer:
214 			timerstop(timer);
215 			alts[KTimer].c = nil;
216 			alts[KTimer].op = CHANNOP;
217 			break;
218 		case KKey:
219 		casekeyboard:
220 			typetext = rowwhich(&row, mouse->xy, r, TRUE);
221 			t = typetext;
222 			if(t!=nil && t->col!=nil &&
223 			    !(r==Kdown || r==Kleft || r==Kright))
224 				/* scrolling doesn't change activecol */
225 				activecol = t->col;
226 			if(timer != nil)
227 				timercancel(timer);
228 			if(t!=nil){
229 				texttype(t, r);
230 				timer = timerstart(500);
231 				alts[KTimer].c = timer->c;
232 				alts[KTimer].op = CHANRCV;
233 			}else{
234 				timer = nil;
235 				alts[KTimer].c = nil;
236 				alts[KTimer].op = CHANNOP;
237 			}
238 			if(nbrecv(keyboardctl->c, &r) > 0)
239 				goto casekeyboard;
240 			flushimage(display, 1);
241 			break;
242 		}
243 	}
244 }
245 
246 void
mousethread(void *)247 mousethread(void *)
248 {
249 	Plumbmsg *pm;
250 	Mouse m;
251 	Text *t;
252 	int but;
253 	enum { MResize, MMouse, MPlumb, MRefresh, NMALT };
254 	static Alt alts[NMALT+1];
255 
256 	threadsetname("mousethread");
257 	alts[MResize].c = mousectl->resizec;
258 	alts[MResize].v = nil;
259 	alts[MResize].op = CHANRCV;
260 	alts[MMouse].c = mousectl->c;
261 	alts[MMouse].v = &mousectl->Mouse;
262 	alts[MMouse].op = CHANRCV;
263 	alts[MPlumb].c = cplumb;
264 	alts[MPlumb].v = &pm;
265 	alts[MPlumb].op = CHANRCV;
266 	alts[MRefresh].c = crefresh;
267 	alts[MRefresh].v = nil;
268 	alts[MRefresh].op = CHANRCV;
269 	if(cplumb == nil)
270 		alts[MPlumb].op = CHANNOP;
271 	alts[NMALT].op = CHANEND;
272 
273 	for(;;){
274 		qlock(&row);
275 		flushrefresh();
276 		qunlock(&row);
277 		flushimage(display, 1);
278 		switch(alt(alts)){
279 		case MResize:
280 			if(getwindow(display, Refnone) < 0)
281 				error("resized");
282 			scrlresize();
283 			tmpresize();
284 			rowresize(&row, screen->clipr);
285 			break;
286 		case MPlumb:
287 			plumblook(pm);
288 			plumbfree(pm);
289 			break;
290 		case MRefresh:
291 			break;
292 		case MMouse:
293 			m = mousectl->Mouse;
294 			if(m.buttons == 0)
295 				continue;
296 
297 			qlock(&row);
298 			but = 0;
299 			if(m.buttons == 1)
300 				but = 1;
301 			else if(m.buttons == 2)
302 				but = 2;
303 			else if(m.buttons == 4)
304 				but = 3;
305 
306 			if(m.buttons & (8|16)){
307 				if(m.buttons & 8)
308 					but = Kscrolloneup;
309 				else
310 					but = Kscrollonedown;
311 				rowwhich(&row, m.xy, but, TRUE);
312 			}else	if(but){
313 				t = rowwhich(&row, m.xy, but, FALSE);
314 				if(t)
315 					textmouse(t, m.xy, but);
316 			}
317 			qunlock(&row);
318 			break;
319 		}
320 	}
321 }
322 
323 Cursor boxcursor = {
324 	{-7, -7},
325 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
326 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
327 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
328 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
329 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
330 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
331 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
332 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
333 };
334 
335 void
iconinit(void)336 iconinit(void)
337 {
338 	Rectangle r;
339 
340 	/* Green */
341 	tagcols[BACK] = allocimagemix(display, DPalegreen, DWhite);
342 	if(tagcols[BACK] == nil)
343 		error("allocimagemix");
344 	tagcols[HIGH] = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen);
345 	tagcols[BORD] = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen);
346 	tagcols[TEXT] = display->black;
347 	tagcols[HTEXT] = display->black;
348 
349 	/* Grey */
350 	textcols[BACK] = display->white;
351 	textcols[HIGH] = eallocimage(display, Rect(0,0,1,1), CMAP8,1, 0xCCCCCCFF);
352 	textcols[BORD] = display->black;
353 	textcols[TEXT] = display->black;
354 	textcols[HTEXT] = display->black;
355 
356 	r = Rect(0, 0, Scrollsize+2, font->height+1);
357 	button = eallocimage(display, r, screen->chan, 0, DNofill);
358 	draw(button, r, tagcols[BACK], nil, r.min);
359 	r.max.x -= 2;
360 	border(button, r, 2, tagcols[BORD], ZP);
361 
362 	r = button->r;
363 	colbutton = eallocimage(display, r, screen->chan, 0, 0x00994CFF);
364 
365 	but2col = eallocimage(display, Rect(0,0,1,2), screen->chan, 1, 0xAA0000FF);
366 	but3col = eallocimage(display, Rect(0,0,1,2), screen->chan, 1, 0x444488FF);
367 
368 	passfont = openfont(display, fontnames[1]);
369 	if(passfont == nil)
370 		error("openfont");
371 }
372 
373 /*
374  * /dev/snarf updates when the file is closed, so we must open our own
375  * fd here rather than use snarffd
376  */
377 
378 /*
379  * rio truncates large snarf buffers, so this avoids using the
380  * service if the string is huge
381  */
382 
383 enum
384 {
385 	NSnarf = 1000,
386 	MAXSNARF = 100*1024,
387 };
388 
389 void
putsnarf(Runestr * rs)390 putsnarf(Runestr *rs)
391 {
392 	int fd, i, n;
393 
394 	if(snarffd<0 || rs->nr==0)
395 		return;
396 	if(rs->nr > MAXSNARF)
397 		return;
398 	fd = open("/dev/snarf", OWRITE);
399 	if(fd < 0)
400 		return;
401 	for(i=0; i<rs->nr; i+=n){
402 		n = rs->nr-i;
403 		if(n > NSnarf)
404 			n =NSnarf;
405 		if(fprint(fd, "%.*S", n, rs->r) < 0)
406 			break;
407 	}
408 	close(fd);
409 }
410 
411 void
getsnarf(Runestr * rs)412 getsnarf(Runestr *rs)
413 {
414 	int i, n, nb, nulls;
415 	char *sn, buf[BUFSIZE];
416 
417 	if(snarffd < 0)
418 		return;
419 	sn = nil;
420 	i = 0;
421 	seek(snarffd, 0, 0);
422 	while((n=read(snarffd, buf, sizeof(buf))) > 0){
423 		sn = erealloc(sn, i+n+1);
424 		memmove(sn+i, buf, n);
425 		i += n;
426 		sn[i] = 0;
427 	}
428 	if(i > 0){
429 		rs->r = runemalloc(i+1);
430 		cvttorunes(sn, i, rs->r, &nb, &rs->nr, &nulls);
431 		free(sn);
432 	}
433 }
434