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