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