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("locked", key)) {
316 set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
317 } else if (streq("sticky", key)) {
318 set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
319 } else if (streq("private", key)) {
320 set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
322 fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
325 } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
328 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
331 if (trg.node == NULL || trg.node->vacant) {
335 if (streq("cancel", *args)) {
336 cancel_presel(trg.monitor, trg.desktop, trg.node);
338 bool alternate = false;
339 if ((*args)[0] == '~') {
344 if (parse_direction(*args, &dir)) {
345 if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
346 cancel_presel(trg.monitor, trg.desktop, trg.node);
348 presel_dir(trg.monitor, trg.desktop, trg.node, dir);
349 if (!IS_RECEPTACLE(trg.node)) {
350 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
354 fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
358 } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
361 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
364 if (trg.node == NULL || trg.node->vacant) {
369 if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
370 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
373 presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
374 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
376 } else if (streq("-v", *args) || streq("--move", *args)) {
379 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
383 if (sscanf(*args, "%i", &dx) == 1) {
385 if (sscanf(*args, "%i", &dy) == 1) {
386 if (!move_client(&trg, dx, dy)) {
391 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
395 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
398 } else if (streq("-z", *args) || streq("--resize", *args)) {
401 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
405 if (parse_resize_handle(*args, &rh)) {
408 if (sscanf(*args, "%i", &dx) == 1) {
410 if (sscanf(*args, "%i", &dy) == 1) {
411 if (!resize_client(&trg, rh, dx, dy)) {
416 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
420 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
424 fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
427 } else if (streq("-r", *args) || streq("--ratio", *args)) {
430 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
433 if (trg.node == NULL) {
437 if ((*args)[0] == '+' || (*args)[0] == '-') {
439 if (sscanf(*args, "%i", &pix) == 1) {
440 int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
441 double rat = ((max * trg.node->split_ratio) + pix) / max;
442 if (rat > 0 && rat < 1) {
443 set_ratio(trg.node, rat);
449 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
454 if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
455 set_ratio(trg.node, rat);
457 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
462 } else if (streq("-F", *args) || streq("--flip", *args)) {
465 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
468 if (trg.node == NULL) {
473 if (parse_flip(*args, &flp)) {
474 flip_tree(trg.node, flp);
480 } else if (streq("-R", *args) || streq("--rotate", *args)) {
483 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
486 if (trg.node == NULL) {
491 if (parse_degree(*args, °)) {
492 rotate_tree(trg.node, deg);
495 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
498 } else if (streq("-E", *args) || streq("--equalize", *args)) {
499 if (trg.node == NULL) {
503 equalize_tree(trg.node);
505 } else if (streq("-B", *args) || streq("--balance", *args)) {
506 if (trg.node == NULL) {
510 balance_tree(trg.node);
512 } else if (streq("-C", *args) || streq("--circulate", *args)) {
515 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
518 if (trg.node == NULL) {
523 if (parse_circulate_direction(*args, &cir)) {
524 circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
527 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
530 } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
531 insert_receptacle(trg.monitor, trg.desktop, trg.node);
533 } else if (streq("-c", *args) || streq("--close", *args)) {
535 fail(rsp, "node %s: Trailing commands.\n", *args);
538 if (trg.node == NULL || locked_count(trg.node) > 0) {
542 close_node(trg.node);
544 } else if (streq("-k", *args) || streq("--kill", *args)) {
546 fail(rsp, "node %s: Trailing commands.\n", *args);
549 if (trg.node == NULL) {
553 kill_node(trg.monitor, trg.desktop, trg.node);
557 fail(rsp, "node: Unknown command: '%s'.\n", *args);
565 arrange(trg.monitor, trg.desktop);
569 void cmd_desktop(char **args, int num, FILE *rsp)
572 fail(rsp, "desktop: Missing commands.\n");
576 coordinates_t ref = {mon, mon->desk, NULL};
577 coordinates_t trg = ref;
579 if ((*args)[0] != OPT_CHR) {
581 if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
584 handle_failure(ret, "desktop", *args, rsp);
589 bool changed = false;
592 if (streq("-f", *args) || streq("--focus", *args)) {
593 coordinates_t dst = trg;
594 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
597 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
598 handle_failure(ret, "desktop -f", *args, rsp);
602 focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
603 } else if (streq("-a", *args) || streq("--activate", *args)) {
604 coordinates_t dst = trg;
605 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
608 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
609 handle_failure(ret, "desktop -a", *args, rsp);
613 activate_desktop(dst.monitor, dst.desktop);
614 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
617 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
620 if (trg.monitor->desk_head == trg.monitor->desk_tail) {
626 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
627 if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
628 trg.monitor = dst.monitor;
634 handle_failure(ret, "desktop -m", *args, rsp);
637 } else if (streq("-s", *args) || streq("--swap", *args)) {
640 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
645 if ((ret = desktop_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
646 if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
647 trg.monitor = dst.monitor;
653 handle_failure(ret, "desktop -s", *args, rsp);
656 } else if (streq("-b", *args) || streq("--bubble", *args)) {
659 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
663 if (parse_cycle_direction(*args, &cyc)) {
664 desktop_t *d = trg.desktop;
665 if (cyc == CYCLE_PREV) {
666 if (d->prev == NULL) {
667 while (d->next != NULL) {
668 swap_desktops(trg.monitor, d, trg.monitor, d->next);
671 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
674 if (d->next == NULL) {
675 while (d->prev != NULL) {
676 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
679 swap_desktops(trg.monitor, d, trg.monitor, d->next);
683 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
686 } else if (streq("-l", *args) || streq("--layout", *args)) {
689 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
694 if (parse_cycle_direction(*args, &cyc)) {
695 change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
696 } else if (parse_layout(*args, &lyt)) {
697 change_layout(trg.monitor, trg.desktop, lyt);
699 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
702 } else if (streq("-n", *args) || streq("--rename", *args)) {
705 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
708 rename_desktop(trg.monitor, trg.desktop, *args);
709 } else if (streq("-r", *args) || streq("--remove", *args)) {
711 fail(rsp, "desktop %s: Trailing commands.\n", *args);
714 if (trg.desktop->root == NULL &&
715 trg.monitor->desk_head != trg.monitor->desk_tail) {
716 remove_desktop(trg.monitor, trg.desktop);
723 fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
730 arrange(trg.monitor, trg.desktop);
734 void cmd_monitor(char **args, int num, FILE *rsp)
737 fail(rsp, "monitor: Missing commands.\n");
741 coordinates_t ref = {mon, NULL, NULL};
742 coordinates_t trg = ref;
744 if ((*args)[0] != OPT_CHR) {
746 if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
749 handle_failure(ret, "monitor", *args, rsp);
755 if (streq("-f", *args) || streq("--focus", *args)) {
756 coordinates_t dst = trg;
757 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
760 if ((ret = monitor_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
761 handle_failure(ret, "monitor -f", *args, rsp);
766 focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
767 } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
770 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
773 desktop_t *d = trg.monitor->desk_head;
774 while (num > 0 && d != NULL) {
775 rename_desktop(trg.monitor, d, *args);
779 put_status(SBSC_MASK_REPORT);
781 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
785 desktop_t *next = d->next;
786 if (d == mon->desk) {
787 focus_node(trg.monitor, d->prev, d->prev->focus);
789 merge_desktops(trg.monitor, d, mon, mon->desk);
790 remove_desktop(trg.monitor, d);
793 } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
796 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
800 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
803 } else if (streq("-r", *args) || streq("--remove", *args)) {
805 fail(rsp, "monitor %s: Trailing commands.\n", *args);
808 if (mon_head == mon_tail) {
812 remove_monitor(trg.monitor);
814 } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
817 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
820 desktop_t *d = trg.monitor->desk_head;
821 while (d != NULL && num > 0) {
822 desktop_t *next = d->next;
824 if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
825 swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
826 if (next == dst.desktop) {
833 } else if (streq("-g", *args) || streq("--rectangle", *args)) {
836 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
840 if (parse_rectangle(*args, &r)) {
841 update_root(trg.monitor, &r);
843 fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
846 } else if (streq("-n", *args) || streq("--rename", *args)) {
849 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
852 rename_monitor(trg.monitor, *args);
853 } else if (streq("-s", *args) || streq("--swap", *args)) {
856 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
861 if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
862 if (!swap_monitors(trg.monitor, dst.monitor)) {
867 handle_failure(ret, "monitor -s", *args, rsp);
871 fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
878 void cmd_query(char **args, int num, FILE *rsp)
880 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
881 coordinates_t trg = {NULL, NULL, NULL};
882 monitor_select_t *monitor_sel = NULL;
883 desktop_select_t *desktop_sel = NULL;
884 node_select_t *node_sel = NULL;
885 domain_t dom = DOMAIN_TREE;
889 if (streq("-T", *args) || streq("--tree", *args)) {
890 dom = DOMAIN_TREE, d++;
891 } else if (streq("-M", *args) || streq("--monitors", *args)) {
892 dom = DOMAIN_MONITOR, d++;
893 } else if (streq("-D", *args) || streq("--desktops", *args)) {
894 dom = DOMAIN_DESKTOP, d++;
895 } else if (streq("-N", *args) || streq("--nodes", *args)) {
896 dom = DOMAIN_NODE, d++;
897 } else if (streq("-m", *args) || streq("--monitor", *args)) {
898 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
901 if ((*args)[0] == '.') {
903 monitor_sel = malloc(sizeof(monitor_select_t));
904 *monitor_sel = make_monitor_select();
905 char *desc = copy_string(*args, strlen(*args));
906 if (!parse_monitor_modifiers(desc, monitor_sel)) {
907 handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
912 } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
913 handle_failure(ret, "query -m", *args, rsp);
917 trg.monitor = ref.monitor;
920 } else if (streq("-d", *args) || streq("--desktop", *args)) {
921 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
924 if ((*args)[0] == '.') {
926 desktop_sel = malloc(sizeof(desktop_select_t));
927 *desktop_sel = make_desktop_select();
928 char *desc = copy_string(*args, strlen(*args));
929 if (!parse_desktop_modifiers(desc, desktop_sel)) {
930 handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
935 } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
936 handle_failure(ret, "query -d", *args, rsp);
940 trg.monitor = ref.monitor;
941 trg.desktop = ref.desktop;
944 } else if (streq("-n", *args) || streq("--node", *args)) {
945 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
948 if ((*args)[0] == '.') {
950 node_sel = malloc(sizeof(node_select_t));
951 *node_sel = make_node_select();
952 char *desc = copy_string(*args, strlen(*args));
953 if (!parse_node_modifiers(desc, node_sel)) {
954 handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
959 } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
960 handle_failure(ret, "query -n", *args, rsp);
965 if (ref.node == NULL) {
972 fail(rsp, "query: Unknown option: '%s'.\n", *args);
978 if (d != 1 || t > 1) {
979 fail(rsp, "query: Exactly one domain and at most one constraint must be specified.\n");
983 if (dom == DOMAIN_NODE) {
984 if (query_node_ids(trg, node_sel, rsp) < 1) {
987 } else if (dom == DOMAIN_DESKTOP) {
988 if (query_desktop_ids(trg, desktop_sel, rsp) < 1) {
991 } else if (dom == DOMAIN_MONITOR) {
992 if (query_monitor_ids(trg, monitor_sel, rsp) < 1) {
996 if (trg.node != NULL) {
997 query_node(trg.node, rsp);
998 } else if (trg.desktop != NULL) {
999 query_desktop(trg.desktop, rsp);
1000 } else if (trg.monitor != NULL) {
1001 query_monitor(trg.monitor, rsp);
1015 void cmd_rule(char **args, int num, FILE *rsp)
1018 fail(rsp, "rule: Missing commands.\n");
1023 if (streq("-a", *args) || streq("--add", *args)) {
1026 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1029 rule_t *rule = make_rule();
1030 char *class_name = strtok(*args, COL_TOK);
1031 char *instance_name = strtok(NULL, COL_TOK);
1032 snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
1033 snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
1037 if (streq("-o", *args) || streq("--one-shot", *args)) {
1038 rule->one_shot = true;
1040 for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
1041 rule->effect[i] = (*args)[j];
1043 if (num > 1 && i < sizeof(rule->effect)) {
1044 rule->effect[i++] = ' ';
1049 rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
1051 } else if (streq("-r", *args) || streq("--remove", *args)) {
1054 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1059 if (parse_index(*args, &idx)) {
1060 remove_rule_by_index(idx - 1);
1061 } else if (streq("tail", *args)) {
1062 remove_rule(rule_tail);
1063 } else if (streq("head", *args)) {
1064 remove_rule(rule_head);
1066 remove_rule_by_cause(*args);
1070 } else if (streq("-l", *args) || streq("--list", *args)) {
1073 fail(rsp, "rule: 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("pointer_modifier", name)) {
1385 if (parse_modifier_mask(value, &pointer_modifier)) {
1389 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1392 } else if (streq("pointer_action1", name) ||
1393 streq("pointer_action2", name) ||
1394 streq("pointer_action3", name)) {
1395 int index = name[14] - '1';
1396 if (!parse_pointer_action(value, &pointer_actions[index])) {
1397 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1400 } else if (streq("click_to_focus", name)) {
1401 if (parse_bool(value, &click_to_focus)) {
1405 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1408 } else if (streq("focus_follows_pointer", name)) {
1410 if (parse_bool(value, &b)) {
1411 if (b == focus_follows_pointer) {
1414 focus_follows_pointer = b;
1415 uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
1416 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1417 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1418 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
1419 if (n->client == NULL) {
1422 xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
1426 if (focus_follows_pointer) {
1427 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1428 window_show(m->root);
1431 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1432 window_hide(m->root);
1434 disable_motion_recorder();
1438 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1441 #define SET_BOOL(s) \
1442 } else if (streq(#s, name)) { \
1443 if (!parse_bool(value, &s)) { \
1444 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1447 SET_BOOL(borderless_monocle)
1448 SET_BOOL(gapless_monocle)
1449 SET_BOOL(paddingless_monocle)
1450 SET_BOOL(single_monocle)
1451 SET_BOOL(pointer_follows_focus)
1452 SET_BOOL(pointer_follows_monitor)
1453 SET_BOOL(history_aware_focus)
1454 SET_BOOL(focus_by_distance)
1455 SET_BOOL(ignore_ewmh_focus)
1456 SET_BOOL(center_pseudo_tiled)
1457 SET_BOOL(honor_size_hints)
1459 #define SET_MON_BOOL(s) \
1460 } else if (streq(#s, name)) { \
1461 if (!parse_bool(value, &s)) { \
1462 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1466 update_monitors(); \
1468 SET_MON_BOOL(remove_disabled_monitors)
1469 SET_MON_BOOL(remove_unplugged_monitors)
1470 SET_MON_BOOL(merge_overlapping_monitors)
1473 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1477 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1478 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1480 if (colors_changed) {
1481 update_colors_in(d->root, d, m);
1487 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1489 if (streq("split_ratio", name)) {
1490 fprintf(rsp, "%lf", split_ratio);
1491 } else if (streq("border_width", name)) {
1492 if (loc.node != NULL) {
1493 fprintf(rsp, "%u", loc.node->client->border_width);
1494 } else if (loc.desktop != NULL) {
1495 fprintf(rsp, "%u", loc.desktop->border_width);
1496 } else if (loc.monitor != NULL) {
1497 fprintf(rsp, "%u", loc.monitor->border_width);
1499 fprintf(rsp, "%u", border_width);
1501 } else if (streq("window_gap", name)) {
1502 if (loc.desktop != NULL) {
1503 fprintf(rsp, "%i", loc.desktop->window_gap);
1504 } else if (loc.monitor != NULL) {
1505 fprintf(rsp, "%i", loc.monitor->window_gap);
1507 fprintf(rsp, "%i", window_gap);
1509 #define GET_DEF_MON_DESK(k) \
1510 if (loc.desktop != NULL) { \
1511 fprintf(rsp, "%i", loc.desktop->k); \
1512 } else if (loc.monitor != NULL) { \
1513 fprintf(rsp, "%i", loc.monitor->k); \
1515 fprintf(rsp, "%i", k); \
1517 } else if (streq("top_padding", name)) {
1518 GET_DEF_MON_DESK(padding.top)
1519 } else if (streq("right_padding", name)) {
1520 GET_DEF_MON_DESK(padding.right)
1521 } else if (streq("bottom_padding", name)) {
1522 GET_DEF_MON_DESK(padding.bottom)
1523 } else if (streq("left_padding", name)) {
1524 GET_DEF_MON_DESK(padding.left)
1525 #undef GET_DEF_MON_DESK
1526 } else if (streq("external_rules_command", name)) {
1527 fprintf(rsp, "%s", external_rules_command);
1528 } else if (streq("status_prefix", name)) {
1529 fprintf(rsp, "%s", status_prefix);
1530 } else if (streq("initial_polarity", name)) {
1531 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1532 } else if (streq("pointer_modifier", name)) {
1533 print_modifier_mask(pointer_modifier, rsp);
1534 } else if (streq("pointer_action1", name) ||
1535 streq("pointer_action2", name) ||
1536 streq("pointer_action3", name)) {
1537 int index = name[14] - '1';
1538 print_pointer_action(pointer_actions[index], rsp);
1539 #define GET_COLOR(s) \
1540 } else if (streq(#s, name)) { \
1541 fprintf(rsp, "%s", s);
1542 GET_COLOR(normal_border_color)
1543 GET_COLOR(active_border_color)
1544 GET_COLOR(focused_border_color)
1545 GET_COLOR(presel_feedback_color)
1547 #define GET_BOOL(s) \
1548 } else if (streq(#s, name)) { \
1549 fprintf(rsp, "%s", BOOL_STR(s));
1550 GET_BOOL(borderless_monocle)
1551 GET_BOOL(gapless_monocle)
1552 GET_BOOL(paddingless_monocle)
1553 GET_BOOL(single_monocle)
1554 GET_BOOL(click_to_focus)
1555 GET_BOOL(focus_follows_pointer)
1556 GET_BOOL(pointer_follows_focus)
1557 GET_BOOL(pointer_follows_monitor)
1558 GET_BOOL(history_aware_focus)
1559 GET_BOOL(focus_by_distance)
1560 GET_BOOL(ignore_ewmh_focus)
1561 GET_BOOL(center_pseudo_tiled)
1562 GET_BOOL(honor_size_hints)
1563 GET_BOOL(remove_disabled_monitors)
1564 GET_BOOL(remove_unplugged_monitors)
1565 GET_BOOL(merge_overlapping_monitors)
1568 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1574 void handle_failure(int code, char *src, char *val, FILE *rsp)
1577 case SELECTOR_BAD_DESCRIPTOR:
1578 fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1580 case SELECTOR_BAD_MODIFIERS:
1581 fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1583 case SELECTOR_INVALID:
1589 void fail(FILE *rsp, char *fmt, ...)
1591 fprintf(rsp, FAILURE_MESSAGE);
1594 vfprintf(rsp, fmt, ap);