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("pointer", *args)) {
104 cmd_pointer(++args, --num, rsp);
105 } else if (streq("config", *args)) {
106 cmd_config(++args, --num, rsp);
107 } else if (streq("quit", *args)) {
108 cmd_quit(++args, --num, rsp);
110 fail(rsp, "Unknown domain or command: '%s'.\n", *args);
115 if (ret != SUBSCRIBE_SUCCESS) {
120 void cmd_node(char **args, int num, FILE *rsp)
123 fail(rsp, "node: Missing commands.\n");
127 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
128 coordinates_t trg = ref;
130 if ((*args)[0] != OPT_CHR) {
132 if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
135 handle_failure(ret, "node", *args, rsp);
140 bool changed = false;
143 if (streq("-f", *args) || streq("--focus", *args)) {
144 coordinates_t dst = trg;
145 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
148 if ((ret = node_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
149 handle_failure(ret, "node -f", *args, rsp);
153 if (!focus_node(dst.monitor, dst.desktop, dst.node)) {
157 } else if (streq("-a", *args) || streq("--activate", *args)) {
158 coordinates_t dst = trg;
159 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
162 if ((ret = node_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
163 handle_failure(ret, "node -a", *args, rsp);
167 if (dst.desktop == mon->desk) {
171 if (!activate_node(dst.monitor, dst.desktop, dst.node)) {
175 } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
178 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
183 if ((ret = desktop_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
184 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
185 trg.monitor = dst.monitor;
186 trg.desktop = dst.desktop;
192 handle_failure(ret, "node -d", *args, rsp);
195 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
198 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
203 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
204 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
205 trg.monitor = dst.monitor;
206 trg.desktop = dst.monitor->desk;
212 handle_failure(ret, "node -m", *args, rsp);
215 } else if (streq("-n", *args) || streq("--to-node", *args)) {
218 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
223 if ((ret = node_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
224 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
225 trg.monitor = dst.monitor;
226 trg.desktop = dst.desktop;
232 handle_failure(ret, "node -n", *args, rsp);
235 } else if (streq("-s", *args) || streq("--swap", *args)) {
238 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
243 if ((ret = node_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
244 if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
245 trg.monitor = dst.monitor;
246 trg.desktop = dst.desktop;
252 handle_failure(ret, "node -s", *args, rsp);
255 } else if (streq("-l", *args) || streq("--layer", *args)) {
258 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
262 if (parse_stack_layer(*args, &lyr)) {
263 if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
268 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
271 } else if (streq("-t", *args) || streq("--state", *args)) {
274 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
278 bool alternate = false;
279 if ((*args)[0] == '~') {
283 if (parse_client_state(*args, &cst)) {
284 if (alternate && trg.node != NULL && trg.node->client != NULL &&
285 trg.node->client->state == cst) {
286 cst = trg.node->client->last_state;
288 if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
294 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
297 } else if (streq("-g", *args) || streq("--flag", *args)) {
300 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
303 if (trg.node == NULL || trg.node->client == NULL) {
307 char *key = strtok(*args, EQL_TOK);
308 char *val = strtok(NULL, EQL_TOK);
314 if (parse_bool(val, &b)) {
317 fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
321 if (streq("locked", key)) {
322 set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
323 } else if (streq("sticky", key)) {
324 set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
325 } else if (streq("private", key)) {
326 set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
328 fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
331 } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
334 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
337 if (trg.node == NULL || trg.node->vacant) {
341 if (streq("cancel", *args)) {
342 cancel_presel(trg.monitor, trg.desktop, trg.node);
344 bool alternate = false;
345 if ((*args)[0] == '~') {
350 if (parse_direction(*args, &dir)) {
351 if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
352 cancel_presel(trg.monitor, trg.desktop, trg.node);
354 presel_dir(trg.monitor, trg.desktop, trg.node, dir);
355 if (!IS_RECEPTACLE(trg.node)) {
356 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
360 fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
364 } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
367 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
370 if (trg.node == NULL || trg.node->vacant) {
375 if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
376 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
379 presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
380 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
382 } else if (streq("-r", *args) || streq("--ratio", *args)) {
385 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
388 if (trg.node == NULL) {
392 if ((*args)[0] == '+' || (*args)[0] == '-') {
394 if (sscanf(*args, "%i", &pix) == 1) {
395 int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
396 double rat = ((max * trg.node->split_ratio) + pix) / max;
397 if (rat > 0 && rat < 1) {
398 set_ratio(trg.node, rat);
404 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
409 if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
410 set_ratio(trg.node, rat);
412 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
417 } else if (streq("-F", *args) || streq("--flip", *args)) {
420 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
423 if (trg.node == NULL) {
428 if (parse_flip(*args, &flp)) {
429 flip_tree(trg.node, flp);
435 } else if (streq("-R", *args) || streq("--rotate", *args)) {
438 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
441 if (trg.node == NULL) {
446 if (parse_degree(*args, °)) {
447 rotate_tree(trg.node, deg);
450 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
453 } else if (streq("-E", *args) || streq("--equalize", *args)) {
454 if (trg.node == NULL) {
458 equalize_tree(trg.node);
460 } else if (streq("-B", *args) || streq("--balance", *args)) {
461 if (trg.node == NULL) {
465 balance_tree(trg.node);
467 } else if (streq("-C", *args) || streq("--circulate", *args)) {
470 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
473 if (trg.node == NULL) {
478 if (parse_circulate_direction(*args, &cir)) {
479 circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
482 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
485 } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
486 insert_receptacle(trg.monitor, trg.desktop, trg.node);
488 } else if (streq("-c", *args) || streq("--close", *args)) {
490 fail(rsp, "node %s: Trailing commands.\n", *args);
493 if (trg.node == NULL || locked_count(trg.node) > 0) {
497 close_node(trg.node);
499 } else if (streq("-k", *args) || streq("--kill", *args)) {
501 fail(rsp, "node %s: Trailing commands.\n", *args);
504 if (trg.node == NULL) {
508 kill_node(trg.monitor, trg.desktop, trg.node);
512 fail(rsp, "node: Unknown command: '%s'.\n", *args);
520 arrange(trg.monitor, trg.desktop);
524 void cmd_desktop(char **args, int num, FILE *rsp)
527 fail(rsp, "desktop: Missing commands.\n");
531 coordinates_t ref = {mon, mon->desk, NULL};
532 coordinates_t trg = ref;
534 if ((*args)[0] != OPT_CHR) {
536 if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
539 handle_failure(ret, "desktop", *args, rsp);
544 bool changed = false;
547 if (streq("-f", *args) || streq("--focus", *args)) {
548 coordinates_t dst = trg;
549 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
552 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
553 handle_failure(ret, "desktop -f", *args, rsp);
557 focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
558 } else if (streq("-a", *args) || streq("--activate", *args)) {
559 coordinates_t dst = trg;
560 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
563 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
564 handle_failure(ret, "desktop -a", *args, rsp);
568 activate_desktop(dst.monitor, dst.desktop);
569 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
572 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
575 if (trg.monitor->desk_head == trg.monitor->desk_tail) {
581 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
582 if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
583 trg.monitor = dst.monitor;
589 handle_failure(ret, "desktop -m", *args, rsp);
592 } else if (streq("-s", *args) || streq("--swap", *args)) {
595 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
600 if ((ret = desktop_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
601 if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
602 trg.monitor = dst.monitor;
608 handle_failure(ret, "desktop -s", *args, rsp);
611 } else if (streq("-b", *args) || streq("--bubble", *args)) {
614 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
618 if (parse_cycle_direction(*args, &cyc)) {
619 desktop_t *d = trg.desktop;
620 if (cyc == CYCLE_PREV) {
621 if (d->prev == NULL) {
622 while (d->next != NULL) {
623 swap_desktops(trg.monitor, d, trg.monitor, d->next);
626 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
629 if (d->next == NULL) {
630 while (d->prev != NULL) {
631 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
634 swap_desktops(trg.monitor, d, trg.monitor, d->next);
638 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
641 } else if (streq("-l", *args) || streq("--layout", *args)) {
644 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
649 if (parse_cycle_direction(*args, &cyc)) {
650 change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
651 } else if (parse_layout(*args, &lyt)) {
652 change_layout(trg.monitor, trg.desktop, lyt);
654 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
657 } else if (streq("-n", *args) || streq("--rename", *args)) {
660 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
663 rename_desktop(trg.monitor, trg.desktop, *args);
664 } else if (streq("-r", *args) || streq("--remove", *args)) {
666 fail(rsp, "desktop %s: Trailing commands.\n", *args);
669 if (trg.desktop->root == NULL &&
670 trg.monitor->desk_head != trg.monitor->desk_tail) {
671 remove_desktop(trg.monitor, trg.desktop);
678 fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
685 arrange(trg.monitor, trg.desktop);
689 void cmd_monitor(char **args, int num, FILE *rsp)
692 fail(rsp, "monitor: Missing commands.\n");
696 coordinates_t ref = {mon, NULL, NULL};
697 coordinates_t trg = ref;
699 if ((*args)[0] != OPT_CHR) {
701 if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
704 handle_failure(ret, "monitor", *args, rsp);
710 if (streq("-f", *args) || streq("--focus", *args)) {
711 coordinates_t dst = trg;
712 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
715 if ((ret = monitor_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
716 handle_failure(ret, "monitor -f", *args, rsp);
721 focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
722 } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
725 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
728 desktop_t *d = trg.monitor->desk_head;
729 while (num > 0 && d != NULL) {
730 rename_desktop(trg.monitor, d, *args);
734 put_status(SBSC_MASK_REPORT);
736 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
740 desktop_t *next = d->next;
741 if (d == mon->desk) {
742 focus_node(trg.monitor, d->prev, d->prev->focus);
744 merge_desktops(trg.monitor, d, mon, mon->desk);
745 remove_desktop(trg.monitor, d);
748 } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
751 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
755 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
758 } else if (streq("-r", *args) || streq("--remove", *args)) {
760 fail(rsp, "monitor %s: Trailing commands.\n", *args);
763 if (mon_head == mon_tail) {
767 remove_monitor(trg.monitor);
769 } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
772 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
775 desktop_t *d = trg.monitor->desk_head;
776 while (d != NULL && num > 0) {
777 desktop_t *next = d->next;
779 if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
780 swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
781 if (next == dst.desktop) {
788 } else if (streq("-g", *args) || streq("--rectangle", *args)) {
791 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
795 if (parse_rectangle(*args, &r)) {
796 update_root(trg.monitor, &r);
798 fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
801 } else if (streq("-n", *args) || streq("--rename", *args)) {
804 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
807 rename_monitor(trg.monitor, *args);
808 } else if (streq("-s", *args) || streq("--swap", *args)) {
811 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
816 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
817 if (!swap_monitors(trg.monitor, dst.monitor)) {
822 handle_failure(ret, "monitor -s", *args, rsp);
826 fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
833 void cmd_query(char **args, int num, FILE *rsp)
835 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
836 coordinates_t trg = {NULL, NULL, NULL};
837 monitor_select_t *monitor_sel = NULL;
838 desktop_select_t *desktop_sel = NULL;
839 node_select_t *node_sel = NULL;
840 domain_t dom = DOMAIN_TREE;
844 if (streq("-T", *args) || streq("--tree", *args)) {
845 dom = DOMAIN_TREE, d++;
846 } else if (streq("-M", *args) || streq("--monitors", *args)) {
847 dom = DOMAIN_MONITOR, d++;
848 } else if (streq("-D", *args) || streq("--desktops", *args)) {
849 dom = DOMAIN_DESKTOP, d++;
850 } else if (streq("-N", *args) || streq("--nodes", *args)) {
851 dom = DOMAIN_NODE, d++;
852 } else if (streq("-m", *args) || streq("--monitor", *args)) {
853 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
856 if ((*args)[0] == '.') {
858 monitor_sel = malloc(sizeof(monitor_select_t));
859 *monitor_sel = make_monitor_select();
860 char *desc = copy_string(*args, strlen(*args));
861 if (!parse_monitor_modifiers(desc, monitor_sel)) {
862 handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
867 } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
868 handle_failure(ret, "query -m", *args, rsp);
872 trg.monitor = ref.monitor;
875 } else if (streq("-d", *args) || streq("--desktop", *args)) {
876 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
879 if ((*args)[0] == '.') {
881 desktop_sel = malloc(sizeof(desktop_select_t));
882 *desktop_sel = make_desktop_select();
883 char *desc = copy_string(*args, strlen(*args));
884 if (!parse_desktop_modifiers(desc, desktop_sel)) {
885 handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
890 } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
891 handle_failure(ret, "query -d", *args, rsp);
895 trg.monitor = ref.monitor;
896 trg.desktop = ref.desktop;
899 } else if (streq("-n", *args) || streq("--node", *args)) {
900 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
903 if ((*args)[0] == '.') {
905 node_sel = malloc(sizeof(node_select_t));
906 *node_sel = make_node_select();
907 char *desc = copy_string(*args, strlen(*args));
908 if (!parse_node_modifiers(desc, node_sel)) {
909 handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
914 } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
915 handle_failure(ret, "query -n", *args, rsp);
920 if (ref.node == NULL) {
927 fail(rsp, "query: Unknown option: '%s'.\n", *args);
933 if (d != 1 || t > 1) {
934 fail(rsp, "query: Exactly one domain and at most one constraint must be specified.\n");
938 if (dom == DOMAIN_NODE) {
939 if (query_node_ids(trg, node_sel, rsp) < 1) {
942 } else if (dom == DOMAIN_DESKTOP) {
943 if (query_desktop_ids(trg, desktop_sel, rsp) < 1) {
946 } else if (dom == DOMAIN_MONITOR) {
947 if (query_monitor_ids(trg, monitor_sel, rsp) < 1) {
951 if (trg.node != NULL) {
952 query_node(trg.node, rsp);
953 } else if (trg.desktop != NULL) {
954 query_desktop(trg.desktop, rsp);
955 } else if (trg.monitor != NULL) {
956 query_monitor(trg.monitor, rsp);
970 void cmd_rule(char **args, int num, FILE *rsp)
973 fail(rsp, "rule: Missing commands.\n");
978 if (streq("-a", *args) || streq("--add", *args)) {
981 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
984 rule_t *rule = make_rule();
985 char *class_name = strtok(*args, COL_TOK);
986 char *instance_name = strtok(NULL, COL_TOK);
987 snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
988 snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
992 if (streq("-o", *args) || streq("--one-shot", *args)) {
993 rule->one_shot = true;
995 for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
996 rule->effect[i] = (*args)[j];
998 if (num > 1 && i < sizeof(rule->effect)) {
999 rule->effect[i++] = ' ';
1004 rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
1006 } else if (streq("-r", *args) || streq("--remove", *args)) {
1009 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1014 if (parse_index(*args, &idx)) {
1015 remove_rule_by_index(idx - 1);
1016 } else if (streq("tail", *args)) {
1017 remove_rule(rule_tail);
1018 } else if (streq("head", *args)) {
1019 remove_rule(rule_head);
1021 remove_rule_by_cause(*args);
1025 } else if (streq("-l", *args) || streq("--list", *args)) {
1028 fail(rsp, "rule: Unknown command: '%s'.\n", *args);
1035 void cmd_pointer(char **args, int num, FILE *rsp)
1038 fail(rsp, "pointer: Missing commands.\n");
1043 if (streq("-t", *args) || streq("--track", *args)) {
1046 fail(rsp, "pointer %s: Not enough arguments.\n", *(args - 1));
1050 if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1) {
1051 track_pointer(x, y);
1057 } else if (streq("-g", *args) || streq("--grab", *args)) {
1060 fail(rsp, "pointer %s: Not enough arguments.\n", *(args - 1));
1063 pointer_action_t pac;
1064 if (parse_pointer_action(*args, &pac)) {
1067 fail(rsp, "pointer %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1070 } else if (streq("-u", *args) || streq("--ungrab", *args)) {
1073 fail(rsp, "pointer: Unknown command: '%s'.\n", *args);
1080 void cmd_wm(char **args, int num, FILE *rsp)
1083 fail(rsp, "wm: Missing commands.\n");
1088 if (streq("-d", *args) || streq("--dump-state", *args)) {
1091 } else if (streq("-l", *args) || streq("--load-state", *args)) {
1094 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1097 if (!restore_tree(*args)) {
1101 } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
1104 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1110 if (parse_rectangle(*args, &r)) {
1111 monitor_t *m = make_monitor(&r, XCB_NONE);
1112 snprintf(m->name, sizeof(m->name), "%s", name);
1114 add_desktop(m, make_desktop(NULL, XCB_NONE));
1116 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1119 } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
1121 } else if (streq("-g", *args) || streq("--get-status", *args)) {
1123 } else if (streq("-h", *args) || streq("--record-history", *args)) {
1126 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1130 if (parse_bool(*args, &b)) {
1133 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1137 fail(rsp, "wm: Unkown command: '%s'.\n", *args);
1144 int cmd_subscribe(char **args, int num, FILE *rsp)
1148 field = SBSC_MASK_REPORT;
1150 subscriber_mask_t mask;
1152 if (parse_subscriber_mask(*args, &mask)) {
1155 fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
1156 return SUBSCRIBE_FAILURE;
1162 add_subscriber(rsp, field);
1163 return SUBSCRIBE_SUCCESS;
1166 void cmd_quit(char **args, int num, FILE *rsp)
1168 if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
1169 fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
1175 void cmd_config(char **args, int num, FILE *rsp)
1178 fail(rsp, "config: Missing arguments.\n");
1182 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
1183 coordinates_t trg = {NULL, NULL, NULL};
1185 while (num > 0 && (*args)[0] == OPT_CHR) {
1186 if (streq("-m", *args) || streq("--monitor", *args)) {
1189 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1193 if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1194 handle_failure(ret, "config -m", *args, rsp);
1197 } else if (streq("-d", *args) || streq("--desktop", *args)) {
1200 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1204 if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1205 handle_failure(ret, "config -d", *args, rsp);
1208 } else if (streq("-n", *args) || streq("--node", *args)) {
1211 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1215 if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1216 handle_failure(ret, "config -n", *args, rsp);
1220 fail(rsp, "config: Unknown option: '%s'.\n", *args);
1226 set_setting(trg, *args, *(args + 1), rsp);
1227 } else if (num == 1) {
1228 get_setting(trg, *args, rsp);
1230 fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
1234 void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
1236 bool colors_changed = false;
1237 #define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
1238 if (loc.node != NULL) { \
1239 loc.node->client->k = v; \
1240 } else if (loc.desktop != NULL) { \
1241 loc.desktop->k = v; \
1242 for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
1243 if (n->client != NULL) { \
1247 } else if (loc.monitor != NULL) { \
1248 loc.monitor->k = v; \
1249 for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1251 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1252 if (n->client != NULL) { \
1259 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1261 for (desktop_t *d = m->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 if (streq("border_width", name)) {
1273 if (sscanf(value, "%u", &bw) != 1) {
1274 fail(rsp, "config: %s: Invalid value: '%s'.", name, value);
1277 SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
1278 #undef SET_DEF_DEFMON_DEFDESK_WIN
1279 #define SET_DEF_DEFMON_DESK(k, v) \
1280 if (loc.desktop != NULL) { \
1281 loc.desktop->k = v; \
1282 } else if (loc.monitor != NULL) { \
1283 loc.monitor->k = v; \
1284 for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1289 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1291 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1296 } else if (streq("window_gap", name)) {
1298 if (sscanf(value, "%i", &wg) != 1) {
1302 SET_DEF_DEFMON_DESK(window_gap, wg)
1303 #undef SET_DEF_DEFMON_DESK
1304 #define SET_DEF_MON_DESK(k, v) \
1305 if (loc.desktop != NULL) { \
1306 loc.desktop->k = v; \
1307 } else if (loc.monitor != NULL) { \
1308 loc.monitor->k = v; \
1311 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1315 } else if (streq("top_padding", name)) {
1317 if (sscanf(value, "%i", &tp) != 1) {
1318 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1321 SET_DEF_MON_DESK(padding.top, tp)
1322 } else if (streq("right_padding", name)) {
1324 if (sscanf(value, "%i", &rp) != 1) {
1325 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1328 SET_DEF_MON_DESK(padding.right, rp)
1329 } else if (streq("bottom_padding", name)) {
1331 if (sscanf(value, "%i", &bp) != 1) {
1332 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1335 SET_DEF_MON_DESK(padding.bottom, bp)
1336 } else if (streq("left_padding", name)) {
1338 if (sscanf(value, "%i", &lp) != 1) {
1339 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1342 SET_DEF_MON_DESK(padding.left, lp)
1343 #undef SET_DEF_MON_DESK
1344 #define SET_STR(s) \
1345 } else if (streq(#s, name)) { \
1346 if (snprintf(s, sizeof(s), "%s", value) < 0) { \
1350 SET_STR(external_rules_command)
1351 SET_STR(status_prefix)
1353 } else if (streq("split_ratio", name)) {
1355 if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
1358 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1362 #define SET_COLOR(s) \
1363 } else if (streq(#s, name)) { \
1364 if (!is_hex_color(value)) { \
1365 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1368 snprintf(s, sizeof(s), "%s", value); \
1369 colors_changed = true; \
1371 SET_COLOR(normal_border_color)
1372 SET_COLOR(active_border_color)
1373 SET_COLOR(focused_border_color)
1374 SET_COLOR(presel_feedback_color)
1376 } else if (streq("initial_polarity", name)) {
1378 if (parse_child_polarity(value, &p)) {
1379 initial_polarity = p;
1381 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1384 } else if (streq("focus_follows_pointer", name)) {
1386 if (parse_bool(value, &b)) {
1387 if (b == focus_follows_pointer) {
1390 focus_follows_pointer = b;
1391 uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
1392 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1393 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1394 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
1395 if (n->client == NULL) {
1398 xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
1402 if (focus_follows_pointer) {
1403 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1404 window_show(m->root);
1407 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1408 window_hide(m->root);
1410 disable_motion_recorder();
1414 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1417 #define SET_BOOL(s) \
1418 } else if (streq(#s, name)) { \
1419 if (!parse_bool(value, &s)) { \
1420 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1423 SET_BOOL(borderless_monocle)
1424 SET_BOOL(gapless_monocle)
1425 SET_BOOL(paddingless_monocle)
1426 SET_BOOL(single_monocle)
1427 SET_BOOL(pointer_follows_focus)
1428 SET_BOOL(pointer_follows_monitor)
1429 SET_BOOL(history_aware_focus)
1430 SET_BOOL(focus_by_distance)
1431 SET_BOOL(ignore_ewmh_focus)
1432 SET_BOOL(center_pseudo_tiled)
1434 #define SET_MON_BOOL(s) \
1435 } else if (streq(#s, name)) { \
1436 if (!parse_bool(value, &s)) { \
1437 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1441 update_monitors(); \
1443 SET_MON_BOOL(remove_disabled_monitors)
1444 SET_MON_BOOL(remove_unplugged_monitors)
1445 SET_MON_BOOL(merge_overlapping_monitors)
1448 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1452 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1453 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1455 if (colors_changed) {
1456 update_colors_in(d->root, d, m);
1462 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1464 if (streq("split_ratio", name)) {
1465 fprintf(rsp, "%lf", split_ratio);
1466 } else if (streq("border_width", name)) {
1467 if (loc.node != NULL) {
1468 fprintf(rsp, "%u", loc.node->client->border_width);
1469 } else if (loc.desktop != NULL) {
1470 fprintf(rsp, "%u", loc.desktop->border_width);
1471 } else if (loc.monitor != NULL) {
1472 fprintf(rsp, "%u", loc.monitor->border_width);
1474 fprintf(rsp, "%u", border_width);
1476 } else if (streq("window_gap", name)) {
1477 if (loc.desktop != NULL) {
1478 fprintf(rsp, "%i", loc.desktop->window_gap);
1479 } else if (loc.monitor != NULL) {
1480 fprintf(rsp, "%i", loc.monitor->window_gap);
1482 fprintf(rsp, "%i", window_gap);
1484 #define GET_DEF_MON_DESK(k) \
1485 if (loc.desktop != NULL) { \
1486 fprintf(rsp, "%i", loc.desktop->k); \
1487 } else if (loc.monitor != NULL) { \
1488 fprintf(rsp, "%i", loc.monitor->k); \
1490 fprintf(rsp, "%i", k); \
1492 } else if (streq("top_padding", name)) {
1493 GET_DEF_MON_DESK(padding.top)
1494 } else if (streq("right_padding", name)) {
1495 GET_DEF_MON_DESK(padding.right)
1496 } else if (streq("bottom_padding", name)) {
1497 GET_DEF_MON_DESK(padding.bottom)
1498 } else if (streq("left_padding", name)) {
1499 GET_DEF_MON_DESK(padding.left)
1500 #undef GET_DEF_MON_DESK
1501 } else if (streq("external_rules_command", name)) {
1502 fprintf(rsp, "%s", external_rules_command);
1503 } else if (streq("status_prefix", name)) {
1504 fprintf(rsp, "%s", status_prefix);
1505 } else if (streq("initial_polarity", name)) {
1506 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1507 #define GET_COLOR(s) \
1508 } else if (streq(#s, name)) { \
1509 fprintf(rsp, "%s", s);
1510 GET_COLOR(normal_border_color)
1511 GET_COLOR(active_border_color)
1512 GET_COLOR(focused_border_color)
1513 GET_COLOR(presel_feedback_color)
1515 #define GET_BOOL(s) \
1516 } else if (streq(#s, name)) { \
1517 fprintf(rsp, "%s", BOOL_STR(s));
1518 GET_BOOL(borderless_monocle)
1519 GET_BOOL(gapless_monocle)
1520 GET_BOOL(paddingless_monocle)
1521 GET_BOOL(single_monocle)
1522 GET_BOOL(focus_follows_pointer)
1523 GET_BOOL(pointer_follows_focus)
1524 GET_BOOL(pointer_follows_monitor)
1525 GET_BOOL(history_aware_focus)
1526 GET_BOOL(focus_by_distance)
1527 GET_BOOL(ignore_ewmh_focus)
1528 GET_BOOL(center_pseudo_tiled)
1529 GET_BOOL(remove_disabled_monitors)
1530 GET_BOOL(remove_unplugged_monitors)
1531 GET_BOOL(merge_overlapping_monitors)
1534 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1540 void handle_failure(int code, char *src, char *val, FILE *rsp)
1543 case SELECTOR_BAD_DESCRIPTOR:
1544 fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1546 case SELECTOR_BAD_MODIFIERS:
1547 fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1549 case SELECTOR_INVALID:
1555 void fail(FILE *rsp, char *fmt, ...)
1557 fprintf(rsp, FAILURE_MESSAGE);
1560 vfprintf(rsp, fmt, ap);