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