]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/main.c
unsticky scrollbars
[plan9front.git] / sys / src / cmd / abaco / main.c
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 = &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
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 }