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 = ±
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