]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rio/wctl.c
rio: use libdraw's badrect() to exclude some extreme cases in goodrect()
[plan9front.git] / sys / src / cmd / rio / wctl.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13 #include <ctype.h>
14
15 char    Ebadwr[]                = "bad rectangle in wctl request";
16 char    Ewalloc[]               = "window allocation failed in wctl request";
17
18 /* >= Top are disallowed if mouse button is pressed */
19 enum
20 {
21         New,
22         Resize,
23         Move,
24         Scroll,
25         Noscroll,
26         Set,
27         Top,
28         Bottom,
29         Current,
30         Hide,
31         Unhide,
32         Delete,
33 };
34
35 static char *cmds[] = {
36         [New]   = "new",
37         [Resize]        = "resize",
38         [Move]  = "move",
39         [Scroll]        = "scroll",
40         [Noscroll]      = "noscroll",
41         [Set]           = "set",
42         [Top]   = "top",
43         [Bottom]        = "bottom",
44         [Current]       = "current",
45         [Hide]  = "hide",
46         [Unhide]        = "unhide",
47         [Delete]        = "delete",
48         nil
49 };
50
51 enum
52 {
53         Cd,
54         Deltax,
55         Deltay,
56         Hidden,
57         Id,
58         Maxx,
59         Maxy,
60         Minx,
61         Miny,
62         PID,
63         R,
64         Scrolling,
65         Noscrolling,
66 };
67
68 static char *params[] = {
69         [Cd]                            = "-cd",
70         [Deltax]                        = "-dx",
71         [Deltay]                        = "-dy",
72         [Hidden]                        = "-hide",
73         [Id]                            = "-id",
74         [Maxx]                  = "-maxx",
75         [Maxy]                  = "-maxy",
76         [Minx]                  = "-minx",
77         [Miny]                  = "-miny",
78         [PID]                           = "-pid",
79         [R]                             = "-r",
80         [Scrolling]                     = "-scroll",
81         [Noscrolling]           = "-noscroll",
82         nil
83 };
84
85 /*
86  * Check that newly created window will be of manageable size
87  */
88 int
89 goodrect(Rectangle r)
90 {
91         if(badrect(r) || !eqrect(canonrect(r), r))
92                 return 0;
93         /* reasonable sizes only please */
94         if(Dx(r) > BIG*Dx(screen->r))
95                 return 0;
96         if(Dy(r) > BIG*Dy(screen->r))
97                 return 0;
98         /*
99          * the height has to be big enough to fit one line of text.
100          * that includes the border on each side with an extra pixel
101          * so that the text is still drawn
102          */
103         if(Dx(r) < 100 || Dy(r) < 2*(Borderwidth+1)+font->height)
104                 return 0;
105         /* window must be on screen */
106         if(!rectXrect(screen->r, r))
107                 return 0;
108         /* must have some screen and border visible so we can move it out of the way */
109         if(rectinrect(screen->r, insetrect(r, Borderwidth)))
110                 return 0;
111         return 1;
112 }
113
114 static
115 int
116 word(char **sp, char *tab[])
117 {
118         char *s, *t;
119         int i;
120
121         s = *sp;
122         while(isspace(*s))
123                 s++;
124         t = s;
125         while(*s!='\0' && !isspace(*s))
126                 s++;
127         for(i=0; tab[i]!=nil; i++)
128                 if(strncmp(tab[i], t, strlen(tab[i])) == 0){
129                         *sp = s;
130                         return i;
131         }
132         return -1;
133 }
134
135 int
136 set(int sign, int neg, int abs, int pos)
137 {
138         if(sign < 0)
139                 return neg;
140         if(sign > 0)
141                 return pos;
142         return abs;
143 }
144
145 Rectangle
146 newrect(void)
147 {
148         static int i = 0;
149         int minx, miny, dx, dy;
150
151         dx = min(600, Dx(screen->r) - 2*Borderwidth);
152         dy = min(400, Dy(screen->r) - 2*Borderwidth);
153         minx = 32 + 16*i;
154         miny = 32 + 16*i;
155         i++;
156         i %= 10;
157
158         return Rect(minx, miny, minx+dx, miny+dy);
159 }
160
161 void
162 shift(int *minp, int *maxp, int min, int max)
163 {
164         if(*maxp > max){
165                 *minp += max-*maxp;
166                 *maxp = max;
167         }
168         if(*minp < min){
169                 *maxp += min-*minp;
170                 if(*maxp > max)
171                         *maxp = max;
172                 *minp = min;
173         }
174 }
175
176 Rectangle
177 rectonscreen(Rectangle r)
178 {
179         shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x);
180         shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y);
181         return r;
182 }
183
184 /* permit square brackets, in the manner of %R */
185 int
186 riostrtol(char *s, char **t)
187 {
188         int n;
189
190         while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='['))
191                 s++;
192         if(*s == '[')
193                 s++;
194         n = strtol(s, t, 10);
195         if(*t != s)
196                 while((*t)[0] == ']')
197                         (*t)++;
198         return n;
199 }
200
201
202 int
203 parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err)
204 {
205         int cmd, param, xy, sign;
206         char *t;
207
208         *pidp = 0;
209         *hiddenp = 0;
210         *scrollingp = scrolling;
211         *cdp = nil;
212         cmd = word(&s, cmds);
213         if(cmd < 0){
214                 strcpy(err, "unrecognized wctl command");
215                 return -1;
216         }
217         if(cmd == New)
218                 r = newrect();
219
220         strcpy(err, "missing or bad wctl parameter");
221         while((param = word(&s, params)) >= 0){
222                 switch(param){  /* special cases */
223                 case Hidden:
224                         *hiddenp = 1;
225                         continue;
226                 case Scrolling:
227                         *scrollingp = 1;
228                         continue;
229                 case Noscrolling:
230                         *scrollingp = 0;
231                         continue;
232                 case R:
233                         r.min.x = riostrtol(s, &t);
234                         if(t == s)
235                                 return -1;
236                         s = t;
237                         r.min.y = riostrtol(s, &t);
238                         if(t == s)
239                                 return -1;
240                         s = t;
241                         r.max.x = riostrtol(s, &t);
242                         if(t == s)
243                                 return -1;
244                         s = t;
245                         r.max.y = riostrtol(s, &t);
246                         if(t == s)
247                                 return -1;
248                         s = t;
249                         continue;
250                 }
251                 while(isspace(*s))
252                         s++;
253                 if(param == Cd){
254                         *cdp = s;
255                         while(*s && !isspace(*s))
256                                 s++;
257                         if(*s != '\0')
258                                 *s++ = '\0';
259                         continue;
260                 }
261                 sign = 0;
262                 if(*s == '-'){
263                         sign = -1;
264                         s++;
265                 }else if(*s == '+'){
266                         sign = +1;
267                         s++;
268                 }
269                 if(!isdigit(*s))
270                         return -1;
271                 xy = riostrtol(s, &s);
272                 switch(param){
273                 case -1:
274                         strcpy(err, "unrecognized wctl parameter");
275                         return -1;
276                 case Minx:
277                         r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy);
278                         break;
279                 case Miny:
280                         r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy);
281                         break;
282                 case Maxx:
283                         r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy);
284                         break;
285                 case Maxy:
286                         r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy);
287                         break;
288                 case Deltax:
289                         r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy);
290                         break;
291                 case Deltay:
292                         r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy);
293                         break;
294                 case Id:
295                         if(idp != nil)
296                                 *idp = xy;
297                         break;
298                 case PID:
299                         if(pidp != nil)
300                                 *pidp = xy;
301                         break;
302                 }
303         }
304
305         *rp = rectonscreen(rectaddpt(r, screen->r.min));
306
307         while(isspace(*s))
308                 s++;
309         if(cmd!=New && *s!='\0'){
310                 strcpy(err, "extraneous text in wctl message");
311                 return -1;
312         }
313
314         if(argp)
315                 *argp = s;
316
317         return cmd;
318 }
319
320 int
321 wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err)
322 {
323         char **argv;
324         Image *i;
325
326         if(!goodrect(rect)){
327                 strcpy(err, Ebadwr);
328                 return -1;
329         }
330         argv = emalloc(4*sizeof(char*));
331         argv[0] = "rc";
332         argv[1] = "-c";
333         while(isspace(*arg))
334                 arg++;
335         if(*arg == '\0'){
336                 argv[1] = "-i";
337                 argv[2] = nil;
338         }else{
339                 argv[2] = arg;
340                 argv[3] = nil;
341         }
342         if(hideit)
343                 i = allocimage(display, rect, screen->chan, 0, DNofill);
344         else
345                 i = allocwindow(wscreen, rect, Refbackup, DNofill);
346         if(i == nil){
347                 strcpy(err, Ewalloc);
348                 return -1;
349         }
350
351         new(i, hideit, scrollit, pid, dir, "/bin/rc", argv);
352
353         free(argv);     /* when new() returns, argv and args have been copied */
354         return 1;
355 }
356
357 int
358 wctlcmd(Window *w, Rectangle r, int cmd, char *err)
359 {
360         Image *i;
361
362         switch(cmd){
363         case Move:
364                 r = Rect(r.min.x, r.min.y, r.min.x+Dx(w->screenr), r.min.y+Dy(w->screenr));
365                 r = rectonscreen(r);
366                 /* fall through */
367         case Resize:
368                 if(!goodrect(r)){
369                         strcpy(err, Ebadwr);
370                         return -1;
371                 }
372                 if(Dx(w->screenr) > 0){
373                         if(eqrect(r, w->screenr))
374                                 return 1;
375                         if(w != input){
376                                 strcpy(err, "window not current");
377                                 return -1;
378                         }
379                         i = allocwindow(wscreen, r, Refbackup, DNofill);
380                 } else { /* hidden */
381                         if(eqrect(r, w->i->r))
382                                 return 1;
383                         i = allocimage(display, r, w->i->chan, 0, DNofill);
384                         r = ZR;
385                 }
386                 if(i == nil){
387                         strcpy(err, Ewalloc);
388                         return -1;
389                 }
390                 wsendctlmesg(w, Reshaped, r, i);
391                 return 1;
392         case Scroll:
393                 w->scrolling = 1;
394                 wshow(w, w->nr);
395                 wsendctlmesg(w, Wakeup, ZR, nil);
396                 return 1;
397         case Noscroll:
398                 w->scrolling = 0;
399                 wsendctlmesg(w, Wakeup, ZR, nil);
400                 return 1;
401         case Top:
402                 wtopme(w);
403                 return 1;
404         case Bottom:
405                 wbottomme(w);
406                 return 1;
407         case Current:
408                 if(Dx(w->screenr)<=0){
409                         strcpy(err, "window is hidden");
410                         return -1;
411                 }
412                 wtopme(w);
413                 wcurrent(w);
414                 return 1;
415         case Hide:
416                 switch(whide(w)){
417                 case -1:
418                         strcpy(err, "window already hidden");
419                         return -1;
420                 case 0:
421                         strcpy(err, "hide failed");
422                         return -1;
423                 default:
424                         break;
425                 }
426                 return 1;
427         case Unhide:
428                 switch(wunhide(w)){
429                 case -1:
430                         strcpy(err, "window not hidden");
431                         return -1;
432                 case 0:
433                         strcpy(err, "hide failed");
434                         return -1;
435                 default:
436                         break;
437                 }
438                 return 1;
439         case Delete:
440                 wsendctlmesg(w, Deleted, ZR, nil);
441                 return 1;
442         }
443
444         strcpy(err, "invalid wctl message");
445         return -1;
446 }
447
448 int
449 writewctl(Xfid *x, char *err)
450 {
451         int cnt, cmd, id, hideit, scrollit, pid;
452         char *arg, *dir;
453         Rectangle r;
454         Window *w;
455
456         w = x->f->w;
457         cnt = x->count;
458         x->data[cnt] = '\0';
459         id = 0;
460
461         r = rectsubpt(w->screenr, screen->r.min);
462         cmd = parsewctl(&arg, r, &r, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
463         if(cmd < 0)
464                 return -1;
465
466         if(id != 0){
467                 w = wlookid(id);
468                 if(w == 0){
469                         strcpy(err, "no such window id");
470                         return -1;
471                 }
472         }
473
474         switch(cmd){
475         case New:
476                 return wctlnew(r, arg, pid, hideit, scrollit, dir, err);
477         case Set:
478                 if(pid > 0)
479                         wsetpid(w, pid, 0);
480                 return 1;
481         }
482
483         incref(w);
484         id = wctlcmd(w, r, cmd, err);
485         wclose(w);
486
487         return id;
488 }
489
490 void
491 wctlthread(void *v)
492 {
493         char *buf, *arg, *dir;
494         int cmd, id, pid, hideit, scrollit;
495         Rectangle rect;
496         char err[ERRMAX];
497         Channel *c;
498
499         c = v;
500
501         threadsetname("WCTLTHREAD");
502
503         for(;;){
504                 buf = recvp(c);
505                 cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err);
506
507                 switch(cmd){
508                 case New:
509                         wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
510                 }
511                 free(buf);
512         }
513 }
514
515 void
516 wctlproc(void *v)
517 {
518         char *buf;
519         int n, eofs;
520         Channel *c;
521
522         threadsetname("WCTLPROC");
523         c = v;
524
525         eofs = 0;
526         for(;;){
527                 buf = emalloc(messagesize);
528                 n = read(wctlfd, buf, messagesize-1);   /* room for \0 */
529                 if(n < 0)
530                         break;
531                 if(n == 0){
532                         if(++eofs > 20)
533                                 break;
534                         continue;
535                 }
536                 eofs = 0;
537
538                 buf[n] = '\0';
539                 sendp(c, buf);
540         }
541 }