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 45 derror(Display *, char *s) 46 { 47 error(s); 48 } 49 50 static 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 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 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 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 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 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 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 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 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 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