]> git.lizzy.rs Git - bspwm.git/blob - src/messages.c
f4c7064df2914b2b7ad0d686e174cd0bf8ce82a1
[bspwm.git] / src / messages.c
1 /* Copyright (c) 2012, Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
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.
12  *
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.
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdbool.h>
29 #include <stdarg.h>
30 #include <inttypes.h>
31 #include <unistd.h>
32 #include "bspwm.h"
33 #include "desktop.h"
34 #include "monitor.h"
35 #include "pointer.h"
36 #include "query.h"
37 #include "rule.h"
38 #include "restore.h"
39 #include "settings.h"
40 #include "tree.h"
41 #include "window.h"
42 #include "common.h"
43 #include "parse.h"
44 #include "messages.h"
45
46 void handle_message(char *msg, int msg_len, FILE *rsp)
47 {
48         int cap = INIT_CAP;
49         int num = 0;
50         char **args = calloc(cap, sizeof(char *));
51
52         if (args == NULL) {
53                 perror("Handle message: calloc");
54                 return;
55         }
56
57         for (int i = 0, j = 0; i < msg_len; i++) {
58                 if (msg[i] == 0) {
59                         args[num++] = msg + j;
60                         j = i + 1;
61                 }
62                 if (num >= cap) {
63                         cap *= 2;
64                         char **new = realloc(args, cap * sizeof(char *));
65                         if (new == NULL) {
66                                 free(args);
67                                 perror("Handle message: realloc");
68                                 return;
69                         } else {
70                                 args = new;
71                         }
72                 }
73         }
74
75         if (num < 1) {
76                 free(args);
77                 fail(rsp, "No arguments given.\n");
78                 return;
79         }
80
81         char **args_orig = args;
82         process_message(args, num, rsp);
83         free(args_orig);
84 }
85
86 void process_message(char **args, int num, FILE *rsp)
87 {
88         if (streq("node", *args)) {
89                 cmd_node(++args, --num, rsp);
90         } else if (streq("desktop", *args)) {
91                 cmd_desktop(++args, --num, rsp);
92         } else if (streq("monitor", *args)) {
93                 cmd_monitor(++args, --num, rsp);
94         } else if (streq("query", *args)) {
95                 cmd_query(++args, --num, rsp);
96         } else if (streq("subscribe", *args)) {
97                 cmd_subscribe(++args, --num, rsp);
98                 return;
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);
107         } else {
108                 fail(rsp, "Unknown domain or command: '%s'.\n", *args);
109         }
110
111         fflush(rsp);
112         fclose(rsp);
113 }
114
115 void cmd_node(char **args, int num, FILE *rsp)
116 {
117         if (num < 1) {
118                 fail(rsp, "node: Missing arguments.\n");
119                 return;
120         }
121
122         coordinates_t ref = {mon, mon->desk, mon->desk->focus};
123         coordinates_t trg = ref;
124
125         if ((*args)[0] != OPT_CHR) {
126                 int ret;
127                 if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
128                         num--, args++;
129                 } else {
130                         handle_failure(ret, "node", *args, rsp);
131                         return;
132                 }
133         }
134
135         if (num < 1) {
136                 fail(rsp, "node: Missing commands.\n");
137                 return;
138         }
139
140         bool changed = false;
141
142         while (num > 0) {
143                 if (streq("-f", *args) || streq("--focus", *args)) {
144                         coordinates_t dst = trg;
145                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
146                                 num--, args++;
147                                 int ret;
148                                 if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
149                                         handle_failure(ret, "node -f", *args, rsp);
150                                         break;
151                                 }
152                         }
153                         if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
154                                 fail(rsp, "");
155                                 break;
156                         }
157                 } else if (streq("-a", *args) || streq("--activate", *args)) {
158                         coordinates_t dst = trg;
159                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
160                                 num--, args++;
161                                 int ret;
162                                 if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
163                                         handle_failure(ret, "node -a", *args, rsp);
164                                         break;
165                                 }
166                         }
167                         if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
168                                 fail(rsp, "");
169                                 break;
170                         }
171                 } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
172                         num--, args++;
173                         if (num < 1) {
174                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
175                                 break;
176                         }
177                         coordinates_t dst;
178                         int ret;
179                         if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
180                                 bool follow = false;
181                                 if (num > 1 && streq("--follow", *(args+1))) {
182                                         follow = true;
183                                         num--, args++;
184                                 }
185                                 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus, follow)) {
186                                         trg.monitor = dst.monitor;
187                                         trg.desktop = dst.desktop;
188                                 } else {
189                                         fail(rsp, "");
190                                         break;
191                                 }
192                         } else {
193                                 handle_failure(ret, "node -d", *args, rsp);
194                                 break;
195                         }
196                 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
197                         num--, args++;
198                         if (num < 1) {
199                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
200                                 break;
201                         }
202                         coordinates_t dst;
203                         int ret;
204                         if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
205                                 bool follow = false;
206                                 if (num > 1 && streq("--follow", *(args+1))) {
207                                         follow = true;
208                                         num--, args++;
209                                 }
210                                 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus, follow)) {
211                                         trg.monitor = dst.monitor;
212                                         trg.desktop = dst.monitor->desk;
213                                 } else {
214                                         fail(rsp, "");
215                                         break;
216                                 }
217                         } else {
218                                 handle_failure(ret, "node -m", *args, rsp);
219                                 break;
220                         }
221                 } else if (streq("-n", *args) || streq("--to-node", *args)) {
222                         num--, args++;
223                         if (num < 1) {
224                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
225                                 break;
226                         }
227                         coordinates_t dst;
228                         int ret;
229                         if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
230                                 bool follow = false;
231                                 if (num > 1 && streq("--follow", *(args+1))) {
232                                         follow = true;
233                                         num--, args++;
234                                 }
235                                 if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node, follow)) {
236                                         trg.monitor = dst.monitor;
237                                         trg.desktop = dst.desktop;
238                                 } else {
239                                         fail(rsp, "");
240                                         break;
241                                 }
242                         } else {
243                                 handle_failure(ret, "node -n", *args, rsp);
244                                 break;
245                         }
246                 } else if (streq("-s", *args) || streq("--swap", *args)) {
247                         num--, args++;
248                         if (num < 1) {
249                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
250                                 break;
251                         }
252                         coordinates_t dst;
253                         int ret;
254                         if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
255                                 bool follow = false;
256                                 if (num > 1 && streq("--follow", *(args+1))) {
257                                         follow = true;
258                                         num--, args++;
259                                 }
260                                 if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node, follow)) {
261                                         trg.monitor = dst.monitor;
262                                         trg.desktop = dst.desktop;
263                                 } else {
264                                         fail(rsp, "");
265                                         break;
266                                 }
267                         } else {
268                                 handle_failure(ret, "node -s", *args, rsp);
269                                 break;
270                         }
271                 } else if (streq("-l", *args) || streq("--layer", *args)) {
272                         num--, args++;
273                         if (num < 1) {
274                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
275                                 break;
276                         }
277                         stack_layer_t lyr;
278                         if (parse_stack_layer(*args, &lyr)) {
279                                 if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
280                                         fail(rsp, "");
281                                         break;
282                                 }
283                         } else {
284                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
285                                 break;
286                         }
287                 } else if (streq("-t", *args) || streq("--state", *args)) {
288                         num--, args++;
289                         if (num < 1) {
290                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
291                                 break;
292                         }
293                         client_state_t cst;
294                         bool alternate = false;
295                         if ((*args)[0] == '~') {
296                                 alternate = true;
297                                 (*args)++;
298                         }
299                         if (parse_client_state(*args, &cst)) {
300                                 if (alternate && trg.node != NULL && trg.node->client != NULL &&
301                                     trg.node->client->state == cst) {
302                                         cst = trg.node->client->last_state;
303                                 }
304                                 if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
305                                         fail(rsp, "");
306                                         break;
307                                 }
308                                 changed = true;
309                         } else {
310                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
311                                 break;
312                         }
313                 } else if (streq("-g", *args) || streq("--flag", *args)) {
314                         num--, args++;
315                         if (num < 1) {
316                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
317                                 break;
318                         }
319                         if (trg.node == NULL) {
320                                 fail(rsp, "");
321                                 break;
322                         }
323                         char *key = strtok(*args, EQL_TOK);
324                         char *val = strtok(NULL, EQL_TOK);
325                         alter_state_t a;
326                         bool b;
327                         if (val == NULL) {
328                                 a = ALTER_TOGGLE;
329                         } else {
330                                 if (parse_bool(val, &b)) {
331                                         a = ALTER_SET;
332                                 } else {
333                                         fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
334                                         break;
335                                 }
336                         }
337                         if (streq("hidden", key)) {
338                                 set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
339                                 changed = true;
340                         } else if (streq("sticky", key)) {
341                                 set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
342                         } else if (streq("private", key)) {
343                                 set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
344                         } else if (streq("locked", key)) {
345                                 set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
346                         } else if (streq("marked", key)) {
347                                 set_marked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->marked));
348                         } else {
349                                 fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
350                                 break;
351                         }
352                 } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
353                         num--, args++;
354                         if (num < 1) {
355                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
356                                 break;
357                         }
358                         if (trg.node == NULL || trg.node->vacant) {
359                                 fail(rsp, "");
360                                 break;
361                         }
362                         if (streq("cancel", *args)) {
363                                 cancel_presel(trg.monitor, trg.desktop, trg.node);
364                         } else {
365                                 bool alternate = false;
366                                 if ((*args)[0] == '~') {
367                                         alternate = true;
368                                         (*args)++;
369                                 }
370                                 direction_t dir;
371                                 if (parse_direction(*args, &dir)) {
372                                         if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
373                                                 cancel_presel(trg.monitor, trg.desktop, trg.node);
374                                         } else {
375                                                 presel_dir(trg.monitor, trg.desktop, trg.node, dir);
376                                                 if (!IS_RECEPTACLE(trg.node)) {
377                                                         draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
378                                                 }
379                                         }
380                                 } else {
381                                         fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
382                                         break;
383                                 }
384                         }
385                 } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
386                         num--, args++;
387                         if (num < 1) {
388                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
389                                 break;
390                         }
391                         if (trg.node == NULL || trg.node->vacant) {
392                                 fail(rsp, "");
393                                 break;
394                         }
395                         double rat;
396                         if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
397                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
398                                 break;
399                         } else {
400                                 presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
401                                 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
402                         }
403                 } else if (streq("-v", *args) || streq("--move", *args)) {
404                         num--, args++;
405                         if (num < 2) {
406                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
407                                 break;
408                         }
409                         int dx = 0, dy = 0;
410                         if (sscanf(*args, "%i", &dx) == 1) {
411                                 num--, args++;
412                                 if (sscanf(*args, "%i", &dy) == 1) {
413                                         if (!move_client(&trg, dx, dy)) {
414                                                 fail(rsp, "");
415                                                 break;
416                                         }
417                                 } else {
418                                         fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
419                                         break;
420                                 }
421                         } else {
422                                 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
423                                 break;
424                         }
425                 } else if (streq("-z", *args) || streq("--resize", *args)) {
426                         num--, args++;
427                         if (num < 3) {
428                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
429                                 break;
430                         }
431                         resize_handle_t rh;
432                         if (parse_resize_handle(*args, &rh)) {
433                                 num--, args++;
434                                 int dx = 0, dy = 0;
435                                 if (sscanf(*args, "%i", &dx) == 1) {
436                                         num--, args++;
437                                         if (sscanf(*args, "%i", &dy) == 1) {
438                                                 if (!resize_client(&trg, rh, dx, dy, true)) {
439                                                         fail(rsp, "");
440                                                         break;
441                                                 }
442                                         } else {
443                                                 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
444                                                 break;
445                                         }
446                                 } else {
447                                         fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
448                                         break;
449                                 }
450                         } else {
451                                 fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
452                                 break;
453                         }
454                 } else if (streq("-r", *args) || streq("--ratio", *args)) {
455                         num--, args++;
456                         if (num < 1) {
457                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
458                                 break;
459                         }
460                         if (trg.node == NULL) {
461                                 fail(rsp, "");
462                                 break;
463                         }
464                         if ((*args)[0] == '+' || (*args)[0] == '-') {
465                                 float delta;
466                                 if (sscanf(*args, "%f", &delta) == 1) {
467                                         double rat = trg.node->split_ratio;
468                                         if (delta > -1 && delta < 1) {
469                                                 rat += delta;
470                                         } else {
471                                                 int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
472                                                 rat = ((max * rat) + delta) / max;
473                                         }
474                                         if (rat > 0 && rat < 1) {
475                                                 set_ratio(trg.node, rat);
476                                         } else {
477                                                 fail(rsp, "");
478                                                 break;
479                                         }
480                                 } else {
481                                         fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
482                                         break;
483                                 }
484                         } else {
485                                 double rat;
486                                 if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
487                                         set_ratio(trg.node, rat);
488                                 } else {
489                                         fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
490                                         break;
491                                 }
492                         }
493                         changed = true;
494                 } else if (streq("-F", *args) || streq("--flip", *args)) {
495                         num--, args++;
496                         if (num < 1) {
497                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
498                                 break;
499                         }
500                         if (trg.node == NULL) {
501                                 fail(rsp, "");
502                                 break;
503                         }
504                         flip_t flp;
505                         if (parse_flip(*args, &flp)) {
506                                 flip_tree(trg.node, flp);
507                                 changed = true;
508                         } else {
509                                 fail(rsp, "");
510                                 break;
511                         }
512                 } else if (streq("-R", *args) || streq("--rotate", *args)) {
513                         num--, args++;
514                         if (num < 1) {
515                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
516                                 break;
517                         }
518                         if (trg.node == NULL) {
519                                 fail(rsp, "");
520                                 break;
521                         }
522                         int deg;
523                         if (parse_degree(*args, &deg)) {
524                                 rotate_tree(trg.node, deg);
525                                 changed = true;
526                         } else {
527                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
528                                 break;
529                         }
530                 } else if (streq("-E", *args) || streq("--equalize", *args)) {
531                         if (trg.node == NULL) {
532                                 fail(rsp, "");
533                                 break;
534                         }
535                         equalize_tree(trg.node);
536                         changed = true;
537                 } else if (streq("-B", *args) || streq("--balance", *args)) {
538                         if (trg.node == NULL) {
539                                 fail(rsp, "");
540                                 break;
541                         }
542                         balance_tree(trg.node);
543                         changed = true;
544                 } else if (streq("-C", *args) || streq("--circulate", *args)) {
545                         num--, args++;
546                         if (num < 1) {
547                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
548                                 break;
549                         }
550                         if (trg.node == NULL) {
551                                 fail(rsp, "");
552                                 break;
553                         }
554                         circulate_dir_t cir;
555                         if (parse_circulate_direction(*args, &cir)) {
556                                 circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
557                                 changed = true;
558                         } else {
559                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
560                                 break;
561                         }
562                 } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
563                         insert_receptacle(trg.monitor, trg.desktop, trg.node);
564                         changed = true;
565                 } else if (streq("-c", *args) || streq("--close", *args)) {
566                         if (num > 1) {
567                                 fail(rsp, "node %s: Trailing commands.\n", *args);
568                                 break;
569                         }
570                         if (trg.node == NULL || locked_count(trg.node) > 0) {
571                                 fail(rsp, "");
572                                 break;
573                         }
574                         close_node(trg.node);
575                         break;
576                 } else if (streq("-k", *args) || streq("--kill", *args)) {
577                         if (num > 1) {
578                                 fail(rsp, "node %s: Trailing commands.\n", *args);
579                                 break;
580                         }
581                         if (trg.node == NULL) {
582                                 fail(rsp, "");
583                                 break;
584                         }
585                         kill_node(trg.monitor, trg.desktop, trg.node);
586                         changed = true;
587                         break;
588                 } else {
589                         fail(rsp, "node: Unknown command: '%s'.\n", *args);
590                         break;
591                 }
592
593                 num--, args++;
594         }
595
596         if (changed) {
597                 arrange(trg.monitor, trg.desktop);
598         }
599 }
600
601 void cmd_desktop(char **args, int num, FILE *rsp)
602 {
603         if (num < 1) {
604                 fail(rsp, "desktop: Missing arguments.\n");
605                 return;
606         }
607
608         coordinates_t ref = {mon, mon->desk, NULL};
609         coordinates_t trg = ref;
610
611         if ((*args)[0] != OPT_CHR) {
612                 int ret;
613                 if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
614                         num--, args++;
615                 } else {
616                         handle_failure(ret, "desktop", *args, rsp);
617                         return;
618                 }
619         }
620
621         if (num < 1) {
622                 fail(rsp, "desktop: Missing commands.\n");
623                 return;
624         }
625
626         bool changed = false;
627
628         while (num > 0) {
629                 if (streq("-f", *args) || streq("--focus", *args)) {
630                         coordinates_t dst = trg;
631                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
632                                 num--, args++;
633                                 int ret;
634                                 if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
635                                         handle_failure(ret, "desktop -f", *args, rsp);
636                                         break;
637                                 }
638                         }
639                         focus_node(dst.monitor, dst.desktop, NULL);
640                 } else if (streq("-a", *args) || streq("--activate", *args)) {
641                         coordinates_t dst = trg;
642                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
643                                 num--, args++;
644                                 int ret;
645                                 if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
646                                         handle_failure(ret, "desktop -a", *args, rsp);
647                                         break;
648                                 }
649                         }
650                         if (activate_desktop(dst.monitor, dst.desktop)) {
651                                 activate_node(dst.monitor, dst.desktop, NULL);
652                         } else {
653                                 fail(rsp, "");
654                                 break;
655                         }
656                 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
657                         num--, args++;
658                         if (num < 1) {
659                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
660                                 break;
661                         }
662                         if (trg.monitor->desk_head == trg.monitor->desk_tail) {
663                                 fail(rsp, "");
664                                 break;
665                         }
666                         coordinates_t dst;
667                         int ret;
668                         if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
669                                 bool follow = false;
670                                 if (num > 1 && streq("--follow", *(args+1))) {
671                                         follow = true;
672                                         num--, args++;
673                                 }
674                                 if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop, follow)) {
675                                         trg.monitor = dst.monitor;
676                                 } else {
677                                         fail(rsp, "");
678                                         break;
679                                 }
680                         } else {
681                                 handle_failure(ret, "desktop -m", *args, rsp);
682                                 break;
683                         }
684                 } else if (streq("-s", *args) || streq("--swap", *args)) {
685                         num--, args++;
686                         if (num < 1) {
687                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
688                                 break;
689                         }
690                         coordinates_t dst;
691                         int ret;
692                         if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
693                                 bool follow = false;
694                                 if (num > 1 && streq("--follow", *(args+1))) {
695                                         follow = true;
696                                         num--, args++;
697                                 }
698                                 if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop, follow)) {
699                                         trg.monitor = dst.monitor;
700                                 } else {
701                                         fail(rsp, "");
702                                         break;
703                                 }
704                         } else {
705                                 handle_failure(ret, "desktop -s", *args, rsp);
706                                 break;
707                         }
708                 } else if (streq("-b", *args) || streq("--bubble", *args)) {
709                         num--, args++;
710                         if (num < 1) {
711                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
712                                 break;
713                         }
714                         cycle_dir_t cyc;
715                         if (parse_cycle_direction(*args, &cyc)) {
716                                 desktop_t *d = trg.desktop;
717                                 if (cyc == CYCLE_PREV) {
718                                         if (d->prev == NULL) {
719                                                 while (d->next != NULL) {
720                                                         swap_desktops(trg.monitor, d, trg.monitor, d->next, false);
721                                                 }
722                                         } else {
723                                                 swap_desktops(trg.monitor, d, trg.monitor, d->prev, false);
724                                         }
725                                 } else {
726                                         if (d->next == NULL) {
727                                                 while (d->prev != NULL) {
728                                                         swap_desktops(trg.monitor, d, trg.monitor, d->prev, false);
729                                                 }
730                                         } else {
731                                                 swap_desktops(trg.monitor, d, trg.monitor, d->next, false);
732                                         }
733                                 }
734                         } else {
735                                 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
736                                 break;
737                         }
738                 } else if (streq("-l", *args) || streq("--layout", *args)) {
739                         num--, args++;
740                         if (num < 1) {
741                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
742                                 break;
743                         }
744                         bool ret;
745                         layout_t lyt;
746                         cycle_dir_t cyc;
747                         if (parse_cycle_direction(*args, &cyc)) {
748                                 ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->user_layout + 1) % 2, true);
749                         } else if (parse_layout(*args, &lyt)) {
750                                 ret = set_layout(trg.monitor, trg.desktop, lyt, true);
751                         } else {
752                                 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
753                                 break;
754                         }
755                         if (!ret) {
756                                 fail(rsp, "");
757                                 break;
758                         }
759                 } else if (streq("-n", *args) || streq("--rename", *args)) {
760                         num--, args++;
761                         if (num < 1) {
762                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
763                                 break;
764                         }
765                         rename_desktop(trg.monitor, trg.desktop, *args);
766                 } else if (streq("-r", *args) || streq("--remove", *args)) {
767                         if (num > 1) {
768                                 fail(rsp, "desktop %s: Trailing commands.\n", *args);
769                                 break;
770                         }
771                         if (trg.monitor->desk_head != trg.monitor->desk_tail) {
772                                 desktop_t *fallback = trg.desktop->prev == NULL ?
773                                                       trg.desktop->next :
774                                                       trg.desktop->prev;
775                                 merge_desktops(trg.monitor, trg.desktop, trg.monitor, fallback);
776                                 remove_desktop(trg.monitor, trg.desktop);
777                                 return;
778                         } else {
779                                 fail(rsp, "");
780                                 break;
781                         }
782                 } else {
783                         fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
784                         break;
785                 }
786                 num--, args++;
787         }
788
789         if (changed) {
790                 arrange(trg.monitor, trg.desktop);
791         }
792 }
793
794 void cmd_monitor(char **args, int num, FILE *rsp)
795 {
796         if (num < 1) {
797                 fail(rsp, "monitor: Missing arguments.\n");
798                 return;
799         }
800
801         coordinates_t ref = {mon, NULL, NULL};
802         coordinates_t trg = ref;
803
804         if ((*args)[0] != OPT_CHR) {
805                 int ret;
806                 if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
807                         num--, args++;
808                 } else {
809                         handle_failure(ret, "monitor", *args, rsp);
810                         return;
811                 }
812         }
813
814         if (num < 1) {
815                 fail(rsp, "monitor: Missing commands.\n");
816                 return;
817         }
818
819         while (num > 0) {
820                 if (streq("-f", *args) || streq("--focus", *args)) {
821                         coordinates_t dst = trg;
822                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
823                                 num--, args++;
824                                 int ret;
825                                 if ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
826                                         handle_failure(ret, "monitor -f", *args, rsp);
827                                         fail(rsp, "");
828                                         return;
829                                 }
830                         }
831                         focus_node(dst.monitor, NULL, NULL);
832                 } else if (streq("-s", *args) || streq("--swap", *args)) {
833                         num--, args++;
834                         if (num < 1) {
835                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
836                                 return;
837                         }
838                         coordinates_t dst;
839                         int ret;
840                         if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
841                                 if (!swap_monitors(trg.monitor, dst.monitor)) {
842                                         fail(rsp, "");
843                                         return;
844                                 }
845                         } else {
846                                 handle_failure(ret, "monitor -s", *args, rsp);
847                                 return;
848                         }
849                 } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
850                         num--, args++;
851                         if (num < 1) {
852                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
853                                 return;
854                         }
855                         desktop_t *d = trg.monitor->desk_head;
856                         while (num > 0 && d != NULL) {
857                                 rename_desktop(trg.monitor, d, *args);
858                                 d = d->next;
859                                 num--, args++;
860                         }
861                         put_status(SBSC_MASK_REPORT);
862                         while (num > 0) {
863                                 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
864                                 num--, args++;
865                         }
866                         while (d != NULL) {
867                                 desktop_t *next = d->next;
868                                 if (d == mon->desk) {
869                                         focus_node(trg.monitor, d->prev, d->prev->focus);
870                                 }
871                                 merge_desktops(trg.monitor, d, mon, mon->desk);
872                                 remove_desktop(trg.monitor, d);
873                                 d = next;
874                         }
875                 } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
876                         num--, args++;
877                         if (num < 1) {
878                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
879                                 return;
880                         }
881                         while (num > 0) {
882                                 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
883                                 num--, args++;
884                         }
885                 } else if (streq("-r", *args) || streq("--remove", *args)) {
886                         if (num > 1) {
887                                 fail(rsp, "monitor %s: Trailing commands.\n", *args);
888                                 return;
889                         }
890                         if (mon_head == mon_tail) {
891                                 fail(rsp, "");
892                                 return;
893                         }
894                         remove_monitor(trg.monitor);
895                         return;
896                 } else if (streq("-o", *args) || streq("--reorder-desktops", *args)) {
897                         num--, args++;
898                         if (num < 1) {
899                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
900                                 return;
901                         }
902                         desktop_t *d = trg.monitor->desk_head;
903                         while (d != NULL && num > 0) {
904                                 desktop_t *next = d->next;
905                                 coordinates_t dst;
906                                 if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
907                                         swap_desktops(trg.monitor, d, dst.monitor, dst.desktop, false);
908                                         if (next == dst.desktop) {
909                                                 next = d;
910                                         }
911                                 }
912                                 d = next;
913                                 num--, args++;
914                         }
915                 } else if (streq("-g", *args) || streq("--rectangle", *args)) {
916                         num--, args++;
917                         if (num < 1) {
918                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
919                                 return;
920                         }
921                         xcb_rectangle_t r;
922                         if (parse_rectangle(*args, &r)) {
923                                 update_root(trg.monitor, &r);
924                         } else {
925                                 fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
926                                 return;
927                         }
928                 } else if (streq("-n", *args) || streq("--rename", *args)) {
929                         num--, args++;
930                         if (num < 1) {
931                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
932                                 return;
933                         }
934                         rename_monitor(trg.monitor, *args);
935                 } else {
936                         fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
937                         return;
938                 }
939                 num--, args++;
940         }
941 }
942
943 void cmd_query(char **args, int num, FILE *rsp)
944 {
945         coordinates_t ref = {mon, mon->desk, mon->desk->focus};
946         coordinates_t trg = {NULL, NULL, NULL};
947         monitor_select_t *monitor_sel = NULL;
948         desktop_select_t *desktop_sel = NULL;
949         node_select_t *node_sel = NULL;
950         domain_t dom = DOMAIN_TREE;
951         bool print_ids = true;
952         uint8_t d = 0;
953
954         if (num < 1) {
955                 fail(rsp, "query: Missing arguments.\n");
956                 return;
957         }
958
959         while (num > 0) {
960                 if (streq("-T", *args) || streq("--tree", *args)) {
961                         dom = DOMAIN_TREE, d++;
962                 } else if (streq("-M", *args) || streq("--monitors", *args)) {
963                         dom = DOMAIN_MONITOR, d++;
964                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
965                                 num--, args++;
966                                 int ret;
967                                 coordinates_t tmp = ref;
968                                 ref.desktop = NULL;
969                                 ref.node = NULL;
970                                 if ((ret = monitor_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
971                                         handle_failure(ret, "query -M", *args, rsp);
972                                         goto end;
973                                 }
974                         }
975                 } else if (streq("-D", *args) || streq("--desktops", *args)) {
976                         dom = DOMAIN_DESKTOP, d++;
977                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
978                                 num--, args++;
979                                 int ret;
980                                 coordinates_t tmp = ref;
981                                 ref.node = NULL;
982                                 if ((ret = desktop_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
983                                         handle_failure(ret, "query -D", *args, rsp);
984                                         goto end;
985                                 }
986                         }
987                 } else if (streq("-N", *args) || streq("--nodes", *args)) {
988                         dom = DOMAIN_NODE, d++;
989                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
990                                 num--, args++;
991                                 int ret;
992                                 coordinates_t tmp = ref;
993                                 if ((ret = node_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
994                                         handle_failure(ret, "query -N", *args, rsp);
995                                         goto end;
996                                 }
997                         }
998                 } else if (streq("-m", *args) || streq("--monitor", *args)) {
999                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
1000                                 num--, args++;
1001                                 int ret;
1002                                 if ((*args)[0] == '.') {
1003                                         free(monitor_sel);
1004                                         monitor_sel = malloc(sizeof(monitor_select_t));
1005                                         *monitor_sel = make_monitor_select();
1006                                         char *desc = copy_string(*args, strlen(*args));
1007                                         if (!parse_monitor_modifiers(desc, monitor_sel)) {
1008                                                 handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
1009                                                 free(desc);
1010                                                 goto end;
1011                                         }
1012                                         free(desc);
1013                                 } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1014                                         handle_failure(ret, "query -m", *args, rsp);
1015                                         goto end;
1016                                 }
1017                         } else {
1018                                 trg.monitor = ref.monitor;
1019                         }
1020                 } else if (streq("-d", *args) || streq("--desktop", *args)) {
1021                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
1022                                 num--, args++;
1023                                 int ret;
1024                                 if ((*args)[0] == '.') {
1025                                         free(desktop_sel);
1026                                         desktop_sel = malloc(sizeof(desktop_select_t));
1027                                         *desktop_sel = make_desktop_select();
1028                                         char *desc = copy_string(*args, strlen(*args));
1029                                         if (!parse_desktop_modifiers(desc, desktop_sel)) {
1030                                                 handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
1031                                                 free(desc);
1032                                                 goto end;
1033                                         }
1034                                         free(desc);
1035                                 } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1036                                         handle_failure(ret, "query -d", *args, rsp);
1037                                         goto end;
1038                                 }
1039                         } else {
1040                                 trg.monitor = ref.monitor;
1041                                 trg.desktop = ref.desktop;
1042                         }
1043                 } else if (streq("-n", *args) || streq("--node", *args)) {
1044                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
1045                                 num--, args++;
1046                                 int ret;
1047                                 if ((*args)[0] == '.') {
1048                                         free(node_sel);
1049                                         node_sel = malloc(sizeof(node_select_t));
1050                                         *node_sel = make_node_select();
1051                                         char *desc = copy_string(*args, strlen(*args));
1052                                         if (!parse_node_modifiers(desc, node_sel)) {
1053                                                 handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
1054                                                 free(desc);
1055                                                 goto end;
1056                                         }
1057                                         free(desc);
1058                                 } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1059                                         handle_failure(ret, "query -n", *args, rsp);
1060                                         goto end;
1061                                 }
1062                         } else {
1063                                 trg = ref;
1064                                 if (ref.node == NULL) {
1065                                         fail(rsp, "");
1066                                         goto end;
1067                                 }
1068                         }
1069                 } else if (streq("--names", *args)) {
1070                         print_ids = false;
1071                 } else {
1072                         fail(rsp, "query: Unknown option: '%s'.\n", *args);
1073                         goto end;
1074                 }
1075                 num--, args++;
1076         }
1077
1078         if (d < 1) {
1079                 fail(rsp, "query: No commands given.\n");
1080                 goto end;
1081         }
1082
1083         if (d > 1) {
1084                 fail(rsp, "query: Multiple commands given.\n");
1085                 goto end;
1086         }
1087
1088         if (dom == DOMAIN_TREE && trg.monitor == NULL) {
1089                 fail(rsp, "query -T: No options given.\n");
1090                 goto end;
1091         }
1092
1093         if (!print_ids && (dom == DOMAIN_NODE || dom == DOMAIN_TREE)) {
1094                 fail(rsp, "query -%c: --names only applies to -M and -D.\n", dom == DOMAIN_NODE ? 'N' : 'T');
1095                 goto end;
1096         }
1097
1098         if ((dom == DOMAIN_MONITOR && (desktop_sel != NULL || node_sel != NULL)) ||
1099             (dom == DOMAIN_DESKTOP && node_sel != NULL)) {
1100                 fail(rsp, "query -%c: Incompatible descriptor-free constraints.\n", dom == DOMAIN_MONITOR ? 'M' : 'D');
1101                 goto end;
1102         }
1103
1104         if (dom == DOMAIN_NODE) {
1105                 if (query_node_ids(&ref, &trg, monitor_sel, desktop_sel, node_sel, rsp) < 1) {
1106                         fail(rsp, "");
1107                 }
1108         } else if (dom == DOMAIN_DESKTOP) {
1109                 if (query_desktop_ids(&ref, &trg, monitor_sel, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {
1110                         fail(rsp, "");
1111                 }
1112         } else if (dom == DOMAIN_MONITOR) {
1113                 if (query_monitor_ids(&ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {
1114                         fail(rsp, "");
1115                 }
1116         } else {
1117                 if (trg.node != NULL) {
1118                         query_node(trg.node, rsp);
1119                 } else if (trg.desktop != NULL) {
1120                         query_desktop(trg.desktop, rsp);
1121                 } else  {
1122                         query_monitor(trg.monitor, rsp);
1123                 }
1124                 fprintf(rsp, "\n");
1125         }
1126
1127 end:
1128         free(monitor_sel);
1129         free(desktop_sel);
1130         free(node_sel);
1131 }
1132
1133 void cmd_rule(char **args, int num, FILE *rsp)
1134 {
1135         if (num < 1) {
1136                 fail(rsp, "rule: Missing commands.\n");
1137                 return;
1138         }
1139
1140         while (num > 0) {
1141                 if (streq("-a", *args) || streq("--add", *args)) {
1142                         num--, args++;
1143                         if (num < 2) {
1144                                 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1145                                 return;
1146                         }
1147                         rule_t *rule = make_rule();
1148                         char *class_name = strtok(*args, COL_TOK);
1149                         char *instance_name = strtok(NULL, COL_TOK);
1150                         char *name = strtok(NULL, COL_TOK);
1151                         snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
1152                         snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
1153                         snprintf(rule->name, sizeof(rule->name), "%s", name==NULL?MATCH_ANY:name);
1154                         num--, args++;
1155                         size_t i = 0;
1156                         while (num > 0) {
1157                                 if (streq("-o", *args) || streq("--one-shot", *args)) {
1158                                         rule->one_shot = true;
1159                                 } else {
1160                                         for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
1161                                                 rule->effect[i] = (*args)[j];
1162                                         }
1163                                         if (num > 1 && i < sizeof(rule->effect)) {
1164                                                 rule->effect[i++] = ' ';
1165                                         }
1166                                 }
1167                                 num--, args++;
1168                         }
1169                         rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
1170                         add_rule(rule);
1171                 } else if (streq("-r", *args) || streq("--remove", *args)) {
1172                         num--, args++;
1173                         if (num < 1) {
1174                                 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1175                                 return;
1176                         }
1177                         uint16_t idx;
1178                         while (num > 0) {
1179                                 if (parse_index(*args, &idx)) {
1180                                         remove_rule_by_index(idx - 1);
1181                                 } else if (streq("tail", *args)) {
1182                                         remove_rule(rule_tail);
1183                                 } else if (streq("head", *args)) {
1184                                         remove_rule(rule_head);
1185                                 } else {
1186                                         remove_rule_by_cause(*args);
1187                                 }
1188                                 num--, args++;
1189                         }
1190                 } else if (streq("-l", *args) || streq("--list", *args)) {
1191                         list_rules(rsp);
1192                 } else {
1193                         fail(rsp, "rule: Unknown command: '%s'.\n", *args);
1194                         return;
1195                 }
1196                 num--, args++;
1197         }
1198 }
1199
1200 void cmd_wm(char **args, int num, FILE *rsp)
1201 {
1202         if (num < 1) {
1203                 fail(rsp, "wm: Missing commands.\n");
1204                 return;
1205         }
1206
1207         while (num > 0) {
1208                 if (streq("-d", *args) || streq("--dump-state", *args)) {
1209                         query_state(rsp);
1210                         fprintf(rsp, "\n");
1211                 } else if (streq("-l", *args) || streq("--load-state", *args)) {
1212                         num--, args++;
1213                         if (num < 1) {
1214                                 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1215                                 break;
1216                         }
1217                         if (!restore_state(*args)) {
1218                                 fail(rsp, "");
1219                                 break;
1220                         }
1221                 } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
1222                         num--, args++;
1223                         if (num < 2) {
1224                                 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1225                                 break;
1226                         }
1227                         char *name = *args;
1228                         num--, args++;
1229                         xcb_rectangle_t r;
1230                         if (parse_rectangle(*args, &r)) {
1231                                 monitor_t *m = make_monitor(name, &r, XCB_NONE);
1232                                 add_monitor(m);
1233                                 add_desktop(m, make_desktop(NULL, XCB_NONE));
1234                         } else {
1235                                 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1236                                 break;
1237                         }
1238                 } else if (streq("-O", *args) || streq("--reorder-monitors", *args)) {
1239                         num--, args++;
1240                         if (num < 1) {
1241                                 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1242                                 return;
1243                         }
1244                         monitor_t *m = mon_head;
1245                         while (m != NULL && num > 0) {
1246                                 monitor_t *next = m->next;
1247                                 coordinates_t dst;
1248                                 if (locate_monitor(*args, &dst)) {
1249                                         swap_monitors(m, dst.monitor);
1250                                         if (next == dst.monitor) {
1251                                                 next = m;
1252                                         }
1253                                 }
1254                                 m = next;
1255                                 num--, args++;
1256                         }
1257                 } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
1258                         adopt_orphans();
1259                 } else if (streq("-g", *args) || streq("--get-status", *args)) {
1260                         print_report(rsp);
1261                 } else if (streq("-h", *args) || streq("--record-history", *args)) {
1262                         num--, args++;
1263                         if (num < 1) {
1264                                 fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
1265                                 break;
1266                         }
1267                         bool b;
1268                         if (parse_bool(*args, &b)) {
1269                                 record_history = b;
1270                         } else {
1271                                 fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1272                                 break;
1273                         }
1274                 } else if (streq("-r", *args) || streq("--restart", *args)) {
1275                         running = false;
1276                         restart = true;
1277                         break;
1278                 } else {
1279                         fail(rsp, "wm: Unknown command: '%s'.\n", *args);
1280                         break;
1281                 }
1282                 num--, args++;
1283         }
1284 }
1285
1286 void cmd_subscribe(char **args, int num, FILE *rsp)
1287 {
1288         int field = 0;
1289         int count = -1;
1290         FILE *stream = rsp;
1291         char *fifo_path = NULL;
1292         subscriber_mask_t mask;
1293
1294         while (num > 0) {
1295                 if (streq("-c", *args) || streq("--count", *args)) {
1296                         num--, args++;
1297                         if (num < 1) {
1298                                 fail(rsp, "subscribe %s: Not enough arguments.\n", *(args - 1));
1299                                 goto failed;
1300                         }
1301                         if (sscanf(*args, "%i", &count) != 1 || count < 1) {
1302                                 fail(rsp, "subscribe %s: Invalid argument: '%s'.\n", *(args - 1), *args);
1303                                 goto failed;
1304                         }
1305                 } else if (streq("-f", *args) || streq("--fifo", *args)) {
1306                         fifo_path = mktempfifo(FIFO_TEMPLATE);
1307                         if (fifo_path == NULL) {
1308                                 fail(rsp, "subscribe %s: Can't create FIFO.\n", *(args - 1));
1309                                 goto failed;
1310                         }
1311                 } else if (parse_subscriber_mask(*args, &mask)) {
1312                         field |= mask;
1313                 } else {
1314                         fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
1315                         goto failed;
1316                 }
1317                 num--, args++;
1318         }
1319
1320         if (field == 0) {
1321                 field = SBSC_MASK_REPORT;
1322         }
1323
1324         if (fifo_path) {
1325                 fprintf(rsp, "%s\n", fifo_path);
1326                 fflush(rsp);
1327                 fclose(rsp);
1328
1329                 stream = fopen(fifo_path, "w");
1330
1331                 if (stream == NULL) {
1332                         perror("subscribe: fopen");
1333                         goto free_fifo_path;
1334                 }
1335         } else {
1336                 fprintf(rsp, SUBSCRIBE_MESSAGE);
1337                 fflush(rsp);
1338         }
1339
1340         subscriber_list_t *sb = make_subscriber(stream, fifo_path, field, count);
1341         add_subscriber(sb);
1342         return;
1343
1344 failed:
1345         fflush(rsp);
1346         fclose(rsp);
1347
1348 free_fifo_path:
1349         if (fifo_path) {
1350                 unlink(fifo_path);
1351                 free(fifo_path);
1352         }
1353 }
1354
1355 void cmd_quit(char **args, int num, FILE *rsp)
1356 {
1357         if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
1358                 fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
1359                 return;
1360         }
1361         running = false;
1362 }
1363
1364 void cmd_config(char **args, int num, FILE *rsp)
1365 {
1366         if (num < 1) {
1367                 fail(rsp, "config: Missing arguments.\n");
1368                 return;
1369         }
1370
1371         coordinates_t ref = {mon, mon->desk, mon->desk->focus};
1372         coordinates_t trg = {NULL, NULL, NULL};
1373
1374         while (num > 0 && (*args)[0] == OPT_CHR) {
1375                 if (streq("-m", *args) || streq("--monitor", *args)) {
1376                         num--, args++;
1377                         if (num < 1) {
1378                                 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1379                                 return;
1380                         }
1381                         int ret;
1382                         if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1383                                 handle_failure(ret, "config -m", *args, rsp);
1384                                 return;
1385                         }
1386                 } else if (streq("-d", *args) || streq("--desktop", *args)) {
1387                         num--, args++;
1388                         if (num < 1) {
1389                                 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1390                                 return;
1391                         }
1392                         int ret;
1393                         if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1394                                 handle_failure(ret, "config -d", *args, rsp);
1395                                 return;
1396                         }
1397                 } else if (streq("-n", *args) || streq("--node", *args)) {
1398                         num--, args++;
1399                         if (num < 1) {
1400                                 fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
1401                                 return;
1402                         }
1403                         int ret;
1404                         if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
1405                                 handle_failure(ret, "config -n", *args, rsp);
1406                                 return;
1407                         }
1408                 } else {
1409                         fail(rsp, "config: Unknown option: '%s'.\n", *args);
1410                         return;
1411                 }
1412                 num--, args++;
1413         }
1414         if (num == 2) {
1415                 set_setting(trg, *args, *(args + 1), rsp);
1416         } else if (num == 1) {
1417                 get_setting(trg, *args, rsp);
1418         } else {
1419                 fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
1420         }
1421 }
1422
1423 void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
1424 {
1425         bool colors_changed = false;
1426 #define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
1427                 if (loc.node != NULL) { \
1428                         for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \
1429                                 if (n->client != NULL) { \
1430                                         n->client->k = v; \
1431                                 } \
1432                         } \
1433                 } else if (loc.desktop != NULL) { \
1434                         loc.desktop->k = v; \
1435                         for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
1436                                 if (n->client != NULL) { \
1437                                         n->client->k = v; \
1438                                 } \
1439                         } \
1440                 } else if (loc.monitor != NULL) { \
1441                         loc.monitor->k = v; \
1442                         for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1443                                 d->k = v; \
1444                                 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1445                                         if (n->client != NULL) { \
1446                                                 n->client->k = v; \
1447                                         } \
1448                                 } \
1449                         } \
1450                 } else { \
1451                         k = v; \
1452                         for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1453                                 m->k = v; \
1454                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1455                                         d->k = v; \
1456                                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
1457                                                 if (n->client != NULL) { \
1458                                                         n->client->k = v; \
1459                                                 } \
1460                                         } \
1461                                 } \
1462                         } \
1463                 }
1464         if (streq("border_width", name)) {
1465                 unsigned int bw;
1466                 if (sscanf(value, "%u", &bw) != 1) {
1467                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1468                         return;
1469                 }
1470                 SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
1471 #undef SET_DEF_DEFMON_DEFDESK_WIN
1472 #define SET_DEF_DEFMON_DESK(k, v) \
1473                 if (loc.desktop != NULL) { \
1474                         loc.desktop->k = v; \
1475                 } else if (loc.monitor != NULL) { \
1476                         loc.monitor->k = v; \
1477                         for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1478                                 d->k = v; \
1479                         } \
1480                 } else { \
1481                         k = v; \
1482                         for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1483                                 m->k = v; \
1484                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1485                                         d->k = v; \
1486                                 } \
1487                         } \
1488                 }
1489         } else if (streq("window_gap", name)) {
1490                 int wg;
1491                 if (sscanf(value, "%i", &wg) != 1) {
1492                         fail(rsp, "");
1493                         return;
1494                 }
1495                 SET_DEF_DEFMON_DESK(window_gap, wg)
1496 #undef SET_DEF_DEFMON_DESK
1497 #define SET_DEF_MON_DESK(k, v) \
1498                 if (loc.desktop != NULL) { \
1499                         loc.desktop->k = v; \
1500                 } else if (loc.monitor != NULL) { \
1501                         loc.monitor->k = v; \
1502                 } else { \
1503                         k = v; \
1504                         for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1505                                 m->k = v; \
1506                         } \
1507                 }
1508         } else if (streq("top_padding", name)) {
1509                 int tp;
1510                 if (sscanf(value, "%i", &tp) != 1) {
1511                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1512                         return;
1513                 }
1514                 SET_DEF_MON_DESK(padding.top, tp)
1515         } else if (streq("right_padding", name)) {
1516                 int rp;
1517                 if (sscanf(value, "%i", &rp) != 1) {
1518                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1519                         return;
1520                 }
1521                 SET_DEF_MON_DESK(padding.right, rp)
1522         } else if (streq("bottom_padding", name)) {
1523                 int bp;
1524                 if (sscanf(value, "%i", &bp) != 1) {
1525                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1526                         return;
1527                 }
1528                 SET_DEF_MON_DESK(padding.bottom, bp)
1529         } else if (streq("left_padding", name)) {
1530                 int lp;
1531                 if (sscanf(value, "%i", &lp) != 1) {
1532                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1533                         return;
1534                 }
1535                 SET_DEF_MON_DESK(padding.left, lp)
1536 #undef SET_DEF_MON_DESK
1537         } else if (streq("top_monocle_padding", name)) {
1538                 if (sscanf(value, "%i", &monocle_padding.top) != 1) {
1539                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1540                 }
1541         } else if (streq("right_monocle_padding", name)) {
1542                 if (sscanf(value, "%i", &monocle_padding.right) != 1) {
1543                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1544                 }
1545         } else if (streq("bottom_monocle_padding", name)) {
1546                 if (sscanf(value, "%i", &monocle_padding.bottom) != 1) {
1547                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1548                 }
1549         } else if (streq("left_monocle_padding", name)) {
1550                 if (sscanf(value, "%i", &monocle_padding.left) != 1) {
1551                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1552                 }
1553 #define SET_STR(s) \
1554         } else if (streq(#s, name)) { \
1555                 if (snprintf(s, sizeof(s), "%s", value) < 0) { \
1556                         fail(rsp, ""); \
1557                         return; \
1558                 }
1559         SET_STR(external_rules_command)
1560         SET_STR(status_prefix)
1561 #undef SET_STR
1562         } else if (streq("split_ratio", name)) {
1563                 double r;
1564                 if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
1565                         split_ratio = r;
1566                 } else {
1567                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1568                         return;
1569                 }
1570                 return;
1571 #define SET_COLOR(s) \
1572         } else if (streq(#s, name)) { \
1573                 if (!is_hex_color(value)) { \
1574                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1575                         return; \
1576                 } else { \
1577                         snprintf(s, sizeof(s), "%s", value); \
1578                         colors_changed = true; \
1579                 }
1580         SET_COLOR(normal_border_color)
1581         SET_COLOR(active_border_color)
1582         SET_COLOR(focused_border_color)
1583         SET_COLOR(presel_feedback_color)
1584 #undef SET_COLOR
1585         } else if (streq("initial_polarity", name)) {
1586                 child_polarity_t p;
1587                 if (parse_child_polarity(value, &p)) {
1588                         initial_polarity = p;
1589                 } else {
1590                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1591                         return;
1592                 }
1593         } else if (streq("automatic_scheme", name)) {
1594                 automatic_scheme_t a;
1595                 if (parse_automatic_scheme(value, &a)) {
1596                         automatic_scheme = a;
1597                 } else {
1598                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1599                         return;
1600                 }
1601         } else if (streq("mapping_events_count", name)) {
1602                 if (sscanf(value, "%" SCNi8, &mapping_events_count) != 1) {
1603                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1604                         return;
1605                 }
1606         } else if (streq("directional_focus_tightness", name)) {
1607                 tightness_t p;
1608                 if (parse_tightness(value, &p)) {
1609                         directional_focus_tightness = p;
1610                 } else {
1611                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1612                         return;
1613                 }
1614         } else if (streq("ignore_ewmh_fullscreen", name)) {
1615                 state_transition_t m;
1616                 if (parse_state_transition(value, &m)) {
1617                         ignore_ewmh_fullscreen = m;
1618                 } else {
1619                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1620                         return;
1621                 }
1622         } else if (streq("pointer_modifier", name)) {
1623                 if (parse_modifier_mask(value, &pointer_modifier)) {
1624                         ungrab_buttons();
1625                         grab_buttons();
1626                 } else {
1627                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1628                         return;
1629                 }
1630         } else if (streq("pointer_motion_interval", name)) {
1631                 if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
1632                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1633                         return;
1634                 }
1635         } else if (streq("pointer_action1", name) ||
1636                    streq("pointer_action2", name) ||
1637                    streq("pointer_action3", name)) {
1638                 int index = name[14] - '1';
1639                 if (parse_pointer_action(value, &pointer_actions[index])) {
1640                         ungrab_buttons();
1641                         grab_buttons();
1642                 } else {
1643                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1644                         return;
1645                 }
1646         } else if (streq("click_to_focus", name)) {
1647                 if (parse_button_index(value, &click_to_focus)) {
1648                         ungrab_buttons();
1649                         grab_buttons();
1650                 } else {
1651                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1652                         return;
1653                 }
1654         } else if (streq("single_monocle", name)) {
1655                 bool b;
1656                 if (parse_bool(value, &b)) {
1657                         if (b == single_monocle) {
1658                                 fail(rsp, "");
1659                                 return;
1660                         }
1661                         single_monocle = b;
1662                         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1663                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1664                                         layout_t l = (single_monocle && tiled_count(d->root, true) <= 1) ? LAYOUT_MONOCLE : d->user_layout;
1665                                         set_layout(m, d, l, false);
1666                                 }
1667                         }
1668                 } else {
1669                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1670                         return;
1671                 }
1672         } else if (streq("focus_follows_pointer", name)) {
1673                 bool b;
1674                 if (parse_bool(value, &b)) {
1675                         if (b == focus_follows_pointer) {
1676                                 fail(rsp, "");
1677                                 return;
1678                         }
1679                         focus_follows_pointer = b;
1680                         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1681                                 if (focus_follows_pointer) {
1682                                         window_show(m->root);
1683                                 } else {
1684                                         window_hide(m->root);
1685                                 }
1686                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1687                                         listen_enter_notify(d->root, focus_follows_pointer);
1688                                 }
1689                         }
1690                         if (focus_follows_pointer) {
1691                                 update_motion_recorder();
1692                         } else {
1693                                 disable_motion_recorder();
1694                         }
1695                         return;
1696                 } else {
1697                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1698                         return;
1699                 }
1700 #define SET_BOOL(s) \
1701         } else if (streq(#s, name)) { \
1702                 if (!parse_bool(value, &s)) { \
1703                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1704                         return; \
1705                 }
1706                 SET_BOOL(presel_feedback)
1707                 SET_BOOL(borderless_monocle)
1708                 SET_BOOL(gapless_monocle)
1709                 SET_BOOL(swallow_first_click)
1710                 SET_BOOL(pointer_follows_focus)
1711                 SET_BOOL(pointer_follows_monitor)
1712                 SET_BOOL(ignore_ewmh_focus)
1713                 SET_BOOL(ignore_ewmh_struts)
1714                 SET_BOOL(center_pseudo_tiled)
1715                 SET_BOOL(honor_size_hints)
1716                 SET_BOOL(removal_adjustment)
1717 #undef SET_BOOL
1718 #define SET_MON_BOOL(s) \
1719         } else if (streq(#s, name)) { \
1720                 if (!parse_bool(value, &s)) { \
1721                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1722                         return; \
1723                 } \
1724                 if (s) { \
1725                         update_monitors(); \
1726                 }
1727                 SET_MON_BOOL(remove_disabled_monitors)
1728                 SET_MON_BOOL(remove_unplugged_monitors)
1729                 SET_MON_BOOL(merge_overlapping_monitors)
1730 #undef SET_MON_BOOL
1731         } else {
1732                 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1733                 return;
1734         }
1735
1736         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1737                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1738                         arrange(m, d);
1739                         if (colors_changed) {
1740                                 update_colors_in(d->root, d, m);
1741                         }
1742                 }
1743         }
1744 }
1745
1746 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1747 {
1748         if (streq("split_ratio", name)) {
1749                 fprintf(rsp, "%lf", split_ratio);
1750         } else if (streq("border_width", name)) {
1751                 if (loc.node != NULL) {
1752                         for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
1753                                 if (n->client != NULL) {
1754                                         fprintf(rsp, "%u", n->client->border_width);
1755                                         break;
1756                                 }
1757                         }
1758                 } else if (loc.desktop != NULL) {
1759                         fprintf(rsp, "%u", loc.desktop->border_width);
1760                 } else if (loc.monitor != NULL) {
1761                         fprintf(rsp, "%u", loc.monitor->border_width);
1762                 } else {
1763                         fprintf(rsp, "%u", border_width);
1764                 }
1765         } else if (streq("window_gap", name)) {
1766                 if (loc.desktop != NULL) {
1767                         fprintf(rsp, "%i", loc.desktop->window_gap);
1768                 } else if (loc.monitor != NULL) {
1769                         fprintf(rsp, "%i", loc.monitor->window_gap);
1770                 } else {
1771                         fprintf(rsp, "%i", window_gap);
1772                 }
1773 #define GET_DEF_MON_DESK(k) \
1774                 if (loc.desktop != NULL) { \
1775                         fprintf(rsp, "%i", loc.desktop->k); \
1776                 } else if (loc.monitor != NULL) { \
1777                         fprintf(rsp, "%i", loc.monitor->k); \
1778                 } else { \
1779                         fprintf(rsp, "%i", k); \
1780                 }
1781         } else if (streq("top_padding", name)) {
1782                 GET_DEF_MON_DESK(padding.top)
1783         } else if (streq("right_padding", name)) {
1784                 GET_DEF_MON_DESK(padding.right)
1785         } else if (streq("bottom_padding", name)) {
1786                 GET_DEF_MON_DESK(padding.bottom)
1787         } else if (streq("left_padding", name)) {
1788                 GET_DEF_MON_DESK(padding.left)
1789 #undef GET_DEF_MON_DESK
1790         } else if (streq("top_monocle_padding", name)) {
1791                 fprintf(rsp, "%i", monocle_padding.top);
1792         } else if (streq("right_monocle_padding", name)) {
1793                 fprintf(rsp, "%i", monocle_padding.right);
1794         } else if (streq("bottom_monocle_padding", name)) {
1795                 fprintf(rsp, "%i", monocle_padding.bottom);
1796         } else if (streq("left_monocle_padding", name)) {
1797                 fprintf(rsp, "%i", monocle_padding.left);
1798         } else if (streq("external_rules_command", name)) {
1799                 fprintf(rsp, "%s", external_rules_command);
1800         } else if (streq("status_prefix", name)) {
1801                 fprintf(rsp, "%s", status_prefix);
1802         } else if (streq("initial_polarity", name)) {
1803                 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1804         } else if (streq("automatic_scheme", name)) {
1805                 fprintf(rsp, "%s", AUTO_SCM_STR(automatic_scheme));
1806         } else if (streq("mapping_events_count", name)) {
1807                 fprintf(rsp, "%" PRIi8, mapping_events_count);
1808         } else if (streq("directional_focus_tightness", name)) {
1809                 fprintf(rsp, "%s", TIGHTNESS_STR(directional_focus_tightness));
1810         } else if (streq("ignore_ewmh_fullscreen", name)) {
1811                 print_ignore_request(ignore_ewmh_fullscreen, rsp);
1812         } else if (streq("pointer_modifier", name)) {
1813                 print_modifier_mask(pointer_modifier, rsp);
1814         } else if (streq("click_to_focus", name)) {
1815                 print_button_index(click_to_focus, rsp);
1816         } else if (streq("pointer_motion_interval", name)) {
1817                 fprintf(rsp, "%u", pointer_motion_interval);
1818         } else if (streq("pointer_action1", name) ||
1819                    streq("pointer_action2", name) ||
1820                    streq("pointer_action3", name)) {
1821                 int index = name[14] - '1';
1822                 print_pointer_action(pointer_actions[index], rsp);
1823 #define GET_COLOR(s) \
1824         } else if (streq(#s, name)) { \
1825                 fprintf(rsp, "%s", s);
1826         GET_COLOR(normal_border_color)
1827         GET_COLOR(active_border_color)
1828         GET_COLOR(focused_border_color)
1829         GET_COLOR(presel_feedback_color)
1830 #undef GET_COLOR
1831 #define GET_BOOL(s) \
1832         } else if (streq(#s, name)) { \
1833                 fprintf(rsp, "%s", BOOL_STR(s));
1834         GET_BOOL(presel_feedback)
1835         GET_BOOL(borderless_monocle)
1836         GET_BOOL(gapless_monocle)
1837         GET_BOOL(single_monocle)
1838         GET_BOOL(swallow_first_click)
1839         GET_BOOL(focus_follows_pointer)
1840         GET_BOOL(pointer_follows_focus)
1841         GET_BOOL(pointer_follows_monitor)
1842         GET_BOOL(ignore_ewmh_focus)
1843         GET_BOOL(ignore_ewmh_struts)
1844         GET_BOOL(center_pseudo_tiled)
1845         GET_BOOL(honor_size_hints)
1846         GET_BOOL(removal_adjustment)
1847         GET_BOOL(remove_disabled_monitors)
1848         GET_BOOL(remove_unplugged_monitors)
1849         GET_BOOL(merge_overlapping_monitors)
1850 #undef GET_BOOL
1851         } else {
1852                 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1853                 return;
1854         }
1855         fprintf(rsp, "\n");
1856 }
1857
1858 void handle_failure(int code, char *src, char *val, FILE *rsp)
1859 {
1860         switch (code) {
1861                 case SELECTOR_BAD_DESCRIPTOR:
1862                         fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1863                         break;
1864                 case SELECTOR_BAD_MODIFIERS:
1865                         fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1866                         break;
1867                 case SELECTOR_INVALID:
1868                         fail(rsp, "");
1869                         break;
1870         }
1871 }
1872
1873 void fail(FILE *rsp, char *fmt, ...)
1874 {
1875         fprintf(rsp, FAILURE_MESSAGE);
1876         va_list ap;
1877         va_start(ap, fmt);
1878         vfprintf(rsp, fmt, ap);
1879         va_end(ap);
1880 }