]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/winwatch.c
winwatch: always reflow when possible (thanks telephil9)
[plan9front.git] / sys / src / cmd / winwatch.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <regexp.h>
7 #include <keyboard.h>
8
9 enum {
10         VISIBLE = 1,
11         CURRENT = 2,
12 };
13
14 typedef struct Win Win;
15 struct Win {
16         int n;
17         int dirty;
18         int state;
19         char *label;
20         Rectangle r;
21 };
22
23 Reprog  *exclude  = nil;
24 Win *win;
25 int nwin;
26 int mwin;
27 int onwin;
28 int rows, cols;
29 Image *lightblue;
30 Image *statecol[4];
31
32 enum {
33         PAD = 3,
34         MARGIN = 5
35 };
36
37 void*
38 erealloc(void *v, ulong n)
39 {
40         v = realloc(v, n);
41         if(v == nil)
42                 sysfatal("out of memory reallocating %lud", n);
43         return v;
44 }
45
46 void*
47 emalloc(ulong n)
48 {
49         void *v;
50
51         v = malloc(n);
52         if(v == nil)
53                 sysfatal("out of memory allocating %lud", n);
54         memset(v, 0, n);
55         return v;
56 }
57
58 char*
59 estrdup(char *s)
60 {
61         int l;
62         char *t;
63
64         if (s == nil)
65                 return nil;
66         l = strlen(s)+1;
67         t = emalloc(l);
68         memcpy(t, s, l);
69
70         return t;
71 }
72
73 int
74 readfile(char *buf, int nbuf, char *file, ...)
75 {
76         va_list arg;
77         int n, fd;
78
79         va_start(arg, file);
80         vsnprint(buf, nbuf, file, arg);
81         va_end(arg);
82
83         if((fd = open(buf, OREAD)) < 0){
84                 buf[0] = 0;
85                 return -1;
86         }
87         n = read(fd, buf, nbuf-1);
88         close(fd);
89         if(n < 0){
90                 buf[0] = 0;
91                 return -1;
92         }
93         buf[n] = 0;
94         return n;
95 }
96
97 void
98 refreshwin(void)
99 {
100         char label[128], wctl[128], *tok[8];
101         int i, fd, n, nr, nw, state;
102         static int mywinid = -1;
103         Dir *pd;
104
105         if(mywinid < 0){
106                 if(readfile(wctl, sizeof(wctl), "/dev/winid") > 0)
107                         mywinid = atoi(wctl);
108         }
109
110         if((fd = open("/dev/wsys", OREAD)) < 0)
111                 return;
112
113         nw = 0;
114 /* i'd rather read one at a time but rio won't let me */
115         while((nr=dirread(fd, &pd)) > 0){
116                 for(i=0; i<nr; i++){
117                         n = atoi(pd[i].name);
118                         if(n == mywinid)
119                                 continue;
120                         if(readfile(label, sizeof(label), "/dev/wsys/%d/label", n) < 0)
121                                 continue;
122                         if(exclude != nil && regexec(exclude,label,nil,0))
123                                 continue;
124                         if(readfile(wctl, sizeof(wctl), "/dev/wsys/%d/wctl", n) <= 0)
125                                 continue;
126                         if(tokenize(wctl, tok, nelem(tok)) != 6)
127                                 continue;
128                         state = 0;
129                         if(strcmp(tok[4], "current") == 0)
130                                 state |= CURRENT;
131                         if(strcmp(tok[5], "visible") == 0)
132                                 state |= VISIBLE;
133                         if(nw < nwin && win[nw].n == n && win[nw].state == state && 
134                            strcmp(win[nw].label, label)==0){
135                                 nw++;
136                                 continue;
137                         }
138         
139                         if(nw < nwin){
140                                 free(win[nw].label);
141                                 win[nw].label = nil;
142                         }
143                         
144                         if(nw >= mwin){
145                                 mwin += 8;
146                                 win = erealloc(win, mwin*sizeof(win[0]));
147                         }
148                         win[nw].n = n;
149                         win[nw].label = estrdup(label);
150                         win[nw].state = state;
151                         win[nw].dirty = 1;
152                         win[nw].r = Rect(0,0,0,0);
153                         nw++;
154                 }
155                 free(pd);
156         }
157         while(nwin > nw)
158                 free(win[--nwin].label);
159         nwin = nw;
160         close(fd);
161 }
162
163 void
164 drawnowin(int i)
165 {
166         Rectangle r;
167
168         r = Rect(0,0,(Dx(screen->r)-2*MARGIN+PAD)/cols-PAD, font->height);
169         r = rectaddpt(rectaddpt(r, Pt(MARGIN+(PAD+Dx(r))*(i/rows),
170                                 MARGIN+(PAD+Dy(r))*(i%rows))), screen->r.min);
171         draw(screen, insetrect(r, -1), lightblue, nil, ZP);
172 }
173
174 void
175 drawwin(int i)
176 {
177         draw(screen, win[i].r, statecol[win[i].state], nil, ZP);
178         _string(screen, addpt(win[i].r.min, Pt(2,0)), display->black, ZP,
179                 font, win[i].label, nil, strlen(win[i].label), 
180                 win[i].r, nil, ZP, SoverD);
181         border(screen, win[i].r, 1, display->black, ZP);        
182         win[i].dirty = 0;
183 }
184
185 int
186 geometry(void)
187 {
188         int i, nrows, ncols, z;
189         Rectangle r;
190
191         z = 0;
192         nrows = (Dy(screen->r)-2*MARGIN+PAD)/(font->height+PAD);
193         if(nrows <= 0)
194                 nrows = 1;
195         if(nrows != rows){
196                 rows = nrows;
197                 z = 1;
198         }
199         ncols = nwin <= 0 ? 1 : (nwin+rows-1)/rows;
200         if(ncols != cols){
201                 cols = ncols;
202                 z = 1;
203         }
204
205         r = Rect(0,0,(Dx(screen->r)-2*MARGIN+PAD)/cols-PAD, font->height);
206         for(i=0; i<nwin; i++)
207                 win[i].r = rectaddpt(rectaddpt(r, Pt(MARGIN+(PAD+Dx(r))*(i/rows),
208                                         MARGIN+(PAD+Dy(r))*(i%rows))), screen->r.min);
209
210         return z;
211 }
212
213 void
214 redraw(Image *screen, int all)
215 {
216         int i;
217
218         all |= geometry();
219         if(all)
220                 draw(screen, screen->r, lightblue, nil, ZP);
221         for(i=0; i<nwin; i++)
222                 if(all || win[i].dirty)
223                         drawwin(i);
224         if(!all)
225                 for(; i<onwin; i++)
226                         drawnowin(i);
227
228         onwin = nwin;
229 }
230
231 void
232 eresized(int new)
233 {
234         if(new && getwindow(display, Refmesg) < 0)
235                 fprint(2,"can't reattach to window");
236         geometry();
237         redraw(screen, 1);
238 }
239
240 int
241 label(Win w, Mouse m)
242 {
243         char buf[512], fname[128];
244         int n, fd;
245
246         snprint(buf, sizeof(buf), "%s", w.label);
247         n = eenter(nil, buf, sizeof(buf), &m);
248         if(n <= 0)
249                 return 0;
250         sprint(fname, "/dev/wsys/%d/label", w.n);
251         if((fd = open(fname, OWRITE)) < 0)
252                 return 0;
253         write(fd, buf, n);
254         close(fd);
255         refreshwin();
256         redraw(screen, 1);
257         return 1;
258 }
259
260 int
261 unhide(Win w)
262 {
263         char buf[128];
264         int fd;
265
266         sprint(buf, "/dev/wsys/%d/wctl", w.n);
267         if((fd = open(buf, OWRITE)) < 0)
268                 return 0;
269         if(w.state == (CURRENT|VISIBLE))
270                 write(fd, "hide\n", 5);
271         else {
272                 write(fd, "unhide\n", 7);
273                 write(fd, "top\n", 4);
274                 write(fd, "current\n", 8);
275         }
276         close(fd);
277         return 1;
278 }
279
280 int
281 click(Mouse m)
282 {
283         int i, b;
284
285         b = m.buttons & 7;
286         if(b != 2 && b != 4)
287                 return 0;
288         for(i=0; i<nwin; i++)
289                 if(ptinrect(m.xy, win[i].r))
290                         break;
291         if(i == nwin)
292                 return 0;
293         do
294                 m = emouse();
295         while((m.buttons & 7) == b);
296         if((m.buttons & 7) || !ptinrect(m.xy, win[i].r))
297                 return 0;
298
299         switch(b) {
300         case 2:
301                 return label(win[i], m);
302         case 4:
303                 return unhide(win[i]);
304         default:
305                 return 0;
306         }
307 }
308
309 void
310 usage(void)
311 {
312         fprint(2, "usage: winwatch [-e exclude] [-f font]\n");
313         exits("usage");
314 }
315
316 void
317 main(int argc, char **argv)
318 {
319         char *fontname = nil;
320         int Etimer;
321         Event e;
322         int i;
323
324         ARGBEGIN{
325         case 'f':
326                 fontname = EARGF(usage());
327                 break;
328         case 'e':
329                 exclude = regcomp(EARGF(usage()));
330                 if(exclude == nil)
331                         sysfatal("Bad regexp");
332                 break;
333         default:
334                 usage();
335         }ARGEND
336
337         if(argc)
338                 usage();
339
340         if(initdraw(0, fontname, "winwatch") < 0)
341                 sysfatal("initdraw: %r");
342         lightblue = allocimagemix(display, DPalebluegreen, DWhite);
343
344         statecol[0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
345         statecol[1] = lightblue;
346         statecol[2] = lightblue;
347         statecol[3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
348
349         for(i=0; i<nelem(statecol); i++)
350                 if(statecol[i] == nil)
351                         sysfatal("allocimage: %r");
352
353         refreshwin();
354         redraw(screen, 1);
355         einit(Emouse|Ekeyboard);
356         Etimer = etimer(0, 2500);
357
358         for(;;){
359                 switch(eread(Emouse|Ekeyboard|Etimer, &e)){
360                 case Ekeyboard:
361                         if(e.kbdc==Kdel || e.kbdc=='q')
362                                 exits(0);
363                         break;
364                 case Emouse:
365                         if(click(e.mouse) == 0)
366                                 continue;
367                         /* fall through  */
368                 default:        /* Etimer */
369                         refreshwin();
370                         redraw(screen, 0);
371                         break;
372                 }
373         }
374 }