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 = calloc(cap, sizeof(char *));
52 perror("Handle message: calloc");
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 arguments.\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);
139 fail(rsp, "node: Missing commands.\n");
143 bool changed = false;
146 if (streq("-f", *args) || streq("--focus", *args)) {
147 coordinates_t dst = trg;
148 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
151 if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
152 handle_failure(ret, "node -f", *args, rsp);
156 if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
160 } else if (streq("-a", *args) || streq("--activate", *args)) {
161 coordinates_t dst = trg;
162 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
165 if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
166 handle_failure(ret, "node -a", *args, rsp);
170 if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
174 } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
177 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
182 if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
183 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
184 trg.monitor = dst.monitor;
185 trg.desktop = dst.desktop;
191 handle_failure(ret, "node -d", *args, rsp);
194 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
197 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
202 if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
203 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
204 trg.monitor = dst.monitor;
205 trg.desktop = dst.monitor->desk;
211 handle_failure(ret, "node -m", *args, rsp);
214 } else if (streq("-n", *args) || streq("--to-node", *args)) {
217 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
222 if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
223 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
224 trg.monitor = dst.monitor;
225 trg.desktop = dst.desktop;
231 handle_failure(ret, "node -n", *args, rsp);
234 } else if (streq("-s", *args) || streq("--swap", *args)) {
237 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
242 if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
243 if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
244 trg.monitor = dst.monitor;
245 trg.desktop = dst.desktop;
251 handle_failure(ret, "node -s", *args, rsp);
254 } else if (streq("-l", *args) || streq("--layer", *args)) {
257 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
261 if (parse_stack_layer(*args, &lyr)) {
262 if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
267 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
270 } else if (streq("-t", *args) || streq("--state", *args)) {
273 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
277 bool alternate = false;
278 if ((*args)[0] == '~') {
282 if (parse_client_state(*args, &cst)) {
283 if (alternate && trg.node != NULL && trg.node->client != NULL &&
284 trg.node->client->state == cst) {
285 cst = trg.node->client->last_state;
287 if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
293 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
296 } else if (streq("-g", *args) || streq("--flag", *args)) {
299 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
302 if (trg.node == NULL) {
306 char *key = strtok(*args, EQL_TOK);
307 char *val = strtok(NULL, EQL_TOK);
313 if (parse_bool(val, &b)) {
316 fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
320 if (streq("hidden", key)) {
321 set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
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));
327 } else if (streq("locked", key)) {
328 set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
330 fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
333 } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
336 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
339 if (trg.node == NULL || trg.node->vacant) {
343 if (streq("cancel", *args)) {
344 cancel_presel(trg.monitor, trg.desktop, trg.node);
346 bool alternate = false;
347 if ((*args)[0] == '~') {
352 if (parse_direction(*args, &dir)) {
353 if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
354 cancel_presel(trg.monitor, trg.desktop, trg.node);
356 presel_dir(trg.monitor, trg.desktop, trg.node, dir);
357 if (!IS_RECEPTACLE(trg.node)) {
358 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
362 fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
366 } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
369 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
372 if (trg.node == NULL || trg.node->vacant) {
377 if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
378 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
381 presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
382 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
384 } else if (streq("-v", *args) || streq("--move", *args)) {
387 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
391 if (sscanf(*args, "%i", &dx) == 1) {
393 if (sscanf(*args, "%i", &dy) == 1) {
394 if (!move_client(&trg, dx, dy)) {
399 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
403 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
406 } else if (streq("-z", *args) || streq("--resize", *args)) {
409 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
413 if (parse_resize_handle(*args, &rh)) {
416 if (sscanf(*args, "%i", &dx) == 1) {
418 if (sscanf(*args, "%i", &dy) == 1) {
419 if (!resize_client(&trg, rh, dx, dy)) {
424 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
428 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
432 fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
435 } else if (streq("-r", *args) || streq("--ratio", *args)) {
438 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
441 if (trg.node == NULL) {
445 if ((*args)[0] == '+' || (*args)[0] == '-') {
447 if (sscanf(*args, "%f", &delta) == 1) {
448 double rat = trg.node->split_ratio;
449 if (delta > -1 && delta < 1) {
452 int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
453 rat = ((max * rat) + delta) / max;
455 if (rat > 0 && rat < 1) {
456 set_ratio(trg.node, rat);
462 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
467 if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
468 set_ratio(trg.node, rat);
470 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
475 } else if (streq("-F", *args) || streq("--flip", *args)) {
478 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
481 if (trg.node == NULL) {
486 if (parse_flip(*args, &flp)) {
487 flip_tree(trg.node, flp);
493 } else if (streq("-R", *args) || streq("--rotate", *args)) {
496 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
499 if (trg.node == NULL) {
504 if (parse_degree(*args, °)) {
505 rotate_tree(trg.node, deg);
508 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
511 } else if (streq("-E", *args) || streq("--equalize", *args)) {
512 if (trg.node == NULL) {
516 equalize_tree(trg.node);
518 } else if (streq("-B", *args) || streq("--balance", *args)) {
519 if (trg.node == NULL) {
523 balance_tree(trg.node);
525 } else if (streq("-C", *args) || streq("--circulate", *args)) {
528 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
531 if (trg.node == NULL) {
536 if (parse_circulate_direction(*args, &cir)) {
537 circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
540 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
543 } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
544 insert_receptacle(trg.monitor, trg.desktop, trg.node);
546 } else if (streq("-c", *args) || streq("--close", *args)) {
548 fail(rsp, "node %s: Trailing commands.\n", *args);
551 if (trg.node == NULL || locked_count(trg.node) > 0) {
555 close_node(trg.node);
557 } else if (streq("-k", *args) || streq("--kill", *args)) {
559 fail(rsp, "node %s: Trailing commands.\n", *args);
562 if (trg.node == NULL) {
566 kill_node(trg.monitor, trg.desktop, trg.node);
570 fail(rsp, "node: Unknown command: '%s'.\n", *args);
578 arrange(trg.monitor, trg.desktop);
582 void cmd_desktop(char **args, int num, FILE *rsp)
585 fail(rsp, "desktop: Missing arguments.\n");
589 coordinates_t ref = {mon, mon->desk, NULL};
590 coordinates_t trg = ref;
592 if ((*args)[0] != OPT_CHR) {
594 if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
597 handle_failure(ret, "desktop", *args, rsp);
603 fail(rsp, "desktop: Missing commands.\n");
607 bool changed = false;
610 if (streq("-f", *args) || streq("--focus", *args)) {
611 coordinates_t dst = trg;
612 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
615 if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
616 handle_failure(ret, "desktop -f", *args, rsp);
620 focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
621 } else if (streq("-a", *args) || streq("--activate", *args)) {
622 coordinates_t dst = trg;
623 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
626 if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
627 handle_failure(ret, "desktop -a", *args, rsp);
631 if (!activate_desktop(dst.monitor, dst.desktop)) {
635 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
638 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
641 if (trg.monitor->desk_head == trg.monitor->desk_tail) {
647 if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
648 if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
649 trg.monitor = dst.monitor;
655 handle_failure(ret, "desktop -m", *args, rsp);
658 } else if (streq("-s", *args) || streq("--swap", *args)) {
661 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
666 if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
667 if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
668 trg.monitor = dst.monitor;
674 handle_failure(ret, "desktop -s", *args, rsp);
677 } else if (streq("-b", *args) || streq("--bubble", *args)) {
680 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
684 if (parse_cycle_direction(*args, &cyc)) {
685 desktop_t *d = trg.desktop;
686 if (cyc == CYCLE_PREV) {
687 if (d->prev == NULL) {
688 while (d->next != NULL) {
689 swap_desktops(trg.monitor, d, trg.monitor, d->next);
692 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
695 if (d->next == NULL) {
696 while (d->prev != NULL) {
697 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
700 swap_desktops(trg.monitor, d, trg.monitor, d->next);
704 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
707 } else if (streq("-l", *args) || streq("--layout", *args)) {
710 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
716 if (parse_cycle_direction(*args, &cyc)) {
717 ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
718 } else if (parse_layout(*args, &lyt)) {
719 ret = set_layout(trg.monitor, trg.desktop, lyt);
721 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
728 } else if (streq("-n", *args) || streq("--rename", *args)) {
731 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
734 rename_desktop(trg.monitor, trg.desktop, *args);
735 } else if (streq("-r", *args) || streq("--remove", *args)) {
737 fail(rsp, "desktop %s: Trailing commands.\n", *args);
740 if (trg.desktop->root == NULL &&
741 trg.monitor->desk_head != trg.monitor->desk_tail) {
742 remove_desktop(trg.monitor, trg.desktop);
749 fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
756 arrange(trg.monitor, trg.desktop);
760 void cmd_monitor(char **args, int num, FILE *rsp)
763 fail(rsp, "monitor: Missing arguments.\n");
767 coordinates_t ref = {mon, NULL, NULL};
768 coordinates_t trg = ref;
770 if ((*args)[0] != OPT_CHR) {
772 if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
775 handle_failure(ret, "monitor", *args, rsp);
781 fail(rsp, "monitor: Missing commands.\n");
786 if (streq("-f", *args) || streq("--focus", *args)) {
787 coordinates_t dst = trg;
788 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
791 if ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
792 handle_failure(ret, "monitor -f", *args, rsp);
797 focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
798 } else if (streq("-s", *args) || streq("--swap", *args)) {
801 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
806 if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
807 if (!swap_monitors(trg.monitor, dst.monitor)) {
812 handle_failure(ret, "monitor -s", *args, rsp);
815 } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
818 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
821 desktop_t *d = trg.monitor->desk_head;
822 while (num > 0 && d != NULL) {
823 rename_desktop(trg.monitor, d, *args);
827 put_status(SBSC_MASK_REPORT);
829 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
833 desktop_t *next = d->next;
834 if (d == mon->desk) {
835 focus_node(trg.monitor, d->prev, d->prev->focus);
837 merge_desktops(trg.monitor, d, mon, mon->desk);
838 remove_desktop(trg.monitor, d);
841 } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
844 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
848 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
851 } else if (streq("-r", *args) || streq("--remove", *args)) {
853 fail(rsp, "monitor %s: Trailing commands.\n", *args);
856 if (mon_head == mon_tail) {
860 remove_monitor(trg.monitor);
862 } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
865 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
868 desktop_t *d = trg.monitor->desk_head;
869 while (d != NULL && num > 0) {
870 desktop_t *next = d->next;
872 if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
873 swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
874 if (next == dst.desktop) {
881 } else if (streq("-g", *args) || streq("--rectangle", *args)) {
884 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
888 if (parse_rectangle(*args, &r)) {
889 update_root(trg.monitor, &r);
891 fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
894 } else if (streq("-n", *args) || streq("--rename", *args)) {
897 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
900 rename_monitor(trg.monitor, *args);
902 fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
909 void cmd_query(char **args, int num, FILE *rsp)
911 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
912 coordinates_t trg = {NULL, NULL, NULL};
913 monitor_select_t *monitor_sel = NULL;
914 desktop_select_t *desktop_sel = NULL;
915 node_select_t *node_sel = NULL;
916 domain_t dom = DOMAIN_TREE;
917 bool print_ids = true;
921 fail(rsp, "query: Missing arguments.\n");
926 if (streq("-T", *args) || streq("--tree", *args)) {
927 dom = DOMAIN_TREE, d++;
928 } else if (streq("-M", *args) || streq("--monitors", *args)) {
929 dom = DOMAIN_MONITOR, d++;
930 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
933 coordinates_t tmp = ref;
934 if ((ret = monitor_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
935 handle_failure(ret, "query -M", *args, rsp);
939 } else if (streq("-D", *args) || streq("--desktops", *args)) {
940 dom = DOMAIN_DESKTOP, d++;
941 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
944 coordinates_t tmp = ref;
945 if ((ret = desktop_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
946 handle_failure(ret, "query -D", *args, rsp);
950 } else if (streq("-N", *args) || streq("--nodes", *args)) {
951 dom = DOMAIN_NODE, d++;
952 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
955 coordinates_t tmp = ref;
956 if ((ret = node_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
957 handle_failure(ret, "query -N", *args, rsp);
961 } else if (streq("-m", *args) || streq("--monitor", *args)) {
962 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
965 if ((*args)[0] == '.') {
967 monitor_sel = malloc(sizeof(monitor_select_t));
968 *monitor_sel = make_monitor_select();
969 char *desc = copy_string(*args, strlen(*args));
970 if (!parse_monitor_modifiers(desc, monitor_sel)) {
971 handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
976 } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
977 handle_failure(ret, "query -m", *args, rsp);
981 trg.monitor = ref.monitor;
983 } else if (streq("-d", *args) || streq("--desktop", *args)) {
984 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
987 if ((*args)[0] == '.') {
989 desktop_sel = malloc(sizeof(desktop_select_t));
990 *desktop_sel = make_desktop_select();
991 char *desc = copy_string(*args, strlen(*args));
992 if (!parse_desktop_modifiers(desc, desktop_sel)) {
993 handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
998 } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
999 handle_failure(ret, "query -d", *args, rsp);
1003 trg.monitor = ref.monitor;
1004 trg.desktop = ref.desktop;
1006 } else if (streq("-n", *args) || streq("--node", *args)) {
1007 if (num > 1 && *(args + 1)[0] != OPT_CHR) {
1010 if ((*args)[0] == '.') {
1012 node_sel = malloc(sizeof(node_select_t));
1013 *node_sel = make_node_select();
1014 char *desc = copy_string(*args, strlen(*args));
1015 if (!parse_node_modifiers(desc, node_sel)) {
1016 handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
1021 } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1022 handle_failure(ret, "query -n", *args, rsp);
1027 if (ref.node == NULL) {
1032 } else if (streq("--names", *args)) {
1035 fail(rsp, "query: Unknown option: '%s'.\n", *args);
1042 fail(rsp, "query: No commands given.\n");
1047 fail(rsp, "query: Multiple commands given.\n");
1051 if (dom == DOMAIN_TREE && trg.monitor == NULL) {
1052 fail(rsp, "query -T: No options given.\n");
1056 if (dom == DOMAIN_NODE) {
1057 if (query_node_ids(&ref, &trg, node_sel, rsp) < 1) {
1060 } else if (dom == DOMAIN_DESKTOP) {
1061 if (query_desktop_ids(&ref, &trg, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {
1064 } else if (dom == DOMAIN_MONITOR) {
1065 if (query_monitor_ids(&ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {
1069 if (trg.node != NULL) {
1070 query_node(trg.node, rsp);
1071 } else if (trg.desktop != NULL) {
1072 query_desktop(trg.desktop, rsp);
1074 query_monitor(trg.monitor, rsp);
1085 void cmd_rule(char **args, int num, FILE *rsp)
1088 fail(rsp, "rule: Missing commands.\n");
1093 if (streq("-a", *args) || streq("--add", *args)) {
1096 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1099 rule_t *rule = make_rule();
1100 char *class_name = strtok(*args, COL_TOK);
1101 char *instance_name = strtok(NULL, COL_TOK);
1102 snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
1103 snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
1107 if (streq("-o", *args) || streq("--one-shot", *args)) {
1108 rule->one_shot = true;
1110 for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
1111 rule->effect[i] = (*args)[j];
1113 if (num > 1 && i < sizeof(rule->effect)) {
1114 rule->effect[i++] = ' ';
1119 rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
1121 } else if (streq("-r", *args) || streq("--remove", *args)) {
1124 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1129 if (parse_index(*args, &idx)) {
1130 remove_rule_by_index(idx - 1);
1131 } else if (streq("tail", *args)) {
1132 remove_rule(rule_tail);
1133 } else if (streq("head", *args)) {
1134 remove_rule(rule_head);
1136 remove_rule_by_cause(*args);
1140 } else if (streq("-l", *args) || streq("--list", *args)) {
1143 fail(rsp, "rule: Unknown command: '%s'.\n", *args);
1150 void cmd_wm(char **args, int num, FILE *rsp)
1153 fail(rsp, "wm: Missing commands.\n");
1158 if (streq("-d", *args) || streq("--dump-state", *args)) {
1161 } else if (streq("-l", *args) || streq("--load-state", *args)) {
1164 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1167 if (!restore_tree(*args)) {
1171 } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
1174 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1180 if (parse_rectangle(*args, &r)) {
1181 monitor_t *m = make_monitor(name, &r, XCB_NONE);
1183 add_desktop(m, make_desktop(NULL, XCB_NONE));
1185 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1188 } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
1190 } else if (streq("-g", *args) || streq("--get-status", *args)) {
1192 } else if (streq("-h", *args) || streq("--record-history", *args)) {
1195 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1199 if (parse_bool(*args, &b)) {
1202 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1206 fail(rsp, "wm: Unknown command: '%s'.\n", *args);
1213 int cmd_subscribe(char **args, int num, FILE *rsp)
1217 field = SBSC_MASK_REPORT;
1219 subscriber_mask_t mask;
1221 if (parse_subscriber_mask(*args, &mask)) {
1224 fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
1225 return SUBSCRIBE_FAILURE;
1231 add_subscriber(rsp, field);
1232 return SUBSCRIBE_SUCCESS;
1235 void cmd_quit(char **args, int num, FILE *rsp)
1237 if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
1238 fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
1244 void cmd_config(char **args, int num, FILE *rsp)
1247 fail(rsp, "config: Missing arguments.\n");
1251 coordinates_t ref = {mon, mon->desk, mon->desk->focus};
1252 coordinates_t trg = {NULL, NULL, NULL};
1254 while (num > 0 && (*args)[0] == OPT_CHR) {
1255 if (streq("-m", *args) || streq("--monitor", *args)) {
1258 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1262 if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1263 handle_failure(ret, "config -m", *args, rsp);
1266 } else if (streq("-d", *args) || streq("--desktop", *args)) {
1269 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1273 if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1274 handle_failure(ret, "config -d", *args, rsp);
1277 } else if (streq("-n", *args) || streq("--node", *args)) {
1280 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1284 if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1285 handle_failure(ret, "config -n", *args, rsp);
1289 fail(rsp, "config: Unknown option: '%s'.\n", *args);
1295 set_setting(trg, *args, *(args + 1), rsp);
1296 } else if (num == 1) {
1297 get_setting(trg, *args, rsp);
1299 fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
1303 void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
1305 bool colors_changed = false;
1306 #define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
1307 if (loc.node != NULL) { \
1308 for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \
1309 if (n->client != NULL) { \
1313 } else if (loc.desktop != NULL) { \
1314 loc.desktop->k = v; \
1315 for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
1316 if (n->client != NULL) { \
1320 } else if (loc.monitor != NULL) { \
1321 loc.monitor->k = v; \
1322 for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1324 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1325 if (n->client != NULL) { \
1332 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1334 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1336 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1337 if (n->client != NULL) { \
1344 if (streq("border_width", name)) {
1346 if (sscanf(value, "%u", &bw) != 1) {
1347 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1350 SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
1351 #undef SET_DEF_DEFMON_DEFDESK_WIN
1352 #define SET_DEF_DEFMON_DESK(k, v) \
1353 if (loc.desktop != NULL) { \
1354 loc.desktop->k = v; \
1355 } else if (loc.monitor != NULL) { \
1356 loc.monitor->k = v; \
1357 for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1362 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1364 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1369 } else if (streq("window_gap", name)) {
1371 if (sscanf(value, "%i", &wg) != 1) {
1375 SET_DEF_DEFMON_DESK(window_gap, wg)
1376 #undef SET_DEF_DEFMON_DESK
1377 #define SET_DEF_MON_DESK(k, v) \
1378 if (loc.desktop != NULL) { \
1379 loc.desktop->k = v; \
1380 } else if (loc.monitor != NULL) { \
1381 loc.monitor->k = v; \
1384 for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1388 } else if (streq("top_padding", name)) {
1390 if (sscanf(value, "%i", &tp) != 1) {
1391 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1394 SET_DEF_MON_DESK(padding.top, tp)
1395 } else if (streq("right_padding", name)) {
1397 if (sscanf(value, "%i", &rp) != 1) {
1398 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1401 SET_DEF_MON_DESK(padding.right, rp)
1402 } else if (streq("bottom_padding", name)) {
1404 if (sscanf(value, "%i", &bp) != 1) {
1405 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1408 SET_DEF_MON_DESK(padding.bottom, bp)
1409 } else if (streq("left_padding", name)) {
1411 if (sscanf(value, "%i", &lp) != 1) {
1412 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1415 SET_DEF_MON_DESK(padding.left, lp)
1416 #undef SET_DEF_MON_DESK
1417 #define SET_STR(s) \
1418 } else if (streq(#s, name)) { \
1419 if (snprintf(s, sizeof(s), "%s", value) < 0) { \
1423 SET_STR(external_rules_command)
1424 SET_STR(status_prefix)
1426 } else if (streq("split_ratio", name)) {
1428 if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
1431 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1435 #define SET_COLOR(s) \
1436 } else if (streq(#s, name)) { \
1437 if (!is_hex_color(value)) { \
1438 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1441 snprintf(s, sizeof(s), "%s", value); \
1442 colors_changed = true; \
1444 SET_COLOR(normal_border_color)
1445 SET_COLOR(active_border_color)
1446 SET_COLOR(focused_border_color)
1447 SET_COLOR(presel_feedback_color)
1449 } else if (streq("initial_polarity", name)) {
1451 if (parse_child_polarity(value, &p)) {
1452 initial_polarity = p;
1454 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1457 } else if (streq("directional_focus_tightness", name)) {
1459 if (parse_tightness(value, &p)) {
1460 directional_focus_tightness = p;
1462 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1465 } else if (streq("pointer_modifier", name)) {
1466 if (parse_modifier_mask(value, &pointer_modifier)) {
1470 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1473 } else if (streq("pointer_motion_interval", name)) {
1474 if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
1475 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1478 } else if (streq("pointer_action1", name) ||
1479 streq("pointer_action2", name) ||
1480 streq("pointer_action3", name)) {
1481 int index = name[14] - '1';
1482 if (parse_pointer_action(value, &pointer_actions[index])) {
1486 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1489 } else if (streq("click_to_focus", name)) {
1490 if (parse_button_index(value, &click_to_focus)) {
1494 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1497 } else if (streq("focus_follows_pointer", name)) {
1499 if (parse_bool(value, &b)) {
1500 if (b == focus_follows_pointer) {
1504 focus_follows_pointer = b;
1505 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1506 if (focus_follows_pointer) {
1507 window_show(m->root);
1509 window_hide(m->root);
1511 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1512 listen_enter_notify(d->root, focus_follows_pointer);
1515 if (focus_follows_pointer) {
1516 update_motion_recorder();
1518 disable_motion_recorder();
1522 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1525 #define SET_BOOL(s) \
1526 } else if (streq(#s, name)) { \
1527 if (!parse_bool(value, &s)) { \
1528 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1531 SET_BOOL(borderless_monocle)
1532 SET_BOOL(gapless_monocle)
1533 SET_BOOL(paddingless_monocle)
1534 SET_BOOL(single_monocle)
1535 SET_BOOL(swallow_first_click)
1536 SET_BOOL(pointer_follows_focus)
1537 SET_BOOL(pointer_follows_monitor)
1538 SET_BOOL(ignore_ewmh_focus)
1539 SET_BOOL(center_pseudo_tiled)
1540 SET_BOOL(honor_size_hints)
1542 #define SET_MON_BOOL(s) \
1543 } else if (streq(#s, name)) { \
1544 if (!parse_bool(value, &s)) { \
1545 fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1549 update_monitors(); \
1551 SET_MON_BOOL(remove_disabled_monitors)
1552 SET_MON_BOOL(remove_unplugged_monitors)
1553 SET_MON_BOOL(merge_overlapping_monitors)
1556 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1560 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1561 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1563 if (colors_changed) {
1564 update_colors_in(d->root, d, m);
1570 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1572 if (streq("split_ratio", name)) {
1573 fprintf(rsp, "%lf", split_ratio);
1574 } else if (streq("border_width", name)) {
1575 if (loc.node != NULL) {
1576 for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
1577 if (n->client != NULL) {
1578 fprintf(rsp, "%u", n->client->border_width);
1582 } else if (loc.desktop != NULL) {
1583 fprintf(rsp, "%u", loc.desktop->border_width);
1584 } else if (loc.monitor != NULL) {
1585 fprintf(rsp, "%u", loc.monitor->border_width);
1587 fprintf(rsp, "%u", border_width);
1589 } else if (streq("window_gap", name)) {
1590 if (loc.desktop != NULL) {
1591 fprintf(rsp, "%i", loc.desktop->window_gap);
1592 } else if (loc.monitor != NULL) {
1593 fprintf(rsp, "%i", loc.monitor->window_gap);
1595 fprintf(rsp, "%i", window_gap);
1597 #define GET_DEF_MON_DESK(k) \
1598 if (loc.desktop != NULL) { \
1599 fprintf(rsp, "%i", loc.desktop->k); \
1600 } else if (loc.monitor != NULL) { \
1601 fprintf(rsp, "%i", loc.monitor->k); \
1603 fprintf(rsp, "%i", k); \
1605 } else if (streq("top_padding", name)) {
1606 GET_DEF_MON_DESK(padding.top)
1607 } else if (streq("right_padding", name)) {
1608 GET_DEF_MON_DESK(padding.right)
1609 } else if (streq("bottom_padding", name)) {
1610 GET_DEF_MON_DESK(padding.bottom)
1611 } else if (streq("left_padding", name)) {
1612 GET_DEF_MON_DESK(padding.left)
1613 #undef GET_DEF_MON_DESK
1614 } else if (streq("external_rules_command", name)) {
1615 fprintf(rsp, "%s", external_rules_command);
1616 } else if (streq("status_prefix", name)) {
1617 fprintf(rsp, "%s", status_prefix);
1618 } else if (streq("initial_polarity", name)) {
1619 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1620 } else if (streq("directional_focus_tightness", name)) {
1621 fprintf(rsp, "%s", TIGHTNESS_STR(directional_focus_tightness));
1622 } else if (streq("pointer_modifier", name)) {
1623 print_modifier_mask(pointer_modifier, rsp);
1624 } else if (streq("click_to_focus", name)) {
1625 print_button_index(click_to_focus, rsp);
1626 } else if (streq("pointer_motion_interval", name)) {
1627 fprintf(rsp, "%u", pointer_motion_interval);
1628 } else if (streq("pointer_action1", name) ||
1629 streq("pointer_action2", name) ||
1630 streq("pointer_action3", name)) {
1631 int index = name[14] - '1';
1632 print_pointer_action(pointer_actions[index], rsp);
1633 #define GET_COLOR(s) \
1634 } else if (streq(#s, name)) { \
1635 fprintf(rsp, "%s", s);
1636 GET_COLOR(normal_border_color)
1637 GET_COLOR(active_border_color)
1638 GET_COLOR(focused_border_color)
1639 GET_COLOR(presel_feedback_color)
1641 #define GET_BOOL(s) \
1642 } else if (streq(#s, name)) { \
1643 fprintf(rsp, "%s", BOOL_STR(s));
1644 GET_BOOL(borderless_monocle)
1645 GET_BOOL(gapless_monocle)
1646 GET_BOOL(paddingless_monocle)
1647 GET_BOOL(single_monocle)
1648 GET_BOOL(swallow_first_click)
1649 GET_BOOL(focus_follows_pointer)
1650 GET_BOOL(pointer_follows_focus)
1651 GET_BOOL(pointer_follows_monitor)
1652 GET_BOOL(ignore_ewmh_focus)
1653 GET_BOOL(center_pseudo_tiled)
1654 GET_BOOL(honor_size_hints)
1655 GET_BOOL(remove_disabled_monitors)
1656 GET_BOOL(remove_unplugged_monitors)
1657 GET_BOOL(merge_overlapping_monitors)
1660 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1666 void handle_failure(int code, char *src, char *val, FILE *rsp)
1669 case SELECTOR_BAD_DESCRIPTOR:
1670 fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1672 case SELECTOR_BAD_MODIFIERS:
1673 fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1675 case SELECTOR_INVALID:
1681 void fail(FILE *rsp, char *fmt, ...)
1683 fprintf(rsp, FAILURE_MESSAGE);
1686 vfprintf(rsp, fmt, ap);