1 /* Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com>
2  * See LICENSE file for license details.
3  */
4 #include "dat.h"
5 #include <assert.h>
6 #include <ctype.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include "fns.h"
11 
12 static char
13 	Ebadcmd[] = "bad command",
14 	Ebadvalue[] = "bad value",
15 	Ebadusage[] = "bad usage";
16 
17 /* Edit |sort   Edit s/"([^"]+)"/L\1/g   Edit |tr 'a-z' 'A-Z' */
18 enum {
19 	LFULLSCREEN,
20 	LURGENT,
21 	LBORDER,
22 	LCLIENT,
23 	LCOLMODE,
24 	LDOWN,
25 	LEXEC,
26 	LFOCUSCOLORS,
27 	LFONT,
28 	LGRABMOD,
29 	LKILL,
30 	LLEFT,
31 	LNORMCOLORS,
32 	LOFF,
33 	LON,
34 	LQUIT,
35 	LRIGHT,
36 	LSELCOLORS,
37 	LSELECT,
38 	LSEND,
39 	LSWAP,
40 	LTOGGLE,
41 	LUP,
42 	LVIEW,
43 	LTILDE,
44 };
45 char *symtab[] = {
46 	"Fullscreen",
47 	"Urgent",
48 	"border",
49 	"client",
50 	"colmode",
51 	"down",
52 	"exec",
53 	"focuscolors",
54 	"font",
55 	"grabmod",
56 	"kill",
57 	"left",
58 	"normcolors",
59 	"off",
60 	"on",
61 	"quit",
62 	"right",
63 	"selcolors",
64 	"select",
65 	"send",
66 	"swap",
67 	"toggle",
68 	"up",
69 	"view",
70 	"~",
71 };
72 
73 /* Edit ,y/^[a-zA-Z].*\n.* {\n/d
74  * Edit s/^([a-zA-Z].*)\n(.*) {\n/\1 \2;\n/
75  * Edit ,x/^static.*\n/d
76  */
77 
78 static int
getsym(char * s)79 getsym(char *s) {
80 	int i, n, m, cmp;
81 
82 	if(s == nil)
83 		return -1;
84 
85 	n = nelem(symtab);
86 	i = 0;
87 	while(n) {
88 		m = n/2;
89 		cmp = strcmp(s, symtab[i+m]);
90 		if(cmp == 0)
91 			return i+m;
92 		if(cmp < 0 || m == 0)
93 			n = m;
94 		else {
95 			i += m;
96 			n = n-m;
97 		}
98 	}
99 	return -1;
100 }
101 
102 static int
gettoggle(IxpMsg * m)103 gettoggle(IxpMsg *m) {
104 	switch(getsym(getword(m))) {
105 	case LON:
106 		return On;
107 	case LOFF:
108 		return Off;
109 	case LTOGGLE:
110 		return Toggle;
111 	default:
112 		return -1;
113 	}
114 }
115 
116 static void
eatrunes(IxpMsg * m,int (* p)(Rune),int val)117 eatrunes(IxpMsg *m, int (*p)(Rune), int val) {
118 	Rune r;
119 	int n;
120 
121 	while(m->pos < m->end) {
122 		n = chartorune(&r, (char*)m->pos);
123 		if(p(r) != val)
124 			break;
125 		m->pos += n;
126 	}
127 	if(m->pos > m->end)
128 		m->pos = m->end;
129 }
130 
131 char *
getword(IxpMsg * m)132 getword(IxpMsg *m) {
133 	char *ret;
134 	Rune r;
135 	int n;
136 
137 	eatrunes(m, isspacerune, 1);
138 	ret = (char*)m->pos;
139 	eatrunes(m, isspacerune, 0);
140 	n = chartorune(&r, (char*)m->pos);
141 	*m->pos = '\0';
142 	m->pos += n;
143 	eatrunes(m, isspacerune, 1);
144 
145 	if(ret == (char*)m->end)
146 		return nil;
147 	return ret;
148 }
149 
150 #define strbcmp(str, const) (strncmp((str), (const), sizeof(const)-1))
151 static int
getbase(char ** s)152 getbase(char **s) {
153 	char *p;
154 
155 	p = *s;
156 	if(!strbcmp(p, "0x")) {
157 		*s += 2;
158 		return 16;
159 	}
160 	if(isdigit(p[0]) && p[1] == 'r') {
161 		*s += 2;
162 		return p[0] - '0';
163 	}
164 	if(isdigit(p[0]) && isdigit(p[1]) && p[2] == 'r') {
165 		*s += 3;
166 		return 10*(p[0]-'0') + (p[1]-'0');
167 	}
168 	if(!strbcmp(p, "0")) {
169 		*s += 1;
170 		return 8;
171 	}
172 	return 10;
173 }
174 
175 int
getlong(char * s,long * ret)176 getlong(char *s, long *ret) {
177 	char *end, *rend;
178 	int base;
179 
180 	end = s+strlen(s);
181 	base = getbase(&s);
182 
183 	*ret = strtol(s, &rend, base);
184 	return (end == rend);
185 }
186 
187 int
getulong(char * s,ulong * ret)188 getulong(char *s, ulong *ret) {
189 	char *end, *rend;
190 	int base;
191 
192 	end = s+strlen(s);
193 	base = getbase(&s);
194 
195 	*ret = strtoul(s, &rend, base);
196 	return (end == rend);
197 }
198 
199 static Client *
strclient(View * v,char * s)200 strclient(View *v, char *s) {
201 	ulong id;
202 
203 	if(!strcmp(s, "sel"))
204 		return view_selclient(v);
205 	if(getulong(s, &id))
206 		return win2client(id);
207 
208 	return nil;
209 }
210 
211 Area *
strarea(View * v,char * s)212 strarea(View *v, char *s) {
213 	Area *a;
214 	long i;
215 
216 	if(!strcmp(s, "sel"))
217 		return v->sel;
218 	if(!strcmp(s, "~"))
219 		return v->area;
220 	if(!getlong(s, &i) || i == 0)
221 		return nil;
222 
223 	if(i > 0)
224 		for(a = v->area; a; a = a->next) {
225 			if(i-- == 0) break;
226 		}
227 	else {
228 		for(a = v->area; a->next; a = a->next)
229 			;
230 		for(; a != v->area; a = a->prev)
231 			if(++i == 0) break;
232 		if(a == v->area)
233 			a = nil;
234 	}
235 	return a;
236 }
237 
238 char *
message_view(View * v,IxpMsg * m)239 message_view(View *v, IxpMsg *m) {
240 	Area *a;
241 	char *s;
242 	int i;
243 
244 	s = getword(m);
245 
246 	switch(getsym(s)) {
247 	case LSEND:
248 		return send_client(v, m, 0);
249 	case LSWAP:
250 		return send_client(v, m, 1);
251 	case LSELECT:
252 		return select_area(v->sel, m);
253 	case LCOLMODE:
254 		s = getword(m);
255 		if((a = strarea(v, s)) == nil || a->floating)
256 			return Ebadvalue;
257 
258 		s = getword(m);
259 		if((i = str2colmode(s)) == -1)
260 			return Ebadvalue;
261 
262 		a->mode = i;
263 		arrange_column(a, True);
264 		restack_view(v);
265 
266 		if(v == screen->sel)
267 			focus_view(screen, v);
268 		draw_frames();
269 		return nil;
270 	default:
271 		return Ebadcmd;
272 	}
273 	/* not reached */
274 }
275 
276 char *
parse_colors(IxpMsg * m,CTuple * col)277 parse_colors(IxpMsg *m, CTuple *col) {
278 	static char Ebad[] = "bad color string";
279 	Rune r;
280 	char c, *p;
281 	int i, j;
282 
283 	/* '#%6x #%6x #%6x' */
284 	p = (char*)m->pos;
285 	for(i = 0; i < 3 && p < (char*)m->end; i++) {
286 		if(*p++ != '#')
287 			return Ebad;
288 		for(j = 0; j < 6 && p < (char*)m->end; j++)
289 			if(!isxdigit(*p++))
290 				return Ebad;
291 
292 		chartorune(&r, p);
293 		if(i < 2) {
294 			if(r != ' ')
295 				return Ebad;
296 			p++;
297 		}else if(!isspacerune(r) && *p != '\0')
298 			return Ebad;
299 	}
300 
301 	c = *p;
302 	*p = '\0';
303 	loadcolor(col, (char*)m->pos);
304 	*p = c;
305 
306 	m->pos = (uchar*)p;
307 	eatrunes(m, isspacerune, 1);
308 	return nil;
309 }
310 
311 char *
message_root(void * p,IxpMsg * m)312 message_root(void *p, IxpMsg *m) {
313 	Font *fn;
314 	char *s, *ret;
315 	ulong n;
316 
317 	USED(p);
318 	ret = nil;
319 	s = getword(m);
320 
321 	switch(getsym(s)) {
322 	case LQUIT:
323 		srv.running = 0;
324 		break;
325 	case LEXEC:
326 		execstr = smprint("exec %s", (char*)m->pos);
327 		srv.running = 0;
328 		break;
329 	case LVIEW:
330 		select_view((char*)m->pos);
331 		break;
332 	case LSELCOLORS:
333 		fprint(2, "%s: warning: selcolors have been removed\n", argv0);
334 		return Ebadcmd;
335 	case LFOCUSCOLORS:
336 		ret = parse_colors(m, &def.focuscolor);
337 		focus_view(screen, screen->sel);
338 		break;
339 	case LNORMCOLORS:
340 		ret = parse_colors(m, &def.normcolor);
341 		focus_view(screen, screen->sel);
342 		break;
343 	case LFONT:
344 		fn = loadfont((char*)m->pos);
345 		if(fn) {
346 			freefont(def.font);
347 			def.font = fn;
348 			resize_bar(screen);
349 		}else
350 			ret = "can't load font";
351 		focus_view(screen, screen->sel);
352 		break;
353 	case LBORDER:
354 		if(!getulong(getword(m), &n))
355 			return Ebadvalue;
356 		def.border = n;
357 		focus_view(screen, screen->sel);
358 		break;
359 	case LGRABMOD:
360 		s = getword(m);
361 		n = str2modmask(s);
362 
363 		if((n & (Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) == 0)
364 			return Ebadvalue;
365 
366 		utflcpy(def.grabmod, s, sizeof(def.grabmod));
367 		def.mod = n;
368 		break;
369 	default:
370 		return Ebadcmd;
371 	}
372 	return ret;
373 }
374 
375 char *
read_root_ctl(void)376 read_root_ctl(void) {
377 	char *b, *e;
378 
379 	b = buffer;
380 	e = b + sizeof(buffer) - 1;
381 	b = seprint(b, e, "view %s\n", screen->sel->name);
382 	b = seprint(b, e, "focuscolors %s\n", def.focuscolor.colstr);
383 	b = seprint(b, e, "normcolors %s\n", def.normcolor.colstr);
384 	b = seprint(b, e, "font %s\n", def.font->name);
385 	b = seprint(b, e, "grabmod %s\n", def.grabmod);
386 	b = seprint(b, e, "border %d\n", def.border);
387 	USED(b);
388 	return buffer;
389 }
390 
391 char *
message_client(Client * c,IxpMsg * m)392 message_client(Client *c, IxpMsg *m) {
393 	char *s;
394 	int i;
395 
396 	s = getword(m);
397 
398 	switch(getsym(s)) {
399 	case LKILL:
400 		kill_client(c);
401 		break;
402 	case LURGENT:
403 		i = gettoggle(m);
404 		if(i == -1)
405 			return Ebadusage;
406 		set_urgent(c, i, True);
407 		break;
408 	case LFULLSCREEN:
409 		i = gettoggle(m);
410 		if(i == -1)
411 			return Ebadusage;
412 		fullscreen(c, i);
413 		break;
414 	default:
415 		return Ebadcmd;
416 	}
417 	return nil;
418 }
419 
420 static char*
send_frame(Frame * f,int sym,Bool swap)421 send_frame(Frame *f, int sym, Bool swap) {
422 	Frame *fp;
423 
424 	SET(fp);
425 	switch(sym) {
426 	case LUP:
427 		fp = f->aprev;
428 		if(!fp)
429 			return Ebadvalue;
430 		fp = fp->aprev;
431 		break;
432 	case LDOWN:
433 		fp = f->anext;
434 		if(!fp)
435 			return Ebadvalue;
436 		break;
437 	default:
438 		assert(!"can't get here");
439 	}
440 
441 	if(swap) {
442 		if(!fp)
443 			return Ebadvalue;
444 		swap_frames(f, fp);
445 	}else {
446 		remove_frame(f);
447 		insert_frame(fp, f);
448 	}
449 
450 	arrange_view(f->view);
451 
452 	flushevents(EnterWindowMask, False);
453 	focus_frame(f, True);
454 	update_views();
455 	return nil;
456 }
457 
458 char *
send_client(View * v,IxpMsg * m,Bool swap)459 send_client(View *v, IxpMsg *m, Bool swap) {
460 	Area *to, *a;
461 	Frame *f;
462 	Client *c;
463 	char *s;
464 	ulong i;
465 	int sym;
466 
467 	s = getword(m);
468 
469 	c = strclient(v, s);
470 	if(c == nil)
471 		return Ebadvalue;
472 
473 	f = view_clientframe(v, c);
474 	if(f == nil)
475 		return Ebadvalue;
476 
477 	a = f->area;
478 	to = nil;
479 
480 	s = getword(m);
481 	sym = getsym(s);
482 
483 	switch(sym) {
484 	case LUP:
485 	case LDOWN:
486 		return send_frame(f, sym, swap);
487 	case LLEFT:
488 		if(a->floating)
489 			return Ebadvalue;
490 
491 		if(a->prev != v->area)
492 			to = a->prev;
493 		a = v->area;
494 		break;
495 	case LRIGHT:
496 		if(a->floating)
497 			return Ebadvalue;
498 
499 		to = a->next;
500 		break;
501 	case LTOGGLE:
502 		if(!a->floating)
503 			to = v->area;
504 		else if(c->revert && !c->revert->floating)
505 			to = c->revert;
506 		else
507 			to = v->area->next;
508 		break;
509 	default:
510 		if(!getulong(s, &i) || i == 0)
511 			return Ebadvalue;
512 
513 		for(to=v->area; to; to=to->next)
514 			if(!i--) break;
515 		break;
516 	}
517 
518 	if(!to && !swap && (f->anext || f != f->area->frame))
519 		to = new_column(v, a, 0);
520 
521 	if(!to)
522 		return Ebadvalue;
523 
524 	if(!swap)
525 		send_to_area(to, f);
526 	else if(to->sel)
527 		swap_frames(f, to->sel);
528 	else
529 		return Ebadvalue;
530 
531 	flushevents(EnterWindowMask, False);
532 	focus_frame(f, True);
533 	arrange_view(v);
534 	update_views();
535 	return nil;
536 }
537 
538 static char*
select_frame(Frame * f,IxpMsg * m,int sym)539 select_frame(Frame *f, IxpMsg *m, int sym) {
540 	Frame *fp;
541 	Client *c;
542 	Area *a;
543 	char *s;
544 	ulong i;
545 
546 	if(!f)
547 		return Ebadvalue;
548 	a = f->area;
549 
550 	SET(fp);
551 	switch(sym) {
552 	case LUP:
553 		for(fp = a->frame; fp->anext; fp = fp->anext)
554 			if(fp->anext == f) break;
555 		break;
556 	case LDOWN:
557 		fp = f->anext;
558 		if(fp == nil)
559 			fp = a->frame;
560 		break;
561 	case LCLIENT:
562 		s = getword(m);
563 		if(s == nil || !getulong(s, &i))
564 			return "usage: select client <client>";
565 		c = win2client(i);
566 		if(c == nil)
567 			return "unknown client";
568 		fp = view_clientframe(f->view, c);
569 		break;
570 	default:
571 		assert(!"can't get here");
572 	}
573 
574 	if(fp == nil)
575 		return "invalid selection";
576 
577 	focus_frame(fp, False);
578 	frame_to_top(fp);
579 	if(f->view == screen->sel)
580 		restack_view(f->view);
581 	return nil;
582 }
583 
584 char*
select_area(Area * a,IxpMsg * m)585 select_area(Area *a, IxpMsg *m) {
586 	Frame *f;
587 	Area *ap;
588 	View *v;
589 	char *s;
590 	ulong i;
591 	int sym;
592 
593 	v = a->view;
594 	s = getword(m);
595 	sym = getsym(s);
596 
597 	switch(sym) {
598 	case LTOGGLE:
599 		if(!a->floating)
600 			ap = v->area;
601 		else if(v->revert && v->revert != a)
602 			ap = v->revert;
603 		else
604 			ap = v->area->next;
605 		break;
606 	case LUP:
607 	case LDOWN:
608 	case LCLIENT:
609 		return select_frame(a->sel, m, sym);
610 	case LLEFT:
611 		if(a->floating)
612 			return Ebadvalue;
613 		for(ap=v->area->next; ap->next; ap=ap->next)
614 			if(ap->next == a) break;
615 		break;
616 	case LRIGHT:
617 		if(a->floating)
618 			return Ebadvalue;
619 		ap = a->next;
620 		if(ap == nil)
621 			ap = v->area->next;
622 		break;
623 	case LTILDE:
624 		ap = v->area;
625 		break;
626 	default:
627 		if(!strcmp(s, "sel"))
628 			ap = v->sel;
629 		else {
630 			if(!getulong(s, &i) || i == 0)
631 				return Ebadvalue;
632 			for(ap=v->area->next; ap; ap=ap->next)
633 				if(--i == 0) break;
634 			if(i != 0)
635 				return Ebadvalue;
636 		}
637 		if((s = getword(m))) {
638 			if(!getulong(s, &i))
639 				return Ebadvalue;
640 			for(f = ap->frame; f; f = f->anext)
641 				if(--i == 0) break;
642 			if(i != 0)
643 				return Ebadvalue;
644 			focus_frame(f, True);
645 			return nil;
646 		}
647 	}
648 
649 	focus_area(ap);
650 	return nil;
651 }
652