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