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