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