]> git.lizzy.rs Git - bspwm.git/blob - src/messages.c
Allow cycling the splitting type of a node
[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 #undef SET_DEF_DEFMON_DEFDESK_WIN
1498 #define SET_DEF_DEFMON_DESK(k, v) \
1499                 if (loc.desktop != NULL) { \
1500                         loc.desktop->k = v; \
1501                 } else if (loc.monitor != NULL) { \
1502                         loc.monitor->k = v; \
1503                         for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
1504                                 d->k = v; \
1505                         } \
1506                 } else { \
1507                         k = v; \
1508                         for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1509                                 m->k = v; \
1510                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
1511                                         d->k = v; \
1512                                 } \
1513                         } \
1514                 }
1515         } else if (streq("window_gap", name)) {
1516                 int wg;
1517                 if (sscanf(value, "%i", &wg) != 1) {
1518                         fail(rsp, "");
1519                         return;
1520                 }
1521                 SET_DEF_DEFMON_DESK(window_gap, wg)
1522 #undef SET_DEF_DEFMON_DESK
1523 #define SET_DEF_MON_DESK(k, v) \
1524                 if (loc.desktop != NULL) { \
1525                         loc.desktop->k = v; \
1526                 } else if (loc.monitor != NULL) { \
1527                         loc.monitor->k = v; \
1528                 } else { \
1529                         k = v; \
1530                         for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
1531                                 m->k = v; \
1532                         } \
1533                 }
1534         } else if (streq("top_padding", name)) {
1535                 int tp;
1536                 if (sscanf(value, "%i", &tp) != 1) {
1537                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1538                         return;
1539                 }
1540                 SET_DEF_MON_DESK(padding.top, tp)
1541         } else if (streq("right_padding", name)) {
1542                 int rp;
1543                 if (sscanf(value, "%i", &rp) != 1) {
1544                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1545                         return;
1546                 }
1547                 SET_DEF_MON_DESK(padding.right, rp)
1548         } else if (streq("bottom_padding", name)) {
1549                 int bp;
1550                 if (sscanf(value, "%i", &bp) != 1) {
1551                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1552                         return;
1553                 }
1554                 SET_DEF_MON_DESK(padding.bottom, bp)
1555         } else if (streq("left_padding", name)) {
1556                 int lp;
1557                 if (sscanf(value, "%i", &lp) != 1) {
1558                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1559                         return;
1560                 }
1561                 SET_DEF_MON_DESK(padding.left, lp)
1562 #undef SET_DEF_MON_DESK
1563         } else if (streq("top_monocle_padding", name)) {
1564                 if (sscanf(value, "%i", &monocle_padding.top) != 1) {
1565                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1566                 }
1567         } else if (streq("right_monocle_padding", name)) {
1568                 if (sscanf(value, "%i", &monocle_padding.right) != 1) {
1569                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1570                 }
1571         } else if (streq("bottom_monocle_padding", name)) {
1572                 if (sscanf(value, "%i", &monocle_padding.bottom) != 1) {
1573                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1574                 }
1575         } else if (streq("left_monocle_padding", name)) {
1576                 if (sscanf(value, "%i", &monocle_padding.left) != 1) {
1577                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1578                 }
1579 #define SET_STR(s) \
1580         } else if (streq(#s, name)) { \
1581                 if (snprintf(s, sizeof(s), "%s", value) < 0) { \
1582                         fail(rsp, ""); \
1583                         return; \
1584                 }
1585         SET_STR(external_rules_command)
1586         SET_STR(status_prefix)
1587 #undef SET_STR
1588         } else if (streq("split_ratio", name)) {
1589                 double r;
1590                 if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
1591                         split_ratio = r;
1592                 } else {
1593                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1594                         return;
1595                 }
1596                 return;
1597 #define SET_COLOR(s) \
1598         } else if (streq(#s, name)) { \
1599                 if (!is_hex_color(value)) { \
1600                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1601                         return; \
1602                 } else { \
1603                         snprintf(s, sizeof(s), "%s", value); \
1604                         colors_changed = true; \
1605                 }
1606         SET_COLOR(normal_border_color)
1607         SET_COLOR(active_border_color)
1608         SET_COLOR(focused_border_color)
1609         SET_COLOR(presel_feedback_color)
1610 #undef SET_COLOR
1611         } else if (streq("initial_polarity", name)) {
1612                 child_polarity_t p;
1613                 if (parse_child_polarity(value, &p)) {
1614                         initial_polarity = p;
1615                 } else {
1616                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1617                         return;
1618                 }
1619         } else if (streq("automatic_scheme", name)) {
1620                 automatic_scheme_t a;
1621                 if (parse_automatic_scheme(value, &a)) {
1622                         automatic_scheme = a;
1623                 } else {
1624                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1625                         return;
1626                 }
1627         } else if (streq("mapping_events_count", name)) {
1628                 if (sscanf(value, "%" SCNi8, &mapping_events_count) != 1) {
1629                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1630                         return;
1631                 }
1632         } else if (streq("directional_focus_tightness", name)) {
1633                 tightness_t p;
1634                 if (parse_tightness(value, &p)) {
1635                         directional_focus_tightness = p;
1636                 } else {
1637                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1638                         return;
1639                 }
1640         } else if (streq("ignore_ewmh_fullscreen", name)) {
1641                 state_transition_t m;
1642                 if (parse_state_transition(value, &m)) {
1643                         ignore_ewmh_fullscreen = m;
1644                 } else {
1645                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1646                         return;
1647                 }
1648         } else if (streq("pointer_modifier", name)) {
1649                 if (parse_modifier_mask(value, &pointer_modifier)) {
1650                         ungrab_buttons();
1651                         grab_buttons();
1652                 } else {
1653                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1654                         return;
1655                 }
1656         } else if (streq("pointer_motion_interval", name)) {
1657                 if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
1658                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1659                         return;
1660                 }
1661         } else if (streq("pointer_action1", name) ||
1662                    streq("pointer_action2", name) ||
1663                    streq("pointer_action3", name)) {
1664                 int index = name[14] - '1';
1665                 if (parse_pointer_action(value, &pointer_actions[index])) {
1666                         ungrab_buttons();
1667                         grab_buttons();
1668                 } else {
1669                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1670                         return;
1671                 }
1672         } else if (streq("click_to_focus", name)) {
1673                 if (parse_button_index(value, &click_to_focus)) {
1674                         ungrab_buttons();
1675                         grab_buttons();
1676                 } else {
1677                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1678                         return;
1679                 }
1680         } else if (streq("single_monocle", name)) {
1681                 bool b;
1682                 if (parse_bool(value, &b)) {
1683                         if (b == single_monocle) {
1684                                 fail(rsp, "");
1685                                 return;
1686                         }
1687                         single_monocle = b;
1688                         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1689                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1690                                         layout_t l = (single_monocle && tiled_count(d->root, true) <= 1) ? LAYOUT_MONOCLE : d->user_layout;
1691                                         set_layout(m, d, l, false);
1692                                 }
1693                         }
1694                 } else {
1695                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1696                         return;
1697                 }
1698         } else if (streq("focus_follows_pointer", name)) {
1699                 bool b;
1700                 if (parse_bool(value, &b)) {
1701                         if (b == focus_follows_pointer) {
1702                                 fail(rsp, "");
1703                                 return;
1704                         }
1705                         focus_follows_pointer = b;
1706                         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1707                                 if (focus_follows_pointer) {
1708                                         window_show(m->root);
1709                                 } else {
1710                                         window_hide(m->root);
1711                                 }
1712                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1713                                         listen_enter_notify(d->root, focus_follows_pointer);
1714                                 }
1715                         }
1716                         if (focus_follows_pointer) {
1717                                 update_motion_recorder();
1718                         } else {
1719                                 disable_motion_recorder();
1720                         }
1721                         return;
1722                 } else {
1723                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1724                         return;
1725                 }
1726 #define SET_BOOL(s) \
1727         } else if (streq(#s, name)) { \
1728                 if (!parse_bool(value, &s)) { \
1729                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1730                         return; \
1731                 }
1732                 SET_BOOL(presel_feedback)
1733                 SET_BOOL(borderless_monocle)
1734                 SET_BOOL(gapless_monocle)
1735                 SET_BOOL(borderless_singleton)
1736                 SET_BOOL(swallow_first_click)
1737                 SET_BOOL(pointer_follows_focus)
1738                 SET_BOOL(pointer_follows_monitor)
1739                 SET_BOOL(ignore_ewmh_focus)
1740                 SET_BOOL(ignore_ewmh_struts)
1741                 SET_BOOL(center_pseudo_tiled)
1742                 SET_BOOL(honor_size_hints)
1743                 SET_BOOL(removal_adjustment)
1744 #undef SET_BOOL
1745 #define SET_MON_BOOL(s) \
1746         } else if (streq(#s, name)) { \
1747                 if (!parse_bool(value, &s)) { \
1748                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1749                         return; \
1750                 } \
1751                 if (s) { \
1752                         update_monitors(); \
1753                 }
1754                 SET_MON_BOOL(remove_disabled_monitors)
1755                 SET_MON_BOOL(remove_unplugged_monitors)
1756                 SET_MON_BOOL(merge_overlapping_monitors)
1757 #undef SET_MON_BOOL
1758         } else {
1759                 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1760                 return;
1761         }
1762
1763         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1764                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1765                         arrange(m, d);
1766                         if (colors_changed) {
1767                                 update_colors_in(d->root, d, m);
1768                         }
1769                 }
1770         }
1771 }
1772
1773 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1774 {
1775         if (streq("split_ratio", name)) {
1776                 fprintf(rsp, "%lf", split_ratio);
1777         } else if (streq("border_width", name)) {
1778                 if (loc.node != NULL) {
1779                         for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
1780                                 if (n->client != NULL) {
1781                                         fprintf(rsp, "%u", n->client->border_width);
1782                                         break;
1783                                 }
1784                         }
1785                 } else if (loc.desktop != NULL) {
1786                         fprintf(rsp, "%u", loc.desktop->border_width);
1787                 } else if (loc.monitor != NULL) {
1788                         fprintf(rsp, "%u", loc.monitor->border_width);
1789                 } else {
1790                         fprintf(rsp, "%u", border_width);
1791                 }
1792         } else if (streq("window_gap", name)) {
1793                 if (loc.desktop != NULL) {
1794                         fprintf(rsp, "%i", loc.desktop->window_gap);
1795                 } else if (loc.monitor != NULL) {
1796                         fprintf(rsp, "%i", loc.monitor->window_gap);
1797                 } else {
1798                         fprintf(rsp, "%i", window_gap);
1799                 }
1800 #define GET_DEF_MON_DESK(k) \
1801                 if (loc.desktop != NULL) { \
1802                         fprintf(rsp, "%i", loc.desktop->k); \
1803                 } else if (loc.monitor != NULL) { \
1804                         fprintf(rsp, "%i", loc.monitor->k); \
1805                 } else { \
1806                         fprintf(rsp, "%i", k); \
1807                 }
1808         } else if (streq("top_padding", name)) {
1809                 GET_DEF_MON_DESK(padding.top)
1810         } else if (streq("right_padding", name)) {
1811                 GET_DEF_MON_DESK(padding.right)
1812         } else if (streq("bottom_padding", name)) {
1813                 GET_DEF_MON_DESK(padding.bottom)
1814         } else if (streq("left_padding", name)) {
1815                 GET_DEF_MON_DESK(padding.left)
1816 #undef GET_DEF_MON_DESK
1817         } else if (streq("top_monocle_padding", name)) {
1818                 fprintf(rsp, "%i", monocle_padding.top);
1819         } else if (streq("right_monocle_padding", name)) {
1820                 fprintf(rsp, "%i", monocle_padding.right);
1821         } else if (streq("bottom_monocle_padding", name)) {
1822                 fprintf(rsp, "%i", monocle_padding.bottom);
1823         } else if (streq("left_monocle_padding", name)) {
1824                 fprintf(rsp, "%i", monocle_padding.left);
1825         } else if (streq("external_rules_command", name)) {
1826                 fprintf(rsp, "%s", external_rules_command);
1827         } else if (streq("status_prefix", name)) {
1828                 fprintf(rsp, "%s", status_prefix);
1829         } else if (streq("initial_polarity", name)) {
1830                 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1831         } else if (streq("automatic_scheme", name)) {
1832                 fprintf(rsp, "%s", AUTO_SCM_STR(automatic_scheme));
1833         } else if (streq("mapping_events_count", name)) {
1834                 fprintf(rsp, "%" PRIi8, mapping_events_count);
1835         } else if (streq("directional_focus_tightness", name)) {
1836                 fprintf(rsp, "%s", TIGHTNESS_STR(directional_focus_tightness));
1837         } else if (streq("ignore_ewmh_fullscreen", name)) {
1838                 print_ignore_request(ignore_ewmh_fullscreen, rsp);
1839         } else if (streq("pointer_modifier", name)) {
1840                 print_modifier_mask(pointer_modifier, rsp);
1841         } else if (streq("click_to_focus", name)) {
1842                 print_button_index(click_to_focus, rsp);
1843         } else if (streq("pointer_motion_interval", name)) {
1844                 fprintf(rsp, "%u", pointer_motion_interval);
1845         } else if (streq("pointer_action1", name) ||
1846                    streq("pointer_action2", name) ||
1847                    streq("pointer_action3", name)) {
1848                 int index = name[14] - '1';
1849                 print_pointer_action(pointer_actions[index], rsp);
1850 #define GET_COLOR(s) \
1851         } else if (streq(#s, name)) { \
1852                 fprintf(rsp, "%s", s);
1853         GET_COLOR(normal_border_color)
1854         GET_COLOR(active_border_color)
1855         GET_COLOR(focused_border_color)
1856         GET_COLOR(presel_feedback_color)
1857 #undef GET_COLOR
1858 #define GET_BOOL(s) \
1859         } else if (streq(#s, name)) { \
1860                 fprintf(rsp, "%s", BOOL_STR(s));
1861         GET_BOOL(presel_feedback)
1862         GET_BOOL(borderless_monocle)
1863         GET_BOOL(gapless_monocle)
1864         GET_BOOL(single_monocle)
1865         GET_BOOL(borderless_singleton)
1866         GET_BOOL(swallow_first_click)
1867         GET_BOOL(focus_follows_pointer)
1868         GET_BOOL(pointer_follows_focus)
1869         GET_BOOL(pointer_follows_monitor)
1870         GET_BOOL(ignore_ewmh_focus)
1871         GET_BOOL(ignore_ewmh_struts)
1872         GET_BOOL(center_pseudo_tiled)
1873         GET_BOOL(honor_size_hints)
1874         GET_BOOL(removal_adjustment)
1875         GET_BOOL(remove_disabled_monitors)
1876         GET_BOOL(remove_unplugged_monitors)
1877         GET_BOOL(merge_overlapping_monitors)
1878 #undef GET_BOOL
1879         } else {
1880                 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1881                 return;
1882         }
1883         fprintf(rsp, "\n");
1884 }
1885
1886 void handle_failure(int code, char *src, char *val, FILE *rsp)
1887 {
1888         switch (code) {
1889                 case SELECTOR_BAD_DESCRIPTOR:
1890                         fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1891                         break;
1892                 case SELECTOR_BAD_MODIFIERS:
1893                         fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1894                         break;
1895                 case SELECTOR_INVALID:
1896                         fail(rsp, "");
1897                         break;
1898         }
1899 }
1900
1901 void fail(FILE *rsp, char *fmt, ...)
1902 {
1903         fprintf(rsp, FAILURE_MESSAGE);
1904         va_list ap;
1905         va_start(ap, fmt);
1906         vfprintf(rsp, fmt, ap);
1907         va_end(ap);
1908 }