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