]> git.lizzy.rs Git - bspwm.git/blob - src/query.c
Mention forbidden characters
[bspwm.git] / src / query.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 "bspwm.h"
29 #include "desktop.h"
30 #include "history.h"
31 #include "parse.h"
32 #include "monitor.h"
33 #include "window.h"
34 #include "tree.h"
35 #include "query.h"
36
37 void query_tree(FILE *rsp)
38 {
39         fprintf(rsp, "{");
40         fprintf(rsp, "\"focusedMonitorId\":%u,", mon->id);
41         if (pri_mon != NULL) {
42                 fprintf(rsp, "\"primaryMonitorId\":%u,", pri_mon->id);
43         }
44         fprintf(rsp, "\"clientsCount\":%i,", clients_count);
45         fprintf(rsp, "\"monitors\":");
46         fprintf(rsp, "[");
47         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
48                 query_monitor(m, rsp);
49                 if (m->next != NULL) {
50                         fprintf(rsp, ",");
51                 }
52         }
53         fprintf(rsp, "]");
54         fprintf(rsp,",");
55         fprintf(rsp, "\"focusHistory\":");
56         query_history(rsp);
57         fprintf(rsp,",");
58         fprintf(rsp, "\"stackingList\":");
59         query_stack(rsp);
60         fprintf(rsp, "}");
61
62 }
63
64 void query_monitor(monitor_t *m, FILE *rsp)
65 {
66         fprintf(rsp, "{");
67         fprintf(rsp, "\"name\":\"%s\",", m->name);
68         fprintf(rsp, "\"id\":%u,", m->id);
69         fprintf(rsp, "\"randrId\":%u,", m->randr_id);
70         fprintf(rsp, "\"wired\":%s,", BOOL_STR(m->wired));
71         fprintf(rsp, "\"stickyCount\":%i,", m->sticky_count);
72         fprintf(rsp, "\"windowGap\":%i,", m->window_gap);
73         fprintf(rsp, "\"borderWidth\":%u,", m->border_width);
74         fprintf(rsp, "\"focusedDesktopId\":%u,", m->desk->id);
75         fprintf(rsp, "\"padding\":");
76         query_padding(m->padding, rsp);
77         fprintf(rsp,",");
78         fprintf(rsp, "\"rectangle\":");
79         query_rectangle(m->rectangle, rsp);
80         fprintf(rsp,",");
81         fprintf(rsp, "\"desktops\":");
82         fprintf(rsp, "[");
83         for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
84                 query_desktop(d, rsp);
85                 if (d->next != NULL) {
86                         fprintf(rsp,",");
87                 }
88         }
89         fprintf(rsp, "]");
90         fprintf(rsp, "}");
91 }
92
93 void query_desktop(desktop_t *d, FILE *rsp)
94 {
95         fprintf(rsp, "{");
96         fprintf(rsp, "\"name\":\"%s\",", d->name);
97         fprintf(rsp, "\"id\":%u,", d->id);
98         fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout));
99         fprintf(rsp, "\"windowGap\":%i,", d->window_gap);
100         fprintf(rsp, "\"borderWidth\":%u,", d->border_width);
101         fprintf(rsp, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0);
102         fprintf(rsp, "\"padding\":");
103         query_padding(d->padding, rsp);
104         fprintf(rsp,",");
105         fprintf(rsp, "\"root\":");
106         query_node(d->root, rsp);
107         fprintf(rsp, "}");
108 }
109
110 void query_node(node_t *n, FILE *rsp)
111 {
112         if (n == NULL) {
113                 fprintf(rsp, "null");
114         } else {
115                 fprintf(rsp, "{");
116                 fprintf(rsp, "\"id\":%u,", n->id);
117                 fprintf(rsp, "\"splitType\":\"%s\",", SPLIT_TYPE_STR(n->split_type));
118                 fprintf(rsp, "\"splitRatio\":%lf,", n->split_ratio);
119                 fprintf(rsp, "\"birthRotation\":%i,", n->birth_rotation);
120                 fprintf(rsp, "\"vacant\":%s,", BOOL_STR(n->vacant));
121                 fprintf(rsp, "\"hidden\":%s,", BOOL_STR(n->hidden));
122                 fprintf(rsp, "\"sticky\":%s,", BOOL_STR(n->sticky));
123                 fprintf(rsp, "\"private\":%s,", BOOL_STR(n->private));
124                 fprintf(rsp, "\"locked\":%s,", BOOL_STR(n->locked));
125                 fprintf(rsp, "\"presel\":");
126                 query_presel(n->presel, rsp);
127                 fprintf(rsp,",");
128                 fprintf(rsp, "\"rectangle\":");
129                 query_rectangle(n->rectangle, rsp);
130                 fprintf(rsp,",");
131                 fprintf(rsp, "\"constraints\":");
132                 query_constraints(n->constraints, rsp);
133                 fprintf(rsp,",");
134                 fprintf(rsp, "\"firstChild\":");
135                 query_node(n->first_child, rsp);
136                 fprintf(rsp,",");
137                 fprintf(rsp, "\"secondChild\":");
138                 query_node(n->second_child, rsp);
139                 fprintf(rsp,",");
140                 fprintf(rsp, "\"client\":");
141                 query_client(n->client, rsp);
142                 fprintf(rsp, "}");
143         }
144 }
145
146 void query_presel(presel_t *p, FILE *rsp)
147 {
148         if (p == NULL) {
149                 fprintf(rsp, "null");
150         } else {
151                 fprintf(rsp, "{\"splitDir\":\"%s\",\"splitRatio\":%lf}", SPLIT_DIR_STR(p->split_dir), p->split_ratio);
152         }
153 }
154
155 void query_client(client_t *c, FILE *rsp)
156 {
157         if (c == NULL) {
158                 fprintf(rsp, "null");
159         } else {
160                 fprintf(rsp, "{");
161                 fprintf(rsp, "\"className\":\"%s\",", c->class_name);
162                 fprintf(rsp, "\"instanceName\":\"%s\",", c->instance_name);
163                 fprintf(rsp, "\"borderWidth\":%u,", c->border_width);
164                 fprintf(rsp, "\"state\":\"%s\",", STATE_STR(c->state));
165                 fprintf(rsp, "\"lastState\":\"%s\",", STATE_STR(c->last_state));
166                 fprintf(rsp, "\"layer\":\"%s\",", LAYER_STR(c->layer));
167                 fprintf(rsp, "\"lastLayer\":\"%s\",", LAYER_STR(c->last_layer));
168                 fprintf(rsp, "\"urgent\":%s,", BOOL_STR(c->urgent));
169                 fprintf(rsp, "\"shown\":%s,", BOOL_STR(c->shown));
170                 fprintf(rsp, "\"tiledRectangle\":");
171                 query_rectangle(c->tiled_rectangle, rsp);
172                 fprintf(rsp,",");
173                 fprintf(rsp, "\"floatingRectangle\":");
174                 query_rectangle(c->floating_rectangle, rsp);
175                 fprintf(rsp, "}");
176         }
177 }
178
179 void query_rectangle(xcb_rectangle_t r, FILE *rsp)
180 {
181         fprintf(rsp, "{\"x\":%i,\"y\":%i,\"width\":%u,\"height\":%u}", r.x, r.y, r.width, r.height);
182 }
183
184 void query_constraints(constraints_t c, FILE *rsp)
185 {
186         fprintf(rsp, "{\"min_width\":%u,\"min_height\":%u}", c.min_width, c.min_height);
187 }
188
189 void query_padding(padding_t p, FILE *rsp)
190 {
191         fprintf(rsp, "{\"top\":%i,\"right\":%i,\"bottom\":%i,\"left\":%i}", p.top, p.right, p.bottom, p.left);
192 }
193
194 void query_history(FILE *rsp)
195 {
196         fprintf(rsp, "[");
197         for (history_t *h = history_head; h != NULL; h = h->next) {
198                 query_coordinates(&h->loc, rsp);
199                 if (h->next != NULL) {
200                         fprintf(rsp, ",");
201                 }
202         }
203         fprintf(rsp, "]");
204 }
205
206 void query_coordinates(coordinates_t *loc, FILE *rsp)
207 {
208         fprintf(rsp, "{\"monitorId\":%u,\"desktopId\":%u,\"nodeId\":%u}", loc->monitor->id, loc->desktop->id, loc->node!=NULL?loc->node->id:0);
209 }
210
211 void query_stack(FILE *rsp)
212 {
213         fprintf(rsp, "[");
214         for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
215                 fprintf(rsp, "%u", s->node->id);
216                 if (s->next != NULL) {
217                         fprintf(rsp, ",");
218                 }
219         }
220         fprintf(rsp, "]");
221 }
222
223 int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
224 {
225         int count = 0;
226         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
227                 if (trg->monitor != NULL && m != trg->monitor) {
228                         continue;
229                 }
230                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
231                         if (trg->desktop != NULL && d != trg->desktop) {
232                                 continue;
233                         }
234                         count += query_node_ids_in(d->root, d, m, ref, trg, sel, rsp);
235                 }
236         }
237         return count;
238 }
239
240 int query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
241 {
242         int count = 0;
243         if (n == NULL) {
244                 return 0;
245         } else {
246                 coordinates_t loc = {m, d, n};
247                 if ((trg->node == NULL || n == trg->node) &&
248                     (sel == NULL || node_matches(&loc, ref, *sel))) {
249                         fprintf(rsp, "0x%08X\n", n->id);
250                         count++;
251                 }
252                 count += query_node_ids_in(n->first_child, d, m, ref, trg, sel, rsp);
253                 count += query_node_ids_in(n->second_child, d, m, ref, trg, sel, rsp);
254         }
255         return count;
256 }
257
258 int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp)
259 {
260         int count = 0;
261         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
262                 if (trg->monitor != NULL && m != trg->monitor) {
263                         continue;
264                 }
265                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
266                         coordinates_t loc = {m, d, NULL};
267                         if ((trg->desktop != NULL && d != trg->desktop) ||
268                             (sel != NULL && !desktop_matches(&loc, ref, *sel))) {
269                                 continue;
270                         }
271                         printer(d, rsp);
272                         count++;
273                 }
274         }
275         return count;
276 }
277
278 int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp)
279 {
280         int count = 0;
281         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
282                 coordinates_t loc = {m, NULL, NULL};
283                 if ((trg->monitor != NULL && m != trg->monitor) ||
284                         (sel != NULL && !monitor_matches(&loc, ref, *sel))) {
285                         continue;
286                 }
287                 printer(m, rsp);
288                 count++;
289         }
290         return count;
291 }
292
293 void fprint_monitor_id(monitor_t *m, FILE *rsp)
294 {
295         fprintf(rsp, "0x%08X\n", m->id);
296 }
297
298 void fprint_monitor_name(monitor_t *m, FILE *rsp)
299 {
300         fprintf(rsp, "%s\n", m->name);
301 }
302
303 void fprint_desktop_id(desktop_t *d, FILE *rsp)
304 {
305         fprintf(rsp, "0x%08X\n", d->id);
306 }
307
308 void fprint_desktop_name(desktop_t *d, FILE *rsp)
309 {
310         fprintf(rsp, "%s\n", d->name);
311 }
312
313 void print_modifier_mask(uint16_t m, FILE *rsp)
314 {
315         switch (m) {
316                 case XCB_MOD_MASK_SHIFT:
317                         fprintf(rsp, "shift");
318                         break;
319                 case XCB_MOD_MASK_CONTROL:
320                         fprintf(rsp, "control");
321                         break;
322                 case XCB_MOD_MASK_LOCK:
323                         fprintf(rsp, "lock");
324                         break;
325                 case XCB_MOD_MASK_1:
326                         fprintf(rsp, "mod1");
327                         break;
328                 case XCB_MOD_MASK_2:
329                         fprintf(rsp, "mod2");
330                         break;
331                 case XCB_MOD_MASK_3:
332                         fprintf(rsp, "mod3");
333                         break;
334                 case XCB_MOD_MASK_4:
335                         fprintf(rsp, "mod4");
336                         break;
337                 case XCB_MOD_MASK_5:
338                         fprintf(rsp, "mod5");
339                         break;
340         }
341 }
342
343 void print_button_index(int8_t b, FILE *rsp)
344 {
345         switch (b) {
346                 case XCB_BUTTON_INDEX_ANY:
347                         fprintf(rsp, "any");
348                         break;
349                 case XCB_BUTTON_INDEX_1:
350                         fprintf(rsp, "button1");
351                         break;
352                 case XCB_BUTTON_INDEX_2:
353                         fprintf(rsp, "button2");
354                         break;
355                 case XCB_BUTTON_INDEX_3:
356                         fprintf(rsp, "button3");
357                         break;
358                 case -1:
359                         fprintf(rsp, "none");
360                         break;
361         }
362 }
363
364 void print_pointer_action(pointer_action_t a, FILE *rsp)
365 {
366         switch (a) {
367                 case ACTION_MOVE:
368                         fprintf(rsp, "move");
369                         break;
370                 case ACTION_RESIZE_SIDE:
371                         fprintf(rsp, "resize_side");
372                         break;
373                 case ACTION_RESIZE_CORNER:
374                         fprintf(rsp, "resize_corner");
375                         break;
376                 case ACTION_FOCUS:
377                         fprintf(rsp, "focus");
378                         break;
379                 case ACTION_NONE:
380                         fprintf(rsp, "none");
381                         break;
382         }
383 }
384
385 node_select_t make_node_select(void)
386 {
387         node_select_t sel = {
388                 .automatic = OPTION_NONE,
389                 .focused = OPTION_NONE,
390                 .local = OPTION_NONE,
391                 .active = OPTION_NONE,
392                 .leaf = OPTION_NONE,
393                 .window = OPTION_NONE,
394                 .tiled = OPTION_NONE,
395                 .pseudo_tiled = OPTION_NONE,
396                 .floating = OPTION_NONE,
397                 .fullscreen = OPTION_NONE,
398                 .hidden = OPTION_NONE,
399                 .sticky = OPTION_NONE,
400                 .private = OPTION_NONE,
401                 .locked = OPTION_NONE,
402                 .urgent = OPTION_NONE,
403                 .same_class = OPTION_NONE,
404                 .descendant_of = OPTION_NONE,
405                 .ancestor_of = OPTION_NONE,
406                 .below = OPTION_NONE,
407                 .normal = OPTION_NONE,
408                 .above = OPTION_NONE
409         };
410         return sel;
411 }
412
413 desktop_select_t make_desktop_select(void)
414 {
415         desktop_select_t sel = {
416                 .occupied = OPTION_NONE,
417                 .focused = OPTION_NONE,
418                 .urgent = OPTION_NONE,
419                 .local = OPTION_NONE
420         };
421         return sel;
422 }
423
424 monitor_select_t make_monitor_select(void)
425 {
426         monitor_select_t sel = {
427                 .occupied = OPTION_NONE,
428                 .focused = OPTION_NONE
429         };
430         return sel;
431 }
432
433 int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
434 {
435         coordinates_t ref_copy = *ref;
436         ref = &ref_copy;
437         char *desc_copy = copy_string(desc, strlen(desc));
438         desc = desc_copy;
439
440         char *hash = NULL;
441         char *path = strrchr(desc, '@');
442         if (path == NULL) {
443                 hash = strrchr(desc, '#');
444         } else {
445                 if (path != desc && *(path - 1) == '#') {
446                         hash = path - 1;
447                 }
448         }
449
450         if (hash != NULL) {
451                 *hash = '\0';
452                 int ret;
453                 coordinates_t tmp = {mon, mon->desk, mon->desk->focus};
454                 if ((ret = node_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
455                         desc = hash + 1;
456                 } else {
457                         free(desc_copy);
458                         return ret;
459                 }
460         }
461
462         node_select_t sel = make_node_select();
463         char *colon = strrchr(desc, ':');
464
465         if (!parse_node_modifiers(colon != NULL ? colon : desc, &sel)) {
466                 free(desc_copy);
467                 return SELECTOR_BAD_MODIFIERS;
468         }
469
470         dst->node = NULL;
471
472         direction_t dir;
473         cycle_dir_t cyc;
474         history_dir_t hdi;
475         if (parse_direction(desc, &dir)) {
476                 find_nearest_neighbor(ref, dst, dir, sel);
477         } else if (parse_cycle_direction(desc, &cyc)) {
478                 find_closest_node(ref, dst, cyc, sel);
479         } else if (parse_history_direction(desc, &hdi)) {
480                 history_find_node(hdi, ref, dst, sel);
481         } else if (streq("last", desc)) {
482                 history_find_node(HISTORY_OLDER, ref, dst, sel);
483         } else if (streq("biggest", desc)) {
484                 find_biggest(ref, dst, sel);
485         } else if (streq("pointed", desc)) {
486                 xcb_window_t win = XCB_NONE;
487                 query_pointer(&win, NULL);
488                 if (locate_window(win, dst) && node_matches(dst, ref, sel)) {
489                         return SELECTOR_OK;
490                 } else {
491                         return SELECTOR_INVALID;
492                 }
493         } else if (streq("focused", desc)) {
494                 coordinates_t loc = {mon, mon->desk, mon->desk->focus};
495                 if (node_matches(&loc, ref, sel)) {
496                         *dst = loc;
497                 }
498         } else if (*desc == '@') {
499                 desc++;
500                 *dst = *ref;
501                 if (colon != NULL) {
502                         *colon = '\0';
503                         int ret;
504                         if ((ret = desktop_from_desc(desc, ref, dst)) == SELECTOR_OK) {
505                                 dst->node = dst->desktop->focus;
506                                 desc = colon + 1;
507                         } else {
508                                 free(desc_copy);
509                                 return ret;
510                         }
511                 }
512                 if (*desc == '/') {
513                         dst->node = dst->desktop->root;
514                 }
515                 char *move = strtok(desc, PTH_TOK);
516                 while (move != NULL && dst->node != NULL) {
517                         if (streq("first", move) || streq("1", move)) {
518                                 dst->node = dst->node->first_child;
519                         } else if (streq("second", move) || streq("2", move)) {
520                                 dst->node = dst->node->second_child;
521                         } else if (streq("parent", move)) {
522                                 dst->node = dst->node->parent;
523                         } else if (streq("brother", move)) {
524                                 dst->node = brother_tree(dst->node);
525                         } else {
526                                 direction_t dir;
527                                 if (parse_direction(move, &dir)) {
528                                         dst->node = find_fence(dst->node, dir);
529                                 } else {
530                                         free(desc_copy);
531                                         return SELECTOR_BAD_DESCRIPTOR;
532                                 }
533                         }
534                         move = strtok(NULL, PTH_TOK);
535                 }
536                 free(desc_copy);
537                 if (dst->node != NULL) {
538                         if (node_matches(dst, ref, sel)) {
539                                 return SELECTOR_OK;
540                         } else {
541                                 return SELECTOR_INVALID;
542                         }
543                 } else if (dst->desktop->root != NULL) {
544                         return SELECTOR_INVALID;
545                 }
546                 return SELECTOR_OK;
547         } else {
548                 uint32_t id;
549                 if (parse_id(desc, &id)) {
550                         free(desc_copy);
551                         if (find_by_id(id, dst) && node_matches(dst, ref, sel)) {
552                                 return SELECTOR_OK;
553                         } else {
554                                 return SELECTOR_INVALID;
555                         }
556                 } else {
557                         free(desc_copy);
558                         return SELECTOR_BAD_DESCRIPTOR;
559                 }
560         }
561
562         free(desc_copy);
563
564         if (dst->node == NULL) {
565                 return SELECTOR_INVALID;
566         }
567
568         return SELECTOR_OK;
569 }
570
571 int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
572 {
573         coordinates_t ref_copy = *ref;
574         ref = &ref_copy;
575         char *desc_copy = copy_string(desc, strlen(desc));
576         desc = desc_copy;
577
578         char *hash = strrchr(desc, '#');
579
580         if (hash != NULL) {
581                 *hash = '\0';
582                 int ret;
583                 coordinates_t tmp = {mon, mon->desk, NULL};
584                 if ((ret = desktop_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
585                         desc = hash + 1;
586                 } else {
587                         free(desc_copy);
588                         return ret;
589                 }
590         }
591
592         desktop_select_t sel = make_desktop_select();
593         char *colon = strrchr(desc, ':');
594
595         if (!parse_desktop_modifiers(colon != NULL ? colon : desc, &sel)) {
596                 free(desc_copy);
597                 return SELECTOR_BAD_MODIFIERS;
598         }
599
600         dst->desktop = NULL;
601
602         cycle_dir_t cyc;
603         history_dir_t hdi;
604         uint16_t idx;
605         uint32_t id;
606         if (parse_cycle_direction(desc, &cyc)) {
607                 find_closest_desktop(ref, dst, cyc, sel);
608         } else if (parse_history_direction(desc, &hdi)) {
609                 history_find_desktop(hdi, ref, dst, sel);
610         } else if (streq("last", desc)) {
611                 history_find_desktop(HISTORY_OLDER, ref, dst, sel);
612         } else if (streq("focused", desc)) {
613                 coordinates_t loc = {mon, mon->desk, NULL};
614                 if (desktop_matches(&loc, ref, sel)) {
615                         *dst = loc;
616                 }
617         } else if (colon != NULL) {
618                 *colon = '\0';
619                 int ret;
620                 if ((ret = monitor_from_desc(desc, ref, dst)) == SELECTOR_OK) {
621                         if (streq("focused", colon + 1)) {
622                                 coordinates_t loc = {dst->monitor, dst->monitor->desk, NULL};
623                                 if (desktop_matches(&loc, ref, sel)) {
624                                         *dst = loc;
625                                 }
626                         } else if (parse_index(colon + 1, &idx)) {
627                                 free(desc_copy);
628                                 if (desktop_from_index(idx, dst, dst->monitor) && desktop_matches(dst, ref, sel)) {
629                                         return SELECTOR_OK;
630                                 } else {
631                                         return SELECTOR_INVALID;
632                                 }
633                         } else {
634                                 free(desc_copy);
635                                 return SELECTOR_BAD_DESCRIPTOR;
636                         }
637                 } else {
638                         free(desc_copy);
639                         return ret;
640                 }
641         } else if (parse_index(desc, &idx) && desktop_from_index(idx, dst, NULL)) {
642                 free(desc_copy);
643                 if (desktop_matches(dst, ref, sel)) {
644                         return SELECTOR_OK;
645                 } else {
646                         return SELECTOR_INVALID;
647                 }
648         } else if (parse_id(desc, &id) && desktop_from_id(id, dst, NULL)) {
649                 free(desc_copy);
650                 if (desktop_matches(dst, ref, sel)) {
651                         return SELECTOR_OK;
652                 } else {
653                         return SELECTOR_INVALID;
654                 }
655         } else {
656                 int hits = 0;
657                 if (desktop_from_name(desc, ref, dst, sel, &hits)) {
658                         free(desc_copy);
659                         return SELECTOR_OK;
660                 } else {
661                         free(desc_copy);
662                         if (hits > 0) {
663                                 return SELECTOR_INVALID;
664                         } else {
665                                 return SELECTOR_BAD_DESCRIPTOR;
666                         }
667                 }
668         }
669
670         free(desc_copy);
671
672         if (dst->desktop == NULL) {
673                 return SELECTOR_INVALID;
674         }
675
676         return SELECTOR_OK;
677 }
678
679 int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
680 {
681         coordinates_t ref_copy = *ref;
682         ref = &ref_copy;
683         char *desc_copy = copy_string(desc, strlen(desc));
684         desc = desc_copy;
685
686         char *hash = strrchr(desc, '#');
687
688         if (hash != NULL) {
689                 *hash = '\0';
690                 int ret;
691                 coordinates_t tmp = {mon, NULL, NULL};
692                 if ((ret = monitor_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
693                         desc = hash + 1;
694                 } else {
695                         free(desc_copy);
696                         return ret;
697                 }
698         }
699
700         monitor_select_t sel = make_monitor_select();
701
702         if (!parse_monitor_modifiers(desc, &sel)) {
703                 free(desc_copy);
704                 return SELECTOR_BAD_MODIFIERS;
705         }
706
707         dst->monitor = NULL;
708
709         direction_t dir;
710         cycle_dir_t cyc;
711         history_dir_t hdi;
712         uint16_t idx;
713         uint32_t id;
714         if (parse_direction(desc, &dir)) {
715                 dst->monitor = nearest_monitor(ref->monitor, dir, sel);
716         } else if (parse_cycle_direction(desc, &cyc)) {
717                 dst->monitor = closest_monitor(ref->monitor, cyc, sel);
718         } else if (parse_history_direction(desc, &hdi)) {
719                 history_find_monitor(hdi, ref, dst, sel);
720         } else if (streq("last", desc)) {
721                 history_find_monitor(HISTORY_OLDER, ref, dst, sel);
722         } else if (streq("primary", desc)) {
723                 if (pri_mon != NULL) {
724                         coordinates_t loc = {pri_mon, NULL, NULL};
725                         if (monitor_matches(&loc, ref, sel)) {
726                                 dst->monitor = pri_mon;
727                         }
728                 }
729         } else if (streq("focused", desc)) {
730                 coordinates_t loc = {mon, NULL, NULL};
731                 if (monitor_matches(&loc, ref, sel)) {
732                         dst->monitor = mon;
733                 }
734         } else if (parse_index(desc, &idx) && monitor_from_index(idx, dst)) {
735                 free(desc_copy);
736                 if (monitor_matches(dst, ref, sel)) {
737                         return SELECTOR_OK;
738                 } else {
739                         return SELECTOR_INVALID;
740                 }
741         } else if (parse_id(desc, &id) && monitor_from_id(id, dst)) {
742                 free(desc_copy);
743                 if (monitor_matches(dst, ref, sel)) {
744                         return SELECTOR_OK;
745                 } else {
746                         return SELECTOR_INVALID;
747                 }
748         } else {
749                 if (locate_monitor(desc, dst)) {
750                         free(desc_copy);
751                         if (monitor_matches(dst, ref, sel)) {
752                                 return SELECTOR_OK;
753                         } else {
754                                 return SELECTOR_INVALID;
755                         }
756                 } else {
757                         free(desc_copy);
758                         return SELECTOR_BAD_DESCRIPTOR;
759                 }
760         }
761
762         free(desc_copy);
763
764         if (dst->monitor == NULL) {
765                 return SELECTOR_INVALID;
766         }
767
768         return SELECTOR_OK;
769 }
770
771 bool locate_window(xcb_window_t win, coordinates_t *loc)
772 {
773         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
774                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
775                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
776                                 if (n->client == NULL) {
777                                         continue;
778                                 }
779                                 if (n->id == win) {
780                                         loc->monitor = m;
781                                         loc->desktop = d;
782                                         loc->node = n;
783                                         return true;
784                                 }
785                         }
786                 }
787         }
788         return false;
789 }
790
791 bool locate_desktop(char *name, coordinates_t *loc)
792 {
793         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
794                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
795                         if (streq(d->name, name)) {
796                                 loc->monitor = m;
797                                 loc->desktop = d;
798                                 return true;
799                         }
800                 }
801         }
802         return false;
803 }
804
805 bool locate_monitor(char *name, coordinates_t *loc)
806 {
807         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
808                 if (streq(m->name, name)) {
809                         loc->monitor = m;
810                         return true;
811                 }
812         }
813         return false;
814 }
815
816 bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm)
817 {
818         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
819                 if (mm != NULL && m != mm) {
820                         continue;
821                 }
822                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
823                         if (d->id == id) {
824                                 loc->monitor = m;
825                                 loc->desktop = d;
826                                 loc->node = NULL;
827                                 return true;
828                         }
829                 }
830         }
831         return false;
832 }
833
834 bool desktop_from_name(char *name, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel, int *hits)
835 {
836         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
837                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
838                         if (streq(d->name, name)) {
839                                 if (hits != NULL) {
840                                         (*hits)++;
841                                 }
842                                 coordinates_t loc = {m, d, NULL};
843                                 if (desktop_matches(&loc, ref, sel)) {
844                                         dst->monitor = m;
845                                         dst->desktop = d;
846                                         return true;
847                                 }
848                         }
849                 }
850         }
851         return false;
852 }
853
854 bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm)
855 {
856         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
857                 if (mm != NULL && m != mm) {
858                         continue;
859                 }
860                 for (desktop_t *d = m->desk_head; d != NULL; d = d->next, idx--) {
861                         if (idx == 1) {
862                                 loc->monitor = m;
863                                 loc->desktop = d;
864                                 loc->node = NULL;
865                                 return true;
866                         }
867                 }
868         }
869         return false;
870 }
871
872 bool monitor_from_id(uint32_t id, coordinates_t *loc)
873 {
874         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
875                 if (m->id == id) {
876                         loc->monitor = m;
877                         loc->desktop = NULL;
878                         loc->node = NULL;
879                         return true;
880                 }
881         }
882         return false;
883 }
884
885 bool monitor_from_index(int idx, coordinates_t *loc)
886 {
887         for (monitor_t *m = mon_head; m != NULL; m = m->next, idx--) {
888                 if (idx == 1) {
889                         loc->monitor = m;
890                         loc->desktop = NULL;
891                         loc->node = NULL;
892                         return true;
893                 }
894         }
895         return false;
896 }
897
898 bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
899 {
900         if (loc->node == NULL) {
901                 return false;
902         }
903
904         if (sel.focused != OPTION_NONE &&
905             loc->node != loc->desktop->focus
906             ? sel.focused == OPTION_TRUE
907             : sel.focused == OPTION_FALSE) {
908                 return false;
909         }
910
911         if (sel.automatic != OPTION_NONE &&
912             loc->node->presel != NULL
913             ? sel.automatic == OPTION_TRUE
914             : sel.automatic == OPTION_FALSE) {
915                 return false;
916         }
917
918         if (sel.local != OPTION_NONE &&
919             loc->desktop != ref->desktop
920             ? sel.local == OPTION_TRUE
921             : sel.local == OPTION_FALSE) {
922                 return false;
923         }
924
925         if (sel.active != OPTION_NONE &&
926             loc->desktop != loc->monitor->desk
927             ? sel.active == OPTION_TRUE
928             : sel.active == OPTION_FALSE) {
929                 return false;
930         }
931
932         if (sel.leaf != OPTION_NONE &&
933             !is_leaf(loc->node)
934             ? sel.leaf == OPTION_TRUE
935             : sel.leaf == OPTION_FALSE) {
936                 return false;
937         }
938
939         if (sel.window != OPTION_NONE &&
940             loc->node->client == NULL
941             ? sel.window == OPTION_TRUE
942             : sel.window == OPTION_FALSE) {
943                 return false;
944         }
945
946 #define NFLAG(p) \
947         if (sel.p != OPTION_NONE && \
948             !loc->node->p \
949             ? sel.p == OPTION_TRUE \
950             : sel.p == OPTION_FALSE) { \
951                 return false; \
952         }
953         NFLAG(hidden)
954         NFLAG(sticky)
955         NFLAG(private)
956         NFLAG(locked)
957 #undef NFLAG
958
959         if (loc->node->client == NULL &&
960                 (sel.same_class != OPTION_NONE ||
961                  sel.tiled != OPTION_NONE ||
962                  sel.pseudo_tiled != OPTION_NONE ||
963                  sel.floating != OPTION_NONE ||
964                  sel.fullscreen != OPTION_NONE ||
965                  sel.below != OPTION_NONE ||
966                  sel.normal != OPTION_NONE ||
967                  sel.above != OPTION_NONE ||
968                  sel.urgent != OPTION_NONE)) {
969                 return false;
970         }
971
972         if (ref->node != NULL && ref->node->client != NULL &&
973             sel.same_class != OPTION_NONE &&
974             streq(loc->node->client->class_name, ref->node->client->class_name)
975             ? sel.same_class == OPTION_FALSE
976             : sel.same_class == OPTION_TRUE) {
977                 return false;
978         }
979
980         if (sel.descendant_of != OPTION_NONE &&
981             !is_descendant(loc->node, ref->node)
982             ? sel.descendant_of == OPTION_TRUE
983             : sel.descendant_of == OPTION_FALSE) {
984                 return false;
985         }
986
987         if (sel.ancestor_of != OPTION_NONE &&
988             !is_descendant(ref->node, loc->node)
989             ? sel.ancestor_of == OPTION_TRUE
990             : sel.ancestor_of == OPTION_FALSE) {
991                 return false;
992         }
993
994 #define WSTATE(p, e) \
995         if (sel.p != OPTION_NONE && \
996             loc->node->client->state != e \
997             ? sel.p == OPTION_TRUE \
998             : sel.p == OPTION_FALSE) { \
999                 return false; \
1000         }
1001         WSTATE(tiled, STATE_TILED)
1002         WSTATE(pseudo_tiled, STATE_PSEUDO_TILED)
1003         WSTATE(floating, STATE_FLOATING)
1004         WSTATE(fullscreen, STATE_FULLSCREEN)
1005 #undef WSTATE
1006
1007 #define WLAYER(p, e) \
1008         if (sel.p != OPTION_NONE && \
1009             loc->node->client->layer != e \
1010             ? sel.p == OPTION_TRUE \
1011             : sel.p == OPTION_FALSE) { \
1012                 return false; \
1013         }
1014         WLAYER(below, LAYER_BELOW)
1015         WLAYER(normal, LAYER_NORMAL)
1016         WLAYER(above, LAYER_ABOVE)
1017 #undef WLAYER
1018
1019 #define WFLAG(p) \
1020         if (sel.p != OPTION_NONE && \
1021             !loc->node->client->p \
1022             ? sel.p == OPTION_TRUE \
1023             : sel.p == OPTION_FALSE) { \
1024                 return false; \
1025         }
1026         WFLAG(urgent)
1027 #undef WFLAG
1028
1029         return true;
1030 }
1031
1032 bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel)
1033 {
1034         if (sel.occupied != OPTION_NONE &&
1035             loc->desktop->root == NULL
1036             ? sel.occupied == OPTION_TRUE
1037             : sel.occupied == OPTION_FALSE) {
1038                 return false;
1039         }
1040
1041         if (sel.focused != OPTION_NONE &&
1042             loc->desktop != loc->monitor->desk
1043             ? sel.focused == OPTION_TRUE
1044             : sel.focused == OPTION_FALSE) {
1045                 return false;
1046         }
1047
1048         if (sel.urgent != OPTION_NONE &&
1049             !is_urgent(loc->desktop)
1050             ? sel.urgent == OPTION_TRUE
1051             : sel.urgent == OPTION_FALSE) {
1052                 return false;
1053         }
1054
1055         if (sel.local != OPTION_NONE &&
1056             ref->monitor != loc->monitor
1057             ? sel.local == OPTION_TRUE
1058             : sel.local == OPTION_FALSE) {
1059                 return false;
1060         }
1061
1062         return true;
1063 }
1064
1065 bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel)
1066 {
1067         if (sel.occupied != OPTION_NONE &&
1068             loc->monitor->desk->root == NULL
1069             ? sel.occupied == OPTION_TRUE
1070             : sel.occupied == OPTION_FALSE) {
1071                 return false;
1072         }
1073
1074         if (sel.focused != OPTION_NONE &&
1075             mon != loc->monitor
1076             ? sel.focused == OPTION_TRUE
1077             : sel.focused == OPTION_FALSE) {
1078                 return false;
1079         }
1080
1081         return true;
1082 }