1 /* Copyright (c) 2012, Bastien Dejean
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 void handle_message(char *msg, int msg_len, FILE *rsp)
49 char **args = malloc(cap * sizeof(char *));
52 perror("Handle message: malloc");
56 for (int i = 0, j = 0; i < msg_len; i++) {
58 args[num++] = msg + j;
63 char **new = realloc(args, cap * sizeof(char *));
66 perror("Handle message: realloc");
76 fail(rsp, "No arguments given.\n");
80 char **args_orig = args;
81 process_message(args, num, rsp);
85 void process_message(char **args, int num, FILE *rsp)
87 int ret = SUBSCRIBE_FAILURE;
89 if (streq("node", *args)) {
90 cmd_node(++args, --num, rsp);
91 } else if (streq("desktop", *args)) {
92 cmd_desktop(++args, --num, rsp);
93 } else if (streq("monitor", *args)) {
94 cmd_monitor(++args, --num, rsp);
95 } else if (streq("query", *args)) {
96 cmd_query(++args, --num, rsp);
97 } else if (streq("subscribe", *args)) {
98 ret = cmd_subscribe(++args, --num, rsp);
99 } else if (streq("wm", *args)) {
100 cmd_wm(++args, --num, rsp);
101 } else if (streq("rule", *args)) {
102 cmd_rule(++args, --num, rsp);
103 } else if (streq("config", *args)) {
104 cmd_config(++args, --num, rsp);
105 } else if (streq("quit", *args)) {
106 cmd_quit(++args, --num, rsp);
108 fail(rsp, "Unknown domain or command: '%s'.\n", *args);
113 if (ret != SUBSCRIBE_SUCCESS) {
118 void cmd_node(char **args, int num, FILE *rsp)
121 fail(rsp, "node: Missing commands.\n");
125 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
126 coordinates_t trg = ref;
128 if ((*args)[0] != OPT_CHR) {
130 if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
133 handle_failure(ret, "node", *args, rsp);
138 bool changed = false;
141 if (streq("-f", *args) || streq("--focus", *args)) {
142 coordinates_t dst = trg;
143 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
146 if ((ret = node_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
147 handle_failure(ret, "node -f", *args, rsp);
151 if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
155 } else if (streq("-a", *args) || streq("--activate", *args)) {
156 coordinates_t dst = trg;
157 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
160 if ((ret = node_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
161 handle_failure(ret, "node -a", *args, rsp);
165 if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
169 } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
172 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
177 if ((ret = desktop_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
178 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
179 trg.monitor = dst.monitor;
180 trg.desktop = dst.desktop;
186 handle_failure(ret, "node -d", *args, rsp);
189 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
192 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
197 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
198 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
199 trg.monitor = dst.monitor;
200 trg.desktop = dst.monitor->desk;
206 handle_failure(ret, "node -m", *args, rsp);
209 } else if (streq("-n", *args) || streq("--to-node", *args)) {
212 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
217 if ((ret = node_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
218 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
219 trg.monitor = dst.monitor;
220 trg.desktop = dst.desktop;
226 handle_failure(ret, "node -n", *args, rsp);
229 } else if (streq("-s", *args) || streq("--swap", *args)) {
232 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
237 if ((ret = node_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
238 if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
239 trg.monitor = dst.monitor;
240 trg.desktop = dst.desktop;
246 handle_failure(ret, "node -s", *args, rsp);
249 } else if (streq("-l", *args) || streq("--layer", *args)) {
252 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
256 if (parse_stack_layer(*args, &lyr)) {
257 if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
262 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
265 } else if (streq("-t", *args) || streq("--state", *args)) {
268 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
272 bool alternate = false;
273 if ((*args)[0] == '~') {
277 if (parse_client_state(*args, &cst)) {
278 if (alternate && trg.node != NULL && trg.node->client != NULL &&
279 trg.node->client->state == cst) {
280 cst = trg.node->client->last_state;
282 if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
288 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
291 } else if (streq("-g", *args) || streq("--flag", *args)) {
294 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
297 if (trg.node == NULL) {
301 char *key = strtok(*args, EQL_TOK);
302 char *val = strtok(NULL, EQL_TOK);
308 if (parse_bool(val, &b)) {
311 fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
315 if (streq("hidden", key)) {
316 set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
318 } else if (streq("sticky", key)) {
319 set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
320 } else if (streq("private", key)) {
321 set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
322 } else if (streq("locked", key)) {
323 set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
325 fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
328 } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
331 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
334 if (trg.node == NULL || trg.node->vacant) {
338 if (streq("cancel", *args)) {
339 cancel_presel(trg.monitor, trg.desktop, trg.node);
341 bool alternate = false;
342 if ((*args)[0] == '~') {
347 if (parse_direction(*args, &dir)) {
348 if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
349 cancel_presel(trg.monitor, trg.desktop, trg.node);
351 presel_dir(trg.monitor, trg.desktop, trg.node, dir);
352 if (!IS_RECEPTACLE(trg.node)) {
353 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
357 fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
361 } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
364 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
367 if (trg.node == NULL || trg.node->vacant) {
372 if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
373 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
376 presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
377 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
379 } else if (streq("-v", *args) || streq("--move", *args)) {
382 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
386 if (sscanf(*args, "%i", &dx) == 1) {
388 if (sscanf(*args, "%i", &dy) == 1) {
389 if (!move_client(&trg, dx, dy)) {
394 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
398 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
401 } else if (streq("-z", *args) || streq("--resize", *args)) {
404 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
408 if (parse_resize_handle(*args, &rh)) {
411 if (sscanf(*args, "%i", &dx) == 1) {
413 if (sscanf(*args, "%i", &dy) == 1) {
414 if (!resize_client(&trg, rh, dx, dy)) {
419 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
423 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
427 fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
430 } else if (streq("-r", *args) || streq("--ratio", *args)) {
433 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
436 if (trg.node == NULL) {
440 if ((*args)[0] == '+' || (*args)[0] == '-') {
442 if (sscanf(*args, "%i", &pix) == 1) {
443 int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
444 double rat = ((max * trg.node->split_ratio) + pix) / max;
445 if (rat > 0 && rat < 1) {
446 set_ratio(trg.node, rat);
452 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
457 if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
458 set_ratio(trg.node, rat);
460 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
465 } else if (streq("-F", *args) || streq("--flip", *args)) {
468 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
471 if (trg.node == NULL) {
476 if (parse_flip(*args, &flp)) {
477 flip_tree(trg.node, flp);
483 } else if (streq("-R", *args) || streq("--rotate", *args)) {
486 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
489 if (trg.node == NULL) {
494 if (parse_degree(*args, °)) {
495 rotate_tree(trg.node, deg);
498 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
501 } else if (streq("-E", *args) || streq("--equalize", *args)) {
502 if (trg.node == NULL) {
506 equalize_tree(trg.node);
508 } else if (streq("-B", *args) || streq("--balance", *args)) {
509 if (trg.node == NULL) {
513 balance_tree(trg.node);
515 } else if (streq("-C", *args) || streq("--circulate", *args)) {
518 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
521 if (trg.node == NULL) {
526 if (parse_circulate_direction(*args, &cir)) {
527 circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
530 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
533 } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
534 insert_receptacle(trg.monitor, trg.desktop, trg.node);
536 } else if (streq("-c", *args) || streq("--close", *args)) {
538 fail(rsp, "node %s: Trailing commands.\n", *args);
541 if (trg.node == NULL || locked_count(trg.node) > 0) {
545 close_node(trg.node);
547 } else if (streq("-k", *args) || streq("--kill", *args)) {
549 fail(rsp, "node %s: Trailing commands.\n", *args);
552 if (trg.node == NULL) {
556 kill_node(trg.monitor, trg.desktop, trg.node);
560 fail(rsp, "node: Unknown command: '%s'.\n", *args);
568 arrange(trg.monitor, trg.desktop);
572 void cmd_desktop(char **args, int num, FILE *rsp)
575 fail(rsp, "desktop: Missing commands.\n");
579 coordinates_t ref = {mon, mon->desk, NULL};
580 coordinates_t trg = ref;
582 if ((*args)[0] != OPT_CHR) {
584 if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
587 handle_failure(ret, "desktop", *args, rsp);
592 bool changed = false;
595 if (streq("-f", *args) || streq("--focus", *args)) {
596 coordinates_t dst = trg;
597 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
600 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
601 handle_failure(ret, "desktop -f", *args, rsp);
605 focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
606 } else if (streq("-a", *args) || streq("--activate", *args)) {
607 coordinates_t dst = trg;
608 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
611 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
612 handle_failure(ret, "desktop -a", *args, rsp);
616 activate_desktop(dst.monitor, dst.desktop);
617 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
620 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
623 if (trg.monitor->desk_head == trg.monitor->desk_tail) {
629 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
630 if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
631 trg.monitor = dst.monitor;
637 handle_failure(ret, "desktop -m", *args, rsp);
640 } else if (streq("-s", *args) || streq("--swap", *args)) {
643 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
648 if ((ret = desktop_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
649 if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
650 trg.monitor = dst.monitor;
656 handle_failure(ret, "desktop -s", *args, rsp);
659 } else if (streq("-b", *args) || streq("--bubble", *args)) {
662 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
666 if (parse_cycle_direction(*args, &cyc)) {
667 desktop_t *d = trg.desktop;
668 if (cyc == CYCLE_PREV) {
669 if (d->prev == NULL) {
670 while (d->next != NULL) {
671 swap_desktops(trg.monitor, d, trg.monitor, d->next);
674 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
677 if (d->next == NULL) {
678 while (d->prev != NULL) {
679 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
682 swap_desktops(trg.monitor, d, trg.monitor, d->next);
686 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
689 } else if (streq("-l", *args) || streq("--layout", *args)) {
692 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
697 if (parse_cycle_direction(*args, &cyc)) {
698 change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
699 } else if (parse_layout(*args, &lyt)) {
700 change_layout(trg.monitor, trg.desktop, lyt);
702 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
705 } else if (streq("-n", *args) || streq("--rename", *args)) {
708 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
711 rename_desktop(trg.monitor, trg.desktop, *args);
712 } else if (streq("-r", *args) || streq("--remove", *args)) {
714 fail(rsp, "desktop %s: Trailing commands.\n", *args);
717 if (trg.desktop->root == NULL &&
718 trg.monitor->desk_head != trg.monitor->desk_tail) {
719 remove_desktop(trg.monitor, trg.desktop);
726 fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
733 arrange(trg.monitor, trg.desktop);
737 void cmd_monitor(char **args, int num, FILE *rsp)
740 fail(rsp, "monitor: Missing commands.\n");
744 coordinates_t ref = {mon, NULL, NULL};
745 coordinates_t trg = ref;
747 if ((*args)[0] != OPT_CHR) {
749 if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
752 handle_failure(ret, "monitor", *args, rsp);
758 if (streq("-f", *args) || streq("--focus", *args)) {
759 coordinates_t dst = trg;
760 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
763 if ((ret = monitor_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
764 handle_failure(ret, "monitor -f", *args, rsp);
769 focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
770 } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
773 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
776 desktop_t *d = trg.monitor->desk_head;
777 while (num > 0 && d != NULL) {
778 rename_desktop(trg.monitor, d, *args);
782 put_status(SBSC_MASK_REPORT);
784 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
788 desktop_t *next = d->next;
789 if (d == mon->desk) {
790 focus_node(trg.monitor, d->prev, d->prev->focus);
792 merge_desktops(trg.monitor, d, mon, mon->desk);
793 remove_desktop(trg.monitor, d);
796 } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
799 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
803 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
806 } else if (streq("-r", *args) || streq("--remove", *args)) {
808 fail(rsp, "monitor %s: Trailing commands.\n", *args);
811 if (mon_head == mon_tail) {
815 remove_monitor(trg.monitor);
817 } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
820 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
823 desktop_t *d = trg.monitor->desk_head;
824 while (d != NULL && num > 0) {
825 desktop_t *next = d->next;
827 if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
828 swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
829 if (next == dst.desktop) {
836 } else if (streq("-g", *args) || streq("--rectangle", *args)) {
839 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
843 if (parse_rectangle(*args, &r)) {
844 update_root(trg.monitor, &r);
846 fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
849 } else if (streq("-n", *args) || streq("--rename", *args)) {
852 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
855 rename_monitor(trg.monitor, *args);
856 } else if (streq("-s", *args) || streq("--swap", *args)) {
859 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
864 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
865 if (!swap_monitors(trg.monitor, dst.monitor)) {
870 handle_failure(ret, "monitor -s", *args, rsp);
874 fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
881 void cmd_query(char **args, int num, FILE *rsp)
883 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
884 coordinates_t trg = {NULL, NULL, NULL};
885 monitor_select_t *monitor_sel = NULL;
886 desktop_select_t *desktop_sel = NULL;
887 node_select_t *node_sel = NULL;
888 domain_t dom = DOMAIN_TREE;
892 fail(rsp, "query: Not enough arguments.\n");
897 if (streq("-T", *args) || streq("--tree", *args)) {
898 dom = DOMAIN_TREE, d++;
899 } else if (streq("-M", *args) || streq("--monitors", *args)) {
900 dom = DOMAIN_MONITOR, d++;
901 } else if (streq("-D", *args) || streq("--desktops", *args)) {
902 dom = DOMAIN_DESKTOP, d++;
903 } else if (streq("-N", *args) || streq("--nodes", *args)) {
904 dom = DOMAIN_NODE, d++;
905 } else if (streq("-m", *args) || streq("--monitor", *args)) {
906 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
909 if ((*args)[0] == '.') {
911 monitor_sel = malloc(sizeof(monitor_select_t));
912 *monitor_sel = make_monitor_select();
913 char *desc = copy_string(*args, strlen(*args));
914 if (!parse_monitor_modifiers(desc, monitor_sel)) {
915 handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
920 } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
921 handle_failure(ret, "query -m", *args, rsp);
925 trg.monitor = ref.monitor;
927 } else if (streq("-d", *args) || streq("--desktop", *args)) {
928 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
931 if ((*args)[0] == '.') {
933 desktop_sel = malloc(sizeof(desktop_select_t));
934 *desktop_sel = make_desktop_select();
935 char *desc = copy_string(*args, strlen(*args));
936 if (!parse_desktop_modifiers(desc, desktop_sel)) {
937 handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
942 } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
943 handle_failure(ret, "query -d", *args, rsp);
947 trg.monitor = ref.monitor;
948 trg.desktop = ref.desktop;
950 } else if (streq("-n", *args) || streq("--node", *args)) {
951 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
954 if ((*args)[0] == '.') {
956 node_sel = malloc(sizeof(node_select_t));
957 *node_sel = make_node_select();
958 char *desc = copy_string(*args, strlen(*args));
959 if (!parse_node_modifiers(desc, node_sel)) {
960 handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
965 } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
966 handle_failure(ret, "query -n", *args, rsp);
971 if (ref.node == NULL) {
977 fail(rsp, "query: Unknown option: '%s'.\n", *args);
984 fail(rsp, "query: No commands given.\n");
989 fail(rsp, "query: Multiple commands given.\n");
993 if (dom == DOMAIN_TREE && trg.monitor == NULL) {
994 fail(rsp, "query -T: No options given.\n");
998 if (dom == DOMAIN_NODE) {
999 if (query_node_ids(trg, node_sel, rsp) < 1) {
1002 } else if (dom == DOMAIN_DESKTOP) {
1003 if (query_desktop_ids(trg, desktop_sel, rsp) < 1) {
1006 } else if (dom == DOMAIN_MONITOR) {
1007 if (query_monitor_ids(trg, monitor_sel, rsp) < 1) {
1011 if (trg.node != NULL) {
1012 query_node(trg.node, rsp);
1013 } else if (trg.desktop != NULL) {
1014 query_desktop(trg.desktop, rsp);
1016 query_monitor(trg.monitor, rsp);
1027 void cmd_rule(char **args, int num, FILE *rsp)
1030 fail(rsp, "rule: Missing commands.\n");
1035 if (streq("-a", *args) || streq("--add", *args)) {
1038 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1041 rule_t *rule = make_rule();
1042 char *class_name = strtok(*args, COL_TOK);
1043 char *instance_name = strtok(NULL, COL_TOK);
1044 snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
1045 snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
1049 if (streq("-o", *args) || streq("--one-shot", *args)) {
1050 rule->one_shot = true;
1052 for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
1053 rule->effect[i] = (*args)[j];
1055 if (num > 1 && i < sizeof(rule->effect)) {
1056 rule->effect[i++] = ' ';
1061 rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
1063 } else if (streq("-r", *args) || streq("--remove", *args)) {
1066 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1071 if (parse_index(*args, &idx)) {
1072 remove_rule_by_index(idx - 1);
1073 } else if (streq("tail", *args)) {
1074 remove_rule(rule_tail);
1075 } else if (streq("head", *args)) {
1076 remove_rule(rule_head);
1078 remove_rule_by_cause(*args);
1082 } else if (streq("-l", *args) || streq("--list", *args)) {
1085 fail(rsp, "rule: Unknown command: '%s'.\n", *args);
1092 void cmd_wm(char **args, int num, FILE *rsp)
1095 fail(rsp, "wm: Missing commands.\n");
1100 if (streq("-d", *args) || streq("--dump-state", *args)) {
1103 } else if (streq("-l", *args) || streq("--load-state", *args)) {
1106 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1109 if (!restore_tree(*args)) {
1113 } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
1116 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1122 if (parse_rectangle(*args, &r)) {
1123 monitor_t *m = make_monitor(&r, XCB_NONE);
1124 snprintf(m->name, sizeof(m->name), "%s", name);
1126 add_desktop(m, make_desktop(NULL, XCB_NONE));
1128 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1131 } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
1133 } else if (streq("-g", *args) || streq("--get-status", *args)) {
1135 } else if (streq("-h", *args) || streq("--record-history", *args)) {
1138 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1142 if (parse_bool(*args, &b)) {
1145 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1149 fail(rsp, "wm: Unkown command: '%s'.\n", *args);
1156 int cmd_subscribe(char **args, int num, FILE *rsp)
1160 field = SBSC_MASK_REPORT;
1162 subscriber_mask_t mask;
1164 if (parse_subscriber_mask(*args, &mask)) {
1167 fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
1168 return SUBSCRIBE_FAILURE;
1174 add_subscriber(rsp, field);
1175 return SUBSCRIBE_SUCCESS;
1178 void cmd_quit(char **args, int num, FILE *rsp)
1180 if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
1181 fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
1187 void cmd_config(char **args, int num, FILE *rsp)
1190 fail(rsp, "config: Missing arguments.\n");
1194 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
1195 coordinates_t trg = {NULL, NULL, NULL};
1197 while (num > 0 && (*args)[0] == OPT_CHR) {
1198 if (streq("-m", *args) || streq("--monitor", *args)) {
1201 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1205 if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1206 handle_failure(ret, "config -m", *args, rsp);
1209 } else if (streq("-d", *args) || streq("--desktop", *args)) {
1212 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1216 if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1217 handle_failure(ret, "config -d", *args, rsp);
1220 } else if (streq("-n", *args) || streq("--node", *args)) {
1223 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1227 if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1228 handle_failure(ret, "config -n", *args, rsp);
1232 fail(rsp, "config: Unknown option: '%s'.\n", *args);
1238 set_setting(trg, *args, *(args + 1), rsp);
1239 } else if (num == 1) {
1240 get_setting(trg, *args, rsp);
1242 fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
1246 void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
1248 bool colors_changed = false;
1249 #define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
1250 if (loc.node != NULL) { \
1251 loc.node->client->k = v; \
1252 } else if (loc.desktop != NULL) { \
1253 loc.desktop->k = v; \
1254 for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
1255 if (n->client != NULL) { \
1259 } else if (loc.monitor != NULL) { \
1260 loc.monitor->k = v; \
1261 for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1263 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1264 if (n->client != NULL) { \
1271 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1273 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1275 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1276 if (n->client != NULL) { \
1283 if (streq("border_width", name)) {
1285 if (sscanf(value, "%u", &bw) != 1) {
1286 fail(rsp, "config: %s: Invalid value: '%s'.", name, value);
1289 SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
1290 #undef SET_DEF_DEFMON_DEFDESK_WIN
1291 #define SET_DEF_DEFMON_DESK(k, v) \
1292 if (loc.desktop != NULL) { \
1293 loc.desktop->k = v; \
1294 } else if (loc.monitor != NULL) { \
1295 loc.monitor->k = v; \
1296 for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1301 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1303 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1308 } else if (streq("window_gap", name)) {
1310 if (sscanf(value, "%i", &wg) != 1) {
1314 SET_DEF_DEFMON_DESK(window_gap, wg)
1315 #undef SET_DEF_DEFMON_DESK
1316 #define SET_DEF_MON_DESK(k, v) \
1317 if (loc.desktop != NULL) { \
1318 loc.desktop->k = v; \
1319 } else if (loc.monitor != NULL) { \
1320 loc.monitor->k = v; \
1323 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1327 } else if (streq("top_padding", name)) {
1329 if (sscanf(value, "%i", &tp) != 1) {
1330 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1333 SET_DEF_MON_DESK(padding.top, tp)
1334 } else if (streq("right_padding", name)) {
1336 if (sscanf(value, "%i", &rp) != 1) {
1337 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1340 SET_DEF_MON_DESK(padding.right, rp)
1341 } else if (streq("bottom_padding", name)) {
1343 if (sscanf(value, "%i", &bp) != 1) {
1344 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1347 SET_DEF_MON_DESK(padding.bottom, bp)
1348 } else if (streq("left_padding", name)) {
1350 if (sscanf(value, "%i", &lp) != 1) {
1351 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1354 SET_DEF_MON_DESK(padding.left, lp)
1355 #undef SET_DEF_MON_DESK
1356 #define SET_STR(s) \
1357 } else if (streq(#s, name)) { \
1358 if (snprintf(s, sizeof(s), "%s", value) < 0) { \
1362 SET_STR(external_rules_command)
1363 SET_STR(status_prefix)
1365 } else if (streq("split_ratio", name)) {
1367 if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
1370 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1374 #define SET_COLOR(s) \
1375 } else if (streq(#s, name)) { \
1376 if (!is_hex_color(value)) { \
1377 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1380 snprintf(s, sizeof(s), "%s", value); \
1381 colors_changed = true; \
1383 SET_COLOR(normal_border_color)
1384 SET_COLOR(active_border_color)
1385 SET_COLOR(focused_border_color)
1386 SET_COLOR(presel_feedback_color)
1388 } else if (streq("initial_polarity", name)) {
1390 if (parse_child_polarity(value, &p)) {
1391 initial_polarity = p;
1393 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1396 } else if (streq("pointer_modifier", name)) {
1397 if (parse_modifier_mask(value, &pointer_modifier)) {
1401 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1404 } else if (streq("pointer_action1", name) ||
1405 streq("pointer_action2", name) ||
1406 streq("pointer_action3", name)) {
1407 int index = name[14] - '1';
1408 if (!parse_pointer_action(value, &pointer_actions[index])) {
1409 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1412 } else if (streq("click_to_focus", name)) {
1413 if (parse_bool(value, &click_to_focus)) {
1417 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1420 } else if (streq("focus_follows_pointer", name)) {
1422 if (parse_bool(value, &b)) {
1423 if (b == focus_follows_pointer) {
1426 focus_follows_pointer = b;
1427 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1429 window_show(m->root);
1431 window_hide(m->root);
1433 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1434 listen_enter_notify(d->root, b);
1439 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1442 #define SET_BOOL(s) \
1443 } else if (streq(#s, name)) { \
1444 if (!parse_bool(value, &s)) { \
1445 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1448 SET_BOOL(borderless_monocle)
1449 SET_BOOL(gapless_monocle)
1450 SET_BOOL(paddingless_monocle)
1451 SET_BOOL(single_monocle)
1452 SET_BOOL(pointer_follows_focus)
1453 SET_BOOL(pointer_follows_monitor)
1454 SET_BOOL(history_aware_focus)
1455 SET_BOOL(focus_by_distance)
1456 SET_BOOL(ignore_ewmh_focus)
1457 SET_BOOL(center_pseudo_tiled)
1458 SET_BOOL(honor_size_hints)
1460 #define SET_MON_BOOL(s) \
1461 } else if (streq(#s, name)) { \
1462 if (!parse_bool(value, &s)) { \
1463 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1467 update_monitors(); \
1469 SET_MON_BOOL(remove_disabled_monitors)
1470 SET_MON_BOOL(remove_unplugged_monitors)
1471 SET_MON_BOOL(merge_overlapping_monitors)
1474 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1478 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1479 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1481 if (colors_changed) {
1482 update_colors_in(d->root, d, m);
1488 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1490 if (streq("split_ratio", name)) {
1491 fprintf(rsp, "%lf", split_ratio);
1492 } else if (streq("border_width", name)) {
1493 if (loc.node != NULL) {
1494 fprintf(rsp, "%u", loc.node->client->border_width);
1495 } else if (loc.desktop != NULL) {
1496 fprintf(rsp, "%u", loc.desktop->border_width);
1497 } else if (loc.monitor != NULL) {
1498 fprintf(rsp, "%u", loc.monitor->border_width);
1500 fprintf(rsp, "%u", border_width);
1502 } else if (streq("window_gap", name)) {
1503 if (loc.desktop != NULL) {
1504 fprintf(rsp, "%i", loc.desktop->window_gap);
1505 } else if (loc.monitor != NULL) {
1506 fprintf(rsp, "%i", loc.monitor->window_gap);
1508 fprintf(rsp, "%i", window_gap);
1510 #define GET_DEF_MON_DESK(k) \
1511 if (loc.desktop != NULL) { \
1512 fprintf(rsp, "%i", loc.desktop->k); \
1513 } else if (loc.monitor != NULL) { \
1514 fprintf(rsp, "%i", loc.monitor->k); \
1516 fprintf(rsp, "%i", k); \
1518 } else if (streq("top_padding", name)) {
1519 GET_DEF_MON_DESK(padding.top)
1520 } else if (streq("right_padding", name)) {
1521 GET_DEF_MON_DESK(padding.right)
1522 } else if (streq("bottom_padding", name)) {
1523 GET_DEF_MON_DESK(padding.bottom)
1524 } else if (streq("left_padding", name)) {
1525 GET_DEF_MON_DESK(padding.left)
1526 #undef GET_DEF_MON_DESK
1527 } else if (streq("external_rules_command", name)) {
1528 fprintf(rsp, "%s", external_rules_command);
1529 } else if (streq("status_prefix", name)) {
1530 fprintf(rsp, "%s", status_prefix);
1531 } else if (streq("initial_polarity", name)) {
1532 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1533 } else if (streq("pointer_modifier", name)) {
1534 print_modifier_mask(pointer_modifier, rsp);
1535 } else if (streq("pointer_action1", name) ||
1536 streq("pointer_action2", name) ||
1537 streq("pointer_action3", name)) {
1538 int index = name[14] - '1';
1539 print_pointer_action(pointer_actions[index], rsp);
1540 #define GET_COLOR(s) \
1541 } else if (streq(#s, name)) { \
1542 fprintf(rsp, "%s", s);
1543 GET_COLOR(normal_border_color)
1544 GET_COLOR(active_border_color)
1545 GET_COLOR(focused_border_color)
1546 GET_COLOR(presel_feedback_color)
1548 #define GET_BOOL(s) \
1549 } else if (streq(#s, name)) { \
1550 fprintf(rsp, "%s", BOOL_STR(s));
1551 GET_BOOL(borderless_monocle)
1552 GET_BOOL(gapless_monocle)
1553 GET_BOOL(paddingless_monocle)
1554 GET_BOOL(single_monocle)
1555 GET_BOOL(click_to_focus)
1556 GET_BOOL(focus_follows_pointer)
1557 GET_BOOL(pointer_follows_focus)
1558 GET_BOOL(pointer_follows_monitor)
1559 GET_BOOL(history_aware_focus)
1560 GET_BOOL(focus_by_distance)
1561 GET_BOOL(ignore_ewmh_focus)
1562 GET_BOOL(center_pseudo_tiled)
1563 GET_BOOL(honor_size_hints)
1564 GET_BOOL(remove_disabled_monitors)
1565 GET_BOOL(remove_unplugged_monitors)
1566 GET_BOOL(merge_overlapping_monitors)
1569 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1575 void handle_failure(int code, char *src, char *val, FILE *rsp)
1578 case SELECTOR_BAD_DESCRIPTOR:
1579 fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1581 case SELECTOR_BAD_MODIFIERS:
1582 fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1584 case SELECTOR_INVALID:
1590 void fail(FILE *rsp, char *fmt, ...)
1592 fprintf(rsp, FAILURE_MESSAGE);
1595 vfprintf(rsp, fmt, ap);