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