message.c (20457B)
1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #include "dat.h" 5 #include <ctype.h> 6 #include "fns.h" 7 8 static char* msg_grow(View*, IxpMsg*); 9 static char* msg_nudge(View*, IxpMsg*); 10 static char* msg_selectframe(Area*, IxpMsg*, int); 11 static char* msg_sendframe(Frame*, int, bool); 12 13 #define DIR(s) (\ 14 s == LUP ? North : \ 15 s == LDOWN ? South : \ 16 s == LLEFT ? West : \ 17 s == LRIGHT ? East : \ 18 (error(Ebadvalue), 0)) 19 20 static char 21 Ebadcmd[] = "bad command", 22 Ebadvalue[] = "bad value", 23 Ebadusage[] = "bad usage"; 24 25 /* Edit |sort Edit |sed 's/"([^"]+)"/L\1/g' | tr 'a-z' 'A-Z' */ 26 enum { 27 LALLOW, 28 LBAR, 29 LBORDER, 30 LCLIENT, 31 LCOLMODE, 32 LCOLORS, 33 LDEBUG, 34 LDOWN, 35 LEXEC, 36 LFLOATING, 37 LFOCUSCOLORS, 38 LFONT, 39 LFONTPAD, 40 LFULLSCREEN, 41 LGRABMOD, 42 LGROUP, 43 LGROW, 44 LINCMODE, 45 LKILL, 46 LLABEL, 47 LLEFT, 48 LNORMCOLORS, 49 LNUDGE, 50 LOFF, 51 LON, 52 LQUIT, 53 LRIGHT, 54 LSELCOLORS, 55 LSELECT, 56 LSEND, 57 LSLAY, 58 LSPAWN, 59 LSWAP, 60 LTAGS, 61 LTOGGLE, 62 LUP, 63 LURGENT, 64 LVIEW, 65 LTILDE, 66 }; 67 char *symtab[] = { 68 "allow", 69 "bar", 70 "border", 71 "client", 72 "colmode", 73 "colors", 74 "debug", 75 "down", 76 "exec", 77 "floating", 78 "focuscolors", 79 "font", 80 "fontpad", 81 "fullscreen", 82 "grabmod", 83 "group", 84 "grow", 85 "incmode", 86 "kill", 87 "label", 88 "left", 89 "normcolors", 90 "nudge", 91 "off", 92 "on", 93 "quit", 94 "right", 95 "selcolors", 96 "select", 97 "send", 98 "slay", 99 "spawn", 100 "swap", 101 "tags", 102 "toggle", 103 "up", 104 "urgent", 105 "view", 106 "~", 107 }; 108 109 static char* barpostab[] = { 110 "bottom", "top", 111 }; 112 char* debugtab[] = { 113 "9p", 114 "dnd", 115 "event", 116 "ewmh", 117 "focus", 118 "generic", 119 "stack", 120 nil 121 }; 122 static char* permtab[] = { 123 "activate", nil 124 }; 125 static char* incmodetab[] = { 126 "ignore", "show", "squeeze", 127 }; 128 static char* floatingtab[] = { 129 "never", "off", "on", "always" 130 }; 131 static char* toggletab[] = { 132 "off", "on", "toggle", 133 }; 134 135 /* Edit ,y/^[a-zA-Z].*\n.* {\n/d 136 * Edit s/^([a-zA-Z].*)\n(.*) {\n/\1 \2;\n/ 137 * Edit ,x/^static.*\n/d 138 */ 139 140 static int 141 _bsearch(char *from, char **tab, int ntab) { 142 int i, n, m, cmp; 143 char *to, *end; 144 Rune r; 145 146 if(from == nil) 147 return -1; 148 149 end = buffer + sizeof buffer - UTFmax - 1; 150 for(to=buffer; *from && to < end;) { 151 from += chartorune(&r, from); 152 if(r != 0x80) { 153 r = tolowerrune(r); 154 to += runetochar(to, &r); 155 } 156 } 157 *to = '\0'; 158 to = buffer; 159 160 n = ntab; 161 i = 0; 162 while(n) { 163 m = n/2; 164 cmp = strcmp(to, tab[i+m]); 165 if(cmp == 0) 166 return i+m; 167 if(cmp < 0 || m == 0) 168 n = m; 169 else { 170 i += m; 171 n = n-m; 172 } 173 } 174 return -1; 175 } 176 177 static int 178 _lsearch(char *from, char **tab, int ntab) { 179 int i; 180 181 if(from != nil) 182 for(i=0; i < ntab; i++) 183 if(!strcmp(from, tab[i])) 184 return i; 185 error(Ebadvalue); 186 return 0; 187 } 188 189 static int 190 getsym(char *s) { 191 return _bsearch(s, symtab, nelem(symtab)); 192 } 193 194 static void 195 setdef(int *ptr, char *s, char *tab[], int ntab) { 196 int i; 197 198 i = _bsearch(s, tab, ntab); 199 if(i < 0) 200 error(Ebadvalue); 201 *ptr = i; 202 } 203 204 static int 205 gettoggle(char *s) { 206 return _lsearch(s, toggletab, nelem(toggletab)); 207 } 208 209 static int 210 getdirection(IxpMsg *m) { 211 int i; 212 213 switch(i = getsym(msg_getword(m, 0))) { 214 case LLEFT: 215 case LRIGHT: 216 case LUP: 217 case LDOWN: 218 return i; 219 } 220 error(Ebadusage); 221 return -1; 222 } 223 224 static ulong 225 msg_getulong(const char *s) { 226 ulong l; 227 228 if(!(s && getulong(s, &l))) 229 error(Ebadvalue); 230 return l; 231 } 232 233 static long 234 msg_getlong(const char *s) { 235 long l; 236 237 if(!(s && getlong(s, &l))) 238 error(Ebadvalue); 239 return l; 240 } 241 242 void 243 msg_eatrunes(IxpMsg *m, int (*p)(Rune), int val) { 244 Rune r; 245 int n; 246 247 while(m->pos < m->end) { 248 n = chartorune(&r, m->pos); 249 if(p(r) != val) 250 break; 251 m->pos += n; 252 } 253 if(m->pos > m->end) 254 m->pos = m->end; 255 } 256 257 char* 258 msg_getword(IxpMsg *m, char *errmsg) { 259 char *ret; 260 Rune r; 261 int n; 262 263 msg_eatrunes(m, isspacerune, true); 264 ret = m->pos; 265 msg_eatrunes(m, isspacerune, false); 266 n = chartorune(&r, m->pos); 267 *m->pos = '\0'; 268 m->pos += n; 269 msg_eatrunes(m, isspacerune, true); 270 271 /* Filter out comments. */ 272 if(*ret == '#') { 273 *ret = '\0'; 274 m->pos = m->end; 275 } 276 if(*ret == '\\') 277 if(ret[1] == '\\' || ret[1] == '#') 278 ret++; 279 if(*ret == '\0') 280 ret = nil; 281 if(ret == nil && errmsg) 282 error(errmsg); 283 return ret; 284 } 285 286 typedef struct Mask Mask; 287 struct Mask { 288 long* mask; 289 char** table; 290 }; 291 292 static int 293 Mfmt(Fmt *f) { 294 Mask m; 295 296 m = va_arg(f->args, Mask); 297 return unmask(f, *m.mask, m.table, '+'); 298 } 299 300 char* 301 mask(char **s, int *add, int *old) { 302 static char seps[] = "+-^"; 303 char *p, *q; 304 305 again: 306 p = *s; 307 if(*old == '\0') 308 return nil; 309 *add = *old; 310 311 if(*p == '/') { 312 /* Check for regex. */ 313 if(!(q = strchr(p+1, '/'))) 314 goto fail; 315 if(*q++ != '/' || !memchr(seps, (*old=*q), sizeof seps)) 316 goto fail; 317 } 318 else { 319 for(q=p; (*old=*q) && !strchr(seps, *q);) 320 q++; 321 if(memchr(p, '/', q-p)) 322 goto fail; 323 } 324 325 *q++ = '\0'; 326 *s = q; 327 if(p + 1 == q) 328 goto again; 329 return p; 330 fail: 331 while((*old=*q) && !strchr(seps, *q)) 332 q++; 333 goto again; 334 } 335 336 static void 337 setmask(Mask m, char *s) { 338 char *opt; 339 int add, old, i, n; 340 long newmask; 341 342 if(s == nil) 343 s = ""; 344 for(n=0; m.table[n]; n++) 345 ; 346 newmask = memchr("+-^", s[0], 3) ? *m.mask : 0L; 347 348 old = '+'; 349 while((opt = mask(&s, &add, &old))) { 350 i = _bsearch(opt, m.table, n); 351 if(i == -1) 352 error(Ebadvalue); 353 else if(add = '^') 354 newmask ^= 1<<i; 355 else if(add == '+') 356 newmask |= 1<<i; 357 else if(add == '-') 358 newmask &= ~(1<<i); 359 } 360 *m.mask = newmask; 361 } 362 363 void 364 msg_debug(char *s) { 365 setmask((Mask){&debugflag, debugtab}, s); 366 } 367 368 static Client* 369 strclient(View *v, char *s) { 370 Client *c; 371 372 /* 373 * sel 374 * 0x<window xid> 375 */ 376 377 if(s && !strcmp(s, "sel")) 378 c = view_selclient(v); 379 else 380 c = win2client(msg_getulong(s)); 381 if(c == nil) 382 error(Ebadvalue); 383 return c; 384 } 385 386 Area* 387 strarea(View *v, ulong scrn, const char *area) { 388 Area *a; 389 const char *screen; 390 char *p; 391 long i; 392 393 /* 394 * sel 395 * ~ 396 * <column number> 397 */ 398 399 if(area == nil) 400 error(Ebadvalue); 401 402 if((p = strchr(area, ':'))) { 403 /* <screen>:<area> */ 404 *p++ = '\0'; 405 screen = area; 406 area = p; 407 408 if(!strcmp(screen, "sel")) 409 scrn = v->selscreen; 410 else 411 scrn = msg_getulong(screen); 412 } 413 else if(!strcmp(area, "sel")) 414 return v->sel; 415 416 if(!strcmp(area, "sel")) { 417 if(scrn != v->selscreen) 418 error(Ebadvalue); 419 return v->sel; 420 } 421 422 if(!strcmp(area, "~")) 423 return v->floating; 424 425 if(scrn < 0) 426 error(Ebadvalue); 427 428 i = msg_getlong(area); 429 if(i == 0) 430 error(Ebadvalue); 431 432 if(i > 0) { 433 for(a = v->areas[scrn]; a; a = a->next) 434 if(i-- == 1) break; 435 } 436 else { 437 /* FIXME: Switch to circularly linked list. */ 438 for(a = v->areas[scrn]; a->next; a = a->next) 439 ; 440 for(; a; a = a->prev) 441 if(++i == 0) break; 442 } 443 if(a == nil) 444 error(Ebadvalue); 445 return a; 446 } 447 448 static Frame* 449 getframe(View *v, int scrn, IxpMsg *m) { 450 Frame *f; 451 Area *a; 452 char *s; 453 ulong l; 454 455 s = msg_getword(m, Ebadvalue); 456 if(!strcmp(s, "client")) 457 f = client_viewframe(strclient(v, msg_getword(m, Ebadvalue)), 458 v); 459 else { 460 /* XXX: Multihead */ 461 a = strarea(v, scrn, s); 462 463 s = msg_getword(m, Ebadvalue); 464 f = nil; 465 if(!strcmp(s, "sel")) 466 f = a->sel; 467 else { 468 l = msg_getulong(s); 469 for(f=a->frame; f; f=f->anext) 470 if(--l == 0) break; 471 } 472 } 473 if(f == nil) 474 error(Ebadvalue); 475 return f; 476 } 477 478 char* 479 readctl_bar(Bar *b) { 480 bufclear(); 481 bufprint("colors %s\n", b->colors.colstr); 482 bufprint("label %s\n", b->text); 483 return buffer; 484 } 485 486 char* 487 message_bar(Bar *b, IxpMsg *m) { 488 489 switch(getsym(msg_getword(m, nil))) { 490 case LCOLORS: 491 msg_parsecolors(m, &b->colors); 492 break; 493 case LLABEL: 494 utflcpy(b->text, (char*)m->pos, sizeof b->text); 495 break; 496 default: 497 error(Ebadvalue); 498 } 499 bar_draw(b->screen); 500 return nil; 501 } 502 503 char* 504 readctl_client(Client *c) { 505 bufclear(); 506 bufprint("%#C\n", c); 507 bufprint("allow %M\n", (Mask){&c->permission, permtab}); 508 bufprint("floating %s\n", floatingtab[c->floating + 1]); 509 if(c->fullscreen >= 0) 510 bufprint("fullscreen %d\n", c->fullscreen); 511 else 512 bufprint("fullscreen off\n"); 513 bufprint("group %#ulx\n", c->group ? c->group->leader : 0); 514 if(c->pid) 515 bufprint("pid %d\n", c->pid); 516 bufprint("tags %s\n", c->tags); 517 bufprint("urgent %s\n", TOGGLE(c->urgent)); 518 return buffer; 519 } 520 521 char* 522 message_client(Client *c, IxpMsg *m) { 523 char *s; 524 long l; 525 526 s = msg_getword(m, Ebadcmd); 527 528 /* 529 * Toggle ::= on 530 * | off 531 * | toggle 532 * | <screen> 533 * floating <toggle> 534 * fullscreen <toggle> 535 * kill 536 * slay 537 * tags <tags> 538 * urgent <toggle> 539 */ 540 541 switch(getsym(s)) { 542 case LALLOW: 543 setmask((Mask){&c->permission, permtab}, msg_getword(m, 0)); 544 break; 545 case LFLOATING: 546 c->floating = -1 + _lsearch(msg_getword(m, Ebadvalue), floatingtab, nelem(floatingtab)); 547 break; 548 case LFULLSCREEN: 549 s = msg_getword(m, Ebadvalue); 550 if(getlong(s, &l)) 551 fullscreen(c, On, l); 552 else 553 fullscreen(c, gettoggle(s), -1); 554 break; 555 case LGROUP: 556 group_remove(c); 557 c->w.hints->group = msg_getulong(msg_getword(m, Ebadvalue)); 558 if(c->w.hints->group) 559 group_init(c); 560 break; 561 case LKILL: 562 client_kill(c, true); 563 break; 564 case LSLAY: 565 client_kill(c, false); 566 break; 567 case LTAGS: 568 client_applytags(c, m->pos); 569 break; 570 case LURGENT: 571 client_seturgent(c, gettoggle(msg_getword(m, Ebadvalue)), UrgManager); 572 break; 573 default: 574 error(Ebadcmd); 575 } 576 return nil; 577 } 578 579 char* 580 message_root(void *p, IxpMsg *m) { 581 Font *fn; 582 char *s, *ret; 583 ulong n; 584 int i; 585 586 USED(p); 587 ret = nil; 588 s = msg_getword(m, 0); 589 if(s == nil) 590 return nil; 591 592 if(!strcmp(s, "backtrace")) { 593 backtrace(m->pos); 594 return nil; 595 } 596 597 if(!strcmp(s, "xinerama")) { 598 setenv("XINERAMA_SCREENS", m->pos, 1); 599 init_screens(); 600 return nil; 601 } 602 603 switch(getsym(s)) { 604 case LBAR: /* bar on? <"top" | "bottom"> */ 605 s = msg_getword(m, Ebadvalue); 606 if(!strcmp(s, "on")) 607 s = msg_getword(m, Ebadvalue); 608 setdef(&screen->barpos, s, barpostab, nelem(barpostab)); 609 view_update(selview); 610 break; 611 case LBORDER: 612 def.border = msg_getulong(msg_getword(m, 0));; 613 view_update(selview); 614 break; 615 case LCOLMODE: 616 setdef(&def.colmode, msg_getword(m, 0), modes, Collast); 617 break; 618 case LDEBUG: 619 msg_debug(msg_getword(m, 0)); 620 break; 621 case LEXEC: 622 execstr = strdup(m->pos); 623 srv.running = 0; 624 break; 625 case LSPAWN: 626 spawn_command(m->pos); 627 break; 628 case LFOCUSCOLORS: 629 msg_parsecolors(m, &def.focuscolor); 630 goto updatecolors; 631 case LFONT: 632 fn = loadfont(m->pos); 633 if(fn) { 634 freefont(def.font); 635 def.font = fn; 636 for(n=0; n < nscreens; n++) 637 bar_resize(screens[n]); 638 }else 639 ret = "can't load font"; 640 view_update(selview); 641 break; 642 case LFONTPAD: 643 if(!getint(msg_getword(m, 0), &def.font->pad.min.x) || 644 !getint(msg_getword(m, 0), &def.font->pad.max.x) || 645 !getint(msg_getword(m, 0), &def.font->pad.max.y) || 646 !getint(msg_getword(m, 0), &def.font->pad.min.y)) 647 ret = "invalid rectangle"; 648 else { 649 for(n=0; n < nscreens; n++) 650 bar_resize(screens[n]); 651 view_update(selview); 652 } 653 break; 654 case LGRABMOD: 655 s = msg_getword(m, Ebadvalue); 656 if(!parsekey(s, &i, nil) || i == 0) 657 return Ebadvalue; 658 659 def.mod = i; 660 break; 661 case LINCMODE: 662 setdef(&def.incmode, msg_getword(m, 0), incmodetab, nelem(incmodetab)); 663 view_update(selview); 664 break; 665 case LNORMCOLORS: 666 msg_parsecolors(m, &def.normcolor); 667 updatecolors: 668 for(Client *c=client; c; c=c->next) 669 client_reparent(c); 670 view_update(selview); 671 break; 672 case LSELCOLORS: 673 warning("selcolors have been removed"); 674 return Ebadcmd; 675 case LVIEW: 676 view_select(m->pos); 677 break; 678 case LQUIT: 679 srv.running = 0; 680 break; 681 default: 682 return Ebadcmd; 683 } 684 return ret; 685 } 686 687 char* 688 readctl_root(void) { 689 fmtinstall('M', Mfmt); 690 bufclear(); 691 bufprint("bar on %s\n", barpostab[screen->barpos]); 692 bufprint("border %d\n", def.border); 693 bufprint("colmode %s\n", modes[def.colmode]); 694 if(debugflag) 695 bufprint("debug %M\n", (Mask){&debugflag, debugtab}); 696 if(debugfile) 697 bufprint("debugfile %M", (Mask){&debugfile, debugtab}); 698 bufprint("focuscolors %s\n", def.focuscolor.colstr); 699 bufprint("font %s\n", def.font->name); 700 bufprint("fontpad %d %d %d %d\n", def.font->pad.min.x, def.font->pad.max.x, 701 def.font->pad.max.y, def.font->pad.min.y); 702 bufprint("grabmod %s\n", (Mask){&def.mod, modkey_names}); 703 bufprint("incmode %s\n", incmodetab[def.incmode]); 704 bufprint("normcolors %s\n", def.normcolor.colstr); 705 bufprint("view %s\n", selview->name); 706 return buffer; 707 } 708 709 char* 710 message_view(View *v, IxpMsg *m) { 711 Area *a; 712 char *s; 713 714 s = msg_getword(m, 0); 715 if(s == nil) 716 return nil; 717 718 /* 719 * area ::= ~ 720 * | <column number> 721 * | sel 722 * direction ::= left 723 * | right 724 * | up 725 * | down 726 * # This *should* be identical to <frame> 727 * place ::= <column number> 728 * #| ~ # This should be, but isn't. 729 * | <direction> 730 * | toggle 731 * colmode ::= default 732 * | stack 733 * | normal 734 * column ::= sel 735 * | <column number> 736 * frame ::= up 737 * | down 738 * | left 739 * | right 740 * | toggle 741 * | client <window xid> 742 * | sel 743 * | ~ 744 * | <column> <frame number> 745 * | <column> 746 * amount ::= 747 * | <number> 748 * | <number>px 749 * 750 * colmode <area> <colmode> 751 * select <area> 752 * send <frame> <place> 753 * swap <frame> <place> 754 * grow <thing> <direction> <amount> 755 * nudge <thing> <direction> <amount> 756 * select <ordframe> 757 */ 758 759 switch(getsym(s)) { 760 case LCOLMODE: 761 s = msg_getword(m, Ebadvalue); 762 a = strarea(v, screen->idx, s); 763 764 s = msg_getword(m, Ebadvalue); 765 if(!column_setmode(a, s)) 766 return Ebadvalue; 767 768 column_arrange(a, false); 769 view_restack(v); 770 771 view_update(v); 772 return nil; 773 case LGROW: 774 return msg_grow(v, m); 775 case LNUDGE: 776 return msg_nudge(v, m); 777 case LSELECT: 778 return msg_selectarea(v->sel, m); 779 case LSEND: 780 return msg_sendclient(v, m, false); 781 case LSWAP: 782 return msg_sendclient(v, m, true); 783 default: 784 return Ebadcmd; 785 } 786 /* not reached */ 787 } 788 789 char* 790 readctl_view(View *v) { 791 Area *a; 792 int s; 793 794 bufclear(); 795 bufprint("%s\n", v->name); 796 797 bufprint("urgent %s\n", TOGGLE(v->urgent)); 798 799 /* select <area>[ <frame>] */ 800 bufprint("select %a", v->sel); 801 if(v->sel->sel) 802 bufprint(" %d", frame_idx(v->sel->sel)); 803 bufprint("\n"); 804 805 /* select client <client> */ 806 if(v->sel->sel) 807 bufprint("select client %#C\n", v->sel->sel->client); 808 809 foreach_area(v, s, a) 810 bufprint("colmode %a %s\n", a, column_getmode(a)); 811 return buffer; 812 } 813 814 static void 815 getamt(IxpMsg *m, Point *amt) { 816 char *s, *p; 817 long l; 818 819 s = msg_getword(m, 0); 820 if(s) { 821 p = strend(s, 2); 822 if(!strcmp(p, "px")) { 823 *p = '\0'; 824 amt->x = 1; 825 amt->y = 1; 826 } 827 828 l = msg_getlong(s); 829 amt->x *= l; 830 amt->y *= l; 831 } 832 } 833 834 static char* 835 msg_grow(View *v, IxpMsg *m) { 836 Client *c; 837 Frame *f; 838 Rectangle h, r; 839 Point amount; 840 int dir; 841 842 f = getframe(v, screen->idx, m); 843 c = f->client; 844 h = c->w.hints->aspect; 845 846 dir = getdirection(m); 847 848 amount.x = Dy(f->titlebar); 849 amount.y = Dy(f->titlebar); 850 if(amount.x < c->w.hints->inc.x) 851 amount.x = c->w.hints->inc.x; 852 if(amount.y < c->w.hints->inc.y) 853 amount.y = c->w.hints->inc.y; 854 getamt(m, &amount); 855 856 if (dir == LLEFT || dir == LRIGHT) 857 amount.y = h.min.x ? amount.x * h.min.y / h.min.x : 0; 858 else 859 amount.x = h.min.y ? amount.y * h.min.x / h.min.y : 0; 860 861 if(f->area->floating) 862 r = f->r; 863 else 864 r = f->colr; 865 866 if (dir == LLEFT || dir == LUP) 867 r.min = subpt(r.min, amount); 868 else if (dir == LRIGHT || dir == LDOWN) 869 r.max = addpt(r.max, amount); 870 871 if(f->area->floating) 872 float_resizeframe(f, r); 873 else 874 column_resizeframe(f, r); 875 876 return nil; 877 } 878 879 static char* 880 msg_nudge(View *v, IxpMsg *m) { 881 Frame *f; 882 Rectangle r; 883 Point amount; 884 int dir; 885 886 f = getframe(v, screen->idx, m); 887 dir = getdirection(m); 888 889 amount.x = Dy(f->titlebar); 890 amount.y = Dy(f->titlebar); 891 getamt(m, &amount); 892 893 if(f->area->floating) 894 r = f->r; 895 else 896 r = f->colr; 897 switch(dir) { 898 case LLEFT: r = rectaddpt(r, Pt(-amount.x, 0)); break; 899 case LRIGHT: r = rectaddpt(r, Pt( amount.x, 0)); break; 900 case LUP: r = rectaddpt(r, Pt(0, -amount.y)); break; 901 case LDOWN: r = rectaddpt(r, Pt(0, amount.y)); break; 902 default: abort(); 903 } 904 905 if(f->area->floating) 906 float_resizeframe(f, r); 907 else 908 column_resizeframe(f, r); 909 return nil; 910 } 911 912 void 913 msg_parsecolors(IxpMsg *m, CTuple *col) { 914 CTuple tpl; 915 char n; 916 917 n = loadcolor(&tpl, m->pos, m->end); 918 m->pos += n; 919 if(n == 0 || msg_getword(m, nil)) 920 error("bad color string"); 921 *col = tpl; 922 } 923 924 char* 925 msg_selectarea(Area *a, IxpMsg *m) { 926 Frame *f; 927 Area *ap; 928 View *v; 929 char *s; 930 ulong i; 931 int sym; 932 933 v = a->view; 934 s = msg_getword(m, Ebadvalue); 935 sym = getsym(s); 936 937 switch(sym) { 938 case LTOGGLE: 939 if(!a->floating) 940 ap = v->floating; 941 else if(v->revert && v->revert != a) 942 ap = v->revert; 943 else 944 ap = v->firstarea; 945 break; 946 case LLEFT: 947 case LRIGHT: 948 case LUP: 949 case LDOWN: 950 case LCLIENT: 951 return msg_selectframe(a, m, sym); 952 case LTILDE: 953 ap = v->floating; 954 break; 955 default: 956 /* XXX: Multihead */ 957 ap = strarea(v, a->screen, s); 958 if(ap->floating) 959 return Ebadvalue; 960 961 if((s = msg_getword(m, 0))) { 962 i = msg_getulong(s); 963 for(f = ap->frame; f; f = f->anext) 964 if(--i == 0) break; 965 if(i != 0) 966 return Ebadvalue; 967 frame_focus(f); 968 return nil; 969 } 970 } 971 972 area_focus(ap); 973 return nil; 974 } 975 976 static char* 977 msg_selectframe(Area *a, IxpMsg *m, int sym) { 978 Client *c; 979 Frame *f, *fp; 980 char *s; 981 bool stack; 982 ulong i, dy; 983 984 f = a->sel; 985 fp = f; 986 987 stack = false; 988 if(sym == LUP || sym == LDOWN) 989 if((s = msg_getword(m, 0))) 990 if(!strcmp(s, "stack")) 991 stack = true; 992 else 993 return Ebadvalue; 994 995 if(sym == LCLIENT) { 996 s = msg_getword(m, Ebadvalue); 997 i = msg_getulong(s); 998 c = win2client(i); 999 if(c == nil) 1000 return Ebadvalue; 1001 f = client_viewframe(c, a->view); 1002 if(!f) 1003 return Ebadvalue; 1004 } 1005 else if(!find(&a, &f, DIR(sym), true, stack)) 1006 return Ebadvalue; 1007 1008 area_focus(a); 1009 1010 if(f != nil) { 1011 /* XXX */ 1012 if(fp && fp->area == a) 1013 if(f->collapsed && !f->area->floating && f->area->mode == Coldefault) { 1014 dy = Dy(f->colr); 1015 f->colr.max.y = f->colr.min.y + Dy(fp->colr); 1016 fp->colr.max.y = fp->colr.min.y + dy; 1017 column_arrange(a, false); 1018 } 1019 1020 frame_focus(f); 1021 frame_restack(f, nil); 1022 if(f->view == selview) 1023 view_restack(a->view); 1024 } 1025 return nil; 1026 } 1027 1028 static char* 1029 sendarea(Frame *f, Area *to, bool swap) { 1030 Client *c; 1031 1032 c = f->client; 1033 if(!to) 1034 return Ebadvalue; 1035 1036 if(!swap) 1037 area_moveto(to, f); 1038 else if(to->sel) 1039 frame_swap(f, to->sel); 1040 else 1041 return Ebadvalue; 1042 1043 frame_focus(client_viewframe(c, f->view)); 1044 /* view_arrange(v); */ 1045 view_update_all(); 1046 return nil; 1047 } 1048 1049 char* 1050 msg_sendclient(View *v, IxpMsg *m, bool swap) { 1051 Area *to, *a; 1052 Frame *f, *ff; 1053 Client *c; 1054 char *s; 1055 int sym; 1056 1057 c = strclient(v, msg_getword(m, 0)); 1058 f = client_viewframe(c, v); 1059 if(f == nil) 1060 return Ebadvalue; 1061 1062 a = f->area; 1063 to = nil; 1064 1065 s = msg_getword(m, Ebadvalue); 1066 sym = getsym(s); 1067 1068 /* FIXME: Should use a helper function. */ 1069 switch(sym) { 1070 case LUP: 1071 case LDOWN: 1072 return msg_sendframe(f, sym, swap); 1073 case LLEFT: 1074 if(a->floating) 1075 return Ebadvalue; 1076 to = a->prev; 1077 break; 1078 case LRIGHT: 1079 if(a->floating) 1080 return Ebadvalue; 1081 to = a->next; 1082 break; 1083 case LTOGGLE: 1084 if(!a->floating) 1085 to = v->floating; 1086 else if(f->column) 1087 to = view_findarea(v, f->screen, f->column, true); 1088 else 1089 to = view_findarea(v, v->selscreen, v->selcol, true); 1090 break; 1091 case LTILDE: 1092 if(a->floating) 1093 return Ebadvalue; 1094 to = v->floating; 1095 break; 1096 default: 1097 to = strarea(v, v->selscreen, s); 1098 // to = view_findarea(v, scrn, i, true); 1099 break; 1100 } 1101 1102 1103 if(!to && !swap) { 1104 /* XXX: Multihead - clean this up, move elsewhere. */ 1105 if(!f->anext && f == f->area->frame) { 1106 ff = f; 1107 to = a; 1108 if(!find(&to, &ff, DIR(sym), false, false)) 1109 return Ebadvalue; 1110 } 1111 else { 1112 to = (sym == LLEFT) ? nil : a; 1113 to = column_new(v, to, a->screen, 0); 1114 } 1115 } 1116 1117 return sendarea(f, to, swap); 1118 } 1119 1120 static char* 1121 msg_sendframe(Frame *f, int sym, bool swap) { 1122 Client *c; 1123 Area *a; 1124 Frame *fp; 1125 1126 SET(fp); 1127 c = f->client; 1128 1129 a = f->area; 1130 fp = f; 1131 if(!find(&a, &fp, DIR(sym), false, false)) 1132 return Ebadvalue; 1133 if(a != f->area) 1134 return sendarea(f, a, swap); 1135 1136 switch(sym) { 1137 case LUP: 1138 fp = f->aprev; 1139 if(!fp) 1140 return Ebadvalue; 1141 if(!swap) 1142 fp = fp->aprev; 1143 break; 1144 case LDOWN: 1145 fp = f->anext; 1146 if(!fp) 1147 return Ebadvalue; 1148 break; 1149 default: 1150 die("can't get here"); 1151 } 1152 1153 if(swap) 1154 frame_swap(f, fp); 1155 else { 1156 frame_remove(f); 1157 frame_insert(f, fp); 1158 } 1159 1160 /* view_arrange(f->view); */ 1161 1162 frame_focus(client_viewframe(c, f->view)); 1163 view_update_all(); 1164 return nil; 1165 } 1166 1167 void 1168 warning(const char *fmt, ...) { 1169 va_list ap; 1170 char *s; 1171 1172 va_start(ap, fmt); 1173 s = vsmprint(fmt, ap); 1174 va_end(ap); 1175 1176 event("Warning %s\n", s); 1177 fprint(2, "%s: warning: %s\n", argv0, s); 1178 free(s); 1179 } 1180