]> git.lizzy.rs Git - bspwm.git/blob - messages.c
5059f839bb8f753937e536db6b3e19be7c28287b
[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("locked", key)) {
316                                 set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
317                         } else if (streq("sticky", key)) {
318                                 set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
319                         } else if (streq("private", key)) {
320                                 set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
321                         } else {
322                                 fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
323                                 break;
324                         }
325                 } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
326                         num--, args++;
327                         if (num < 1) {
328                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
329                                 break;
330                         }
331                         if (trg.node == NULL || trg.node->vacant) {
332                                 fail(rsp, "");
333                                 break;
334                         }
335                         if (streq("cancel", *args)) {
336                                 cancel_presel(trg.monitor, trg.desktop, trg.node);
337                         } else {
338                                 bool alternate = false;
339                                 if ((*args)[0] == '~') {
340                                         alternate = true;
341                                         (*args)++;
342                                 }
343                                 direction_t dir;
344                                 if (parse_direction(*args, &dir)) {
345                                         if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
346                                                 cancel_presel(trg.monitor, trg.desktop, trg.node);
347                                         } else {
348                                                 presel_dir(trg.monitor, trg.desktop, trg.node, dir);
349                                                 if (!IS_RECEPTACLE(trg.node)) {
350                                                         draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
351                                                 }
352                                         }
353                                 } else {
354                                         fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
355                                         break;
356                                 }
357                         }
358                 } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
359                         num--, args++;
360                         if (num < 1) {
361                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
362                                 break;
363                         }
364                         if (trg.node == NULL || trg.node->vacant) {
365                                 fail(rsp, "");
366                                 break;
367                         }
368                         double rat;
369                         if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
370                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
371                                 break;
372                         } else {
373                                 presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
374                                 draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
375                         }
376                 } else if (streq("-v", *args) || streq("--move", *args)) {
377                         num--, args++;
378                         if (num < 2) {
379                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
380                                 break;
381                         }
382                         int dx = 0, dy = 0;
383                         if (sscanf(*args, "%i", &dx) == 1) {
384                                 num--, args++;
385                                 if (sscanf(*args, "%i", &dy) == 1) {
386                                         if (!move_client(&trg, dx, dy)) {
387                                                 fail(rsp, "");
388                                                 break;
389                                         }
390                                 } else {
391                                         fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
392                                         break;
393                                 }
394                         } else {
395                                 fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
396                                 break;
397                         }
398                 } else if (streq("-z", *args) || streq("--resize", *args)) {
399                         num--, args++;
400                         if (num < 3) {
401                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
402                                 break;
403                         }
404                         resize_handle_t rh;
405                         if (parse_resize_handle(*args, &rh)) {
406                                 num--, args++;
407                                 int dx = 0, dy = 0;
408                                 if (sscanf(*args, "%i", &dx) == 1) {
409                                         num--, args++;
410                                         if (sscanf(*args, "%i", &dy) == 1) {
411                                                 if (!resize_client(&trg, rh, dx, dy)) {
412                                                         fail(rsp, "");
413                                                         break;
414                                                 }
415                                         } else {
416                                                 fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
417                                                 break;
418                                         }
419                                 } else {
420                                         fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
421                                         break;
422                                 }
423                         } else {
424                                 fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
425                                 break;
426                         }
427                 } else if (streq("-r", *args) || streq("--ratio", *args)) {
428                         num--, args++;
429                         if (num < 1) {
430                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
431                                 break;
432                         }
433                         if (trg.node == NULL) {
434                                 fail(rsp, "");
435                                 break;
436                         }
437                         if ((*args)[0] == '+' || (*args)[0] == '-') {
438                                 int pix;
439                                 if (sscanf(*args, "%i", &pix) == 1) {
440                                         int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
441                                         double rat = ((max * trg.node->split_ratio) + pix) / max;
442                                         if (rat > 0 && rat < 1) {
443                                                 set_ratio(trg.node, rat);
444                                         } else {
445                                                 fail(rsp, "");
446                                                 break;
447                                         }
448                                 } else {
449                                         fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
450                                         break;
451                                 }
452                         } else {
453                                 double rat;
454                                 if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
455                                         set_ratio(trg.node, rat);
456                                 } else {
457                                         fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
458                                         break;
459                                 }
460                         }
461                         changed = true;
462                 } else if (streq("-F", *args) || streq("--flip", *args)) {
463                         num--, args++;
464                         if (num < 1) {
465                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
466                                 break;
467                         }
468                         if (trg.node == NULL) {
469                                 fail(rsp, "");
470                                 break;
471                         }
472                         flip_t flp;
473                         if (parse_flip(*args, &flp)) {
474                                 flip_tree(trg.node, flp);
475                                 changed = true;
476                         } else {
477                                 fail(rsp, "");
478                                 break;
479                         }
480                 } else if (streq("-R", *args) || streq("--rotate", *args)) {
481                         num--, args++;
482                         if (num < 1) {
483                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
484                                 break;
485                         }
486                         if (trg.node == NULL) {
487                                 fail(rsp, "");
488                                 break;
489                         }
490                         int deg;
491                         if (parse_degree(*args, &deg)) {
492                                 rotate_tree(trg.node, deg);
493                                 changed = true;
494                         } else {
495                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
496                                 break;
497                         }
498                 } else if (streq("-E", *args) || streq("--equalize", *args)) {
499                         if (trg.node == NULL) {
500                                 fail(rsp, "");
501                                 break;
502                         }
503                         equalize_tree(trg.node);
504                         changed = true;
505                 } else if (streq("-B", *args) || streq("--balance", *args)) {
506                         if (trg.node == NULL) {
507                                 fail(rsp, "");
508                                 break;
509                         }
510                         balance_tree(trg.node);
511                         changed = true;
512                 } else if (streq("-C", *args) || streq("--circulate", *args)) {
513                         num--, args++;
514                         if (num < 1) {
515                                 fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
516                                 break;
517                         }
518                         if (trg.node == NULL) {
519                                 fail(rsp, "");
520                                 break;
521                         }
522                         circulate_dir_t cir;
523                         if (parse_circulate_direction(*args, &cir)) {
524                                 circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
525                                 changed = true;
526                         } else {
527                                 fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
528                                 break;
529                         }
530                 } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
531                         insert_receptacle(trg.monitor, trg.desktop, trg.node);
532                         changed = true;
533                 } else if (streq("-c", *args) || streq("--close", *args)) {
534                         if (num > 1) {
535                                 fail(rsp, "node %s: Trailing commands.\n", *args);
536                                 break;
537                         }
538                         if (trg.node == NULL || locked_count(trg.node) > 0) {
539                                 fail(rsp, "");
540                                 break;
541                         }
542                         close_node(trg.node);
543                         break;
544                 } else if (streq("-k", *args) || streq("--kill", *args)) {
545                         if (num > 1) {
546                                 fail(rsp, "node %s: Trailing commands.\n", *args);
547                                 break;
548                         }
549                         if (trg.node == NULL) {
550                                 fail(rsp, "");
551                                 break;
552                         }
553                         kill_node(trg.monitor, trg.desktop, trg.node);
554                         changed = true;
555                         break;
556                 } else {
557                         fail(rsp, "node: Unknown command: '%s'.\n", *args);
558                         break;
559                 }
560
561                 num--, args++;
562         }
563
564         if (changed) {
565                 arrange(trg.monitor, trg.desktop);
566         }
567 }
568
569 void cmd_desktop(char **args, int num, FILE *rsp)
570 {
571         if (num < 1) {
572                 fail(rsp, "desktop: Missing commands.\n");
573                 return;
574         }
575
576         coordinates_t ref = {mon, mon->desk, NULL};
577         coordinates_t trg = ref;
578
579         if ((*args)[0] != OPT_CHR) {
580                 int ret;
581                 if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
582                         num--, args++;
583                 } else {
584                         handle_failure(ret, "desktop", *args, rsp);
585                         return;
586                 }
587         }
588
589         bool changed = false;
590
591         while (num > 0) {
592                 if (streq("-f", *args) || streq("--focus", *args)) {
593                         coordinates_t dst = trg;
594                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
595                                 num--, args++;
596                                 int ret;
597                                 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
598                                         handle_failure(ret, "desktop -f", *args, rsp);
599                                         break;
600                                 }
601                         }
602                         focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
603                 } else if (streq("-a", *args) || streq("--activate", *args)) {
604                         coordinates_t dst = trg;
605                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
606                                 num--, args++;
607                                 int ret;
608                                 if ((ret = desktop_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
609                                         handle_failure(ret, "desktop -a", *args, rsp);
610                                         break;
611                                 }
612                         }
613                         activate_desktop(dst.monitor, dst.desktop);
614                 } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
615                         num--, args++;
616                         if (num < 1) {
617                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
618                                 break;
619                         }
620                         if (trg.monitor->desk_head == trg.monitor->desk_tail) {
621                                 fail(rsp, "");
622                                 break;
623                         }
624                         coordinates_t dst;
625                         int ret;
626                         if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
627                                 if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
628                                         trg.monitor = dst.monitor;
629                                 } else {
630                                         fail(rsp, "");
631                                         break;
632                                 }
633                         } else {
634                                 handle_failure(ret, "desktop -m", *args, rsp);
635                                 break;
636                         }
637                 } else if (streq("-s", *args) || streq("--swap", *args)) {
638                         num--, args++;
639                         if (num < 1) {
640                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
641                                 break;
642                         }
643                         coordinates_t dst;
644                         int ret;
645                         if ((ret = desktop_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
646                                 if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
647                                         trg.monitor = dst.monitor;
648                                 } else {
649                                         fail(rsp, "");
650                                         break;
651                                 }
652                         } else {
653                                 handle_failure(ret, "desktop -s", *args, rsp);
654                                 break;
655                         }
656                 } else if (streq("-b", *args) || streq("--bubble", *args)) {
657                         num--, args++;
658                         if (num < 1) {
659                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
660                                 break;
661                         }
662                         cycle_dir_t cyc;
663                         if (parse_cycle_direction(*args, &cyc)) {
664                                 desktop_t *d = trg.desktop;
665                                 if (cyc == CYCLE_PREV) {
666                                         if (d->prev == NULL) {
667                                                 while (d->next != NULL) {
668                                                         swap_desktops(trg.monitor, d, trg.monitor, d->next);
669                                                 }
670                                         } else {
671                                                 swap_desktops(trg.monitor, d, trg.monitor, d->prev);
672                                         }
673                                 } else {
674                                         if (d->next == NULL) {
675                                                 while (d->prev != NULL) {
676                                                         swap_desktops(trg.monitor, d, trg.monitor, d->prev);
677                                                 }
678                                         } else {
679                                                 swap_desktops(trg.monitor, d, trg.monitor, d->next);
680                                         }
681                                 }
682                         } else {
683                                 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
684                                 break;
685                         }
686                 } else if (streq("-l", *args) || streq("--layout", *args)) {
687                         num--, args++;
688                         if (num < 1) {
689                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
690                                 break;
691                         }
692                         layout_t lyt;
693                         cycle_dir_t cyc;
694                         if (parse_cycle_direction(*args, &cyc)) {
695                                 change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
696                         } else if (parse_layout(*args, &lyt)) {
697                                 change_layout(trg.monitor, trg.desktop, lyt);
698                         } else {
699                                 fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
700                                 break;
701                         }
702                 } else if (streq("-n", *args) || streq("--rename", *args)) {
703                         num--, args++;
704                         if (num < 1) {
705                                 fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
706                                 break;
707                         }
708                         rename_desktop(trg.monitor, trg.desktop, *args);
709                 } else if (streq("-r", *args) || streq("--remove", *args)) {
710                         if (num > 1) {
711                                 fail(rsp, "desktop %s: Trailing commands.\n", *args);
712                                 break;
713                         }
714                         if (trg.desktop->root == NULL &&
715                             trg.monitor->desk_head != trg.monitor->desk_tail) {
716                                 remove_desktop(trg.monitor, trg.desktop);
717                                 return;
718                         } else {
719                                 fail(rsp, "");
720                                 break;
721                         }
722                 } else {
723                         fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
724                         break;
725                 }
726                 num--, args++;
727         }
728
729         if (changed) {
730                 arrange(trg.monitor, trg.desktop);
731         }
732 }
733
734 void cmd_monitor(char **args, int num, FILE *rsp)
735 {
736         if (num < 1) {
737                 fail(rsp, "monitor: Missing commands.\n");
738                 return;
739         }
740
741         coordinates_t ref = {mon, NULL, NULL};
742         coordinates_t trg = ref;
743
744         if ((*args)[0] != OPT_CHR) {
745                 int ret;
746                 if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
747                         num--, args++;
748                 } else {
749                         handle_failure(ret, "monitor", *args, rsp);
750                         return;
751                 }
752         }
753
754         while (num > 0) {
755                 if (streq("-f", *args) || streq("--focus", *args)) {
756                         coordinates_t dst = trg;
757                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
758                                 num--, args++;
759                                 int ret;
760                                 if ((ret = monitor_from_desc(*args, &trg, &dst)) != SELECTOR_OK) {
761                                         handle_failure(ret, "monitor -f", *args, rsp);
762                                         fail(rsp, "");
763                                         return;
764                                 }
765                         }
766                         focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
767                 } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
768                         num--, args++;
769                         if (num < 1) {
770                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
771                                 return;
772                         }
773                         desktop_t *d = trg.monitor->desk_head;
774                         while (num > 0 && d != NULL) {
775                                 rename_desktop(trg.monitor, d, *args);
776                                 d = d->next;
777                                 num--, args++;
778                         }
779                         put_status(SBSC_MASK_REPORT);
780                         while (num > 0) {
781                                 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
782                                 num--, args++;
783                         }
784                         while (d != NULL) {
785                                 desktop_t *next = d->next;
786                                 if (d == mon->desk) {
787                                         focus_node(trg.monitor, d->prev, d->prev->focus);
788                                 }
789                                 merge_desktops(trg.monitor, d, mon, mon->desk);
790                                 remove_desktop(trg.monitor, d);
791                                 d = next;
792                         }
793                 } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
794                         num--, args++;
795                         if (num < 1) {
796                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
797                                 return;
798                         }
799                         while (num > 0) {
800                                 add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
801                                 num--, args++;
802                         }
803                 } else if (streq("-r", *args) || streq("--remove", *args)) {
804                         if (num > 1) {
805                                 fail(rsp, "monitor %s: Trailing commands.\n", *args);
806                                 return;
807                         }
808                         if (mon_head == mon_tail) {
809                                 fail(rsp, "");
810                                 return;
811                         }
812                         remove_monitor(trg.monitor);
813                         return;
814                 } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
815                         num--, args++;
816                         if (num < 1) {
817                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
818                                 return;
819                         }
820                         desktop_t *d = trg.monitor->desk_head;
821                         while (d != NULL && num > 0) {
822                                 desktop_t *next = d->next;
823                                 coordinates_t dst;
824                                 if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
825                                         swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
826                                         if (next == dst.desktop) {
827                                                 next = d;
828                                         }
829                                 }
830                                 d = next;
831                                 num--, args++;
832                         }
833                 } else if (streq("-g", *args) || streq("--rectangle", *args)) {
834                         num--, args++;
835                         if (num < 1) {
836                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
837                                 return;
838                         }
839                         xcb_rectangle_t r;
840                         if (parse_rectangle(*args, &r)) {
841                                 update_root(trg.monitor, &r);
842                         } else {
843                                 fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
844                                 return;
845                         }
846                 } else if (streq("-n", *args) || streq("--rename", *args)) {
847                         num--, args++;
848                         if (num < 1) {
849                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
850                                 return;
851                         }
852                         rename_monitor(trg.monitor, *args);
853                 } else if (streq("-s", *args) || streq("--swap", *args)) {
854                         num--, args++;
855                         if (num < 1) {
856                                 fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
857                                 return;
858                         }
859                         coordinates_t dst;
860                         int ret;
861                         if ((ret = monitor_from_desc(*args, &trg, &dst)) == SELECTOR_OK) {
862                                 if (!swap_monitors(trg.monitor, dst.monitor)) {
863                                         fail(rsp, "");
864                                         return;
865                                 }
866                         } else {
867                                 handle_failure(ret, "monitor -s", *args, rsp);
868                                 return;
869                         }
870                 } else {
871                         fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
872                         return;
873                 }
874                 num--, args++;
875         }
876 }
877
878 void cmd_query(char **args, int num, FILE *rsp)
879 {
880         coordinates_t ref = {mon, mon->desk, mon->desk->focus};
881         coordinates_t trg = {NULL, NULL, NULL};
882         monitor_select_t *monitor_sel = NULL;
883         desktop_select_t *desktop_sel = NULL;
884         node_select_t *node_sel = NULL;
885         domain_t dom = DOMAIN_TREE;
886         int d = 0, t = 0;
887
888         while (num > 0) {
889                 if (streq("-T", *args) || streq("--tree", *args)) {
890                         dom = DOMAIN_TREE, d++;
891                 } else if (streq("-M", *args) || streq("--monitors", *args)) {
892                         dom = DOMAIN_MONITOR, d++;
893                 } else if (streq("-D", *args) || streq("--desktops", *args)) {
894                         dom = DOMAIN_DESKTOP, d++;
895                 } else if (streq("-N", *args) || streq("--nodes", *args)) {
896                         dom = DOMAIN_NODE, d++;
897                 } else if (streq("-m", *args) || streq("--monitor", *args)) {
898                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
899                                 num--, args++;
900                                 int ret;
901                                 if ((*args)[0] == '.') {
902                                         free(monitor_sel);
903                                         monitor_sel = malloc(sizeof(monitor_select_t));
904                                         *monitor_sel = make_monitor_select();
905                                         char *desc = copy_string(*args, strlen(*args));
906                                         if (!parse_monitor_modifiers(desc, monitor_sel)) {
907                                                 handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
908                                                 free(desc);
909                                                 goto end;
910                                         }
911                                         free(desc);
912                                 } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
913                                         handle_failure(ret, "query -m", *args, rsp);
914                                         goto end;
915                                 }
916                         } else {
917                                 trg.monitor = ref.monitor;
918                         }
919                         t++;
920                 } else if (streq("-d", *args) || streq("--desktop", *args)) {
921                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
922                                 num--, args++;
923                                 int ret;
924                                 if ((*args)[0] == '.') {
925                                         free(desktop_sel);
926                                         desktop_sel = malloc(sizeof(desktop_select_t));
927                                         *desktop_sel = make_desktop_select();
928                                         char *desc = copy_string(*args, strlen(*args));
929                                         if (!parse_desktop_modifiers(desc, desktop_sel)) {
930                                                 handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
931                                                 free(desc);
932                                                 goto end;
933                                         }
934                                         free(desc);
935                                 } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
936                                         handle_failure(ret, "query -d", *args, rsp);
937                                         goto end;
938                                 }
939                         } else {
940                                 trg.monitor = ref.monitor;
941                                 trg.desktop = ref.desktop;
942                         }
943                         t++;
944                 } else if (streq("-n", *args) || streq("--node", *args)) {
945                         if (num > 1 && *(args + 1)[0] != OPT_CHR) {
946                                 num--, args++;
947                                 int ret;
948                                 if ((*args)[0] == '.') {
949                                         free(node_sel);
950                                         node_sel = malloc(sizeof(node_select_t));
951                                         *node_sel = make_node_select();
952                                         char *desc = copy_string(*args, strlen(*args));
953                                         if (!parse_node_modifiers(desc, node_sel)) {
954                                                 handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
955                                                 free(desc);
956                                                 goto end;
957                                         }
958                                         free(desc);
959                                 } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
960                                         handle_failure(ret, "query -n", *args, rsp);
961                                         goto end;
962                                 }
963                         } else {
964                                 trg = ref;
965                                 if (ref.node == NULL) {
966                                         fail(rsp, "");
967                                         goto end;
968                                 }
969                         }
970                         t++;
971                 } else {
972                         fail(rsp, "query: Unknown option: '%s'.\n", *args);
973                         goto end;
974                 }
975                 num--, args++;
976         }
977
978         if (d != 1 || t > 1) {
979                 fail(rsp, "query: Exactly one domain and at most one constraint must be specified.\n");
980                 goto end;
981         }
982
983         if (dom == DOMAIN_NODE) {
984                 if (query_node_ids(trg, node_sel, rsp) < 1) {
985                         fail(rsp, "");
986                 }
987         } else if (dom == DOMAIN_DESKTOP) {
988                 if (query_desktop_ids(trg, desktop_sel, rsp) < 1) {
989                         fail(rsp, "");
990                 }
991         } else if (dom == DOMAIN_MONITOR) {
992                 if (query_monitor_ids(trg, monitor_sel, rsp) < 1) {
993                         fail(rsp, "");
994                 }
995         } else {
996                 if (trg.node != NULL) {
997                         query_node(trg.node, rsp);
998                 } else if (trg.desktop != NULL) {
999                         query_desktop(trg.desktop, rsp);
1000                 } else  if (trg.monitor != NULL) {
1001                         query_monitor(trg.monitor, rsp);
1002                 } else {
1003                         fail(rsp, "");
1004                         goto end;
1005                 }
1006                 fprintf(rsp, "\n");
1007         }
1008
1009 end:
1010         free(monitor_sel);
1011         free(desktop_sel);
1012         free(node_sel);
1013 }
1014
1015 void cmd_rule(char **args, int num, FILE *rsp)
1016 {
1017         if (num < 1) {
1018                 fail(rsp, "rule: Missing commands.\n");
1019                 return;
1020         }
1021
1022         while (num > 0) {
1023                 if (streq("-a", *args) || streq("--add", *args)) {
1024                         num--, args++;
1025                         if (num < 2) {
1026                                 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1027                                 return;
1028                         }
1029                         rule_t *rule = make_rule();
1030                         char *class_name = strtok(*args, COL_TOK);
1031                         char *instance_name = strtok(NULL, COL_TOK);
1032                         snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
1033                         snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
1034                         num--, args++;
1035                         size_t i = 0;
1036                         while (num > 0) {
1037                                 if (streq("-o", *args) || streq("--one-shot", *args)) {
1038                                         rule->one_shot = true;
1039                                 } else {
1040                                         for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
1041                                                 rule->effect[i] = (*args)[j];
1042                                         }
1043                                         if (num > 1 && i < sizeof(rule->effect)) {
1044                                                 rule->effect[i++] = ' ';
1045                                         }
1046                                 }
1047                                 num--, args++;
1048                         }
1049                         rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
1050                         add_rule(rule);
1051                 } else if (streq("-r", *args) || streq("--remove", *args)) {
1052                         num--, args++;
1053                         if (num < 1) {
1054                                 fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
1055                                 return;
1056                         }
1057                         uint16_t idx;
1058                         while (num > 0) {
1059                                 if (parse_index(*args, &idx)) {
1060                                         remove_rule_by_index(idx - 1);
1061                                 } else if (streq("tail", *args)) {
1062                                         remove_rule(rule_tail);
1063                                 } else if (streq("head", *args)) {
1064                                         remove_rule(rule_head);
1065                                 } else {
1066                                         remove_rule_by_cause(*args);
1067                                 }
1068                                 num--, args++;
1069                         }
1070                 } else if (streq("-l", *args) || streq("--list", *args)) {
1071                         list_rules(rsp);
1072                 } else {
1073                         fail(rsp, "rule: 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("pointer_modifier", name)) {
1385                 if (parse_modifier_mask(value, &pointer_modifier)) {
1386                         ungrab_buttons();
1387                         grab_buttons();
1388                 } else {
1389                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1390                         return;
1391                 }
1392         } else if (streq("pointer_action1", name) ||
1393                    streq("pointer_action2", name) ||
1394                    streq("pointer_action3", name)) {
1395                 int index = name[14] - '1';
1396                 if (!parse_pointer_action(value, &pointer_actions[index])) {
1397                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1398                         return;
1399                 }
1400         } else if (streq("click_to_focus", name)) {
1401                 if (parse_bool(value, &click_to_focus)) {
1402                         ungrab_buttons();
1403                         grab_buttons();
1404                 } else {
1405                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1406                         return;
1407                 }
1408         } else if (streq("focus_follows_pointer", name)) {
1409                 bool b;
1410                 if (parse_bool(value, &b)) {
1411                         if (b == focus_follows_pointer) {
1412                                 return;
1413                         }
1414                         focus_follows_pointer = b;
1415                         uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
1416                         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1417                                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1418                                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
1419                                                 if (n->client == NULL) {
1420                                                         continue;
1421                                                 }
1422                                                 xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
1423                                         }
1424                                 }
1425                         }
1426                         if (focus_follows_pointer) {
1427                                 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1428                                         window_show(m->root);
1429                                 }
1430                         } else {
1431                                 for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1432                                         window_hide(m->root);
1433                                 }
1434                                 disable_motion_recorder();
1435                         }
1436                         return;
1437                 } else {
1438                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
1439                         return;
1440                 }
1441 #define SET_BOOL(s) \
1442         } else if (streq(#s, name)) { \
1443                 if (!parse_bool(value, &s)) { \
1444                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1445                         return; \
1446                 }
1447                 SET_BOOL(borderless_monocle)
1448                 SET_BOOL(gapless_monocle)
1449                 SET_BOOL(paddingless_monocle)
1450                 SET_BOOL(single_monocle)
1451                 SET_BOOL(pointer_follows_focus)
1452                 SET_BOOL(pointer_follows_monitor)
1453                 SET_BOOL(history_aware_focus)
1454                 SET_BOOL(focus_by_distance)
1455                 SET_BOOL(ignore_ewmh_focus)
1456                 SET_BOOL(center_pseudo_tiled)
1457                 SET_BOOL(honor_size_hints)
1458 #undef SET_BOOL
1459 #define SET_MON_BOOL(s) \
1460         } else if (streq(#s, name)) { \
1461                 if (!parse_bool(value, &s)) { \
1462                         fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
1463                         return; \
1464                 } \
1465                 if (s) { \
1466                         update_monitors(); \
1467                 }
1468                 SET_MON_BOOL(remove_disabled_monitors)
1469                 SET_MON_BOOL(remove_unplugged_monitors)
1470                 SET_MON_BOOL(merge_overlapping_monitors)
1471 #undef SET_MON_BOOL
1472         } else {
1473                 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1474                 return;
1475         }
1476
1477         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
1478                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
1479                         arrange(m, d);
1480                         if (colors_changed) {
1481                                 update_colors_in(d->root, d, m);
1482                         }
1483                 }
1484         }
1485 }
1486
1487 void get_setting(coordinates_t loc, char *name, FILE* rsp)
1488 {
1489         if (streq("split_ratio", name)) {
1490                 fprintf(rsp, "%lf", split_ratio);
1491         } else if (streq("border_width", name)) {
1492                 if (loc.node != NULL) {
1493                         fprintf(rsp, "%u", loc.node->client->border_width);
1494                 } else if (loc.desktop != NULL) {
1495                         fprintf(rsp, "%u", loc.desktop->border_width);
1496                 } else if (loc.monitor != NULL) {
1497                         fprintf(rsp, "%u", loc.monitor->border_width);
1498                 } else {
1499                         fprintf(rsp, "%u", border_width);
1500                 }
1501         } else if (streq("window_gap", name)) {
1502                 if (loc.desktop != NULL) {
1503                         fprintf(rsp, "%i", loc.desktop->window_gap);
1504                 } else if (loc.monitor != NULL) {
1505                         fprintf(rsp, "%i", loc.monitor->window_gap);
1506                 } else {
1507                         fprintf(rsp, "%i", window_gap);
1508                 }
1509 #define GET_DEF_MON_DESK(k) \
1510                 if (loc.desktop != NULL) { \
1511                         fprintf(rsp, "%i", loc.desktop->k); \
1512                 } else if (loc.monitor != NULL) { \
1513                         fprintf(rsp, "%i", loc.monitor->k); \
1514                 } else { \
1515                         fprintf(rsp, "%i", k); \
1516                 }
1517         } else if (streq("top_padding", name)) {
1518                 GET_DEF_MON_DESK(padding.top)
1519         } else if (streq("right_padding", name)) {
1520                 GET_DEF_MON_DESK(padding.right)
1521         } else if (streq("bottom_padding", name)) {
1522                 GET_DEF_MON_DESK(padding.bottom)
1523         } else if (streq("left_padding", name)) {
1524                 GET_DEF_MON_DESK(padding.left)
1525 #undef GET_DEF_MON_DESK
1526         } else if (streq("external_rules_command", name)) {
1527                 fprintf(rsp, "%s", external_rules_command);
1528         } else if (streq("status_prefix", name)) {
1529                 fprintf(rsp, "%s", status_prefix);
1530         } else if (streq("initial_polarity", name)) {
1531                 fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
1532         } else if (streq("pointer_modifier", name)) {
1533                 print_modifier_mask(pointer_modifier, rsp);
1534         } else if (streq("pointer_action1", name) ||
1535                    streq("pointer_action2", name) ||
1536                    streq("pointer_action3", name)) {
1537                 int index = name[14] - '1';
1538                 print_pointer_action(pointer_actions[index], rsp);
1539 #define GET_COLOR(s) \
1540         } else if (streq(#s, name)) { \
1541                 fprintf(rsp, "%s", s);
1542         GET_COLOR(normal_border_color)
1543         GET_COLOR(active_border_color)
1544         GET_COLOR(focused_border_color)
1545         GET_COLOR(presel_feedback_color)
1546 #undef GET_COLOR
1547 #define GET_BOOL(s) \
1548         } else if (streq(#s, name)) { \
1549                 fprintf(rsp, "%s", BOOL_STR(s));
1550         GET_BOOL(borderless_monocle)
1551         GET_BOOL(gapless_monocle)
1552         GET_BOOL(paddingless_monocle)
1553         GET_BOOL(single_monocle)
1554         GET_BOOL(click_to_focus)
1555         GET_BOOL(focus_follows_pointer)
1556         GET_BOOL(pointer_follows_focus)
1557         GET_BOOL(pointer_follows_monitor)
1558         GET_BOOL(history_aware_focus)
1559         GET_BOOL(focus_by_distance)
1560         GET_BOOL(ignore_ewmh_focus)
1561         GET_BOOL(center_pseudo_tiled)
1562         GET_BOOL(honor_size_hints)
1563         GET_BOOL(remove_disabled_monitors)
1564         GET_BOOL(remove_unplugged_monitors)
1565         GET_BOOL(merge_overlapping_monitors)
1566 #undef GET_BOOL
1567         } else {
1568                 fail(rsp, "config: Unknown setting: '%s'.\n", name);
1569                 return;
1570         }
1571         fprintf(rsp, "\n");
1572 }
1573
1574 void handle_failure(int code, char *src, char *val, FILE *rsp)
1575 {
1576         switch (code) {
1577                 case SELECTOR_BAD_DESCRIPTOR:
1578                         fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
1579                         break;
1580                 case SELECTOR_BAD_MODIFIERS:
1581                         fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
1582                         break;
1583                 case SELECTOR_INVALID:
1584                         fail(rsp, "");
1585                         break;
1586         }
1587 }
1588
1589 void fail(FILE *rsp, char *fmt, ...)
1590 {
1591         fprintf(rsp, FAILURE_MESSAGE);
1592         va_list ap;
1593         va_start(ap, fmt);
1594         vfprintf(rsp, fmt, ap);
1595         va_end(ap);
1596 }