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
goodrect(Rectangle r)89 goodrect(Rectangle r)
90 {
91 if(!eqrect(canonrect(r), r))
92 return 0;
93 if(Dx(r)<100 || Dy(r)<3*font->height)
94 return 0;
95 /* must have some screen and border visible so we can move it out of the way */
96 if(Dx(r) >= Dx(screen->r) && Dy(r) >= Dy(screen->r))
97 return 0;
98 /* reasonable sizes only please */
99 if(Dx(r) > BIG*Dx(screen->r))
100 return 0;
101 if(Dy(r) > BIG*Dx(screen->r))
102 return 0;
103 return 1;
104 }
105
106 static
107 int
word(char ** sp,char * tab[])108 word(char **sp, char *tab[])
109 {
110 char *s, *t;
111 int i;
112
113 s = *sp;
114 while(isspace(*s))
115 s++;
116 t = s;
117 while(*s!='\0' && !isspace(*s))
118 s++;
119 for(i=0; tab[i]!=nil; i++)
120 if(strncmp(tab[i], t, strlen(tab[i])) == 0){
121 *sp = s;
122 return i;
123 }
124 return -1;
125 }
126
127 int
set(int sign,int neg,int abs,int pos)128 set(int sign, int neg, int abs, int pos)
129 {
130 if(sign < 0)
131 return neg;
132 if(sign > 0)
133 return pos;
134 return abs;
135 }
136
137 Rectangle
newrect(void)138 newrect(void)
139 {
140 static int i = 0;
141 int minx, miny, dx, dy;
142
143 dx = min(600, Dx(screen->r) - 2*Borderwidth);
144 dy = min(400, Dy(screen->r) - 2*Borderwidth);
145 minx = 32 + 16*i;
146 miny = 32 + 16*i;
147 i++;
148 i %= 10;
149
150 return Rect(minx, miny, minx+dx, miny+dy);
151 }
152
153 void
shift(int * minp,int * maxp,int min,int max)154 shift(int *minp, int *maxp, int min, int max)
155 {
156 if(*minp < min){
157 *maxp += min-*minp;
158 *minp = min;
159 }
160 if(*maxp > max){
161 *minp += max-*maxp;
162 *maxp = max;
163 }
164 }
165
166 Rectangle
rectonscreen(Rectangle r)167 rectonscreen(Rectangle r)
168 {
169 shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x);
170 shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y);
171 return r;
172 }
173
174 /* permit square brackets, in the manner of %R */
175 int
riostrtol(char * s,char ** t)176 riostrtol(char *s, char **t)
177 {
178 int n;
179
180 while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='['))
181 s++;
182 if(*s == '[')
183 s++;
184 n = strtol(s, t, 10);
185 if(*t != s)
186 while((*t)[0] == ']')
187 (*t)++;
188 return n;
189 }
190
191
192 int
parsewctl(char ** argp,Rectangle r,Rectangle * rp,int * pidp,int * idp,int * hiddenp,int * scrollingp,char ** cdp,char * s,char * err)193 parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err)
194 {
195 int cmd, param, xy, sign;
196 char *t;
197
198 *pidp = 0;
199 *hiddenp = 0;
200 *scrollingp = scrolling;
201 *cdp = nil;
202 cmd = word(&s, cmds);
203 if(cmd < 0){
204 strcpy(err, "unrecognized wctl command");
205 return -1;
206 }
207 if(cmd == New)
208 r = newrect();
209
210 strcpy(err, "missing or bad wctl parameter");
211 while((param = word(&s, params)) >= 0){
212 switch(param){ /* special cases */
213 case Hidden:
214 *hiddenp = 1;
215 continue;
216 case Scrolling:
217 *scrollingp = 1;
218 continue;
219 case Noscrolling:
220 *scrollingp = 0;
221 continue;
222 case R:
223 r.min.x = riostrtol(s, &t);
224 if(t == s)
225 return -1;
226 s = t;
227 r.min.y = riostrtol(s, &t);
228 if(t == s)
229 return -1;
230 s = t;
231 r.max.x = riostrtol(s, &t);
232 if(t == s)
233 return -1;
234 s = t;
235 r.max.y = riostrtol(s, &t);
236 if(t == s)
237 return -1;
238 s = t;
239 continue;
240 }
241 while(isspace(*s))
242 s++;
243 if(param == Cd){
244 *cdp = s;
245 while(*s && !isspace(*s))
246 s++;
247 if(*s != '\0')
248 *s++ = '\0';
249 continue;
250 }
251 sign = 0;
252 if(*s == '-'){
253 sign = -1;
254 s++;
255 }else if(*s == '+'){
256 sign = +1;
257 s++;
258 }
259 if(!isdigit(*s))
260 return -1;
261 xy = riostrtol(s, &s);
262 switch(param){
263 case -1:
264 strcpy(err, "unrecognized wctl parameter");
265 return -1;
266 case Minx:
267 r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy);
268 break;
269 case Miny:
270 r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy);
271 break;
272 case Maxx:
273 r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy);
274 break;
275 case Maxy:
276 r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy);
277 break;
278 case Deltax:
279 r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy);
280 break;
281 case Deltay:
282 r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy);
283 break;
284 case Id:
285 if(idp != nil)
286 *idp = xy;
287 break;
288 case PID:
289 if(pidp != nil)
290 *pidp = xy;
291 break;
292 }
293 }
294
295 *rp = rectonscreen(rectaddpt(r, screen->r.min));
296
297 while(isspace(*s))
298 s++;
299 if(cmd!=New && *s!='\0'){
300 strcpy(err, "extraneous text in wctl message");
301 return -1;
302 }
303
304 if(argp)
305 *argp = s;
306
307 return cmd;
308 }
309
310 int
wctlnew(Rectangle rect,char * arg,int pid,int hideit,int scrollit,char * dir,char * err)311 wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err)
312 {
313 char **argv;
314 Image *i;
315
316 if(!goodrect(rect)){
317 strcpy(err, Ebadwr);
318 return -1;
319 }
320 argv = emalloc(4*sizeof(char*));
321 argv[0] = "rc";
322 argv[1] = "-c";
323 while(isspace(*arg))
324 arg++;
325 if(*arg == '\0'){
326 argv[1] = "-i";
327 argv[2] = nil;
328 }else{
329 argv[2] = arg;
330 argv[3] = nil;
331 }
332 if(hideit)
333 i = allocimage(display, rect, screen->chan, 0, DWhite);
334 else
335 i = allocwindow(wscreen, rect, Refbackup, DWhite);
336 if(i == nil){
337 strcpy(err, Ewalloc);
338 return -1;
339 }
340 border(i, rect, Selborder, red, ZP);
341
342 new(i, hideit, scrollit, pid, dir, "/bin/rc", argv);
343
344 free(argv); /* when new() returns, argv and args have been copied */
345 return 1;
346 }
347
348 int
writewctl(Xfid * x,char * err)349 writewctl(Xfid *x, char *err)
350 {
351 int cnt, cmd, j, id, hideit, scrollit, pid;
352 Image *i;
353 char *arg, *dir;
354 Rectangle rect;
355 Window *w;
356
357 w = x->f->w;
358 cnt = x->count;
359 x->data[cnt] = '\0';
360 id = 0;
361
362 rect = rectsubpt(w->screenr, screen->r.min);
363 cmd = parsewctl(&arg, rect, &rect, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
364 if(cmd < 0)
365 return -1;
366
367 if(mouse->buttons!=0 && cmd>=Top){
368 strcpy(err, "action disallowed when mouse active");
369 return -1;
370 }
371
372 if(id != 0){
373 for(j=0; j<nwindow; j++)
374 if(window[j]->id == id)
375 break;
376 if(j == nwindow){
377 strcpy(err, "no such window id");
378 return -1;
379 }
380 w = window[j];
381 if(w->deleted || w->i==nil){
382 strcpy(err, "window deleted");
383 return -1;
384 }
385 }
386
387 switch(cmd){
388 case New:
389 return wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
390 case Set:
391 if(pid > 0)
392 wsetpid(w, pid, 0);
393 return 1;
394 case Move:
395 rect = Rect(rect.min.x, rect.min.y, rect.min.x+Dx(w->screenr), rect.min.y+Dy(w->screenr));
396 rect = rectonscreen(rect);
397 /* fall through */
398 case Resize:
399 if(!goodrect(rect)){
400 strcpy(err, Ebadwr);
401 return -1;
402 }
403 if(eqrect(rect, w->screenr))
404 return 1;
405 i = allocwindow(wscreen, rect, Refbackup, DWhite);
406 if(i == nil){
407 strcpy(err, Ewalloc);
408 return -1;
409 }
410 border(i, rect, Selborder, red, ZP);
411 wsendctlmesg(w, Reshaped, i->r, i);
412 return 1;
413 case Scroll:
414 w->scrolling = 1;
415 wshow(w, w->nr);
416 wsendctlmesg(w, Wakeup, ZR, nil);
417 return 1;
418 case Noscroll:
419 w->scrolling = 0;
420 wsendctlmesg(w, Wakeup, ZR, nil);
421 return 1;
422 case Top:
423 wtopme(w);
424 return 1;
425 case Bottom:
426 wbottomme(w);
427 return 1;
428 case Current:
429 wcurrent(w);
430 return 1;
431 case Hide:
432 switch(whide(w)){
433 case -1:
434 strcpy(err, "window already hidden");
435 return -1;
436 case 0:
437 strcpy(err, "hide failed");
438 return -1;
439 default:
440 break;
441 }
442 return 1;
443 case Unhide:
444 for(j=0; j<nhidden; j++)
445 if(hidden[j] == w)
446 break;
447 if(j == nhidden){
448 strcpy(err, "window not hidden");
449 return -1;
450 }
451 if(wunhide(j) == 0){
452 strcpy(err, "hide failed");
453 return -1;
454 }
455 return 1;
456 case Delete:
457 wsendctlmesg(w, Deleted, ZR, nil);
458 return 1;
459 }
460 strcpy(err, "invalid wctl message");
461 return -1;
462 }
463
464 void
wctlthread(void * v)465 wctlthread(void *v)
466 {
467 char *buf, *arg, *dir;
468 int cmd, id, pid, hideit, scrollit;
469 Rectangle rect;
470 char err[ERRMAX];
471 Channel *c;
472
473 c = v;
474
475 threadsetname("WCTLTHREAD");
476
477 for(;;){
478 buf = recvp(c);
479 cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err);
480
481 switch(cmd){
482 case New:
483 wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
484 }
485 free(buf);
486 }
487 }
488
489 void
wctlproc(void * v)490 wctlproc(void *v)
491 {
492 char *buf;
493 int n, eofs;
494 Channel *c;
495
496 threadsetname("WCTLPROC");
497 c = v;
498
499 eofs = 0;
500 for(;;){
501 buf = emalloc(messagesize);
502 n = read(wctlfd, buf, messagesize-1); /* room for \0 */
503 if(n < 0)
504 break;
505 if(n == 0){
506 if(++eofs > 20)
507 break;
508 continue;
509 }
510 eofs = 0;
511
512 buf[n] = '\0';
513 sendp(c, buf);
514 }
515 }
516