xref: /plan9/sys/src/cmd/rio/wctl.c (revision 3ff48bf5ed603850fcd251ddf13025d23d693782)
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