shodc.c (14588B)
1 #include <err.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 #include "xutil.h" 6 7 #define NAMEMAXLEN 128 8 #define DIRECT_ACTION 2 9 #define _SHOD_MOVERESIZE_RELATIVE ((long)(1 << 16)) 10 11 /* state action */ 12 enum { 13 REMOVE = 0, 14 ADD = 1, 15 TOGGLE = 2 16 }; 17 18 /* long list char positions */ 19 enum { 20 LIST_STICKY, 21 LIST_MAXIMIZED, 22 LIST_MINIMIZED, 23 LIST_FULLSCREEN, 24 LIST_SHADED, 25 LIST_LAYER, 26 LIST_URGENCY, 27 LIST_ACTIVE, 28 LIST_LAST 29 }; 30 31 /* focus relative direction */ 32 enum Direction { 33 _SHOD_FOCUS_ABSOLUTE = 0, 34 _SHOD_FOCUS_LEFT_CONTAINER = 1, 35 _SHOD_FOCUS_RIGHT_CONTAINER = 2, 36 _SHOD_FOCUS_TOP_CONTAINER = 3, 37 _SHOD_FOCUS_BOTTOM_CONTAINER = 4, 38 _SHOD_FOCUS_PREVIOUS_CONTAINER = 5, 39 _SHOD_FOCUS_NEXT_CONTAINER = 6, 40 _SHOD_FOCUS_LEFT_WINDOW = 7, 41 _SHOD_FOCUS_RIGHT_WINDOW = 8, 42 _SHOD_FOCUS_TOP_WINDOW = 9, 43 _SHOD_FOCUS_BOTTOM_WINDOW = 10, 44 _SHOD_FOCUS_PREVIOUS_WINDOW = 11, 45 _SHOD_FOCUS_NEXT_WINDOW = 12, 46 }; 47 48 /* global variables */ 49 static Window active; 50 51 /* show usage and exit */ 52 static void 53 usage(void) 54 { 55 (void)fprintf(stderr, "usage: shodc close [WIN_ID]\n"); 56 (void)fprintf(stderr, " shodc cycle [-s]\n"); 57 (void)fprintf(stderr, " shodc desks\n"); 58 (void)fprintf(stderr, " shodc exit\n"); 59 (void)fprintf(stderr, " shodc focus [-clrtbpnLRTBPN] [WIN_ID]\n"); 60 (void)fprintf(stderr, " shodc geom [-X|-x N] [-Y|-y N] [-W|-w N] [-H|-h N] [WIN_ID]\n"); 61 (void)fprintf(stderr, " shodc goto [-m MON_ID] DESK_ID\n"); 62 (void)fprintf(stderr, " shodc list [-ls] [WIN_ID]\n"); 63 (void)fprintf(stderr, " shodc sendto [-m MON_ID] DESK_ID [WIN_ID]\n"); 64 (void)fprintf(stderr, " shodc state [-ATR] [-abfMms] [WIN_ID]\n"); 65 exit(1); 66 } 67 68 /* send client message to root window */ 69 static void 70 clientmsg(Window win, Atom atom, unsigned long d0, unsigned long d1, unsigned long d2, unsigned long d3, unsigned long d4) 71 { 72 XEvent ev; 73 long mask = SubstructureRedirectMask | SubstructureNotifyMask; 74 75 ev.xclient.type = ClientMessage; 76 ev.xclient.serial = 0; 77 ev.xclient.send_event = True; 78 ev.xclient.message_type = atom; 79 ev.xclient.window = win; 80 ev.xclient.format = 32; 81 ev.xclient.data.l[0] = d0; 82 ev.xclient.data.l[1] = d1; 83 ev.xclient.data.l[2] = d2; 84 ev.xclient.data.l[3] = d3; 85 ev.xclient.data.l[4] = d4; 86 if (!XSendEvent(dpy, root, False, mask, &ev)) { 87 errx(1, "could not send event"); 88 } 89 } 90 91 /* get active window */ 92 static Window 93 getactivewin(void) 94 { 95 Window win; 96 unsigned char *list; 97 unsigned long len; 98 unsigned long dl; /* dummy variable */ 99 int di; /* dummy variable */ 100 Atom da; /* dummy variable */ 101 102 list = NULL; 103 if (XGetWindowProperty(dpy, root, atoms[_NET_ACTIVE_WINDOW], 0L, 1024, False, XA_WINDOW, 104 &da, &di, &len, &dl, &list) != Success || list == NULL) 105 return None; 106 win = *(Window *)list; 107 XFree(list); 108 return win; 109 } 110 111 /* get window from string */ 112 static Window 113 getwin(const char *s) 114 { 115 return (Window)strtol(s, NULL, 0); 116 } 117 118 /* get array of windows */ 119 static unsigned long 120 getwins(Window **wins, int sflag) 121 { 122 if (sflag) 123 return getwinsprop(root, atoms[_NET_CLIENT_LIST_STACKING], wins); 124 else 125 return getwinsprop(root, atoms[_NET_CLIENT_LIST], wins); 126 } 127 128 /* get array of desktop names, return size of array */ 129 static unsigned long 130 getdesknames(char **desknames) 131 { 132 unsigned char *str; 133 unsigned long len; 134 unsigned long dl; /* dummy variable */ 135 int di; /* dummy variable */ 136 Atom da; /* dummy variable */ 137 138 139 if (XGetWindowProperty(dpy, root, atoms[_NET_DESKTOP_NAMES], 0, ~0, False, 140 UTF8_STRING, &da, &di, &len, &dl, &str) == 141 Success && str) { 142 *desknames = (char *)str; 143 } else { 144 *desknames = NULL; 145 len = 0; 146 } 147 148 return len; 149 } 150 151 /* get window name */ 152 char * 153 getwinname(Window win) 154 { 155 XTextProperty tprop; 156 char **list = NULL; 157 char *name = NULL; 158 unsigned char *p = NULL; 159 unsigned long size, dl; 160 int di; 161 Atom da; 162 163 if (XGetWindowProperty(dpy, win, atoms[_NET_WM_NAME], 0L, 8L, False, UTF8_STRING, 164 &da, &di, &size, &dl, &p) == Success && p) { 165 name = estrndup((char *)p, NAMEMAXLEN); 166 XFree(p); 167 } else if (XGetWMName(dpy, win, &tprop) && 168 XmbTextPropertyToTextList(dpy, &tprop, &list, &di) == Success && 169 di > 0 && list && *list) { 170 name = estrndup(*list, NAMEMAXLEN); 171 XFreeStringList(list); 172 XFree(tprop.value); 173 } 174 return name; 175 } 176 177 /* close window */ 178 static void 179 closewin(int argc, char *argv[]) 180 { 181 Window win; 182 183 win = None; 184 if (argc == 1) 185 win = active; 186 else if (argc == 2 && argv[1][0] != '-') 187 win = getwin(argv[1]); 188 else 189 usage(); 190 clientmsg(win, atoms[_NET_CLOSE_WINDOW], CurrentTime, DIRECT_ACTION, 0, 0, 0); 191 } 192 193 /* focus window */ 194 static void 195 focuswin(int argc, char *argv[]) 196 { 197 Window win; 198 enum Direction d; 199 int cycle, c; 200 201 cycle = 0; 202 d = _SHOD_FOCUS_ABSOLUTE; 203 win = None; 204 while ((c = getopt(argc, argv, "clrtbpnLRTBPN")) != -1) { 205 switch (c) { 206 case 'c': 207 cycle = 1; 208 break; 209 case 'L': 210 d = _SHOD_FOCUS_LEFT_WINDOW; 211 break; 212 case 'R': 213 d = _SHOD_FOCUS_RIGHT_WINDOW; 214 break; 215 case 'T': 216 d = _SHOD_FOCUS_TOP_WINDOW; 217 break; 218 case 'B': 219 d = _SHOD_FOCUS_BOTTOM_WINDOW; 220 break; 221 case 'P': 222 d = _SHOD_FOCUS_PREVIOUS_WINDOW; 223 break; 224 case 'N': 225 d = _SHOD_FOCUS_NEXT_WINDOW; 226 break; 227 case 'l': 228 d = _SHOD_FOCUS_LEFT_CONTAINER; 229 break; 230 case 'r': 231 d = _SHOD_FOCUS_RIGHT_CONTAINER; 232 break; 233 case 't': 234 d = _SHOD_FOCUS_TOP_CONTAINER; 235 break; 236 case 'b': 237 d = _SHOD_FOCUS_BOTTOM_CONTAINER; 238 break; 239 case 'p': 240 d = _SHOD_FOCUS_PREVIOUS_CONTAINER; 241 break; 242 case 'n': 243 d = _SHOD_FOCUS_NEXT_CONTAINER; 244 break; 245 default: 246 usage(); 247 break; 248 } 249 } 250 argc -= optind; 251 argv += optind; 252 if (argc > 1) 253 usage(); 254 if (argc == 1) 255 win = getwin(argv[0]); 256 clientmsg(win, atoms[_NET_ACTIVE_WINDOW], DIRECT_ACTION, CurrentTime, 0, d, cycle); 257 } 258 259 /* set container geometry */ 260 static void 261 setgeom(int argc, char *argv[]) 262 { 263 Window win; 264 long x, y, w, h; 265 int rel; 266 int c; 267 268 rel = 0; 269 x = y = w = h = 0; 270 while ((c = getopt(argc, argv, "rx:y:w:h:")) != -1) { 271 switch (c) { 272 case 'r': 273 rel |= _SHOD_MOVERESIZE_RELATIVE; 274 break; 275 case 'x': 276 x = strtol(optarg, NULL, 10); 277 break; 278 case 'y': 279 y = strtol(optarg, NULL, 10); 280 break; 281 case 'w': 282 w = strtol(optarg, NULL, 10); 283 break; 284 case 'h': 285 h = strtol(optarg, NULL, 10); 286 break; 287 default: 288 usage(); 289 break; 290 } 291 } 292 argc -= optind; 293 argv += optind; 294 if (argc > 1) 295 usage(); 296 win = (argc == 1) ? getwin(argv[0]) : active; 297 clientmsg(win, atoms[_NET_MOVERESIZE_WINDOW], rel, x, y, w, h); 298 } 299 300 /* go to desktop */ 301 static void 302 gotodesk(int argc, char *argv[]) 303 { 304 long mon, desk; 305 int c; 306 307 mon = 0; 308 while ((c = getopt(argc, argv, "m")) != -1) { 309 switch (c) { 310 case 'm': 311 mon = strtol(optarg, NULL, 0); 312 break; 313 default: 314 usage(); 315 break; 316 } 317 } 318 argc -= optind; 319 argv += optind; 320 if (argc != 1) 321 usage(); 322 desk = strtol(argv[0], NULL, 0) - 1; 323 clientmsg(None, atoms[_NET_CURRENT_DESKTOP], desk, CurrentTime, mon, 0, 0); 324 } 325 326 /* list window type, states, properties, etc */ 327 static void 328 longlist(Window win) 329 { 330 Atom *as; 331 int x, y; 332 unsigned int w, h, b, du; 333 long desk; 334 unsigned long i, natoms, l; 335 char state[] = "--------"; 336 char *name; 337 XWMHints *wmhints = NULL; 338 Window *list = NULL; 339 Window dw; 340 XID container, tab; 341 342 container = tab = 0x0; 343 if ((wmhints = XGetWMHints(dpy, win)) != NULL) { 344 if (wmhints->flags & XUrgencyHint) 345 state[LIST_URGENCY] = 'u'; 346 XFree(wmhints); 347 } 348 if (getwinsprop(win, atoms[_SHOD_GROUP_CONTAINER], &list) > 0) { 349 if (*list != None) { 350 container = *list; 351 } 352 XFree(list); 353 } 354 if (getwinsprop(win, atoms[_SHOD_GROUP_TAB], &list) > 0) { 355 if (*list != None) { 356 tab = *list; 357 } 358 XFree(list); 359 } 360 if (win == active) 361 state[LIST_ACTIVE] = 'a'; 362 if ((natoms = getatomsprop(win, atoms[_NET_WM_STATE], &as)) > 0) { 363 for (i = 0; i < natoms; i++) { 364 if (as[i] == atoms[_NET_WM_STATE_STICKY]) { 365 state[LIST_STICKY] = 'y'; 366 } else if (as[i] == atoms[_NET_WM_STATE_MAXIMIZED_VERT]) { 367 if (state[LIST_MAXIMIZED] == 'h') { 368 state[LIST_MAXIMIZED] = 'M'; 369 } else { 370 state[LIST_MAXIMIZED] = 'v'; 371 } 372 } else if (as[i] == atoms[_NET_WM_STATE_MAXIMIZED_HORZ]) { 373 if (state[LIST_MAXIMIZED] == 'v') { 374 state[LIST_MAXIMIZED] = 'M'; 375 } else { 376 state[LIST_MAXIMIZED] = 'h'; 377 } 378 } else if (as[i] == atoms[_NET_WM_STATE_HIDDEN]) { 379 state[LIST_MINIMIZED] = 'm'; 380 } else if (as[i] == atoms[_NET_WM_STATE_SHADED]) { 381 state[LIST_SHADED] = 's'; 382 } else if (as[i] == atoms[_NET_WM_STATE_FULLSCREEN]) { 383 state[LIST_FULLSCREEN] = 'F'; 384 } else if (as[i] == atoms[_NET_WM_STATE_ABOVE]) { 385 state[LIST_LAYER] = 'a'; 386 } else if (as[i] == atoms[_NET_WM_STATE_BELOW]) { 387 state[LIST_LAYER] = 'b'; 388 } else if (as[i] == atoms[_NET_WM_STATE_DEMANDS_ATTENTION]) { 389 if (state[LIST_URGENCY] == 'u') { 390 state[LIST_URGENCY] = 'U'; 391 } else { 392 state[LIST_URGENCY] = 'a'; 393 } 394 } else if (as[i] == atoms[_NET_WM_STATE_FOCUSED]) { 395 if (state[LIST_ACTIVE] == 'a') { 396 state[LIST_ACTIVE] = 'A'; 397 } else { 398 state[LIST_ACTIVE] = 'f'; 399 } 400 } 401 } 402 XFree(as); 403 } 404 l = getcardprop(win, atoms[_NET_WM_DESKTOP]); 405 desk = (l == 0xFFFFFFFF) ? -1 : (long)l; 406 407 name = getwinname(win); 408 XGetGeometry(dpy, win, &dw, &x, &y, &w, &h, &b, &du); 409 XTranslateCoordinates(dpy, win, root, x, y, &x, &y, &dw); 410 411 printf("%s\t%ld\t%dx%d%+d%+d\t0x%08lx\t0x%08lx\t0x%08lx\t%s\n", state, desk, w, h, x, y, container, tab, win, name); 412 free(name); 413 } 414 415 /* list desktops */ 416 static void 417 listdesks(int argc, char *argv[]) 418 { 419 unsigned long i, nwins, desk, ndesks, curdesk, len, nameslen; 420 unsigned long *wdesk; 421 int *urgdesks; 422 Window *wins; 423 XWMHints *hints; 424 char *desknames; 425 426 (void)argv; 427 if (argc != 1) 428 usage(); 429 430 /* get variables */ 431 ndesks = getcardprop(root, atoms[_NET_NUMBER_OF_DESKTOPS]); 432 curdesk = getcardprop(root, atoms[_NET_CURRENT_DESKTOP]); 433 nameslen = getdesknames(&desknames); 434 wdesk = ecalloc(ndesks, sizeof *wdesk); 435 urgdesks = ecalloc(ndesks, sizeof *urgdesks); 436 nwins = getwinsprop(root, atoms[_NET_CLIENT_LIST], &wins); 437 for (i = 0; i < nwins; i++) { 438 desk = getcardprop(wins[i], atoms[_NET_WM_DESKTOP]); 439 hints = XGetWMHints(dpy, wins[i]); 440 if (desk < ndesks) { 441 wdesk[desk]++; 442 if (hints && hints->flags & XUrgencyHint) { 443 urgdesks[desk] = 1; 444 } 445 } 446 XFree(hints); 447 } 448 XFree(wins); 449 450 /* list desktops */ 451 for (len = i = 0; i < ndesks; i++) { 452 printf("%c%lu:%s\n", 453 (i == curdesk ? '*' : (urgdesks[i] ? '-' : ' ')), 454 wdesk[i], 455 (desknames && len < nameslen ? desknames+len : "")); 456 if (desknames && len < nameslen) 457 len += strlen(desknames + len) + 1; 458 } 459 XFree(desknames); 460 free(wdesk); 461 } 462 463 /* list windows */ 464 static void 465 list(int argc, char *argv[]) 466 { 467 Window *wins; 468 unsigned long nwins, i; 469 int lflag, sflag; 470 int c; 471 472 lflag = sflag = 0; 473 while ((c = getopt(argc, argv, "dls")) != -1) { 474 switch (c) { 475 case 'l': 476 lflag = 1; 477 break; 478 case 's': 479 sflag = 1; 480 break; 481 default: 482 usage(); 483 break; 484 } 485 } 486 if (argc - optind > 1) 487 usage(); 488 nwins = getwins(&wins, sflag); 489 for (i = 0; i < nwins; i++) { 490 if (lflag) { 491 longlist(wins[i]); 492 } else { 493 printf("0x%08lx\n", wins[i]); 494 } 495 } 496 XFree(wins); 497 } 498 499 /* send container to desktop */ 500 static void 501 sendto(int argc, char *argv[]) 502 { 503 Window win; 504 long mon, desk; 505 int c; 506 507 mon = 0; 508 while ((c = getopt(argc, argv, "m")) != -1) { 509 switch (c) { 510 case 'm': 511 mon = strtol(optarg, NULL, 0); 512 break; 513 default: 514 usage(); 515 break; 516 } 517 } 518 argc -= optind; 519 argv += optind; 520 if (argc > 2) 521 usage(); 522 desk = strtol(argv[0], NULL, 0) - 1; 523 win = (argc == 2) ? getwin(argv[1]) : active; 524 clientmsg(win, atoms[_NET_WM_DESKTOP], desk, DIRECT_ACTION, mon, 0, 0); 525 } 526 527 /* set container state */ 528 static void 529 state(int argc, char *argv[]) 530 { 531 Window win; 532 Atom state1, state2; 533 int action; 534 int c; 535 536 action = TOGGLE; 537 state1 = state2 = None; 538 while ((c = getopt(argc, argv, "ATRabfMmsSy")) != -1) { 539 switch (c) { 540 case 'A': 541 action = ADD; 542 break; 543 case 'T': 544 action = TOGGLE; 545 break; 546 case 'R': 547 action = REMOVE; 548 break; 549 case 'a': 550 state1 = atoms[_NET_WM_STATE_ABOVE]; 551 state2 = None; 552 break; 553 case 'b': 554 state1 = atoms[_NET_WM_STATE_BELOW]; 555 state2 = None; 556 break; 557 case 'f': 558 state1 = atoms[_NET_WM_STATE_FULLSCREEN]; 559 state2 = None; 560 break; 561 case 'M': 562 state1 = atoms[_NET_WM_STATE_MAXIMIZED_VERT]; 563 state2 = atoms[_NET_WM_STATE_MAXIMIZED_HORZ]; 564 break; 565 case 'm': 566 state1 = atoms[_NET_WM_STATE_HIDDEN]; 567 state2 = None; 568 break; 569 case 'S': 570 state1 = atoms[_SHOD_WM_STATE_STRETCHED]; 571 state2 = None; 572 break; 573 case 's': 574 state1 = atoms[_NET_WM_STATE_SHADED]; 575 state2 = None; 576 break; 577 case 'y': 578 state1 = atoms[_NET_WM_STATE_STICKY]; 579 state2 = None; 580 break; 581 default: 582 usage(); 583 break; 584 } 585 } 586 argc -= optind; 587 argv += optind; 588 if (argc > 1) 589 usage(); 590 if (state1 == None) 591 return; 592 win = (argc == 1) ? getwin(argv[0]) : active; 593 clientmsg(win, atoms[_NET_WM_STATE], action, state1, state2, DIRECT_ACTION, 0); 594 } 595 596 /* cycle containers */ 597 static void 598 cycle(int argc, char *argv[]) 599 { 600 int c, shift; 601 602 shift = 0; 603 while ((c = getopt(argc, argv, "s")) != -1) { 604 switch (c) { 605 case 's': 606 shift = 1; 607 break; 608 default: 609 usage(); 610 break; 611 } 612 } 613 clientmsg(None, atoms[_SHOD_CYCLE], shift, 0, 0, 0, 0); 614 } 615 616 /* exit shod */ 617 static void 618 exitshod(int argc, char *argv[]) 619 { 620 Window checkwin; 621 622 (void)argc; 623 (void)argv; 624 checkwin = getwinprop(root, atoms[_NET_SUPPORTING_WM_CHECK]); 625 if (checkwin != None) { 626 XDestroyWindow(dpy, checkwin); 627 } 628 } 629 630 /* shodc: remote controller for shod */ 631 int 632 main(int argc, char *argv[]) 633 { 634 if (argc < 2) 635 usage(); 636 637 xinit(); 638 initatoms(); 639 active = getactivewin(); 640 if (strcmp(argv[1], "close") == 0) 641 closewin(argc - 1, argv + 1); 642 else if (strcmp(argv[1], "desks") == 0) 643 listdesks(argc - 1, argv + 1); 644 else if (strcmp(argv[1], "focus") == 0) 645 focuswin(argc - 1, argv + 1); 646 else if (strcmp(argv[1], "geom") == 0) 647 setgeom(argc - 1, argv + 1); 648 else if (strcmp(argv[1], "goto") == 0) 649 gotodesk(argc - 1, argv + 1); 650 else if (strcmp(argv[1], "list") == 0) 651 list(argc - 1, argv + 1); 652 else if (strcmp(argv[1], "sendto") == 0) 653 sendto(argc - 1, argv + 1); 654 else if (strcmp(argv[1], "state") == 0) 655 state(argc - 1, argv + 1); 656 else if (strcmp(argv[1], "cycle") == 0) 657 cycle(argc - 1, argv + 1); 658 else if (strcmp(argv[1], "exit") == 0) 659 exitshod(argc - 1, argv + 1); 660 else 661 usage(); 662 XCloseDisplay(dpy); 663 return 0; 664 }