]> git.lizzy.rs Git - bspwm.git/commitdiff
Move source files to src/
authorBastien Dejean <nihilhill@gmail.com>
Fri, 13 Jan 2017 09:42:00 +0000 (10:42 +0100)
committerBastien Dejean <nihilhill@gmail.com>
Fri, 13 Jan 2017 09:42:00 +0000 (10:42 +0100)
88 files changed:
Makefile
Sourcedeps
bspc.c [deleted file]
bspwm.c [deleted file]
bspwm.h [deleted file]
common.h [deleted file]
desktop.c [deleted file]
desktop.h [deleted file]
events.c [deleted file]
events.h [deleted file]
ewmh.c [deleted file]
ewmh.h [deleted file]
geometry.c [deleted file]
geometry.h [deleted file]
helpers.c [deleted file]
helpers.h [deleted file]
history.c [deleted file]
history.h [deleted file]
jsmn.c [deleted file]
jsmn.h [deleted file]
messages.c [deleted file]
messages.h [deleted file]
monitor.c [deleted file]
monitor.h [deleted file]
parse.c [deleted file]
parse.h [deleted file]
pointer.c [deleted file]
pointer.h [deleted file]
query.c [deleted file]
query.h [deleted file]
restore.c [deleted file]
restore.h [deleted file]
rule.c [deleted file]
rule.h [deleted file]
settings.c [deleted file]
settings.h [deleted file]
src/bspc.c [new file with mode: 0644]
src/bspwm.c [new file with mode: 0644]
src/bspwm.h [new file with mode: 0644]
src/common.h [new file with mode: 0644]
src/desktop.c [new file with mode: 0644]
src/desktop.h [new file with mode: 0644]
src/events.c [new file with mode: 0644]
src/events.h [new file with mode: 0644]
src/ewmh.c [new file with mode: 0644]
src/ewmh.h [new file with mode: 0644]
src/geometry.c [new file with mode: 0644]
src/geometry.h [new file with mode: 0644]
src/helpers.c [new file with mode: 0644]
src/helpers.h [new file with mode: 0644]
src/history.c [new file with mode: 0644]
src/history.h [new file with mode: 0644]
src/jsmn.c [new file with mode: 0644]
src/jsmn.h [new file with mode: 0644]
src/messages.c [new file with mode: 0644]
src/messages.h [new file with mode: 0644]
src/monitor.c [new file with mode: 0644]
src/monitor.h [new file with mode: 0644]
src/parse.c [new file with mode: 0644]
src/parse.h [new file with mode: 0644]
src/pointer.c [new file with mode: 0644]
src/pointer.h [new file with mode: 0644]
src/query.c [new file with mode: 0644]
src/query.h [new file with mode: 0644]
src/restore.c [new file with mode: 0644]
src/restore.h [new file with mode: 0644]
src/rule.c [new file with mode: 0644]
src/rule.h [new file with mode: 0644]
src/settings.c [new file with mode: 0644]
src/settings.h [new file with mode: 0644]
src/stack.c [new file with mode: 0644]
src/stack.h [new file with mode: 0644]
src/subscribe.c [new file with mode: 0644]
src/subscribe.h [new file with mode: 0644]
src/tree.c [new file with mode: 0644]
src/tree.h [new file with mode: 0644]
src/types.h [new file with mode: 0644]
src/window.c [new file with mode: 0644]
src/window.h [new file with mode: 0644]
stack.c [deleted file]
stack.h [deleted file]
subscribe.c [deleted file]
subscribe.h [deleted file]
tree.c [deleted file]
tree.h [deleted file]
types.h [deleted file]
window.c [deleted file]
window.h [deleted file]

index dee6348d4192fa4f954d519005178281942345ff..fa7a27f8680f537f2cfa451f4ea409ce7b08c022 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,8 @@ all: bspwm bspc
 debug: CFLAGS += -O0 -g
 debug: bspwm bspc
 
+VPATH=src
+
 include Sourcedeps
 
 $(WM_OBJ) $(CLI_OBJ): Makefile
index 286d5b7292471bcf2fad2e8841c4f463860ccc8c..ddc103047ecf9b0d787d83cb6db270d3e2150664 100644 (file)
@@ -1,20 +1,21 @@
 bspc.o: bspc.c common.h helpers.h
-bspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h rule.h settings.h subscribe.h types.h window.h
+bspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h pointer.h rule.h settings.h subscribe.h types.h window.h
 desktop.o: desktop.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
-events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
+events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h pointer.h query.h settings.h subscribe.h tree.h types.h window.h
 ewmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h
+geometry.o: geometry.c geometry.h helpers.h types.h
 helpers.o: helpers.c bspwm.h helpers.h types.h
 history.o: history.c bspwm.h helpers.h query.h tree.h types.h
 jsmn.o: jsmn.c jsmn.h
 messages.o: messages.c bspwm.h common.h desktop.h helpers.h jsmn.h messages.h monitor.h parse.h pointer.h query.h restore.h rule.h settings.h subscribe.h tree.h types.h window.h
-monitor.o: monitor.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
+monitor.o: monitor.c bspwm.h desktop.h ewmh.h geometry.h helpers.h monitor.h pointer.h query.h settings.h subscribe.h tree.h types.h window.h
 parse.o: parse.c helpers.h parse.h subscribe.h types.h
-pointer.o: pointer.c bspwm.h helpers.h monitor.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
-query.o: query.c bspwm.h desktop.h helpers.h history.h monitor.h parse.h query.h subscribe.h tree.h types.h
-restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h query.h restore.h settings.h stack.h subscribe.h tree.h types.h
+pointer.o: pointer.c bspwm.h events.h helpers.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
+query.o: query.c bspwm.h desktop.h helpers.h history.h monitor.h parse.h query.h subscribe.h tree.h types.h window.h
+restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h pointer.h query.h restore.h settings.h stack.h subscribe.h tree.h types.h window.h
 rule.o: rule.c bspwm.h ewmh.h helpers.h parse.h rule.h settings.h subscribe.h types.h window.h
 settings.o: settings.c bspwm.h helpers.h settings.h types.h
 stack.o: stack.c bspwm.h ewmh.h helpers.h stack.h subscribe.h tree.h types.h window.h
-subscribe.o: subscribe.c bspwm.h helpers.h settings.h subscribe.h tree.h types.h
-tree.o: tree.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
-window.o: window.c bspwm.h ewmh.h helpers.h monitor.h parse.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
+subscribe.o: subscribe.c bspwm.h desktop.h helpers.h settings.h subscribe.h types.h
+tree.o: tree.c bspwm.h desktop.h ewmh.h geometry.h helpers.h history.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
+window.o: window.c bspwm.h ewmh.h geometry.h helpers.h monitor.h parse.h pointer.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
diff --git a/bspc.c b/bspc.c
deleted file mode 100644 (file)
index fe23c64..0000000
--- a/bspc.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include "helpers.h"
-#include "common.h"
-
-int main(int argc, char *argv[])
-{
-       int fd;
-       struct sockaddr_un sock_address;
-       char msg[BUFSIZ], rsp[BUFSIZ];
-
-       if (argc < 2) {
-               err("No arguments given.\n");
-       }
-
-       sock_address.sun_family = AF_UNIX;
-       char *sp;
-
-       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
-               err("Failed to create the socket.\n");
-       }
-
-       sp = getenv(SOCKET_ENV_VAR);
-       if (sp != NULL) {
-               snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
-       } else {
-               char *host = NULL;
-               int dn = 0, sn = 0;
-               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
-                       snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);
-               }
-               free(host);
-       }
-
-       if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
-               err("Failed to connect to the socket.\n");
-       }
-
-       argc--, argv++;
-       int msg_len = 0;
-
-       for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) {
-               n = snprintf(msg + offset, rem, "%s%c", *argv, 0);
-               msg_len += n;
-       }
-
-       if (send(fd, msg, msg_len, 0) == -1) {
-               err("Failed to send the data.\n");
-       }
-
-       int ret = EXIT_SUCCESS, nb;
-       while ((nb = recv(fd, rsp, sizeof(rsp)-1, 0)) > 0) {
-               rsp[nb] = '\0';
-               if (rsp[0] == FAILURE_MESSAGE[0]) {
-                       ret = EXIT_FAILURE;
-                       printf("%s", rsp + 1);
-               } else {
-                       printf("%s", rsp);
-               }
-               fflush(stdout);
-       }
-
-       close(fd);
-       return ret;
-}
diff --git a/bspwm.c b/bspwm.c
deleted file mode 100644 (file)
index 6a84237..0000000
--- a/bspwm.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <signal.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <xcb/xinerama.h>
-#include "types.h"
-#include "desktop.h"
-#include "monitor.h"
-#include "settings.h"
-#include "messages.h"
-#include "pointer.h"
-#include "events.h"
-#include "common.h"
-#include "window.h"
-#include "history.h"
-#include "ewmh.h"
-#include "rule.h"
-#include "bspwm.h"
-
-int main(int argc, char *argv[])
-{
-       fd_set descriptors;
-       char socket_path[MAXLEN];
-       config_path[0] = '\0';
-       int sock_fd, cli_fd, dpy_fd, max_fd, n;
-       struct sockaddr_un sock_address;
-       char msg[BUFSIZ] = {0};
-       xcb_generic_event_t *event;
-       int opt;
-
-       while ((opt = getopt(argc, argv, "hvc:")) != -1) {
-               switch (opt) {
-                       case 'h':
-                               printf(WM_NAME " [-h|-v|-c CONFIG_PATH]\n");
-                               exit(EXIT_SUCCESS);
-                               break;
-                       case 'v':
-                               printf("%s\n", VERSION);
-                               exit(EXIT_SUCCESS);
-                               break;
-                       case 'c':
-                               snprintf(config_path, sizeof(config_path), "%s", optarg);
-                               break;
-               }
-       }
-
-       if (config_path[0] == '\0') {
-               char *config_home = getenv(CONFIG_HOME_ENV);
-               if (config_home != NULL) {
-                       snprintf(config_path, sizeof(config_path), "%s/%s/%s", config_home, WM_NAME, CONFIG_NAME);
-               } else {
-                       snprintf(config_path, sizeof(config_path), "%s/%s/%s/%s", getenv("HOME"), ".config", WM_NAME, CONFIG_NAME);
-               }
-       }
-
-       dpy = xcb_connect(NULL, &default_screen);
-
-       if (!check_connection(dpy)) {
-               exit(EXIT_FAILURE);
-       }
-
-       load_settings();
-       setup();
-
-       dpy_fd = xcb_get_file_descriptor(dpy);
-
-       char *sp = getenv(SOCKET_ENV_VAR);
-       if (sp != NULL) {
-               snprintf(socket_path, sizeof(socket_path), "%s", sp);
-       } else {
-               char *host = NULL;
-               int dn = 0, sn = 0;
-               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
-                       snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, host, dn, sn);
-               }
-               free(host);
-       }
-
-       sock_address.sun_family = AF_UNIX;
-       snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path);
-
-       sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-
-       if (sock_fd == -1) {
-               err("Couldn't create the socket.\n");
-       }
-
-       unlink(socket_path);
-       if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
-               err("Couldn't bind a name to the socket.\n");
-       }
-
-       if (listen(sock_fd, SOMAXCONN) == -1) {
-               err("Couldn't listen to the socket.\n");
-       }
-
-       signal(SIGINT, sig_handler);
-       signal(SIGHUP, sig_handler);
-       signal(SIGTERM, sig_handler);
-       signal(SIGCHLD, sig_handler);
-       signal(SIGPIPE, SIG_IGN);
-       run_config();
-       running = true;
-
-       while (running) {
-
-               xcb_flush(dpy);
-
-               FD_ZERO(&descriptors);
-               FD_SET(sock_fd, &descriptors);
-               FD_SET(dpy_fd, &descriptors);
-               max_fd = MAX(sock_fd, dpy_fd);
-
-               for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
-                       FD_SET(pr->fd, &descriptors);
-                       if (pr->fd > max_fd) {
-                               max_fd = pr->fd;
-                       }
-               }
-
-               if (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {
-
-                       pending_rule_t *pr = pending_rule_head;
-                       while (pr != NULL) {
-                               pending_rule_t *next = pr->next;
-                               if (FD_ISSET(pr->fd, &descriptors)) {
-                                       manage_window(pr->win, pr->csq, pr->fd);
-                                       remove_pending_rule(pr);
-                               }
-                               pr = next;
-                       }
-
-                       if (FD_ISSET(sock_fd, &descriptors)) {
-                               cli_fd = accept(sock_fd, NULL, 0);
-                               if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg), 0)) > 0) {
-                                       msg[n] = '\0';
-                                       FILE *rsp = fdopen(cli_fd, "w");
-                                       if (rsp != NULL) {
-                                               handle_message(msg, n, rsp);
-                                       } else {
-                                               warn("Can't open the client socket as file.\n");
-                                               close(cli_fd);
-                                       }
-                               }
-                       }
-
-                       if (FD_ISSET(dpy_fd, &descriptors)) {
-                               while ((event = xcb_poll_for_event(dpy)) != NULL) {
-                                       handle_event(event);
-                                       free(event);
-                               }
-                       }
-
-               }
-
-               if (!check_connection(dpy)) {
-                       running = false;
-               }
-       }
-
-       cleanup();
-       close(sock_fd);
-       unlink(socket_path);
-       ungrab_buttons();
-       xcb_ewmh_connection_wipe(ewmh);
-       xcb_destroy_window(dpy, meta_window);
-       free(ewmh);
-       xcb_flush(dpy);
-       xcb_disconnect(dpy);
-       return exit_status;
-}
-
-void init(void)
-{
-       clients_count = 0;
-       mon = mon_head = mon_tail = pri_mon = NULL;
-       history_head = history_tail = history_needle = NULL;
-       rule_head = rule_tail = NULL;
-       stack_head = stack_tail = NULL;
-       subscribe_head = subscribe_tail = NULL;
-       pending_rule_head = pending_rule_tail = NULL;
-       auto_raise = sticky_still = record_history = true;
-       randr_base = 0;
-       exit_status = 0;
-}
-
-void setup(void)
-{
-       init();
-       ewmh_init();
-       pointer_init();
-
-       screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
-
-       if (screen == NULL) {
-               err("Can't acquire the default screen.\n");
-       }
-
-       root = screen->root;
-       register_events();
-
-       screen_width = screen->width_in_pixels;
-       screen_height = screen->height_in_pixels;
-
-       meta_window = xcb_generate_id(dpy);
-       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, meta_window, root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_NONE, NULL);
-       xcb_icccm_set_wm_class(dpy, meta_window, sizeof(META_WINDOW_IC), META_WINDOW_IC);
-
-       xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
-                                 ewmh->_NET_SUPPORTING_WM_CHECK,
-                                 ewmh->_NET_DESKTOP_NAMES,
-                                 ewmh->_NET_DESKTOP_VIEWPORT,
-                                 ewmh->_NET_NUMBER_OF_DESKTOPS,
-                                 ewmh->_NET_CURRENT_DESKTOP,
-                                 ewmh->_NET_CLIENT_LIST,
-                                 ewmh->_NET_ACTIVE_WINDOW,
-                                 ewmh->_NET_CLOSE_WINDOW,
-                                 ewmh->_NET_WM_STRUT_PARTIAL,
-                                 ewmh->_NET_WM_DESKTOP,
-                                 ewmh->_NET_WM_STATE,
-                                 ewmh->_NET_WM_STATE_HIDDEN,
-                                 ewmh->_NET_WM_STATE_FULLSCREEN,
-                                 ewmh->_NET_WM_STATE_BELOW,
-                                 ewmh->_NET_WM_STATE_ABOVE,
-                                 ewmh->_NET_WM_STATE_STICKY,
-                                 ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
-                                 ewmh->_NET_WM_WINDOW_TYPE,
-                                 ewmh->_NET_WM_WINDOW_TYPE_DOCK,
-                                 ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,
-                                 ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,
-                                 ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
-                                 ewmh->_NET_WM_WINDOW_TYPE_UTILITY,
-                                 ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};
-
-       xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
-       ewmh_set_supporting(meta_window);
-
-#define GETATOM(a) \
-       get_atom(#a, &a);
-       GETATOM(WM_STATE)
-       GETATOM(WM_DELETE_WINDOW)
-       GETATOM(WM_TAKE_FOCUS)
-#undef GETATOM
-
-       const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
-       if (qep->present && update_monitors()) {
-               randr = true;
-               randr_base = qep->first_event;
-               xcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
-       } else {
-               randr = false;
-               warn("Couldn't retrieve monitors via RandR.\n");
-               bool xinerama_is_active = false;
-
-               if (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {
-                       xcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);
-                       if (xia != NULL) {
-                               xinerama_is_active = xia->state;
-                               free(xia);
-                       }
-               }
-
-               if (xinerama_is_active) {
-                       xcb_xinerama_query_screens_reply_t *xsq = xcb_xinerama_query_screens_reply(dpy, xcb_xinerama_query_screens(dpy), NULL);
-                       xcb_xinerama_screen_info_t *xsi = xcb_xinerama_query_screens_screen_info(xsq);
-                       int n = xcb_xinerama_query_screens_screen_info_length(xsq);
-                       for (int i = 0; i < n; i++) {
-                               xcb_xinerama_screen_info_t info = xsi[i];
-                               xcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};
-                               monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
-                               add_monitor(m);
-                               add_desktop(m, make_desktop(NULL, XCB_NONE));
-                       }
-                       free(xsq);
-               } else {
-                       warn("Xinerama is inactive.\n");
-                       xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-                       monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
-                       add_monitor(m);
-                       add_desktop(m, make_desktop(NULL, XCB_NONE));
-               }
-       }
-
-       ewmh_update_number_of_desktops();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-       ewmh_update_current_desktop();
-       xcb_get_input_focus_reply_t *ifo = xcb_get_input_focus_reply(dpy, xcb_get_input_focus(dpy), NULL);
-       if (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE)) {
-               clear_input_focus();
-       }
-       free(ifo);
-}
-
-void register_events(void)
-{
-       uint32_t values[] = {ROOT_EVENT_MASK};
-       xcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));
-       if (e != NULL) {
-               xcb_disconnect(dpy);
-               err("Another window manager is already running.\n");
-       }
-}
-
-void cleanup(void)
-{
-       mon = NULL;
-
-       while (mon_head != NULL) {
-               remove_monitor(mon_head);
-       }
-       while (rule_head != NULL) {
-               remove_rule(rule_head);
-       }
-       while (subscribe_head != NULL) {
-               remove_subscriber(subscribe_head);
-       }
-       while (pending_rule_head != NULL) {
-               remove_pending_rule(pending_rule_head);
-       }
-
-       empty_history();
-}
-
-bool check_connection (xcb_connection_t *dpy)
-{
-       int xerr;
-       if ((xerr = xcb_connection_has_error(dpy)) != 0) {
-               warn("The server closed the connection: ");
-               switch (xerr) {
-                       case XCB_CONN_ERROR:
-                               warn("socket, pipe or stream error.\n");
-                               break;
-                       case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
-                               warn("unsupported extension.\n");
-                               break;
-                       case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
-                               warn("not enough memory.\n");
-                               break;
-                       case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
-                               warn("request length exceeded.\n");
-                               break;
-                       case XCB_CONN_CLOSED_PARSE_ERR:
-                               warn("can't parse display string.\n");
-                               break;
-                       case XCB_CONN_CLOSED_INVALID_SCREEN:
-                               warn("invalid screen.\n");
-                               break;
-                       case XCB_CONN_CLOSED_FDPASSING_FAILED:
-                               warn("failed to pass FD.\n");
-                               break;
-                       default:
-                               warn("unknown error.\n");
-                               break;
-               }
-               return false;
-       } else {
-               return true;
-       }
-}
-
-void sig_handler(int sig)
-{
-       if (sig == SIGCHLD) {
-               signal(sig, sig_handler);
-               while (waitpid(-1, 0, WNOHANG) > 0)
-                       ;
-       } else if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {
-               running = false;
-       }
-}
diff --git a/bspwm.h b/bspwm.h
deleted file mode 100644 (file)
index aa5ca9e..0000000
--- a/bspwm.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_BSPWM_H
-#define BSPWM_BSPWM_H
-
-#include "types.h"
-
-#define ROOT_EVENT_MASK     (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS)
-#define CLIENT_EVENT_MASK   (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
-#define BSPWM_CLASS_NAME    "Bspwm"
-#define META_WINDOW_IC      "wm\0" BSPWM_CLASS_NAME
-#define ROOT_WINDOW_IC      "root\0" BSPWM_CLASS_NAME
-#define PRESEL_FEEDBACK_I   "presel_feedback"
-#define PRESEL_FEEDBACK_IC  PRESEL_FEEDBACK_I "\0" BSPWM_CLASS_NAME
-
-xcb_connection_t *dpy;
-int default_screen, screen_width, screen_height;
-uint32_t clients_count;
-xcb_screen_t *screen;
-xcb_window_t root;
-char config_path[MAXLEN];
-
-monitor_t *mon;
-monitor_t *mon_head;
-monitor_t *mon_tail;
-monitor_t *pri_mon;
-history_t *history_head;
-history_t *history_tail;
-history_t *history_needle;
-rule_t *rule_head;
-rule_t *rule_tail;
-stacking_list_t *stack_head;
-stacking_list_t *stack_tail;
-subscriber_list_t *subscribe_head;
-subscriber_list_t *subscribe_tail;
-pending_rule_t *pending_rule_head;
-pending_rule_t *pending_rule_tail;
-
-xcb_window_t meta_window;
-xcb_atom_t WM_STATE;
-xcb_atom_t WM_TAKE_FOCUS;
-xcb_atom_t WM_DELETE_WINDOW;
-int exit_status;
-
-bool auto_raise;
-bool sticky_still;
-bool record_history;
-bool running;
-bool randr;
-
-void init(void);
-void setup(void);
-void register_events(void);
-void cleanup(void);
-bool check_connection (xcb_connection_t *dpy);
-void sig_handler(int sig);
-
-#endif
diff --git a/common.h b/common.h
deleted file mode 100644 (file)
index f44eef8..0000000
--- a/common.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_COMMON_H
-#define BSPWM_COMMON_H
-
-#define SOCKET_PATH_TPL  "/tmp/bspwm%s_%i_%i-socket"
-#define SOCKET_ENV_VAR   "BSPWM_SOCKET"
-
-#define FAILURE_MESSAGE  "\x07"
-
-#endif
diff --git a/desktop.c b/desktop.c
deleted file mode 100644 (file)
index 8f6a5b6..0000000
--- a/desktop.c
+++ /dev/null
@@ -1,476 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "history.h"
-#include "monitor.h"
-#include "query.h"
-#include "tree.h"
-#include "window.h"
-#include "desktop.h"
-#include "subscribe.h"
-#include "settings.h"
-
-void focus_desktop(monitor_t *m, desktop_t *d)
-{
-       bool changed = (m != mon || m->desk != d);
-
-       focus_monitor(m);
-
-       if (m->desk != d) {
-               if (focus_follows_pointer) {
-                       listen_enter_notify(d->root, false);
-               }
-               show_desktop(d);
-               hide_desktop(m->desk);
-               if (focus_follows_pointer) {
-                       listen_enter_notify(d->root, true);
-               }
-               m->desk = d;
-       }
-
-       if (changed) {
-               ewmh_update_current_desktop();
-               put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
-       }
-}
-
-bool activate_desktop(monitor_t *m, desktop_t *d)
-{
-       if (m == mon || d == m->desk) {
-               return false;
-       }
-
-       if (m->sticky_count > 0) {
-               sticky_still = false;
-               transfer_sticky_nodes(m, m->desk, d, m->desk->root);
-               sticky_still = true;
-       }
-
-       show_desktop(d);
-       hide_desktop(m->desk);
-
-       m->desk = d;
-
-       put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate 0x%08X 0x%08X\n", m->id, d->id);
-       put_status(SBSC_MASK_REPORT);
-
-       return true;
-}
-
-bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel)
-{
-       monitor_t *m = ref->monitor;
-       desktop_t *d = ref->desktop;
-       desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
-
-#define HANDLE_BOUNDARIES(f)  \
-       if (f == NULL) { \
-               m = (dir == CYCLE_PREV ? m->prev : m->next); \
-               if (m == NULL) { \
-                       m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
-               } \
-               f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
-       }
-       HANDLE_BOUNDARIES(f)
-
-       while (f != d) {
-               coordinates_t loc = {m, f, NULL};
-               if (desktop_matches(&loc, ref, sel)) {
-                       *dst = loc;
-                       return true;
-               }
-               f = (dir == CYCLE_PREV ? f->prev : f->next);
-               HANDLE_BOUNDARIES(f)
-       }
-#undef HANDLE_BOUNDARIES
-
-       return false;
-}
-
-bool set_layout(monitor_t *m, desktop_t *d, layout_t l)
-{
-       if (d->layout == l) {
-               return false;
-       }
-
-       d->layout = l;
-
-       handle_presel_feedbacks(m, d);
-
-       arrange(m, d);
-
-       put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(l));
-
-       if (d == m->desk) {
-               put_status(SBSC_MASK_REPORT);
-       }
-
-       return true;
-}
-
-void handle_presel_feedbacks(monitor_t *m, desktop_t *d)
-{
-       if (m->desk != d) {
-               return;
-       }
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, false);
-       }
-       if (d->layout == LAYOUT_MONOCLE) {
-               hide_presel_feedbacks(m, d, d->root);
-       } else {
-               show_presel_feedbacks(m, d, d->root);
-       }
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, true);
-       }
-}
-
-bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
-{
-       if (ms == NULL || md == NULL || d == NULL || ms == md) {
-               return false;
-       }
-
-       bool was_active = (d == ms->desk);
-
-       unlink_desktop(ms, d);
-
-       if (md->desk != NULL) {
-               hide_desktop(d);
-       }
-
-       insert_desktop(md, d);
-       history_transfer_desktop(md, d);
-
-       if (was_active) {
-               if (mon == ms) {
-                       focus_node(ms, NULL, NULL);
-               } else {
-                       activate_node(ms, NULL, NULL);
-               }
-       }
-
-       if (ms->sticky_count > 0 && was_active) {
-               sticky_still = false;
-               transfer_sticky_nodes(ms, d, ms->desk, d->root);
-               sticky_still = true;
-       }
-
-       adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
-       arrange(md, d);
-
-       if (md->desk == d) {
-               if (mon == md) {
-                       focus_node(md, d, d->focus);
-               } else {
-                       activate_node(md, d, d->focus);
-               }
-       }
-
-       ewmh_update_wm_desktops();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-       ewmh_update_current_desktop();
-
-       put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
-       put_status(SBSC_MASK_REPORT);
-
-       return true;
-}
-
-desktop_t *make_desktop(const char *name, uint32_t id)
-{
-       desktop_t *d = calloc(1, sizeof(desktop_t));
-       snprintf(d->name, sizeof(d->name), "%s", name == NULL ? DEFAULT_DESK_NAME : name);
-       if (id == XCB_NONE) {
-               d->id = xcb_generate_id(dpy);
-       }
-       d->prev = d->next = NULL;
-       d->root = d->focus = NULL;
-       d->layout = LAYOUT_TILED;
-       d->padding = (padding_t) PADDING;
-       d->window_gap = window_gap;
-       d->border_width = border_width;
-       return d;
-}
-
-void rename_desktop(monitor_t *m, desktop_t *d, const char *name)
-{
-
-       put_status(SBSC_MASK_DESKTOP_RENAME, "desktop_rename 0x%08X 0x%08X %s %s\n", m->id, d->id, d->name, name);
-
-       snprintf(d->name, sizeof(d->name), "%s", name);
-
-       put_status(SBSC_MASK_REPORT);
-       ewmh_update_desktop_names();
-}
-
-void insert_desktop(monitor_t *m, desktop_t *d)
-{
-       if (m->desk == NULL) {
-               m->desk = d;
-               m->desk_head = d;
-               m->desk_tail = d;
-       } else {
-               m->desk_tail->next = d;
-               d->prev = m->desk_tail;
-               m->desk_tail = d;
-       }
-}
-
-void add_desktop(monitor_t *m, desktop_t *d)
-{
-       put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add 0x%08X %s 0x%08X\n", d->id, d->name, m->id);
-
-       d->border_width = m->border_width;
-       d->window_gap = m->window_gap;
-       insert_desktop(m, d);
-       ewmh_update_number_of_desktops();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-       ewmh_update_wm_desktops();
-       put_status(SBSC_MASK_REPORT);
-}
-
-desktop_t *find_desktop_in(uint32_t id, monitor_t *m)
-{
-       if (m == NULL) {
-               return NULL;
-       }
-
-       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-               if (d->id == id) {
-                       return d;
-               }
-       }
-
-       return NULL;
-}
-
-void unlink_desktop(monitor_t *m, desktop_t *d)
-{
-       desktop_t *prev = d->prev;
-       desktop_t *next = d->next;
-
-       if (prev != NULL) {
-               prev->next = next;
-       }
-
-       if (next != NULL) {
-               next->prev = prev;
-       }
-
-       if (m->desk_head == d) {
-               m->desk_head = next;
-       }
-
-       if (m->desk_tail == d) {
-               m->desk_tail = prev;
-       }
-
-       if (m->desk == d) {
-               m->desk = NULL;
-       }
-
-       d->prev = d->next = NULL;
-}
-
-void remove_desktop(monitor_t *m, desktop_t *d)
-{
-       put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);
-
-       remove_node(m, d, d->root);
-       unlink_desktop(m, d);
-       history_remove(d, NULL, false);
-       free(d);
-
-       ewmh_update_current_desktop();
-       ewmh_update_number_of_desktops();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-
-       if (mon != NULL && m->desk == NULL) {
-               if (m == mon) {
-                       focus_node(m, NULL, NULL);
-               } else {
-                       activate_node(m, NULL, NULL);
-               }
-       }
-
-       put_status(SBSC_MASK_REPORT);
-}
-
-void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
-{
-       if (ds == NULL || dd == NULL || ds == dd) {
-               return;
-       }
-       transfer_node(ms, ds, ds->root, md, dd, dd->focus);
-}
-
-bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
-{
-       if (d1 == NULL || d2 == NULL || d1 == d2 ||
-           (m1->desk == d1 && m1->sticky_count > 0) ||
-           (m2->desk == d2 && m2->sticky_count > 0)) {
-               return false;
-       }
-
-       put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, m2->id, d2->id);
-
-       bool d1_focused = (m1->desk == d1);
-       bool d2_focused = (m2->desk == d2);
-
-       if (m1 != m2) {
-               if (m1->desk == d1) {
-                       m1->desk = d2;
-               }
-               if (m1->desk_head == d1) {
-                       m1->desk_head = d2;
-               }
-               if (m1->desk_tail == d1) {
-                       m1->desk_tail = d2;
-               }
-               if (m2->desk == d2) {
-                       m2->desk = d1;
-               }
-               if (m2->desk_head == d2) {
-                       m2->desk_head = d1;
-               }
-               if (m2->desk_tail == d2) {
-                       m2->desk_tail = d1;
-               }
-       } else {
-               if (m1->desk == d1) {
-                       m1->desk = d2;
-               } else if (m1->desk == d2) {
-                       m1->desk = d1;
-               }
-               if (m1->desk_head == d1) {
-                       m1->desk_head = d2;
-               } else if (m1->desk_head == d2) {
-                       m1->desk_head = d1;
-               }
-               if (m1->desk_tail == d1) {
-                       m1->desk_tail = d2;
-               } else if (m1->desk_tail == d2) {
-                       m1->desk_tail = d1;
-               }
-       }
-
-       desktop_t *p1 = d1->prev;
-       desktop_t *n1 = d1->next;
-       desktop_t *p2 = d2->prev;
-       desktop_t *n2 = d2->next;
-
-       if (p1 != NULL && p1 != d2) {
-               p1->next = d2;
-       }
-       if (n1 != NULL && n1 != d2) {
-               n1->prev = d2;
-       }
-       if (p2 != NULL && p2 != d1) {
-               p2->next = d1;
-       }
-       if (n2 != NULL && n2 != d1) {
-               n2->prev = d1;
-       }
-
-       d1->prev = p2 == d1 ? d2 : p2;
-       d1->next = n2 == d1 ? d2 : n2;
-       d2->prev = p1 == d2 ? d1 : p1;
-       d2->next = n1 == d2 ? d1 : n1;
-
-       if (m1 != m2) {
-               adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
-               adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
-               history_swap_desktops(m1, d1, m2, d2);
-               arrange(m1, d2);
-               arrange(m2, d1);
-       }
-
-       if (d1_focused && !d2_focused) {
-               hide_desktop(d1);
-               show_desktop(d2);
-       } else if (!d1_focused && d2_focused) {
-               show_desktop(d1);
-               hide_desktop(d2);
-       }
-
-       if (d1 == mon->desk) {
-               focus_node(m2, d1, d1->focus);
-       } else if (d1 == m2->desk) {
-               activate_node(m2, d1, d1->focus);
-       }
-
-       if (d2 == mon->desk) {
-               focus_node(m1, d2, d2->focus);
-       } else if (d2 == m1->desk) {
-               activate_node(m1, d2, d2->focus);
-       }
-
-       ewmh_update_wm_desktops();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-       ewmh_update_current_desktop();
-
-       put_status(SBSC_MASK_REPORT);
-
-       return true;
-}
-
-void show_desktop(desktop_t *d)
-{
-       if (d == NULL) {
-               return;
-       }
-       show_node(d, d->root);
-}
-
-void hide_desktop(desktop_t *d)
-{
-       if (d == NULL) {
-               return;
-       }
-       hide_node(d, d->root);
-}
-
-bool is_urgent(desktop_t *d)
-{
-       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-               if (n->client == NULL) {
-                       continue;
-               }
-               if (n->client->urgent) {
-                       return true;
-               }
-       }
-       return false;
-}
diff --git a/desktop.h b/desktop.h
deleted file mode 100644 (file)
index 80b33de..0000000
--- a/desktop.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_DESKTOP_H
-#define BSPWM_DESKTOP_H
-
-#define DEFAULT_DESK_NAME    "Desktop"
-
-void focus_desktop(monitor_t *m, desktop_t *d);
-bool activate_desktop(monitor_t *m, desktop_t *d);
-bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel);
-bool set_layout(monitor_t *m, desktop_t *d, layout_t l);
-void handle_presel_feedbacks(monitor_t *m, desktop_t *d);
-bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
-desktop_t *make_desktop(const char *name, uint32_t id);
-void rename_desktop(monitor_t *m, desktop_t *d, const char *name);
-void insert_desktop(monitor_t *m, desktop_t *d);
-void add_desktop(monitor_t *m, desktop_t *d);
-desktop_t *find_desktop_in(uint32_t id, monitor_t *m);
-void unlink_desktop(monitor_t *m, desktop_t *d);
-void remove_desktop(monitor_t *m, desktop_t *d);
-void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);
-bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
-void show_desktop(desktop_t *d);
-void hide_desktop(desktop_t *d);
-bool is_urgent(desktop_t *d);
-
-#endif
diff --git a/events.c b/events.c
deleted file mode 100644 (file)
index 55656cb..0000000
--- a/events.c
+++ /dev/null
@@ -1,485 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdbool.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "monitor.h"
-#include "query.h"
-#include "settings.h"
-#include "subscribe.h"
-#include "tree.h"
-#include "window.h"
-#include "pointer.h"
-#include "events.h"
-
-void handle_event(xcb_generic_event_t *evt)
-{
-       uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
-       switch (resp_type) {
-               case XCB_MAP_REQUEST:
-                       map_request(evt);
-                       break;
-               case XCB_DESTROY_NOTIFY:
-                       destroy_notify(evt);
-                       break;
-               case XCB_UNMAP_NOTIFY:
-                       unmap_notify(evt);
-                       break;
-               case XCB_CLIENT_MESSAGE:
-                       client_message(evt);
-                       break;
-               case XCB_CONFIGURE_REQUEST:
-                       configure_request(evt);
-                       break;
-               case XCB_CONFIGURE_NOTIFY:
-                       configure_notify(evt);
-                       break;
-               case XCB_PROPERTY_NOTIFY:
-                       property_notify(evt);
-                       break;
-               case XCB_ENTER_NOTIFY:
-                       enter_notify(evt);
-                       break;
-               case XCB_BUTTON_PRESS:
-                       button_press(evt);
-                       break;
-               case XCB_FOCUS_IN:
-                       focus_in(evt);
-                       break;
-               case 0:
-                       process_error(evt);
-                       break;
-               default:
-                       if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
-                               update_monitors();
-                       }
-                       break;
-       }
-}
-
-void map_request(xcb_generic_event_t *evt)
-{
-       xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
-
-       schedule_window(e->window);
-}
-
-void configure_request(xcb_generic_event_t *evt)
-{
-       xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
-
-       coordinates_t loc;
-       bool is_managed = locate_window(e->window, &loc);
-       client_t *c = (is_managed ? loc.node->client : NULL);
-       uint16_t width, height;
-
-       if (!is_managed) {
-               uint16_t mask = 0;
-               uint32_t values[7];
-               unsigned short i = 0;
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_X) {
-                       mask |= XCB_CONFIG_WINDOW_X;
-                       values[i++] = e->x;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
-                       mask |= XCB_CONFIG_WINDOW_Y;
-                       values[i++] = e->y;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
-                       mask |= XCB_CONFIG_WINDOW_WIDTH;
-                       values[i++] = e->width;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
-                       mask |= XCB_CONFIG_WINDOW_HEIGHT;
-                       values[i++] = e->height;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
-                       mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
-                       values[i++] = e->border_width;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
-                       mask |= XCB_CONFIG_WINDOW_SIBLING;
-                       values[i++] = e->sibling;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
-                       mask |= XCB_CONFIG_WINDOW_STACK_MODE;
-                       values[i++] = e->stack_mode;
-               }
-
-               xcb_configure_window(dpy, e->window, mask, values);
-
-       } else if (IS_FLOATING(c)) {
-               width = c->floating_rectangle.width;
-               height = c->floating_rectangle.height;
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_X) {
-                       c->floating_rectangle.x = e->x;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
-                       c->floating_rectangle.y = e->y;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
-                       width = e->width;
-               }
-
-               if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
-                       height = e->height;
-               }
-
-               apply_size_hints(c, &width, &height);
-               c->floating_rectangle.width = width;
-               c->floating_rectangle.height = height;
-               xcb_rectangle_t r = c->floating_rectangle;
-
-               if (focus_follows_pointer) {
-                       listen_enter_notify(loc.desktop->root, false);
-               }
-
-               window_move_resize(e->window, r.x, r.y, r.width, r.height);
-
-               if (focus_follows_pointer) {
-                       listen_enter_notify(loc.desktop->root, true);
-               }
-
-               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, e->window, r.width, r.height, r.x, r.y);
-
-               monitor_t *m = monitor_from_client(c);
-               if (m != loc.monitor) {
-                       transfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus);
-               }
-       } else {
-               if (c->state == STATE_PSEUDO_TILED) {
-                       width = c->floating_rectangle.width;
-                       height = c->floating_rectangle.height;
-                       if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
-                               width = e->width;
-                       }
-                       if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
-                               height = e->height;
-                       }
-                       apply_size_hints(c, &width, &height);
-                       if (width != c->floating_rectangle.width || height != c->floating_rectangle.height) {
-                               c->floating_rectangle.width = width;
-                               c->floating_rectangle.height = height;
-                               arrange(loc.monitor, loc.desktop);
-                       }
-               }
-
-
-               xcb_configure_notify_event_t evt;
-               unsigned int bw = c->border_width;
-
-               xcb_rectangle_t r = IS_FULLSCREEN(c) ? loc.monitor->rectangle : c->tiled_rectangle;
-
-               evt.response_type = XCB_CONFIGURE_NOTIFY;
-               evt.event = e->window;
-               evt.window = e->window;
-               evt.above_sibling = XCB_NONE;
-               evt.x = r.x;
-               evt.y = r.y;
-               evt.width = r.width;
-               evt.height = r.height;
-               evt.border_width = bw;
-               evt.override_redirect = false;
-
-               xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
-       }
-}
-
-void configure_notify(xcb_generic_event_t *evt)
-{
-       xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *) evt;
-
-       if (e->window == root) {
-               screen_width = e->width;
-               screen_height = e->height;
-       }
-}
-
-void destroy_notify(xcb_generic_event_t *evt)
-{
-       xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
-
-       unmanage_window(e->window);
-}
-
-void unmap_notify(xcb_generic_event_t *evt)
-{
-       xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
-
-       unmanage_window(e->window);
-}
-
-void property_notify(xcb_generic_event_t *evt)
-{
-       xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
-
-       if (e->atom == ewmh->_NET_WM_STRUT_PARTIAL && ewmh_handle_struts(e->window)) {
-               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-                       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                               arrange(m, d);
-                       }
-               }
-       }
-
-       if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {
-               return;
-       }
-
-       coordinates_t loc;
-       if (!locate_window(e->window, &loc)) {
-                       return;
-       }
-
-       if (e->atom == XCB_ATOM_WM_HINTS) {
-               xcb_icccm_wm_hints_t hints;
-               if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
-                   (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
-                       set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
-       } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
-               client_t *c = loc.node->client;
-               if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &c->size_hints, NULL) == 1) {
-                       arrange(loc.monitor, loc.desktop);
-               }
-       }
-}
-
-void client_message(xcb_generic_event_t *evt)
-{
-       xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
-
-       if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
-               coordinates_t loc;
-               if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
-                       focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
-               }
-               return;
-       }
-
-       coordinates_t loc;
-       if (!locate_window(e->window, &loc)) {
-               return;
-       }
-
-       if (e->type == ewmh->_NET_WM_STATE) {
-               handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
-               handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
-       } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
-               if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
-                   loc.node == mon->desk->focus) {
-                       return;
-               }
-               focus_node(loc.monitor, loc.desktop, loc.node);
-       } else if (e->type == ewmh->_NET_WM_DESKTOP) {
-               coordinates_t dloc;
-               if (ewmh_locate_desktop(e->data.data32[0], &dloc)) {
-                       transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
-               }
-       } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
-               close_node(loc.node);
-       }
-}
-
-void focus_in(xcb_generic_event_t *evt)
-{
-       xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
-
-       if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB
-           || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT
-           || e->detail == XCB_NOTIFY_DETAIL_NONE) {
-               return;
-       }
-
-       if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) {
-               return;
-       }
-
-       coordinates_t loc;
-       if (locate_window(e->event, &loc)) {
-               // prevent input focus stealing
-               update_input_focus();
-       }
-}
-
-void button_press(xcb_generic_event_t *evt)
-{
-       xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
-       bool replay = false;
-       switch (e->detail) {
-               case XCB_BUTTON_INDEX_1:
-                       if (click_to_focus && cleaned_mask(e->state) == XCB_NONE) {
-                               bool pff = pointer_follows_focus;
-                               bool pfm = pointer_follows_monitor;
-                               pointer_follows_focus = false;
-                               pointer_follows_monitor = false;
-                               replay = !grab_pointer(ACTION_FOCUS) || !swallow_first_click;
-                               pointer_follows_focus = pff;
-                               pointer_follows_monitor = pfm;
-                       } else {
-                               grab_pointer(pointer_actions[0]);
-                       }
-                       break;
-               case XCB_BUTTON_INDEX_2:
-                       grab_pointer(pointer_actions[1]);
-                       break;
-               case XCB_BUTTON_INDEX_3:
-                       grab_pointer(pointer_actions[2]);
-                       break;
-       }
-       xcb_allow_events(dpy, replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, e->time);
-       xcb_flush(dpy);
-}
-
-void enter_notify(xcb_generic_event_t *evt)
-{
-       xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
-       xcb_window_t win = e->event;
-
-       if (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
-               return;
-       }
-
-       xcb_point_t pt = {e->root_x, e->root_y};
-       monitor_t *m = monitor_from_point(pt);
-
-       if (m == NULL) {
-               return;
-       }
-
-       bool pff = pointer_follows_focus;
-       bool pfm = pointer_follows_monitor;
-       pointer_follows_focus = false;
-       pointer_follows_monitor = false;
-       auto_raise = false;
-
-       desktop_t *d = m->desk;
-       node_t *n = NULL;
-
-       for (n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-               if (n->id == win || (n->presel != NULL && n->presel->feedback == win)) {
-                       break;
-               }
-       }
-
-       if (n != mon->desk->focus || mon->desk->focus == NULL) {
-               focus_node(m, d, n == NULL ? d->focus : n);
-       }
-
-       pointer_follows_focus = pff;
-       pointer_follows_monitor = pfm;
-       auto_raise = true;
-}
-
-void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
-{
-       if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
-               if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_state(m, d, n, STATE_FULLSCREEN);
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       if (n->client->state == STATE_FULLSCREEN) {
-                               set_state(m, d, n, n->client->last_state);
-                       }
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN);
-               }
-               arrange(m, d);
-       } else if (state == ewmh->_NET_WM_STATE_BELOW) {
-               if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_layer(m, d, n, LAYER_BELOW);
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       if (n->client->layer == LAYER_BELOW) {
-                               set_layer(m, d, n, n->client->last_layer);
-                       }
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);
-               }
-       } else if (state == ewmh->_NET_WM_STATE_ABOVE) {
-               if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_layer(m, d, n, LAYER_ABOVE);
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       if (n->client->layer == LAYER_ABOVE) {
-                               set_layer(m, d, n, n->client->last_layer);
-                       }
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);
-               }
-       } else if (state == ewmh->_NET_WM_STATE_HIDDEN) {
-               if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_hidden(m, d, n, true);
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       set_hidden(m, d, n, false);
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_hidden(m, d, n, !n->hidden);
-               }
-       } else if (state == ewmh->_NET_WM_STATE_STICKY) {
-               if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_sticky(m, d, n, true);
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       set_sticky(m, d, n, false);
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_sticky(m, d, n, !n->sticky);
-               }
-       } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
-               if (action == XCB_EWMH_WM_STATE_ADD) {
-                       set_urgent(m, d, n, true);
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
-                       set_urgent(m, d, n, false);
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
-                       set_urgent(m, d, n, !n->client->urgent);
-               }
-#define HANDLE_WM_STATE(s)  \
-       } else if (state == ewmh->_NET_WM_STATE_##s) { \
-               if (action == XCB_EWMH_WM_STATE_ADD) { \
-                       n->client->wm_flags |= WM_FLAG_##s; \
-               } else if (action == XCB_EWMH_WM_STATE_REMOVE) { \
-                       n->client->wm_flags &= ~WM_FLAG_##s; \
-               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { \
-                       n->client->wm_flags ^= WM_FLAG_##s; \
-               } \
-               ewmh_wm_state_update(n);
-       HANDLE_WM_STATE(MODAL)
-       HANDLE_WM_STATE(MAXIMIZED_VERT)
-       HANDLE_WM_STATE(MAXIMIZED_HORZ)
-       HANDLE_WM_STATE(SHADED)
-       HANDLE_WM_STATE(SKIP_TASKBAR)
-       HANDLE_WM_STATE(SKIP_PAGER)
-       }
-#undef HANDLE_WM_STATE
-}
-
-void process_error(xcb_generic_event_t *evt)
-{
-       xcb_request_error_t *e = (xcb_request_error_t *) evt;
-       warn("Failed request: %s, %s: 0x%08X.\n", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value);
-}
diff --git a/events.h b/events.h
deleted file mode 100644 (file)
index cb26b4a..0000000
--- a/events.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_EVENTS_H
-#define BSPWM_EVENTS_H
-
-#include <xcb/xcb.h>
-#include <xcb/xcb_event.h>
-
-uint8_t randr_base;
-
-void handle_event(xcb_generic_event_t *evt);
-void map_request(xcb_generic_event_t *evt);
-void configure_request(xcb_generic_event_t *evt);
-void configure_notify(xcb_generic_event_t *evt);
-void destroy_notify(xcb_generic_event_t *evt);
-void unmap_notify(xcb_generic_event_t *evt);
-void property_notify(xcb_generic_event_t *evt);
-void client_message(xcb_generic_event_t *evt);
-void focus_in(xcb_generic_event_t *evt);
-void button_press(xcb_generic_event_t *evt);
-void enter_notify(xcb_generic_event_t *evt);
-void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action);
-void process_error(xcb_generic_event_t *evt);
-
-#endif
diff --git a/ewmh.c b/ewmh.c
deleted file mode 100644 (file)
index 869d406..0000000
--- a/ewmh.c
+++ /dev/null
@@ -1,293 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <string.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "settings.h"
-#include "tree.h"
-#include "ewmh.h"
-
-void ewmh_init(void)
-{
-       ewmh = calloc(1, sizeof(xcb_ewmh_connection_t));
-       if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
-               err("Can't initialize EWMH atoms.\n");
-       }
-}
-
-void ewmh_update_active_window(void)
-{
-       xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
-       xcb_ewmh_set_active_window(ewmh, default_screen, win);
-}
-
-void ewmh_update_number_of_desktops(void)
-{
-       uint32_t desktops_count = 0;
-
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       desktops_count++;
-               }
-       }
-
-       xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
-}
-
-uint32_t ewmh_get_desktop_index(desktop_t *d)
-{
-       uint32_t i = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {
-                       if (d == cd) {
-                               return i;
-                       }
-               }
-       }
-       return 0;
-}
-
-bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
-                       if (i == 0) {
-                               loc->monitor = m;
-                               loc->desktop = d;
-                               loc->node = NULL;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-void ewmh_update_current_desktop(void)
-{
-       if (mon == NULL) {
-               return;
-       }
-       uint32_t i = ewmh_get_desktop_index(mon->desk);
-       xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
-}
-
-void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
-{
-       uint32_t i = ewmh_get_desktop_index(d);
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client == NULL) {
-                       continue;
-               }
-               xcb_ewmh_set_wm_desktop(ewmh, f->id, i);
-       }
-}
-
-void ewmh_update_wm_desktops(void)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       uint32_t i = ewmh_get_desktop_index(d);
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                               if (n->client == NULL) {
-                                       continue;
-                               }
-                               xcb_ewmh_set_wm_desktop(ewmh, n->id, i);
-                       }
-               }
-       }
-}
-
-void ewmh_update_desktop_names(void)
-{
-       char names[MAXLEN];
-       unsigned int i, j;
-       uint32_t names_len;
-       i = 0;
-
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) {
-                               names[i + j] = d->name[j];
-                       }
-                       i += j;
-                       if (i < sizeof(names)) {
-                               names[i++] = '\0';
-                       }
-               }
-       }
-
-       if (i < 1) {
-               xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);
-               return;
-       }
-
-       names_len = i - 1;
-       xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
-}
-
-void ewmh_update_desktop_viewport(void)
-{
-       uint32_t desktops_count = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       desktops_count++;
-               }
-       }
-       xcb_ewmh_coordinates_t coords[desktops_count];
-       uint16_t desktop = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       coords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y};
-               }
-       }
-       xcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords);
-}
-
-bool ewmh_handle_struts(xcb_window_t win)
-{
-       xcb_ewmh_wm_strut_partial_t struts;
-       bool changed = false;
-       if (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) {
-               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-                       xcb_rectangle_t rect = m->rectangle;
-                       if (rect.x < (int16_t) struts.left &&
-                           (int16_t) struts.left < (rect.x + rect.width - 1) &&
-                           (int16_t) struts.left_end_y >= rect.y &&
-                           (int16_t) struts.left_start_y < (rect.y + rect.height)) {
-                               int dx = struts.left - rect.x;
-                               if (m->padding.left < 0) {
-                                       m->padding.left += dx;
-                               } else {
-                                       m->padding.left = MAX(dx, m->padding.left);
-                               }
-                               changed = true;
-                       }
-                       if ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) &&
-                           (int16_t) (screen_width - struts.right) > rect.x &&
-                           (int16_t) struts.right_end_y >= rect.y &&
-                           (int16_t) struts.right_start_y < (rect.y + rect.height)) {
-                               int dx = (rect.x + rect.width) - screen_width + struts.right;
-                               if (m->padding.right < 0) {
-                                       m->padding.right += dx;
-                               } else {
-                                       m->padding.right = MAX(dx, m->padding.right);
-                               }
-                               changed = true;
-                       }
-                       if (rect.y < (int16_t) struts.top &&
-                           (int16_t) struts.top < (rect.y + rect.height - 1) &&
-                           (int16_t) struts.top_end_x >= rect.x &&
-                           (int16_t) struts.top_start_x < (rect.x + rect.width)) {
-                               int dy = struts.top - rect.y;
-                               if (m->padding.top < 0) {
-                                       m->padding.top += dy;
-                               } else {
-                                       m->padding.top = MAX(dy, m->padding.top);
-                               }
-                               changed = true;
-                       }
-                       if ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) &&
-                           (int16_t) (screen_height - struts.bottom) > rect.y &&
-                           (int16_t) struts.bottom_end_x >= rect.x &&
-                           (int16_t) struts.bottom_start_x < (rect.x + rect.width)) {
-                               int dy = (rect.y + rect.height) - screen_height + struts.bottom;
-                               if (m->padding.bottom < 0) {
-                                       m->padding.bottom += dy;
-                               } else {
-                                       m->padding.bottom = MAX(dy, m->padding.bottom);
-                               }
-                               changed = true;
-                       }
-               }
-       }
-       return changed;
-}
-
-void ewmh_update_client_list(bool stacking)
-{
-       if (clients_count == 0) {
-               xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);
-               xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);
-               return;
-       }
-
-       xcb_window_t wins[clients_count];
-       unsigned int i = 0;
-
-       if (stacking) {
-               for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
-                       wins[i++] = s->node->id;
-               }
-               xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);
-       } else {
-               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-                       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                               for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                                       if (n->client == NULL) {
-                                               continue;
-                                       }
-                                       wins[i++] = n->id;
-                               }
-                       }
-               }
-               xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);
-       }
-}
-
-void ewmh_wm_state_update(node_t *n)
-{
-       client_t *c = n->client;
-       size_t count = 0;
-       uint32_t values[12];
-#define HANDLE_WM_STATE(s)  \
-       if (WM_FLAG_##s & c->wm_flags) { \
-               values[count++] = ewmh->_NET_WM_STATE_##s; \
-       }
-       HANDLE_WM_STATE(MODAL)
-       HANDLE_WM_STATE(STICKY)
-       HANDLE_WM_STATE(MAXIMIZED_VERT)
-       HANDLE_WM_STATE(MAXIMIZED_HORZ)
-       HANDLE_WM_STATE(SHADED)
-       HANDLE_WM_STATE(SKIP_TASKBAR)
-       HANDLE_WM_STATE(SKIP_PAGER)
-       HANDLE_WM_STATE(HIDDEN)
-       HANDLE_WM_STATE(FULLSCREEN)
-       HANDLE_WM_STATE(ABOVE)
-       HANDLE_WM_STATE(BELOW)
-       HANDLE_WM_STATE(DEMANDS_ATTENTION)
-#undef HANDLE_WM_STATE
-       xcb_ewmh_set_wm_state(ewmh, n->id, count, values);
-}
-
-void ewmh_set_supporting(xcb_window_t win)
-{
-       pid_t wm_pid = getpid();
-       xcb_ewmh_set_supporting_wm_check(ewmh, root, win);
-       xcb_ewmh_set_supporting_wm_check(ewmh, win, win);
-       xcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME);
-       xcb_ewmh_set_wm_pid(ewmh, win, wm_pid);
-}
diff --git a/ewmh.h b/ewmh.h
deleted file mode 100644 (file)
index 5bf3e18..0000000
--- a/ewmh.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_EWMH_H
-#define BSPWM_EWMH_H
-
-#include <xcb/xcb_ewmh.h>
-
-xcb_ewmh_connection_t *ewmh;
-
-void ewmh_init(void);
-void ewmh_update_active_window(void);
-void ewmh_update_number_of_desktops(void);
-uint32_t ewmh_get_desktop_index(desktop_t *d);
-bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc);
-void ewmh_update_current_desktop(void);
-void ewmh_set_wm_desktop(node_t *n, desktop_t *d);
-void ewmh_update_wm_desktops(void);
-void ewmh_update_desktop_names(void);
-void ewmh_update_desktop_viewport(void);
-bool ewmh_handle_struts(xcb_window_t win);
-void ewmh_update_client_list(bool stacking);
-void ewmh_wm_state_update(node_t *n);
-void ewmh_set_supporting(xcb_window_t win);
-
-#endif
diff --git a/geometry.c b/geometry.c
deleted file mode 100644 (file)
index 7976a4e..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <math.h>
-#include "types.h"
-#include "geometry.h"
-
-bool is_inside(xcb_point_t p, xcb_rectangle_t r)
-{
-       return (p.x >= r.x && p.x < (r.x + r.width) &&
-               p.y >= r.y && p.y < (r.y + r.height));
-}
-
-unsigned int area(xcb_rectangle_t r)
-{
-       return r.width * r.height;
-}
-
-
-dpoint_t center(xcb_rectangle_t r)
-{
-       return (dpoint_t) {(double)r.x + ((double)r.width / 2), (double)r.y + ((double)r.height / 2)};
-}
-
-double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2)
-{
-       dpoint_t r1_center = center(r1);
-       dpoint_t r2_center = center(r2);
-       return hypot(r1_center.x - r2_center.x, r1_center.y - r2_center.y);
-}
-
-bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)
-{
-       dpoint_t r1_max = {r1.x + r1.width, r1.y + r1.height};
-       dpoint_t r2_max = {r2.x + r2.width, r2.y + r2.height};
-       dpoint_t r1_center = center(r1);
-       dpoint_t r2_center = center(r2);
-
-       switch (dir) {
-               case DIR_NORTH:
-                       if (r2_center.y >= r1_center.y)
-                               return false;
-                       break;
-               case DIR_WEST:
-                       if (r2_center.x >= r1_center.x)
-                               return false;
-                       break;
-               case DIR_SOUTH:
-                       if (r1_center.y >= r2_center.y)
-                               return false;
-                       break;
-               case DIR_EAST:
-                       if (r1_center.x >= r2_center.x)
-                               return false;
-                       break;
-               default:
-                       return false;
-       }
-
-       switch (dir) {
-               case DIR_NORTH:
-               case DIR_SOUTH:
-                       return
-                               (r2.x >= r1.x && r2.x <= r1_max.x) ||
-                               (r2_max.x >= r1.x && r2_max.x <= r1_max.x) ||
-                               (r1.x >= r2.x && r1.x <= r2_max.x) ||
-                               (r1_max.x >= r2.x && r1_max.x <= r2_max.x);
-                       break;
-               case DIR_WEST:
-               case DIR_EAST:
-                       return
-                               (r2.y >= r1.y && r2.y <= r1_max.y) ||
-                               (r2_max.y >= r1.y && r2_max.y <= r1_max.y) ||
-                               (r1.y >= r2.y && r1.y <= r2_max.y) ||
-                               (r1_max.y >= r2.y && r1_max.y <= r2_max.y);
-                       break;
-               default:
-                       return false;
-       }
-}
-
-bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b)
-{
-       return (a.x == b.x && a.y == b.y &&
-               a.width == b.width && a.height == b.height);
-}
-
-int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2)
-{
-       if (r1.y >= (r2.y + r2.height)) {
-               return 1;
-       } else if (r2.y >= (r1.y + r1.height)) {
-               return -1;
-       } else {
-               if (r1.x >= (r2.x + r2.width)) {
-                       return 1;
-               } else if (r2.x >= (r1.x + r1.width)) {
-                       return -1;
-               } else {
-                       return area(r1) - area(r2);
-               }
-       }
-}
diff --git a/geometry.h b/geometry.h
deleted file mode 100644 (file)
index 0e1a760..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_GEOMETRY_H
-#define BSPWM_GEOMETRY_H
-
-#include <stdbool.h>
-#include <xcb/xcb.h>
-
-bool is_inside(xcb_point_t p, xcb_rectangle_t r);
-unsigned int area(xcb_rectangle_t r);
-dpoint_t center(xcb_rectangle_t r);
-double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2);
-bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);
-bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b);
-int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2);
-
-#endif
diff --git a/helpers.c b/helpers.c
deleted file mode 100644 (file)
index 39d0a49..0000000
--- a/helpers.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include "bspwm.h"
-
-void warn(char *fmt, ...)
-{
-       va_list ap;
-       va_start(ap, fmt);
-       vfprintf(stderr, fmt, ap);
-       va_end(ap);
-}
-
-__attribute__((noreturn))
-void err(char *fmt, ...)
-{
-       va_list ap;
-       va_start(ap, fmt);
-       vfprintf(stderr, fmt, ap);
-       va_end(ap);
-       exit(EXIT_FAILURE);
-}
-
-char *read_string(const char *file_path, size_t *tlen)
-{
-       if (file_path == NULL) {
-               return NULL;
-       }
-
-       int fd = open(file_path, O_RDONLY);
-
-       if (fd == -1) {
-               perror("Read file: open");
-               return NULL;
-       }
-
-       char buf[BUFSIZ], *content = NULL;
-       size_t len = sizeof(buf);
-
-       if ((content = calloc(len, sizeof(char))) == NULL) {
-               perror("Read file: calloc");
-               goto end;
-       }
-
-       int nb;
-       *tlen = 0;
-
-       while (true) {
-               nb = read(fd, buf, sizeof(buf));
-               if (nb < 0) {
-                       perror("Restore tree: read");
-                       free(content);
-                       content = NULL;
-                       goto end;
-               } else if (nb == 0) {
-                       break;
-               } else {
-                       *tlen += nb;
-                       if (*tlen > len) {
-                               len *= 2;
-                               char *rcontent = realloc(content, len * sizeof(char));
-                               if (rcontent == NULL) {
-                                       perror("Read file: realloc");
-                                       free(content);
-                                       content = NULL;
-                                       goto end;
-                               } else {
-                                       content = rcontent;
-                               }
-                       }
-                       strncpy(content + (*tlen - nb), buf, nb);
-               }
-       }
-
-end:
-       close(fd);
-       return content;
-}
-
-char *copy_string(char *str, size_t len)
-{
-       char *cpy = calloc(1, ((len+1) * sizeof(char)));
-       if (cpy == NULL) {
-               perror("Copy string: calloc");
-               return NULL;
-       }
-       strncpy(cpy, str, len);
-       cpy[len] = '\0';
-       return cpy;
-}
-
-/* Adapted from i3wm */
-uint32_t get_color_pixel(const char *color)
-{
-       unsigned int red, green, blue;
-       if (sscanf(color + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
-               /* We set the first 8 bits high to have 100% opacity in case of a 32 bit
-                * color depth visual. */
-               return (0xFF << 24) | (red << 16 | green << 8 | blue);
-       } else {
-               return screen->black_pixel;
-       }
-}
-
-bool is_hex_color(const char *color)
-{
-       if (color[0] != '#' || strlen(color) != 7) {
-               return false;
-       }
-       for (int i = 1; i < 7; i++) {
-               if (!isxdigit(color[i])) {
-                       return false;
-               }
-       }
-       return true;
-}
diff --git a/helpers.h b/helpers.h
deleted file mode 100644 (file)
index 23d29fb..0000000
--- a/helpers.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_HELPERS_H
-#define BSPWM_HELPERS_H
-
-#include <xcb/xcb.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <float.h>
-
-#define LENGTH(x)         (sizeof(x) / sizeof(*x))
-#define MAX(A, B)         ((A) > (B) ? (A) : (B))
-#define MIN(A, B)         ((A) < (B) ? (A) : (B))
-
-#define IS_TILED(c)       (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED)
-#define IS_FLOATING(c)    (c->state == STATE_FLOATING)
-#define IS_FULLSCREEN(c)  (c->state == STATE_FULLSCREEN)
-#define IS_RECEPTACLE(n)  (is_leaf(n) && n->client == NULL)
-#define IS_MONOCLE(d)     (d->layout == LAYOUT_MONOCLE || (single_monocle && tiled_count(d->root) <= 1))
-
-#define BOOL_STR(A)       ((A) ? "true" : "false")
-#define ON_OFF_STR(A)     ((A) ? "on" : "off")
-#define LAYOUT_STR(A)     ((A) == LAYOUT_TILED ? "tiled" : "monocle")
-#define LAYOUT_CHR(A)     ((A) == LAYOUT_TILED ? 'T' : 'M')
-#define CHILD_POL_STR(A)  ((A) == FIRST_CHILD ? "first_child" : "second_child")
-#define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical")
-#define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual")
-#define SPLIT_DIR_STR(A)  ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east")))
-#define STATE_STR(A)      ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
-#define STATE_CHR(A)      ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))
-#define LAYER_STR(A)      ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
-
-#define XCB_CONFIG_WINDOW_X_Y               (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y)
-#define XCB_CONFIG_WINDOW_WIDTH_HEIGHT      (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
-#define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT  (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
-
-#define MAXLEN    256
-#define SMALEN     32
-#define INIT_CAP    8
-
-#define cleaned_mask(m)   (m & ~(num_lock | scroll_lock | caps_lock))
-#define streq(s1, s2)     (strcmp((s1), (s2)) == 0)
-#define unsigned_subtract(a, b)  \
-       do {                         \
-               if (b > a) {             \
-                       a = 0;               \
-               } else {                 \
-                       a -= b;              \
-               }                        \
-       } while (false)
-
-
-void warn(char *fmt, ...);
-void err(char *fmt, ...);
-char *read_string(const char *file_path, size_t *tlen);
-char *copy_string(char *str, size_t len);
-uint32_t get_color_pixel(const char *color);
-bool is_hex_color(const char *color);
-
-#endif
diff --git a/history.c b/history.c
deleted file mode 100644 (file)
index b20d01c..0000000
--- a/history.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "tree.h"
-#include "query.h"
-
-history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
-{
-       history_t *h = calloc(1, sizeof(history_t));
-       h->loc = (coordinates_t) {m, d, n};
-       h->prev = h->next = NULL;
-       h->latest = true;
-       return h;
-}
-
-void history_add(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (!record_history) {
-               return;
-       }
-       history_needle = NULL;
-       history_t *h = make_history(m, d, n);
-       if (history_head == NULL) {
-               history_head = history_tail = h;
-       } else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) {
-               for (history_t *hh = history_tail; hh != NULL; hh = hh->prev) {
-                       if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) {
-                               hh->latest = false;
-                       }
-               }
-               history_tail->next = h;
-               h->prev = history_tail;
-               history_tail = h;
-       } else {
-               free(h);
-       }
-}
-
-void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n)
-{
-       for (history_t *h = history_head; h != NULL; h = h->next) {
-               if (is_descendant(h->loc.node, n)) {
-                       h->loc.monitor = m;
-                       h->loc.desktop = d;
-               }
-       }
-}
-
-void history_transfer_desktop(monitor_t *m, desktop_t *d)
-{
-       for (history_t *h = history_head; h != NULL; h = h->next) {
-               if (h->loc.desktop == d) {
-                       h->loc.monitor = m;
-               }
-       }
-}
-
-void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
-{
-       for (history_t *h = history_head; h != NULL; h = h->next) {
-               if (is_descendant(h->loc.node, n1)) {
-                       h->loc.monitor = m2;
-                       h->loc.desktop = d2;
-               } else if (is_descendant(h->loc.node, n2)) {
-                       h->loc.monitor = m1;
-                       h->loc.desktop = d1;
-               }
-       }
-}
-
-void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
-{
-       for (history_t *h = history_head; h != NULL; h = h->next) {
-               if (h->loc.desktop == d1) {
-                       h->loc.monitor = m2;
-               } else if (h->loc.desktop == d2) {
-                       h->loc.monitor = m1;
-               }
-       }
-}
-
-void history_remove(desktop_t *d, node_t *n, bool deep)
-{
-       /* removing from the newest to the oldest is required */
-       /* for maintaining the *latest* attribute */
-       history_t *b = history_tail;
-       while (b != NULL) {
-               if ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) ||
-                   (n == NULL && d == b->loc.desktop)) {
-                       history_t *a = b->next;
-                       history_t *c = b->prev;
-                       if (a != NULL) {
-                               /* remove duplicate entries */
-                               while (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) ||
-                                      (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) {
-                                       history_t *p = c->prev;
-                                       if (history_head == c) {
-                                               history_head = history_tail;
-                                       }
-                                       if (history_needle == c) {
-                                               history_needle = history_tail;
-                                       }
-                                       free(c);
-                                       c = p;
-                               }
-                               a->prev = c;
-                       }
-                       if (c != NULL) {
-                               c->next = a;
-                       }
-                       if (history_tail == b) {
-                               history_tail = c;
-                       }
-                       if (history_head == b) {
-                               history_head = a;
-                       }
-                       if (history_needle == b) {
-                               history_needle = c;
-                       }
-                       free(b);
-                       b = c;
-               } else {
-                       b = b->prev;
-               }
-       }
-}
-
-void empty_history(void)
-{
-       history_t *h = history_head;
-       while (h != NULL) {
-               history_t *next = h->next;
-               free(h);
-               h = next;
-       }
-       history_head = history_tail = NULL;
-}
-
-node_t *history_last_node(desktop_t *d, node_t *n)
-{
-       for (history_t *h = history_tail; h != NULL; h = h->prev) {
-               if (h->latest && h->loc.node != NULL && !h->loc.node->hidden &&
-                   !is_descendant(h->loc.node, n) && h->loc.desktop == d) {
-                       return h->loc.node;
-               }
-       }
-       return NULL;
-}
-
-desktop_t *history_last_desktop(monitor_t *m, desktop_t *d)
-{
-       for (history_t *h = history_tail; h != NULL; h = h->prev) {
-               if (h->latest && h->loc.desktop != d && h->loc.monitor == m) {
-                       return h->loc.desktop;
-               }
-       }
-       return NULL;
-}
-
-monitor_t *history_last_monitor(monitor_t *m)
-{
-       for (history_t *h = history_tail; h != NULL; h = h->prev) {
-               if (h->latest && h->loc.monitor != m) {
-                       return h->loc.monitor;
-               }
-       }
-       return NULL;
-}
-
-bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel)
-{
-       if (history_needle == NULL || record_history) {
-               history_needle = history_tail;
-       }
-
-       history_t *h;
-       for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
-               if (!h->latest ||
-                   h->loc.node == NULL ||
-                   h->loc.node == ref->node ||
-                   h->loc.node->hidden ||
-                   !node_matches(&h->loc, ref, sel)) {
-                       continue;
-               }
-               if (!record_history) {
-                       history_needle = h;
-               }
-               *dst = h->loc;
-               return true;
-       }
-       return false;
-}
-
-bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel)
-{
-       if (history_needle == NULL || record_history) {
-               history_needle = history_tail;
-       }
-
-       history_t *h;
-       for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
-               if (!h->latest ||
-                   h->loc.desktop == ref->desktop ||
-                   !desktop_matches(&h->loc, ref, sel)) {
-                       continue;
-               }
-               if (!record_history) {
-                       history_needle = h;
-               }
-               *dst = h->loc;
-               return true;
-       }
-       return false;
-}
-
-bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel)
-{
-       if (history_needle == NULL || record_history) {
-               history_needle = history_tail;
-       }
-
-       history_t *h;
-
-       for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
-               if (!h->latest ||
-                   h->loc.monitor == ref->monitor ||
-                   !monitor_matches(&h->loc, ref, sel)) {
-                       continue;
-               }
-               if (!record_history) {
-                       history_needle = h;
-               }
-               *dst = h->loc;
-               return true;
-       }
-
-       return false;
-}
-
-uint32_t history_rank(node_t *n)
-{
-       uint32_t r = 0;
-       history_t *h = history_tail;
-       while (h != NULL && (!h->latest || h->loc.node != n)) {
-               h = h->prev;
-               r++;
-       }
-       if (h == NULL) {
-               return UINT32_MAX;
-       } else {
-               return r;
-       }
-}
diff --git a/history.h b/history.h
deleted file mode 100644 (file)
index 5df4629..0000000
--- a/history.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_HISTORY_H
-#define BSPWM_HISTORY_H
-
-#include "types.h"
-
-history_t *make_history(monitor_t *m, desktop_t *d, node_t *n);
-void history_add(monitor_t *m, desktop_t *d, node_t *n);
-void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n);
-void history_transfer_desktop(monitor_t *m, desktop_t *d);
-void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
-void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
-void history_remove(desktop_t *d, node_t *n, bool deep);
-void empty_history(void);
-node_t *history_last_node(desktop_t *d, node_t *n);
-desktop_t *history_last_desktop(monitor_t *m, desktop_t *d);
-monitor_t *history_last_monitor(monitor_t *m);
-bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel);
-bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel);
-bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel);
-uint32_t history_rank(node_t *n);
-
-#endif
diff --git a/jsmn.c b/jsmn.c
deleted file mode 100644 (file)
index bbf013d..0000000
--- a/jsmn.c
+++ /dev/null
@@ -1,311 +0,0 @@
-#include "jsmn.h"
-
-/**
- * Allocates a fresh unused token from the token pull.
- */
-static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
-               jsmntok_t *tokens, size_t num_tokens) {
-       jsmntok_t *tok;
-       if (parser->toknext >= num_tokens) {
-               return NULL;
-       }
-       tok = &tokens[parser->toknext++];
-       tok->start = tok->end = -1;
-       tok->size = 0;
-#ifdef JSMN_PARENT_LINKS
-       tok->parent = -1;
-#endif
-       return tok;
-}
-
-/**
- * Fills token type and boundaries.
- */
-static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
-                            int start, int end) {
-       token->type = type;
-       token->start = start;
-       token->end = end;
-       token->size = 0;
-}
-
-/**
- * Fills next available token with JSON primitive.
- */
-static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
-               size_t len, jsmntok_t *tokens, size_t num_tokens) {
-       jsmntok_t *token;
-       int start;
-
-       start = parser->pos;
-
-       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
-               switch (js[parser->pos]) {
-#ifndef JSMN_STRICT
-                       /* In strict mode primitive must be followed by "," or "}" or "]" */
-                       case ':':
-#endif
-                       case '\t' : case '\r' : case '\n' : case ' ' :
-                       case ','  : case ']'  : case '}' :
-                               goto found;
-               }
-               if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
-                       parser->pos = start;
-                       return JSMN_ERROR_INVAL;
-               }
-       }
-#ifdef JSMN_STRICT
-       /* In strict mode primitive must be followed by a comma/object/array */
-       parser->pos = start;
-       return JSMN_ERROR_PART;
-#endif
-
-found:
-       if (tokens == NULL) {
-               parser->pos--;
-               return 0;
-       }
-       token = jsmn_alloc_token(parser, tokens, num_tokens);
-       if (token == NULL) {
-               parser->pos = start;
-               return JSMN_ERROR_NOMEM;
-       }
-       jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
-#ifdef JSMN_PARENT_LINKS
-       token->parent = parser->toksuper;
-#endif
-       parser->pos--;
-       return 0;
-}
-
-/**
- * Filsl next token with JSON string.
- */
-static int jsmn_parse_string(jsmn_parser *parser, const char *js,
-               size_t len, jsmntok_t *tokens, size_t num_tokens) {
-       jsmntok_t *token;
-
-       int start = parser->pos;
-
-       parser->pos++;
-
-       /* Skip starting quote */
-       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
-               char c = js[parser->pos];
-
-               /* Quote: end of string */
-               if (c == '\"') {
-                       if (tokens == NULL) {
-                               return 0;
-                       }
-                       token = jsmn_alloc_token(parser, tokens, num_tokens);
-                       if (token == NULL) {
-                               parser->pos = start;
-                               return JSMN_ERROR_NOMEM;
-                       }
-                       jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
-#ifdef JSMN_PARENT_LINKS
-                       token->parent = parser->toksuper;
-#endif
-                       return 0;
-               }
-
-               /* Backslash: Quoted symbol expected */
-               if (c == '\\' && parser->pos + 1 < len) {
-                       int i;
-                       parser->pos++;
-                       switch (js[parser->pos]) {
-                               /* Allowed escaped symbols */
-                               case '\"': case '/' : case '\\' : case 'b' :
-                               case 'f' : case 'r' : case 'n'  : case 't' :
-                                       break;
-                               /* Allows escaped symbol \uXXXX */
-                               case 'u':
-                                       parser->pos++;
-                                       for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
-                                               /* If it isn't a hex character we have an error */
-                                               if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
-                                                                       (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
-                                                                       (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
-                                                       parser->pos = start;
-                                                       return JSMN_ERROR_INVAL;
-                                               }
-                                               parser->pos++;
-                                       }
-                                       parser->pos--;
-                                       break;
-                               /* Unexpected symbol */
-                               default:
-                                       parser->pos = start;
-                                       return JSMN_ERROR_INVAL;
-                       }
-               }
-       }
-       parser->pos = start;
-       return JSMN_ERROR_PART;
-}
-
-/**
- * Parse JSON string and fill tokens.
- */
-int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
-               jsmntok_t *tokens, unsigned int num_tokens) {
-       int r;
-       int i;
-       jsmntok_t *token;
-       int count = parser->toknext;
-
-       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
-               char c;
-               jsmntype_t type;
-
-               c = js[parser->pos];
-               switch (c) {
-                       case '{': case '[':
-                               count++;
-                               if (tokens == NULL) {
-                                       break;
-                               }
-                               token = jsmn_alloc_token(parser, tokens, num_tokens);
-                               if (token == NULL)
-                                       return JSMN_ERROR_NOMEM;
-                               if (parser->toksuper != -1) {
-                                       tokens[parser->toksuper].size++;
-#ifdef JSMN_PARENT_LINKS
-                                       token->parent = parser->toksuper;
-#endif
-                               }
-                               token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
-                               token->start = parser->pos;
-                               parser->toksuper = parser->toknext - 1;
-                               break;
-                       case '}': case ']':
-                               if (tokens == NULL)
-                                       break;
-                               type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
-#ifdef JSMN_PARENT_LINKS
-                               if (parser->toknext < 1) {
-                                       return JSMN_ERROR_INVAL;
-                               }
-                               token = &tokens[parser->toknext - 1];
-                               for (;;) {
-                                       if (token->start != -1 && token->end == -1) {
-                                               if (token->type != type) {
-                                                       return JSMN_ERROR_INVAL;
-                                               }
-                                               token->end = parser->pos + 1;
-                                               parser->toksuper = token->parent;
-                                               break;
-                                       }
-                                       if (token->parent == -1) {
-                                               break;
-                                       }
-                                       token = &tokens[token->parent];
-                               }
-#else
-                               for (i = parser->toknext - 1; i >= 0; i--) {
-                                       token = &tokens[i];
-                                       if (token->start != -1 && token->end == -1) {
-                                               if (token->type != type) {
-                                                       return JSMN_ERROR_INVAL;
-                                               }
-                                               parser->toksuper = -1;
-                                               token->end = parser->pos + 1;
-                                               break;
-                                       }
-                               }
-                               /* Error if unmatched closing bracket */
-                               if (i == -1) return JSMN_ERROR_INVAL;
-                               for (; i >= 0; i--) {
-                                       token = &tokens[i];
-                                       if (token->start != -1 && token->end == -1) {
-                                               parser->toksuper = i;
-                                               break;
-                                       }
-                               }
-#endif
-                               break;
-                       case '\"':
-                               r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
-                               if (r < 0) return r;
-                               count++;
-                               if (parser->toksuper != -1 && tokens != NULL)
-                                       tokens[parser->toksuper].size++;
-                               break;
-                       case '\t' : case '\r' : case '\n' : case ' ':
-                               break;
-                       case ':':
-                               parser->toksuper = parser->toknext - 1;
-                               break;
-                       case ',':
-                               if (tokens != NULL && parser->toksuper != -1 &&
-                                               tokens[parser->toksuper].type != JSMN_ARRAY &&
-                                               tokens[parser->toksuper].type != JSMN_OBJECT) {
-#ifdef JSMN_PARENT_LINKS
-                                       parser->toksuper = tokens[parser->toksuper].parent;
-#else
-                                       for (i = parser->toknext - 1; i >= 0; i--) {
-                                               if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
-                                                       if (tokens[i].start != -1 && tokens[i].end == -1) {
-                                                               parser->toksuper = i;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-#endif
-                               }
-                               break;
-#ifdef JSMN_STRICT
-                       /* In strict mode primitives are: numbers and booleans */
-                       case '-': case '0': case '1' : case '2': case '3' : case '4':
-                       case '5': case '6': case '7' : case '8': case '9':
-                       case 't': case 'f': case 'n' :
-                               /* And they must not be keys of the object */
-                               if (tokens != NULL && parser->toksuper != -1) {
-                                       jsmntok_t *t = &tokens[parser->toksuper];
-                                       if (t->type == JSMN_OBJECT ||
-                                                       (t->type == JSMN_STRING && t->size != 0)) {
-                                               return JSMN_ERROR_INVAL;
-                                       }
-                               }
-#else
-                       /* In non-strict mode every unquoted value is a primitive */
-                       default:
-#endif
-                               r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
-                               if (r < 0) return r;
-                               count++;
-                               if (parser->toksuper != -1 && tokens != NULL)
-                                       tokens[parser->toksuper].size++;
-                               break;
-
-#ifdef JSMN_STRICT
-                       /* Unexpected char in strict mode */
-                       default:
-                               return JSMN_ERROR_INVAL;
-#endif
-               }
-       }
-
-       if (tokens != NULL) {
-               for (i = parser->toknext - 1; i >= 0; i--) {
-                       /* Unmatched opened object or array */
-                       if (tokens[i].start != -1 && tokens[i].end == -1) {
-                               return JSMN_ERROR_PART;
-                       }
-               }
-       }
-
-       return count;
-}
-
-/**
- * Creates a new parser based over a given  buffer with an array of tokens
- * available.
- */
-void jsmn_init(jsmn_parser *parser) {
-       parser->pos = 0;
-       parser->toknext = 0;
-       parser->toksuper = -1;
-}
-
diff --git a/jsmn.h b/jsmn.h
deleted file mode 100644 (file)
index 01ca99c..0000000
--- a/jsmn.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#ifndef __JSMN_H_
-#define __JSMN_H_
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * JSON type identifier. Basic types are:
- *     o Object
- *     o Array
- *     o String
- *     o Other primitive: number, boolean (true/false) or null
- */
-typedef enum {
-       JSMN_UNDEFINED = 0,
-       JSMN_OBJECT = 1,
-       JSMN_ARRAY = 2,
-       JSMN_STRING = 3,
-       JSMN_PRIMITIVE = 4
-} jsmntype_t;
-
-enum jsmnerr {
-       /* Not enough tokens were provided */
-       JSMN_ERROR_NOMEM = -1,
-       /* Invalid character inside JSON string */
-       JSMN_ERROR_INVAL = -2,
-       /* The string is not a full JSON packet, more bytes expected */
-       JSMN_ERROR_PART = -3
-};
-
-/**
- * JSON token description.
- * @param              type    type (object, array, string etc.)
- * @param              start   start position in JSON data string
- * @param              end             end position in JSON data string
- */
-typedef struct {
-       jsmntype_t type;
-       int start;
-       int end;
-       int size;
-#ifdef JSMN_PARENT_LINKS
-       int parent;
-#endif
-} jsmntok_t;
-
-/**
- * JSON parser. Contains an array of token blocks available. Also stores
- * the string being parsed now and current position in that string
- */
-typedef struct {
-       unsigned int pos; /* offset in the JSON string */
-       unsigned int toknext; /* next token to allocate */
-       int toksuper; /* superior token node, e.g parent object or array */
-} jsmn_parser;
-
-/**
- * Create JSON parser over an array of tokens
- */
-void jsmn_init(jsmn_parser *parser);
-
-/**
- * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
- * a single JSON object.
- */
-int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
-               jsmntok_t *tokens, unsigned int num_tokens);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __JSMN_H_ */
diff --git a/messages.c b/messages.c
deleted file mode 100644 (file)
index fccad2e..0000000
+++ /dev/null
@@ -1,1652 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "monitor.h"
-#include "pointer.h"
-#include "query.h"
-#include "rule.h"
-#include "restore.h"
-#include "settings.h"
-#include "tree.h"
-#include "window.h"
-#include "common.h"
-#include "parse.h"
-#include "messages.h"
-
-void handle_message(char *msg, int msg_len, FILE *rsp)
-{
-       int cap = INIT_CAP;
-       int num = 0;
-       char **args = calloc(cap, sizeof(char *));
-
-       if (args == NULL) {
-               perror("Handle message: calloc");
-               return;
-       }
-
-       for (int i = 0, j = 0; i < msg_len; i++) {
-               if (msg[i] == 0) {
-                       args[num++] = msg + j;
-                       j = i + 1;
-               }
-               if (num >= cap) {
-                       cap *= 2;
-                       char **new = realloc(args, cap * sizeof(char *));
-                       if (new == NULL) {
-                               free(args);
-                               perror("Handle message: realloc");
-                               return;
-                       } else {
-                               args = new;
-                       }
-               }
-       }
-
-       if (num < 1) {
-               free(args);
-               fail(rsp, "No arguments given.\n");
-               return;
-       }
-
-       char **args_orig = args;
-       process_message(args, num, rsp);
-       free(args_orig);
-}
-
-void process_message(char **args, int num, FILE *rsp)
-{
-       int ret = SUBSCRIBE_FAILURE;
-
-       if (streq("node", *args)) {
-               cmd_node(++args, --num, rsp);
-       } else if (streq("desktop", *args)) {
-               cmd_desktop(++args, --num, rsp);
-       } else if (streq("monitor", *args)) {
-               cmd_monitor(++args, --num, rsp);
-       } else if (streq("query", *args)) {
-               cmd_query(++args, --num, rsp);
-       } else if (streq("subscribe", *args)) {
-               ret = cmd_subscribe(++args, --num, rsp);
-       } else if (streq("wm", *args)) {
-               cmd_wm(++args, --num, rsp);
-       } else if (streq("rule", *args)) {
-               cmd_rule(++args, --num, rsp);
-       } else if (streq("config", *args)) {
-               cmd_config(++args, --num, rsp);
-       } else if (streq("quit", *args)) {
-               cmd_quit(++args, --num, rsp);
-       } else {
-               fail(rsp, "Unknown domain or command: '%s'.\n", *args);
-       }
-
-       fflush(rsp);
-
-       if (ret != SUBSCRIBE_SUCCESS) {
-               fclose(rsp);
-       }
-}
-
-void cmd_node(char **args, int num, FILE *rsp)
-{
-       if (num < 1) {
-               fail(rsp, "node: Missing commands.\n");
-               return;
-       }
-
-       coordinates_t ref = {mon, mon->desk, mon->desk->focus};
-       coordinates_t trg = ref;
-
-       if ((*args)[0] != OPT_CHR) {
-               int ret;
-               if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
-                       num--, args++;
-               } else {
-                       handle_failure(ret, "node", *args, rsp);
-                       return;
-               }
-       }
-
-       bool changed = false;
-
-       while (num > 0) {
-               if (streq("-f", *args) || streq("--focus", *args)) {
-                       coordinates_t dst = trg;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
-                                       handle_failure(ret, "node -f", *args, rsp);
-                                       break;
-                               }
-                       }
-                       if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else if (streq("-a", *args) || streq("--activate", *args)) {
-                       coordinates_t dst = trg;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
-                                       handle_failure(ret, "node -a", *args, rsp);
-                                       break;
-                               }
-                       }
-                       if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
-                                       trg.monitor = dst.monitor;
-                                       trg.desktop = dst.desktop;
-                               } else {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               handle_failure(ret, "node -d", *args, rsp);
-                               break;
-                       }
-               } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
-                                       trg.monitor = dst.monitor;
-                                       trg.desktop = dst.monitor->desk;
-                               } else {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               handle_failure(ret, "node -m", *args, rsp);
-                               break;
-                       }
-               } else if (streq("-n", *args) || streq("--to-node", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
-                                       trg.monitor = dst.monitor;
-                                       trg.desktop = dst.desktop;
-                               } else {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               handle_failure(ret, "node -n", *args, rsp);
-                               break;
-                       }
-               } else if (streq("-s", *args) || streq("--swap", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
-                                       trg.monitor = dst.monitor;
-                                       trg.desktop = dst.desktop;
-                               } else {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               handle_failure(ret, "node -s", *args, rsp);
-                               break;
-                       }
-               } else if (streq("-l", *args) || streq("--layer", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       stack_layer_t lyr;
-                       if (parse_stack_layer(*args, &lyr)) {
-                               if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-t", *args) || streq("--state", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       client_state_t cst;
-                       bool alternate = false;
-                       if ((*args)[0] == '~') {
-                               alternate = true;
-                               (*args)++;
-                       }
-                       if (parse_client_state(*args, &cst)) {
-                               if (alternate && trg.node != NULL && trg.node->client != NULL &&
-                                   trg.node->client->state == cst) {
-                                       cst = trg.node->client->last_state;
-                               }
-                               if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                               changed = true;
-                       } else {
-                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-g", *args) || streq("--flag", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       char *key = strtok(*args, EQL_TOK);
-                       char *val = strtok(NULL, EQL_TOK);
-                       alter_state_t a;
-                       bool b;
-                       if (val == NULL) {
-                               a = ALTER_TOGGLE;
-                       } else {
-                               if (parse_bool(val, &b)) {
-                                       a = ALTER_SET;
-                               } else {
-                                       fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
-                                       break;
-                               }
-                       }
-                       if (streq("hidden", key)) {
-                               set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
-                               changed = true;
-                       } else if (streq("sticky", key)) {
-                               set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
-                       } else if (streq("private", key)) {
-                               set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
-                       } else if (streq("locked", key)) {
-                               set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
-                       } else {
-                               fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
-                               break;
-                       }
-               } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL || trg.node->vacant) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       if (streq("cancel", *args)) {
-                               cancel_presel(trg.monitor, trg.desktop, trg.node);
-                       } else {
-                               bool alternate = false;
-                               if ((*args)[0] == '~') {
-                                       alternate = true;
-                                       (*args)++;
-                               }
-                               direction_t dir;
-                               if (parse_direction(*args, &dir)) {
-                                       if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
-                                               cancel_presel(trg.monitor, trg.desktop, trg.node);
-                                       } else {
-                                               presel_dir(trg.monitor, trg.desktop, trg.node, dir);
-                                               if (!IS_RECEPTACLE(trg.node)) {
-                                                       draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
-                                               }
-                                       }
-                               } else {
-                                       fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
-                                       break;
-                               }
-                       }
-               } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL || trg.node->vacant) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       double rat;
-                       if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
-                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       } else {
-                               presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
-                               draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
-                       }
-               } else if (streq("-v", *args) || streq("--move", *args)) {
-                       num--, args++;
-                       if (num < 2) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       int dx = 0, dy = 0;
-                       if (sscanf(*args, "%i", &dx) == 1) {
-                               num--, args++;
-                               if (sscanf(*args, "%i", &dy) == 1) {
-                                       if (!move_client(&trg, dx, dy)) {
-                                               fail(rsp, "");
-                                               break;
-                                       }
-                               } else {
-                                       fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
-                                       break;
-                               }
-                       } else {
-                               fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
-                               break;
-                       }
-               } else if (streq("-z", *args) || streq("--resize", *args)) {
-                       num--, args++;
-                       if (num < 3) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       resize_handle_t rh;
-                       if (parse_resize_handle(*args, &rh)) {
-                               num--, args++;
-                               int dx = 0, dy = 0;
-                               if (sscanf(*args, "%i", &dx) == 1) {
-                                       num--, args++;
-                                       if (sscanf(*args, "%i", &dy) == 1) {
-                                               if (!resize_client(&trg, rh, dx, dy)) {
-                                                       fail(rsp, "");
-                                                       break;
-                                               }
-                                       } else {
-                                               fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
-                                               break;
-                                       }
-                               } else {
-                                       fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
-                                       break;
-                               }
-                       } else {
-                               fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-r", *args) || streq("--ratio", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       if ((*args)[0] == '+' || (*args)[0] == '-') {
-                               int pix;
-                               if (sscanf(*args, "%i", &pix) == 1) {
-                                       int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
-                                       double rat = ((max * trg.node->split_ratio) + pix) / max;
-                                       if (rat > 0 && rat < 1) {
-                                               set_ratio(trg.node, rat);
-                                       } else {
-                                               fail(rsp, "");
-                                               break;
-                                       }
-                               } else {
-                                       fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                                       break;
-                               }
-                       } else {
-                               double rat;
-                               if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
-                                       set_ratio(trg.node, rat);
-                               } else {
-                                       fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                                       break;
-                               }
-                       }
-                       changed = true;
-               } else if (streq("-F", *args) || streq("--flip", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       flip_t flp;
-                       if (parse_flip(*args, &flp)) {
-                               flip_tree(trg.node, flp);
-                               changed = true;
-                       } else {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else if (streq("-R", *args) || streq("--rotate", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       int deg;
-                       if (parse_degree(*args, &deg)) {
-                               rotate_tree(trg.node, deg);
-                               changed = true;
-                       } else {
-                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-E", *args) || streq("--equalize", *args)) {
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       equalize_tree(trg.node);
-                       changed = true;
-               } else if (streq("-B", *args) || streq("--balance", *args)) {
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       balance_tree(trg.node);
-                       changed = true;
-               } else if (streq("-C", *args) || streq("--circulate", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       circulate_dir_t cir;
-                       if (parse_circulate_direction(*args, &cir)) {
-                               circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
-                               changed = true;
-                       } else {
-                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
-                       insert_receptacle(trg.monitor, trg.desktop, trg.node);
-                       changed = true;
-               } else if (streq("-c", *args) || streq("--close", *args)) {
-                       if (num > 1) {
-                               fail(rsp, "node %s: Trailing commands.\n", *args);
-                               break;
-                       }
-                       if (trg.node == NULL || locked_count(trg.node) > 0) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       close_node(trg.node);
-                       break;
-               } else if (streq("-k", *args) || streq("--kill", *args)) {
-                       if (num > 1) {
-                               fail(rsp, "node %s: Trailing commands.\n", *args);
-                               break;
-                       }
-                       if (trg.node == NULL) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       kill_node(trg.monitor, trg.desktop, trg.node);
-                       changed = true;
-                       break;
-               } else {
-                       fail(rsp, "node: Unknown command: '%s'.\n", *args);
-                       break;
-               }
-
-               num--, args++;
-       }
-
-       if (changed) {
-               arrange(trg.monitor, trg.desktop);
-       }
-}
-
-void cmd_desktop(char **args, int num, FILE *rsp)
-{
-       if (num < 1) {
-               fail(rsp, "desktop: Missing commands.\n");
-               return;
-       }
-
-       coordinates_t ref = {mon, mon->desk, NULL};
-       coordinates_t trg = ref;
-
-       if ((*args)[0] != OPT_CHR) {
-               int ret;
-               if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
-                       num--, args++;
-               } else {
-                       handle_failure(ret, "desktop", *args, rsp);
-                       return;
-               }
-       }
-
-       bool changed = false;
-
-       while (num > 0) {
-               if (streq("-f", *args) || streq("--focus", *args)) {
-                       coordinates_t dst = trg;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
-                                       handle_failure(ret, "desktop -f", *args, rsp);
-                                       break;
-                               }
-                       }
-                       focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
-               } else if (streq("-a", *args) || streq("--activate", *args)) {
-                       coordinates_t dst = trg;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
-                                       handle_failure(ret, "desktop -a", *args, rsp);
-                                       break;
-                               }
-                       }
-                       if (!activate_desktop(dst.monitor, dst.desktop)) {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (trg.monitor->desk_head == trg.monitor->desk_tail) {
-                               fail(rsp, "");
-                               break;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
-                                       trg.monitor = dst.monitor;
-                               } else {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               handle_failure(ret, "desktop -m", *args, rsp);
-                               break;
-                       }
-               } else if (streq("-s", *args) || streq("--swap", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
-                                       trg.monitor = dst.monitor;
-                               } else {
-                                       fail(rsp, "");
-                                       break;
-                               }
-                       } else {
-                               handle_failure(ret, "desktop -s", *args, rsp);
-                               break;
-                       }
-               } else if (streq("-b", *args) || streq("--bubble", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       cycle_dir_t cyc;
-                       if (parse_cycle_direction(*args, &cyc)) {
-                               desktop_t *d = trg.desktop;
-                               if (cyc == CYCLE_PREV) {
-                                       if (d->prev == NULL) {
-                                               while (d->next != NULL) {
-                                                       swap_desktops(trg.monitor, d, trg.monitor, d->next);
-                                               }
-                                       } else {
-                                               swap_desktops(trg.monitor, d, trg.monitor, d->prev);
-                                       }
-                               } else {
-                                       if (d->next == NULL) {
-                                               while (d->prev != NULL) {
-                                                       swap_desktops(trg.monitor, d, trg.monitor, d->prev);
-                                               }
-                                       } else {
-                                               swap_desktops(trg.monitor, d, trg.monitor, d->next);
-                                       }
-                               }
-                       } else {
-                               fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-l", *args) || streq("--layout", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       bool ret;
-                       layout_t lyt;
-                       cycle_dir_t cyc;
-                       if (parse_cycle_direction(*args, &cyc)) {
-                               ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
-                       } else if (parse_layout(*args, &lyt)) {
-                               ret = set_layout(trg.monitor, trg.desktop, lyt);
-                       } else {
-                               fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-                       if (!ret) {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else if (streq("-n", *args) || streq("--rename", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       rename_desktop(trg.monitor, trg.desktop, *args);
-               } else if (streq("-r", *args) || streq("--remove", *args)) {
-                       if (num > 1) {
-                               fail(rsp, "desktop %s: Trailing commands.\n", *args);
-                               break;
-                       }
-                       if (trg.desktop->root == NULL &&
-                           trg.monitor->desk_head != trg.monitor->desk_tail) {
-                               remove_desktop(trg.monitor, trg.desktop);
-                               return;
-                       } else {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else {
-                       fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
-                       break;
-               }
-               num--, args++;
-       }
-
-       if (changed) {
-               arrange(trg.monitor, trg.desktop);
-       }
-}
-
-void cmd_monitor(char **args, int num, FILE *rsp)
-{
-       if (num < 1) {
-               fail(rsp, "monitor: Missing commands.\n");
-               return;
-       }
-
-       coordinates_t ref = {mon, NULL, NULL};
-       coordinates_t trg = ref;
-
-       if ((*args)[0] != OPT_CHR) {
-               int ret;
-               if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
-                       num--, args++;
-               } else {
-                       handle_failure(ret, "monitor", *args, rsp);
-                       return;
-               }
-       }
-
-       while (num > 0) {
-               if (streq("-f", *args) || streq("--focus", *args)) {
-                       coordinates_t dst = trg;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
-                                       handle_failure(ret, "monitor -f", *args, rsp);
-                                       fail(rsp, "");
-                                       return;
-                               }
-                       }
-                       focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
-               } else if (streq("-s", *args) || streq("--swap", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       coordinates_t dst;
-                       int ret;
-                       if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
-                               if (!swap_monitors(trg.monitor, dst.monitor)) {
-                                       fail(rsp, "");
-                                       return;
-                               }
-                       } else {
-                               handle_failure(ret, "monitor -s", *args, rsp);
-                               return;
-                       }
-               } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       desktop_t *d = trg.monitor->desk_head;
-                       while (num > 0 && d != NULL) {
-                               rename_desktop(trg.monitor, d, *args);
-                               d = d->next;
-                               num--, args++;
-                       }
-                       put_status(SBSC_MASK_REPORT);
-                       while (num > 0) {
-                               add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
-                               num--, args++;
-                       }
-                       while (d != NULL) {
-                               desktop_t *next = d->next;
-                               if (d == mon->desk) {
-                                       focus_node(trg.monitor, d->prev, d->prev->focus);
-                               }
-                               merge_desktops(trg.monitor, d, mon, mon->desk);
-                               remove_desktop(trg.monitor, d);
-                               d = next;
-                       }
-               } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       while (num > 0) {
-                               add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
-                               num--, args++;
-                       }
-               } else if (streq("-r", *args) || streq("--remove", *args)) {
-                       if (num > 1) {
-                               fail(rsp, "monitor %s: Trailing commands.\n", *args);
-                               return;
-                       }
-                       if (mon_head == mon_tail) {
-                               fail(rsp, "");
-                               return;
-                       }
-                       remove_monitor(trg.monitor);
-                       return;
-               } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       desktop_t *d = trg.monitor->desk_head;
-                       while (d != NULL && num > 0) {
-                               desktop_t *next = d->next;
-                               coordinates_t dst;
-                               if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
-                                       swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
-                                       if (next == dst.desktop) {
-                                               next = d;
-                                       }
-                               }
-                               d = next;
-                               num--, args++;
-                       }
-               } else if (streq("-g", *args) || streq("--rectangle", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       xcb_rectangle_t r;
-                       if (parse_rectangle(*args, &r)) {
-                               update_root(trg.monitor, &r);
-                       } else {
-                               fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               return;
-                       }
-               } else if (streq("-n", *args) || streq("--rename", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       rename_monitor(trg.monitor, *args);
-               } else {
-                       fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
-                       return;
-               }
-               num--, args++;
-       }
-}
-
-void cmd_query(char **args, int num, FILE *rsp)
-{
-       coordinates_t ref = {mon, mon->desk, mon->desk->focus};
-       coordinates_t trg = {NULL, NULL, NULL};
-       monitor_select_t *monitor_sel = NULL;
-       desktop_select_t *desktop_sel = NULL;
-       node_select_t *node_sel = NULL;
-       domain_t dom = DOMAIN_TREE;
-       bool print_ids = true;
-       uint8_t d = 0;
-
-       if (num < 1) {
-               fail(rsp, "query: Not enough arguments.\n");
-               return;
-       }
-
-       while (num > 0) {
-               if (streq("-T", *args) || streq("--tree", *args)) {
-                       dom = DOMAIN_TREE, d++;
-               } else if (streq("-M", *args) || streq("--monitors", *args)) {
-                       dom = DOMAIN_MONITOR, d++;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               coordinates_t tmp = ref;
-                               if ((ret = monitor_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
-                                       handle_failure(ret, "query -M", *args, rsp);
-                                       goto end;
-                               }
-                       }
-               } else if (streq("-D", *args) || streq("--desktops", *args)) {
-                       dom = DOMAIN_DESKTOP, d++;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               coordinates_t tmp = ref;
-                               if ((ret = desktop_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
-                                       handle_failure(ret, "query -D", *args, rsp);
-                                       goto end;
-                               }
-                       }
-               } else if (streq("-N", *args) || streq("--nodes", *args)) {
-                       dom = DOMAIN_NODE, d++;
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               coordinates_t tmp = ref;
-                               if ((ret = node_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
-                                       handle_failure(ret, "query -N", *args, rsp);
-                                       goto end;
-                               }
-                       }
-               } else if (streq("-m", *args) || streq("--monitor", *args)) {
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((*args)[0] == '.') {
-                                       free(monitor_sel);
-                                       monitor_sel = malloc(sizeof(monitor_select_t));
-                                       *monitor_sel = make_monitor_select();
-                                       char *desc = copy_string(*args, strlen(*args));
-                                       if (!parse_monitor_modifiers(desc, monitor_sel)) {
-                                               handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
-                                               free(desc);
-                                               goto end;
-                                       }
-                                       free(desc);
-                               } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
-                                       handle_failure(ret, "query -m", *args, rsp);
-                                       goto end;
-                               }
-                       } else {
-                               trg.monitor = ref.monitor;
-                       }
-               } else if (streq("-d", *args) || streq("--desktop", *args)) {
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((*args)[0] == '.') {
-                                       free(desktop_sel);
-                                       desktop_sel = malloc(sizeof(desktop_select_t));
-                                       *desktop_sel = make_desktop_select();
-                                       char *desc = copy_string(*args, strlen(*args));
-                                       if (!parse_desktop_modifiers(desc, desktop_sel)) {
-                                               handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
-                                               free(desc);
-                                               goto end;
-                                       }
-                                       free(desc);
-                               } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
-                                       handle_failure(ret, "query -d", *args, rsp);
-                                       goto end;
-                               }
-                       } else {
-                               trg.monitor = ref.monitor;
-                               trg.desktop = ref.desktop;
-                       }
-               } else if (streq("-n", *args) || streq("--node", *args)) {
-                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
-                               num--, args++;
-                               int ret;
-                               if ((*args)[0] == '.') {
-                                       free(node_sel);
-                                       node_sel = malloc(sizeof(node_select_t));
-                                       *node_sel = make_node_select();
-                                       char *desc = copy_string(*args, strlen(*args));
-                                       if (!parse_node_modifiers(desc, node_sel)) {
-                                               handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
-                                               free(desc);
-                                               goto end;
-                                       }
-                                       free(desc);
-                               } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
-                                       handle_failure(ret, "query -n", *args, rsp);
-                                       goto end;
-                               }
-                       } else {
-                               trg = ref;
-                               if (ref.node == NULL) {
-                                       fail(rsp, "");
-                                       goto end;
-                               }
-                       }
-               } else if (streq("--names", *args)) {
-                       print_ids = false;
-               } else {
-                       fail(rsp, "query: Unknown option: '%s'.\n", *args);
-                       goto end;
-               }
-               num--, args++;
-       }
-
-       if (d < 1) {
-               fail(rsp, "query: No commands given.\n");
-               goto end;
-       }
-
-       if (d > 1) {
-               fail(rsp, "query: Multiple commands given.\n");
-               goto end;
-       }
-
-       if (dom == DOMAIN_TREE && trg.monitor == NULL) {
-               fail(rsp, "query -T: No options given.\n");
-               goto end;
-       }
-
-       if (dom == DOMAIN_NODE) {
-               if (query_node_ids(&ref, &trg, node_sel, rsp) < 1) {
-                       fail(rsp, "");
-               }
-       } else if (dom == DOMAIN_DESKTOP) {
-               if (query_desktop_ids(&ref, &trg, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {
-                       fail(rsp, "");
-               }
-       } else if (dom == DOMAIN_MONITOR) {
-               if (query_monitor_ids(&ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {
-                       fail(rsp, "");
-               }
-       } else {
-               if (trg.node != NULL) {
-                       query_node(trg.node, rsp);
-               } else if (trg.desktop != NULL) {
-                       query_desktop(trg.desktop, rsp);
-               } else  {
-                       query_monitor(trg.monitor, rsp);
-               }
-               fprintf(rsp, "\n");
-       }
-
-end:
-       free(monitor_sel);
-       free(desktop_sel);
-       free(node_sel);
-}
-
-void cmd_rule(char **args, int num, FILE *rsp)
-{
-       if (num < 1) {
-               fail(rsp, "rule: Missing commands.\n");
-               return;
-       }
-
-       while (num > 0) {
-               if (streq("-a", *args) || streq("--add", *args)) {
-                       num--, args++;
-                       if (num < 2) {
-                               fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       rule_t *rule = make_rule();
-                       char *class_name = strtok(*args, COL_TOK);
-                       char *instance_name = strtok(NULL, COL_TOK);
-                       snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
-                       snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
-                       num--, args++;
-                       size_t i = 0;
-                       while (num > 0) {
-                               if (streq("-o", *args) || streq("--one-shot", *args)) {
-                                       rule->one_shot = true;
-                               } else {
-                                       for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
-                                               rule->effect[i] = (*args)[j];
-                                       }
-                                       if (num > 1 && i < sizeof(rule->effect)) {
-                                               rule->effect[i++] = ' ';
-                                       }
-                               }
-                               num--, args++;
-                       }
-                       rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
-                       add_rule(rule);
-               } else if (streq("-r", *args) || streq("--remove", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       uint16_t idx;
-                       while (num > 0) {
-                               if (parse_index(*args, &idx)) {
-                                       remove_rule_by_index(idx - 1);
-                               } else if (streq("tail", *args)) {
-                                       remove_rule(rule_tail);
-                               } else if (streq("head", *args)) {
-                                       remove_rule(rule_head);
-                               } else {
-                                       remove_rule_by_cause(*args);
-                               }
-                               num--, args++;
-                       }
-               } else if (streq("-l", *args) || streq("--list", *args)) {
-                       list_rules(rsp);
-               } else {
-                       fail(rsp, "rule: Unknown command: '%s'.\n", *args);
-                       return;
-               }
-               num--, args++;
-       }
-}
-
-void cmd_wm(char **args, int num, FILE *rsp)
-{
-       if (num < 1) {
-               fail(rsp, "wm: Missing commands.\n");
-               return;
-       }
-
-       while (num > 0) {
-               if (streq("-d", *args) || streq("--dump-state", *args)) {
-                       query_tree(rsp);
-                       fprintf(rsp, "\n");
-               } else if (streq("-l", *args) || streq("--load-state", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       if (!restore_tree(*args)) {
-                               fail(rsp, "");
-                               break;
-                       }
-               } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
-                       num--, args++;
-                       if (num < 2) {
-                               fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       char *name = *args;
-                       num--, args++;
-                       xcb_rectangle_t r;
-                       if (parse_rectangle(*args, &r)) {
-                               monitor_t *m = make_monitor(name, &r, XCB_NONE);
-                               add_monitor(m);
-                               add_desktop(m, make_desktop(NULL, XCB_NONE));
-                       } else {
-                               fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
-                       adopt_orphans();
-               } else if (streq("-g", *args) || streq("--get-status", *args)) {
-                       print_report(rsp);
-               } else if (streq("-h", *args) || streq("--record-history", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
-                               break;
-                       }
-                       bool b;
-                       if (parse_bool(*args, &b)) {
-                               record_history = b;
-                       } else {
-                               fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
-                               break;
-                       }
-               } else {
-                       fail(rsp, "wm: Unkown command: '%s'.\n", *args);
-                       break;
-               }
-               num--, args++;
-       }
-}
-
-int cmd_subscribe(char **args, int num, FILE *rsp)
-{
-       int field = 0;
-       if (num < 1) {
-               field = SBSC_MASK_REPORT;
-       } else {
-               subscriber_mask_t mask;
-               while (num > 0) {
-                       if (parse_subscriber_mask(*args, &mask)) {
-                               field |= mask;
-                       } else {
-                               fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
-                               return SUBSCRIBE_FAILURE;
-                       }
-                       num--, args++;
-               }
-       }
-
-       add_subscriber(rsp, field);
-       return SUBSCRIBE_SUCCESS;
-}
-
-void cmd_quit(char **args, int num, FILE *rsp)
-{
-       if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
-               fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
-               return;
-       }
-       running = false;
-}
-
-void cmd_config(char **args, int num, FILE *rsp)
-{
-       if (num < 1) {
-               fail(rsp, "config: Missing arguments.\n");
-               return;
-       }
-
-       coordinates_t ref = {mon, mon->desk, mon->desk->focus};
-       coordinates_t trg = {NULL, NULL, NULL};
-
-       while (num > 0 && (*args)[0] == OPT_CHR) {
-               if (streq("-m", *args) || streq("--monitor", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       int ret;
-                       if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
-                               handle_failure(ret, "config -m", *args, rsp);
-                               return;
-                       }
-               } else if (streq("-d", *args) || streq("--desktop", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       int ret;
-                       if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
-                               handle_failure(ret, "config -d", *args, rsp);
-                               return;
-                       }
-               } else if (streq("-n", *args) || streq("--node", *args)) {
-                       num--, args++;
-                       if (num < 1) {
-                               fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
-                               return;
-                       }
-                       int ret;
-                       if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
-                               handle_failure(ret, "config -n", *args, rsp);
-                               return;
-                       }
-               } else {
-                       fail(rsp, "config: Unknown option: '%s'.\n", *args);
-                       return;
-               }
-               num--, args++;
-       }
-       if (num == 2) {
-               set_setting(trg, *args, *(args + 1), rsp);
-       } else if (num == 1) {
-               get_setting(trg, *args, rsp);
-       } else {
-               fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
-       }
-}
-
-void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
-{
-       bool colors_changed = false;
-#define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
-               if (loc.node != NULL) { \
-                       for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \
-                               if (n->client != NULL) { \
-                                       n->client->k = v; \
-                               } \
-                       } \
-               } else if (loc.desktop != NULL) { \
-                       loc.desktop->k = v; \
-                       for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
-                               if (n->client != NULL) { \
-                                       n->client->k = v; \
-                               } \
-                       } \
-               } else if (loc.monitor != NULL) { \
-                       loc.monitor->k = v; \
-                       for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
-                               d->k = v; \
-                               for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
-                                       if (n->client != NULL) { \
-                                               n->client->k = v; \
-                                       } \
-                               } \
-                       } \
-               } else { \
-                       k = v; \
-                       for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
-                               m->k = v; \
-                               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
-                                       d->k = v; \
-                                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
-                                               if (n->client != NULL) { \
-                                                       n->client->k = v; \
-                                               } \
-                                       } \
-                               } \
-                       } \
-               }
-       if (streq("border_width", name)) {
-               unsigned int bw;
-               if (sscanf(value, "%u", &bw) != 1) {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-               SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
-#undef SET_DEF_DEFMON_DEFDESK_WIN
-#define SET_DEF_DEFMON_DESK(k, v) \
-               if (loc.desktop != NULL) { \
-                       loc.desktop->k = v; \
-               } else if (loc.monitor != NULL) { \
-                       loc.monitor->k = v; \
-                       for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
-                               d->k = v; \
-                       } \
-               } else { \
-                       k = v; \
-                       for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
-                               m->k = v; \
-                               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
-                                       d->k = v; \
-                               } \
-                       } \
-               }
-       } else if (streq("window_gap", name)) {
-               int wg;
-               if (sscanf(value, "%i", &wg) != 1) {
-                       fail(rsp, "");
-                       return;
-               }
-               SET_DEF_DEFMON_DESK(window_gap, wg)
-#undef SET_DEF_DEFMON_DESK
-#define SET_DEF_MON_DESK(k, v) \
-               if (loc.desktop != NULL) { \
-                       loc.desktop->k = v; \
-               } else if (loc.monitor != NULL) { \
-                       loc.monitor->k = v; \
-               } else { \
-                       k = v; \
-                       for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
-                               m->k = v; \
-                       } \
-               }
-       } else if (streq("top_padding", name)) {
-               int tp;
-               if (sscanf(value, "%i", &tp) != 1) {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-               SET_DEF_MON_DESK(padding.top, tp)
-       } else if (streq("right_padding", name)) {
-               int rp;
-               if (sscanf(value, "%i", &rp) != 1) {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-               SET_DEF_MON_DESK(padding.right, rp)
-       } else if (streq("bottom_padding", name)) {
-               int bp;
-               if (sscanf(value, "%i", &bp) != 1) {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-               SET_DEF_MON_DESK(padding.bottom, bp)
-       } else if (streq("left_padding", name)) {
-               int lp;
-               if (sscanf(value, "%i", &lp) != 1) {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-               SET_DEF_MON_DESK(padding.left, lp)
-#undef SET_DEF_MON_DESK
-#define SET_STR(s) \
-       } else if (streq(#s, name)) { \
-               if (snprintf(s, sizeof(s), "%s", value) < 0) { \
-                       fail(rsp, ""); \
-                       return; \
-               }
-       SET_STR(external_rules_command)
-       SET_STR(status_prefix)
-#undef SET_STR
-       } else if (streq("split_ratio", name)) {
-               double r;
-               if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
-                       split_ratio = r;
-               } else {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
-                       return;
-               }
-               return;
-#define SET_COLOR(s) \
-       } else if (streq(#s, name)) { \
-               if (!is_hex_color(value)) { \
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
-                       return; \
-               } else { \
-                       snprintf(s, sizeof(s), "%s", value); \
-                       colors_changed = true; \
-               }
-       SET_COLOR(normal_border_color)
-       SET_COLOR(active_border_color)
-       SET_COLOR(focused_border_color)
-       SET_COLOR(presel_feedback_color)
-#undef SET_COLOR
-       } else if (streq("initial_polarity", name)) {
-               child_polarity_t p;
-               if (parse_child_polarity(value, &p)) {
-                       initial_polarity = p;
-               } else {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-       } else if (streq("pointer_modifier", name)) {
-               if (parse_modifier_mask(value, &pointer_modifier)) {
-                       ungrab_buttons();
-                       grab_buttons();
-               } else {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-       } else if (streq("pointer_motion_interval", name)) {
-               if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-       } else if (streq("pointer_action1", name) ||
-                  streq("pointer_action2", name) ||
-                  streq("pointer_action3", name)) {
-               int index = name[14] - '1';
-               if (parse_pointer_action(value, &pointer_actions[index])) {
-                       ungrab_buttons();
-                       grab_buttons();
-               } else {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-       } else if (streq("click_to_focus", name)) {
-               if (parse_bool(value, &click_to_focus)) {
-                       ungrab_buttons();
-                       grab_buttons();
-               } else {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-       } else if (streq("focus_follows_pointer", name)) {
-               bool b;
-               if (parse_bool(value, &b)) {
-                       if (b == focus_follows_pointer) {
-                               fail(rsp, "");
-                               return;
-                       }
-                       focus_follows_pointer = b;
-                       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-                               if (b) {
-                                       window_show(m->root);
-                               } else {
-                                       window_hide(m->root);
-                               }
-                               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                                       listen_enter_notify(d->root, b);
-                               }
-                       }
-                       return;
-               } else {
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
-                       return;
-               }
-#define SET_BOOL(s) \
-       } else if (streq(#s, name)) { \
-               if (!parse_bool(value, &s)) { \
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
-                       return; \
-               }
-               SET_BOOL(borderless_monocle)
-               SET_BOOL(gapless_monocle)
-               SET_BOOL(paddingless_monocle)
-               SET_BOOL(single_monocle)
-               SET_BOOL(swallow_first_click)
-               SET_BOOL(pointer_follows_focus)
-               SET_BOOL(pointer_follows_monitor)
-               SET_BOOL(ignore_ewmh_focus)
-               SET_BOOL(center_pseudo_tiled)
-               SET_BOOL(honor_size_hints)
-#undef SET_BOOL
-#define SET_MON_BOOL(s) \
-       } else if (streq(#s, name)) { \
-               if (!parse_bool(value, &s)) { \
-                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
-                       return; \
-               } \
-               if (s) { \
-                       update_monitors(); \
-               }
-               SET_MON_BOOL(remove_disabled_monitors)
-               SET_MON_BOOL(remove_unplugged_monitors)
-               SET_MON_BOOL(merge_overlapping_monitors)
-#undef SET_MON_BOOL
-       } else {
-               fail(rsp, "config: Unknown setting: '%s'.\n", name);
-               return;
-       }
-
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       arrange(m, d);
-                       if (colors_changed) {
-                               update_colors_in(d->root, d, m);
-                       }
-               }
-       }
-}
-
-void get_setting(coordinates_t loc, char *name, FILE* rsp)
-{
-       if (streq("split_ratio", name)) {
-               fprintf(rsp, "%lf", split_ratio);
-       } else if (streq("border_width", name)) {
-               if (loc.node != NULL) {
-                       for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
-                               if (n->client != NULL) {
-                                       fprintf(rsp, "%u", n->client->border_width);
-                                       break;
-                               }
-                       }
-               } else if (loc.desktop != NULL) {
-                       fprintf(rsp, "%u", loc.desktop->border_width);
-               } else if (loc.monitor != NULL) {
-                       fprintf(rsp, "%u", loc.monitor->border_width);
-               } else {
-                       fprintf(rsp, "%u", border_width);
-               }
-       } else if (streq("window_gap", name)) {
-               if (loc.desktop != NULL) {
-                       fprintf(rsp, "%i", loc.desktop->window_gap);
-               } else if (loc.monitor != NULL) {
-                       fprintf(rsp, "%i", loc.monitor->window_gap);
-               } else {
-                       fprintf(rsp, "%i", window_gap);
-               }
-#define GET_DEF_MON_DESK(k) \
-               if (loc.desktop != NULL) { \
-                       fprintf(rsp, "%i", loc.desktop->k); \
-               } else if (loc.monitor != NULL) { \
-                       fprintf(rsp, "%i", loc.monitor->k); \
-               } else { \
-                       fprintf(rsp, "%i", k); \
-               }
-       } else if (streq("top_padding", name)) {
-               GET_DEF_MON_DESK(padding.top)
-       } else if (streq("right_padding", name)) {
-               GET_DEF_MON_DESK(padding.right)
-       } else if (streq("bottom_padding", name)) {
-               GET_DEF_MON_DESK(padding.bottom)
-       } else if (streq("left_padding", name)) {
-               GET_DEF_MON_DESK(padding.left)
-#undef GET_DEF_MON_DESK
-       } else if (streq("external_rules_command", name)) {
-               fprintf(rsp, "%s", external_rules_command);
-       } else if (streq("status_prefix", name)) {
-               fprintf(rsp, "%s", status_prefix);
-       } else if (streq("initial_polarity", name)) {
-               fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
-       } else if (streq("pointer_modifier", name)) {
-               print_modifier_mask(pointer_modifier, rsp);
-       } else if (streq("pointer_motion_interval", name)) {
-               fprintf(rsp, "%u", pointer_motion_interval);
-       } else if (streq("pointer_action1", name) ||
-                  streq("pointer_action2", name) ||
-                  streq("pointer_action3", name)) {
-               int index = name[14] - '1';
-               print_pointer_action(pointer_actions[index], rsp);
-#define GET_COLOR(s) \
-       } else if (streq(#s, name)) { \
-               fprintf(rsp, "%s", s);
-       GET_COLOR(normal_border_color)
-       GET_COLOR(active_border_color)
-       GET_COLOR(focused_border_color)
-       GET_COLOR(presel_feedback_color)
-#undef GET_COLOR
-#define GET_BOOL(s) \
-       } else if (streq(#s, name)) { \
-               fprintf(rsp, "%s", BOOL_STR(s));
-       GET_BOOL(borderless_monocle)
-       GET_BOOL(gapless_monocle)
-       GET_BOOL(paddingless_monocle)
-       GET_BOOL(single_monocle)
-       GET_BOOL(click_to_focus)
-       GET_BOOL(swallow_first_click)
-       GET_BOOL(focus_follows_pointer)
-       GET_BOOL(pointer_follows_focus)
-       GET_BOOL(pointer_follows_monitor)
-       GET_BOOL(ignore_ewmh_focus)
-       GET_BOOL(center_pseudo_tiled)
-       GET_BOOL(honor_size_hints)
-       GET_BOOL(remove_disabled_monitors)
-       GET_BOOL(remove_unplugged_monitors)
-       GET_BOOL(merge_overlapping_monitors)
-#undef GET_BOOL
-       } else {
-               fail(rsp, "config: Unknown setting: '%s'.\n", name);
-               return;
-       }
-       fprintf(rsp, "\n");
-}
-
-void handle_failure(int code, char *src, char *val, FILE *rsp)
-{
-       switch (code) {
-               case SELECTOR_BAD_DESCRIPTOR:
-                       fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
-                       break;
-               case SELECTOR_BAD_MODIFIERS:
-                       fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
-                       break;
-               case SELECTOR_INVALID:
-                       fail(rsp, "");
-                       break;
-       }
-}
-
-void fail(FILE *rsp, char *fmt, ...)
-{
-       fprintf(rsp, FAILURE_MESSAGE);
-       va_list ap;
-       va_start(ap, fmt);
-       vfprintf(rsp, fmt, ap);
-       va_end(ap);
-}
diff --git a/messages.h b/messages.h
deleted file mode 100644 (file)
index a80ff71..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_MESSAGES_H
-#define BSPWM_MESSAGES_H
-
-#include "types.h"
-#include "subscribe.h"
-
-enum {
-       SUBSCRIBE_SUCCESS,
-       SUBSCRIBE_FAILURE
-};
-
-void handle_message(char *msg, int msg_len, FILE *rsp);
-void process_message(char **args, int num, FILE *rsp);
-void cmd_node(char **args, int num, FILE *rsp);
-void cmd_desktop(char **args, int num, FILE *rsp);
-void cmd_monitor(char **args, int num, FILE *rsp);
-void cmd_query(char **args, int num, FILE *rsp);
-void cmd_rule(char **args, int num, FILE *rsp);
-void cmd_wm(char **args, int num, FILE *rsp);
-int cmd_subscribe(char **args, int num, FILE *rsp);
-void cmd_quit(char **args, int num, FILE *rsp);
-void cmd_config(char **args, int num, FILE *rsp);
-void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp);
-void get_setting(coordinates_t loc, char *name, FILE* rsp);
-void handle_failure(int code, char *src, char *val, FILE *rsp);
-void fail(FILE *rsp, char *fmt, ...);
-
-#endif
diff --git a/monitor.c b/monitor.c
deleted file mode 100644 (file)
index 8e26940..0000000
--- a/monitor.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "ewmh.h"
-#include "query.h"
-#include "pointer.h"
-#include "settings.h"
-#include "geometry.h"
-#include "tree.h"
-#include "subscribe.h"
-#include "window.h"
-#include "monitor.h"
-
-monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id)
-{
-       monitor_t *m = calloc(1, sizeof(monitor_t));
-       if (id == XCB_NONE) {
-               m->id = xcb_generate_id(dpy);
-       }
-       m->randr_id = XCB_NONE;
-       snprintf(m->name, sizeof(m->name), "%s", name == NULL ? DEFAULT_MON_NAME : name);
-       m->padding = padding;
-       m->border_width = border_width;
-       m->window_gap = window_gap;
-       m->root = XCB_NONE;
-       m->prev = m->next = NULL;
-       m->desk = m->desk_head = m->desk_tail = NULL;
-       m->wired = true;
-       m->sticky_count = 0;
-       if (rect != NULL) {
-               update_root(m, rect);
-       } else {
-               m->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-       }
-       return m;
-}
-
-void update_root(monitor_t *m, xcb_rectangle_t *rect)
-{
-       xcb_rectangle_t last_rect = m->rectangle;
-       m->rectangle = *rect;
-       if (m->root == XCB_NONE) {
-               uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
-               m->root = xcb_generate_id(dpy);
-               xcb_create_window(dpy, XCB_COPY_FROM_PARENT, m->root, root,
-                                 rect->x, rect->y, rect->width, rect->height, 0,
-                                 XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
-               xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
-               xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
-               window_lower(m->root);
-               if (focus_follows_pointer) {
-                       window_show(m->root);
-               }
-       } else {
-               window_move_resize(m->root, rect->x, rect->y, rect->width, rect->height);
-               put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry 0x%08X %ux%u+%i+%i\n",
-                          m->id, rect->width, rect->height, rect->x, rect->y);
-       }
-       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-               for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                       if (n->client == NULL) {
-                               continue;
-                       }
-                       adapt_geometry(&last_rect, rect, n);
-               }
-               arrange(m, d);
-       }
-}
-
-void rename_monitor(monitor_t *m, const char *name)
-{
-       put_status(SBSC_MASK_MONITOR_RENAME, "monitor_rename 0x%08X %s %s\n", m->id, m->name, name);
-
-       snprintf(m->name, sizeof(m->name), "%s", name);
-       xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
-
-       put_status(SBSC_MASK_REPORT);
-}
-
-monitor_t *find_monitor(uint32_t id)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (m->id == id) {
-                       return m;
-               }
-       }
-       return NULL;
-}
-
-monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (m->randr_id == id) {
-                       return m;
-               }
-       }
-       return NULL;
-}
-
-void embrace_client(monitor_t *m, client_t *c)
-{
-       if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x) {
-               c->floating_rectangle.x = m->rectangle.x;
-       } else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width)) {
-               c->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;
-       }
-       if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y) {
-               c->floating_rectangle.y = m->rectangle.y;
-       } else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height)) {
-               c->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;
-       }
-}
-
-void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
-{
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client == NULL) {
-                       continue;
-               }
-               client_t *c = f->client;
-               /* Clip the rectangle to fit into the monitor.  Without this, the fitting
-                * algorithm doesn't work as expected. This also conserves the
-                * out-of-bounds regions */
-               int left_adjust = MAX((rs->x - c->floating_rectangle.x), 0);
-               int top_adjust = MAX((rs->y - c->floating_rectangle.y), 0);
-               int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (rs->x + rs->width), 0);
-               int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (rs->y + rs->height), 0);
-               c->floating_rectangle.x += left_adjust;
-               c->floating_rectangle.y += top_adjust;
-               c->floating_rectangle.width -= (left_adjust + right_adjust);
-               c->floating_rectangle.height -= (top_adjust + bottom_adjust);
-
-               int dx_s = c->floating_rectangle.x - rs->x;
-               int dy_s = c->floating_rectangle.y - rs->y;
-
-               int nume_x = dx_s * (rd->width - c->floating_rectangle.width);
-               int nume_y = dy_s * (rd->height - c->floating_rectangle.height);
-
-               int deno_x = rs->width - c->floating_rectangle.width;
-               int deno_y = rs->height - c->floating_rectangle.height;
-
-               int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
-               int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
-
-               /* Translate and undo clipping */
-               c->floating_rectangle.width += left_adjust + right_adjust;
-               c->floating_rectangle.height += top_adjust + bottom_adjust;
-               c->floating_rectangle.x = rd->x + dx_d - left_adjust;
-               c->floating_rectangle.y = rd->y + dy_d - top_adjust;
-       }
-}
-
-void focus_monitor(monitor_t *m)
-{
-       if (mon == m) {
-               return;
-       }
-
-       mon = m;
-
-       if (pointer_follows_monitor) {
-               center_pointer(m->rectangle);
-       }
-
-       put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus 0x%08X\n", m->id);
-}
-
-void add_monitor(monitor_t *m)
-{
-       xcb_rectangle_t r = m->rectangle;
-
-       if (mon == NULL) {
-               mon = m;
-               mon_head = m;
-               mon_tail = m;
-       } else {
-               monitor_t *a = mon_head;
-               while (a != NULL && rect_cmp(m->rectangle, a->rectangle) > 0) {
-                       a = a->next;
-               }
-               if (a != NULL) {
-                       monitor_t *b = a->prev;
-                       if (b != NULL) {
-                               b->next = m;
-                       } else {
-                               mon_head = m;
-                       }
-                       m->prev = b;
-                       m->next = a;
-                       a->prev = m;
-               } else {
-                       mon_tail->next = m;
-                       m->prev = mon_tail;
-                       mon_tail = m;
-               }
-       }
-
-       put_status(SBSC_MASK_MONITOR_ADD, "monitor_add 0x%08X %s %ux%u+%i+%i\n", m->id, m->name, r.width, r.height, r.x, r.y);
-
-       put_status(SBSC_MASK_REPORT);
-}
-
-void unlink_monitor(monitor_t *m)
-{
-       monitor_t *prev = m->prev;
-       monitor_t *next = m->next;
-
-       if (prev != NULL) {
-               prev->next = next;
-       }
-
-       if (next != NULL) {
-               next->prev = prev;
-       }
-
-       if (mon_head == m) {
-               mon_head = next;
-       }
-
-       if (mon_tail == m) {
-               mon_tail = prev;
-       }
-
-       if (pri_mon == m) {
-               pri_mon = NULL;
-       }
-
-       if (mon == m) {
-               mon = NULL;
-       }
-}
-
-void remove_monitor(monitor_t *m)
-{
-       put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove 0x%08X\n", m->id);
-
-       while (m->desk_head != NULL) {
-               remove_desktop(m, m->desk_head);
-       }
-
-       monitor_t *last_mon = mon;
-
-       unlink_monitor(m);
-       xcb_destroy_window(dpy, m->root);
-       free(m);
-
-       if (mon != last_mon) {
-               focus_node(NULL, NULL, NULL);
-       }
-
-       put_status(SBSC_MASK_REPORT);
-}
-
-void merge_monitors(monitor_t *ms, monitor_t *md)
-{
-       if (ms == NULL || md == NULL || ms == md) {
-               return;
-       }
-
-       desktop_t *d = ms->desk_head;
-       while (d != NULL) {
-               desktop_t *next = d->next;
-               transfer_desktop(ms, md, d);
-               d = next;
-       }
-}
-
-bool swap_monitors(monitor_t *m1, monitor_t *m2)
-{
-       if (m1 == NULL || m2 == NULL || m1 == m2) {
-               return false;
-       }
-
-       put_status(SBSC_MASK_MONITOR_SWAP, "monitor_swap 0x%08X 0x%08X\n", m1->id, m2->id);
-
-       if (mon_head == m1) {
-               mon_head = m2;
-       } else if (mon_head == m2) {
-               mon_head = m1;
-       }
-       if (mon_tail == m1) {
-               mon_tail = m2;
-       } else if (mon_tail == m2) {
-               mon_tail = m1;
-       }
-
-       monitor_t *p1 = m1->prev;
-       monitor_t *n1 = m1->next;
-       monitor_t *p2 = m2->prev;
-       monitor_t *n2 = m2->next;
-
-       if (p1 != NULL && p1 != m2) {
-               p1->next = m2;
-       }
-       if (n1 != NULL && n1 != m2) {
-               n1->prev = m2;
-       }
-       if (p2 != NULL && p2 != m1) {
-               p2->next = m1;
-       }
-       if (n2 != NULL && n2 != m1) {
-               n2->prev = m1;
-       }
-
-       m1->prev = p2 == m1 ? m2 : p2;
-       m1->next = n2 == m1 ? m2 : n2;
-       m2->prev = p1 == m2 ? m1 : p1;
-       m2->next = n1 == m2 ? m1 : n1;
-
-       ewmh_update_wm_desktops();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-       ewmh_update_current_desktop();
-
-       put_status(SBSC_MASK_REPORT);
-       return true;
-}
-
-monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel)
-{
-       monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
-
-       if (f == NULL) {
-               f = (dir == CYCLE_PREV ? mon_tail : mon_head);
-       }
-
-       while (f != m) {
-               coordinates_t loc = {f, NULL, NULL};
-               if (monitor_matches(&loc, &loc, sel)) {
-                       return f;
-               }
-               f = (dir == CYCLE_PREV ? f->prev : f->next);
-               if (f == NULL) {
-                       f = (dir == CYCLE_PREV ? mon_tail : mon_head);
-               }
-       }
-
-       return NULL;
-}
-
-bool is_inside_monitor(monitor_t *m, xcb_point_t pt)
-{
-       return is_inside(pt, m->rectangle);
-}
-
-monitor_t *monitor_from_point(xcb_point_t pt)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (is_inside_monitor(m, pt)) {
-                       return m;
-               }
-       }
-       return NULL;
-}
-
-monitor_t *monitor_from_client(client_t *c)
-{
-       int16_t xc = c->floating_rectangle.x + c->floating_rectangle.width/2;
-       int16_t yc = c->floating_rectangle.y + c->floating_rectangle.height/2;
-       xcb_point_t pt = {xc, yc};
-       monitor_t *nearest = monitor_from_point(pt);
-       if (nearest == NULL) {
-               int dmin = INT_MAX;
-               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-                       xcb_rectangle_t r = m->rectangle;
-                       int d = abs((r.x + r.width / 2) - xc) + abs((r.y + r.height / 2) - yc);
-                       if (d < dmin) {
-                               dmin = d;
-                               nearest = m;
-                       }
-               }
-       }
-       return nearest;
-}
-
-monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
-{
-       double dmin = DBL_MAX;
-       monitor_t *nearest = NULL;
-       xcb_rectangle_t rect = m->rectangle;
-       for (monitor_t *f = mon_head; f != NULL; f = f->next) {
-               coordinates_t loc = {f, NULL, NULL};
-               xcb_rectangle_t r = f->rectangle;
-               if (f == m ||
-                   !monitor_matches(&loc, &loc, sel) ||
-                   !on_dir_side(rect, r, dir)) {
-                       continue;
-               }
-               double d = distance_center(rect, r);
-               if (d < dmin) {
-                       dmin = d;
-                       nearest = f;
-               }
-       }
-       return nearest;
-}
-
-bool update_monitors(void)
-{
-       xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
-       if (sres == NULL) {
-               return false;
-       }
-
-       monitor_t *m, *mm = NULL;
-
-       int len = xcb_randr_get_screen_resources_outputs_length(sres);
-       xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);
-
-       xcb_randr_get_output_info_cookie_t cookies[len];
-       for (int i = 0; i < len; i++) {
-               cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
-       }
-
-       for (m = mon_head; m != NULL; m = m->next) {
-               m->wired = false;
-       }
-
-       for (int i = 0; i < len; i++) {
-               xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
-               if (info != NULL) {
-                       if (info->crtc != XCB_NONE) {
-                               xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
-                               if (cir != NULL) {
-                                       xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
-                                       mm = get_monitor_by_randr_id(outputs[i]);
-                                       if (mm != NULL) {
-                                               update_root(mm, &rect);
-                                               mm->wired = true;
-                                       } else {
-                                               char *name = (char *) xcb_randr_get_output_info_name(info);
-                                               size_t len = (size_t) xcb_randr_get_output_info_name_length(info);
-                                               char *name_copy = copy_string(name, len);
-                                               mm = make_monitor(name_copy, &rect, XCB_NONE);
-                                               free(name_copy);
-                                               mm->randr_id = outputs[i];
-                                               add_monitor(mm);
-                                       }
-                               }
-                               free(cir);
-                       } else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
-                               m = get_monitor_by_randr_id(outputs[i]);
-                               if (m != NULL) {
-                                       m->wired = true;
-                               }
-                       }
-               }
-               free(info);
-       }
-
-       xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
-       if (gpo != NULL) {
-               pri_mon = get_monitor_by_randr_id(gpo->output);
-       }
-       free(gpo);
-
-       /* handle overlapping monitors */
-       if (merge_overlapping_monitors) {
-               m = mon_head;
-               while (m != NULL) {
-                       monitor_t *next = m->next;
-                       if (m->wired) {
-                               monitor_t *mb = mon_head;
-                               while (mb != NULL) {
-                                       monitor_t *mb_next = mb->next;
-                                       if (m != mb && mb->wired && contains(m->rectangle, mb->rectangle)) {
-                                               if (mm == mb) {
-                                                       mm = m;
-                                               }
-                                               if (next == mb) {
-                                                       next = mb_next;
-                                               }
-                                               merge_monitors(mb, m);
-                                               remove_monitor(mb);
-                                       }
-                                       mb = mb_next;
-                               }
-                       }
-                       m = next;
-               }
-       }
-
-       /* merge and remove disconnected monitors */
-       if (remove_unplugged_monitors) {
-               m = mon_head;
-               while (m != NULL) {
-                       monitor_t *next = m->next;
-                       if (!m->wired) {
-                               merge_monitors(m, mm);
-                               remove_monitor(m);
-                       }
-                       m = next;
-               }
-       }
-
-       /* add one desktop to each new monitor */
-       for (m = mon_head; m != NULL; m = m->next) {
-               if (m->desk == NULL) {
-                       add_desktop(m, make_desktop(NULL, XCB_NONE));
-               }
-       }
-
-       if (!running && mon != NULL) {
-               if (pri_mon != NULL) {
-                       mon = pri_mon;
-               }
-               center_pointer(mon->rectangle);
-               ewmh_update_current_desktop();
-       }
-
-       free(sres);
-
-       return (mon != NULL);
-}
diff --git a/monitor.h b/monitor.h
deleted file mode 100644 (file)
index d0d3544..0000000
--- a/monitor.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_MONITOR_H
-#define BSPWM_MONITOR_H
-
-#define DEFAULT_MON_NAME     "MONITOR"
-
-monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id);
-void update_root(monitor_t *m, xcb_rectangle_t *rect);
-void rename_monitor(monitor_t *m, const char *name);
-monitor_t *find_monitor(uint32_t id);
-monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id);
-void embrace_client(monitor_t *m, client_t *c);
-void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n);
-void focus_monitor(monitor_t *m);
-void add_monitor(monitor_t *m);
-void unlink_monitor(monitor_t *m);
-void remove_monitor(monitor_t *m);
-void merge_monitors(monitor_t *ms, monitor_t *md);
-bool swap_monitors(monitor_t *m1, monitor_t *m2);
-monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel);
-bool is_inside_monitor(monitor_t *m, xcb_point_t pt);
-monitor_t *monitor_from_point(xcb_point_t pt);
-monitor_t *monitor_from_client(client_t *c);
-monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel);
-bool update_monitors(void);
-
-#endif
diff --git a/parse.c b/parse.c
deleted file mode 100644 (file)
index c5328e8..0000000
--- a/parse.c
+++ /dev/null
@@ -1,468 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <errno.h>
-#include "parse.h"
-
-bool parse_bool(char *value, bool *b)
-{
-       if (streq("true", value) || streq("on", value)) {
-               *b = true;
-               return true;
-       } else if (streq("false", value) || streq("off", value)) {
-               *b = false;
-               return true;
-       }
-       return false;
-}
-
-bool parse_split_type(char *s, split_type_t *t)
-{
-       if (streq("horizontal", s)) {
-               *t = TYPE_HORIZONTAL;
-               return true;
-       } else if (streq("vertical", s)) {
-               *t = TYPE_VERTICAL;
-               return true;
-       }
-       return false;
-}
-
-bool parse_split_mode(char *s, split_mode_t *m)
-{
-       if (streq("automatic", s)) {
-               *m = MODE_AUTOMATIC;
-               return true;
-       } else if (streq("vertical", s)) {
-               *m = MODE_MANUAL;
-               return true;
-       }
-       return false;
-}
-
-bool parse_layout(char *s, layout_t *l)
-{
-       if (streq("monocle", s)) {
-               *l = LAYOUT_MONOCLE;
-               return true;
-       } else if (streq("tiled", s)) {
-               *l = LAYOUT_TILED;
-               return true;
-       }
-       return false;
-}
-
-bool parse_client_state(char *s, client_state_t *t)
-{
-       if (streq("tiled", s)) {
-               *t = STATE_TILED;
-               return true;
-       } else if (streq("pseudo_tiled", s)) {
-               *t = STATE_PSEUDO_TILED;
-               return true;
-       } else if (streq("floating", s)) {
-               *t = STATE_FLOATING;
-               return true;
-       } else if (streq("fullscreen", s)) {
-               *t = STATE_FULLSCREEN;
-               return true;
-       }
-       return false;
-}
-
-bool parse_stack_layer(char *s, stack_layer_t *l)
-{
-       if (streq("below", s)) {
-               *l = LAYER_BELOW;
-               return true;
-       } else if (streq("normal", s)) {
-               *l = LAYER_NORMAL;
-               return true;
-       } else if (streq("above", s)) {
-               *l = LAYER_ABOVE;
-               return true;
-       }
-       return false;
-}
-
-bool parse_direction(char *s, direction_t *d)
-{
-       if (streq("north", s)) {
-               *d = DIR_NORTH;
-               return true;
-       } else if (streq("west", s)) {
-               *d = DIR_WEST;
-               return true;
-       } else if (streq("south", s)) {
-               *d = DIR_SOUTH;
-               return true;
-       } else if (streq("east", s)) {
-               *d = DIR_EAST;
-               return true;
-       }
-       return false;
-}
-
-bool parse_cycle_direction(char *s, cycle_dir_t *d)
-{
-       if (streq("next", s)) {
-               *d = CYCLE_NEXT;
-               return true;
-       } else if (streq("prev", s)) {
-               *d = CYCLE_PREV;
-               return true;
-       }
-       return false;
-}
-
-bool parse_circulate_direction(char *s, circulate_dir_t *d)
-{
-       if (streq("forward", s)) {
-               *d = CIRCULATE_FORWARD;
-               return true;
-       } else if (streq("backward", s)) {
-               *d = CIRCULATE_BACKWARD;
-               return true;
-       }
-       return false;
-}
-
-bool parse_history_direction(char *s, history_dir_t *d)
-{
-       if (streq("older", s)) {
-               *d = HISTORY_OLDER;
-               return true;
-       } else if (streq("newer", s)) {
-               *d = HISTORY_NEWER;
-               return true;
-       }
-       return false;
-}
-
-
-bool parse_flip(char *s, flip_t *f)
-{
-       if (streq("horizontal", s)) {
-               *f = FLIP_HORIZONTAL;
-               return true;
-       } else if (streq("vertical", s)) {
-               *f = FLIP_VERTICAL;
-               return true;
-       }
-       return false;
-}
-
-bool parse_resize_handle(char *s, resize_handle_t *h)
-{
-       if (streq("left", s)) {
-               *h = HANDLE_LEFT;
-               return true;
-       } else if (streq("top", s)) {
-               *h = HANDLE_TOP;
-               return true;
-       } else if (streq("right", s)) {
-               *h = HANDLE_RIGHT;
-               return true;
-       } else if (streq("bottom", s)) {
-               *h = HANDLE_BOTTOM;
-               return true;
-       } else if (streq("top_left", s)) {
-               *h = HANDLE_TOP_LEFT;
-               return true;
-       } else if (streq("top_right", s)) {
-               *h = HANDLE_TOP_RIGHT;
-               return true;
-       } else if (streq("bottom_right", s)) {
-               *h = HANDLE_BOTTOM_RIGHT;
-               return true;
-       } else if (streq("bottom_left", s)) {
-               *h = HANDLE_BOTTOM_LEFT;
-               return true;
-       }
-       return false;
-}
-
-bool parse_modifier_mask(char *s, uint16_t *m)
-{
-       if (strcmp(s, "shift") == 0) {
-               *m = XCB_MOD_MASK_SHIFT;
-               return true;
-       } else if (strcmp(s, "control") == 0) {
-               *m = XCB_MOD_MASK_CONTROL;
-               return true;
-       } else if (strcmp(s, "lock") == 0) {
-               *m = XCB_MOD_MASK_LOCK;
-               return true;
-       } else if (strcmp(s, "mod1") == 0) {
-               *m = XCB_MOD_MASK_1;
-               return true;
-       } else if (strcmp(s, "mod2") == 0) {
-               *m = XCB_MOD_MASK_2;
-               return true;
-       } else if (strcmp(s, "mod3") == 0) {
-               *m = XCB_MOD_MASK_3;
-               return true;
-       } else if (strcmp(s, "mod4") == 0) {
-               *m = XCB_MOD_MASK_4;
-               return true;
-       } else if (strcmp(s, "mod5") == 0) {
-               *m = XCB_MOD_MASK_5;
-               return true;
-       }
-       return false;
-}
-
-bool parse_pointer_action(char *s, pointer_action_t *a)
-{
-       if (streq("move", s)) {
-               *a = ACTION_MOVE;
-               return true;
-       } else if (streq("resize_corner", s)) {
-               *a = ACTION_RESIZE_CORNER;
-               return true;
-       } else if (streq("resize_side", s)) {
-               *a = ACTION_RESIZE_SIDE;
-               return true;
-       } else if (streq("focus", s)) {
-               *a = ACTION_FOCUS;
-               return true;
-       } else if (streq("none", s)) {
-               *a = ACTION_NONE;
-               return true;
-       }
-       return false;
-}
-
-bool parse_child_polarity(char *s, child_polarity_t *p)
-{
-       if (streq("first_child", s)) {
-               *p = FIRST_CHILD;
-               return true;
-       } else if (streq("second_child", s)) {
-               *p = SECOND_CHILD;
-               return true;
-       }
-       return false;
-}
-
-bool parse_degree(char *s, int *d)
-{
-       int i = atoi(s);
-       while (i < 0)
-               i += 360;
-       while (i > 359)
-               i -= 360;
-       if ((i % 90) != 0) {
-               return false;
-       } else {
-               *d = i;
-               return true;
-       }
-}
-
-bool parse_id(char *s, uint32_t *id)
-{
-       char *end;
-       errno = 0;
-       uint32_t v = strtol(s, &end, 0);
-       if (errno != 0 || *end != '\0') {
-               return false;
-       }
-       *id = v;
-       return true;
-}
-
-bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)
-{
-       *key = strtok(s, EQL_TOK);
-       char *v = strtok(NULL, EQL_TOK);
-       if (v == NULL) {
-               *state = ALTER_TOGGLE;
-               return true;
-       } else {
-               if (parse_bool(v, value)) {
-                       *state = ALTER_SET;
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-       return false;
-}
-
-bool parse_index(char *s, uint16_t *idx)
-{
-       return (sscanf(s, "^%hu", idx) == 1);
-}
-
-bool parse_rectangle(char *s, xcb_rectangle_t *r)
-{
-       uint16_t w, h;
-       int16_t x, y;
-       if (sscanf(s, "%hux%hu+%hi+%hi", &w, &h, &x, &y) != 4) {
-               return false;
-       }
-       r->width = w;
-       r->height = h;
-       r->x = x;
-       r->y = y;
-       return true;
-}
-
-bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
-{
-       if (streq("all", s)) {
-               *mask = SBSC_MASK_ALL;
-       } else if (streq("node", s)) {
-               *mask = SBSC_MASK_NODE;
-       } else if (streq("desktop", s)) {
-               *mask = SBSC_MASK_DESKTOP;
-       } else if (streq("monitor", s)) {
-               *mask = SBSC_MASK_MONITOR;
-       } else if (streq("pointer_action", s)) {
-               *mask = SBSC_MASK_POINTER_ACTION;
-       } else if (streq("node_manage", s)) {
-               *mask = SBSC_MASK_NODE_MANAGE;
-       } else if (streq("node_unmanage", s)) {
-               *mask = SBSC_MASK_NODE_UNMANAGE;
-       } else if (streq("node_swap", s)) {
-               *mask = SBSC_MASK_NODE_SWAP;
-       } else if (streq("node_transfer", s)) {
-               *mask = SBSC_MASK_NODE_TRANSFER;
-       } else if (streq("node_focus", s)) {
-               *mask = SBSC_MASK_NODE_FOCUS;
-       } else if (streq("node_presel", s)) {
-               *mask = SBSC_MASK_NODE_PRESEL;
-       } else if (streq("node_stack", s)) {
-               *mask = SBSC_MASK_NODE_STACK;
-       } else if (streq("node_activate", s)) {
-               *mask = SBSC_MASK_NODE_ACTIVATE;
-       } else if (streq("node_geometry", s)) {
-               *mask = SBSC_MASK_NODE_GEOMETRY;
-       } else if (streq("node_state", s)) {
-               *mask = SBSC_MASK_NODE_STATE;
-       } else if (streq("node_flag", s)) {
-               *mask = SBSC_MASK_NODE_FLAG;
-       } else if (streq("node_layer", s)) {
-               *mask = SBSC_MASK_NODE_LAYER;
-       } else if (streq("desktop_add", s)) {
-               *mask = SBSC_MASK_DESKTOP_ADD;
-       } else if (streq("desktop_rename", s)) {
-               *mask = SBSC_MASK_DESKTOP_RENAME;
-       } else if (streq("desktop_remove", s)) {
-               *mask = SBSC_MASK_DESKTOP_REMOVE;
-       } else if (streq("desktop_swap", s)) {
-               *mask = SBSC_MASK_DESKTOP_SWAP;
-       } else if (streq("desktop_transfer", s)) {
-               *mask = SBSC_MASK_DESKTOP_TRANSFER;
-       } else if (streq("desktop_focus", s)) {
-               *mask = SBSC_MASK_DESKTOP_FOCUS;
-       } else if (streq("desktop_activate", s)) {
-               *mask = SBSC_MASK_DESKTOP_ACTIVATE;
-       } else if (streq("desktop_layout", s)) {
-               *mask = SBSC_MASK_DESKTOP_LAYOUT;
-       } else if (streq("monitor_add", s)) {
-               *mask = SBSC_MASK_MONITOR_ADD;
-       } else if (streq("monitor_rename", s)) {
-               *mask = SBSC_MASK_MONITOR_RENAME;
-       } else if (streq("monitor_remove", s)) {
-               *mask = SBSC_MASK_MONITOR_REMOVE;
-       } else if (streq("monitor_swap", s)) {
-               *mask = SBSC_MASK_MONITOR_SWAP;
-       } else if (streq("monitor_focus", s)) {
-               *mask = SBSC_MASK_MONITOR_FOCUS;
-       } else if (streq("monitor_geometry", s)) {
-               *mask = SBSC_MASK_MONITOR_GEOMETRY;
-       } else if (streq("report", s)) {
-               *mask = SBSC_MASK_REPORT;
-       } else {
-               return false;
-       }
-       return true;
-}
-
-
-#define GET_MOD(k) \
-       } else if (streq(#k, tok)) { \
-               sel->k = OPTION_TRUE; \
-       } else if (streq("!" #k, tok)) { \
-               sel->k = OPTION_FALSE;
-
-bool parse_monitor_modifiers(char *desc, monitor_select_t *sel)
-{
-       char *tok;
-       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
-               tok[0] = '\0';
-               tok++;
-               if (streq("occupied", tok)) {
-                       sel->occupied = OPTION_TRUE;
-               } else if (streq("!occupied", tok)) {
-                       sel->occupied = OPTION_FALSE;
-               GET_MOD(focused)
-               } else {
-                       return false;
-               }
-       }
-       return true;
-}
-
-bool parse_desktop_modifiers(char *desc, desktop_select_t *sel)
-{
-       char *tok;
-       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
-               tok[0] = '\0';
-               tok++;
-               if (streq("occupied", tok)) {
-                       sel->occupied = OPTION_TRUE;
-               } else if (streq("!occupied", tok)) {
-                       sel->occupied = OPTION_FALSE;
-               GET_MOD(focused)
-               GET_MOD(urgent)
-               GET_MOD(local)
-               } else {
-                       return false;
-               }
-       }
-       return true;
-
-}
-
-bool parse_node_modifiers(char *desc, node_select_t *sel)
-{
-       char *tok;
-       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
-               tok[0] = '\0';
-               tok++;
-               if (streq("tiled", tok)) {
-                       sel->tiled = OPTION_TRUE;
-               } else if (streq("!tiled", tok)) {
-                       sel->tiled = OPTION_FALSE;
-               GET_MOD(automatic)
-               GET_MOD(focused)
-               GET_MOD(local)
-               GET_MOD(active)
-               GET_MOD(leaf)
-               GET_MOD(window)
-               GET_MOD(pseudo_tiled)
-               GET_MOD(floating)
-               GET_MOD(fullscreen)
-               GET_MOD(hidden)
-               GET_MOD(sticky)
-               GET_MOD(private)
-               GET_MOD(locked)
-               GET_MOD(urgent)
-               GET_MOD(same_class)
-               GET_MOD(descendant_of)
-               GET_MOD(ancestor_of)
-               GET_MOD(below)
-               GET_MOD(normal)
-               GET_MOD(above)
-               } else {
-                       return false;
-               }
-       }
-       return true;
-}
-
-#undef GET_MOD
diff --git a/parse.h b/parse.h
deleted file mode 100644 (file)
index bb25799..0000000
--- a/parse.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef BSPWM_PARSE_H
-#define BSPWM_PARSE_H
-
-#include "types.h"
-#include "subscribe.h"
-
-#define OPT_CHR  '-'
-#define CAT_CHR  '.'
-#define EQL_TOK  "="
-#define COL_TOK  ":"
-
-bool parse_bool(char *value, bool *b);
-bool parse_split_type(char *s, split_type_t *t);
-bool parse_split_mode(char *s, split_mode_t *m);
-bool parse_layout(char *s, layout_t *l);
-bool parse_client_state(char *s, client_state_t *t);
-bool parse_stack_layer(char *s, stack_layer_t *l);
-bool parse_direction(char *s, direction_t *d);
-bool parse_cycle_direction(char *s, cycle_dir_t *d);
-bool parse_circulate_direction(char *s, circulate_dir_t *d);
-bool parse_history_direction(char *s, history_dir_t *d);
-bool parse_flip(char *s, flip_t *f);
-bool parse_resize_handle(char *s, resize_handle_t *h);
-bool parse_modifier_mask(char *s, uint16_t *m);
-bool parse_pointer_action(char *s, pointer_action_t *a);
-bool parse_child_polarity(char *s, child_polarity_t *p);
-bool parse_degree(char *s, int *d);
-bool parse_id(char *s, uint32_t *id);
-bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
-bool parse_index(char *s, uint16_t *idx);
-bool parse_rectangle(char *s, xcb_rectangle_t *r);
-bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
-bool parse_monitor_modifiers(char *desc, monitor_select_t *sel);
-bool parse_desktop_modifiers(char *desc, desktop_select_t *sel);
-bool parse_node_modifiers(char *desc, node_select_t *sel);
-
-#endif
diff --git a/pointer.c b/pointer.c
deleted file mode 100644 (file)
index 3f4be8a..0000000
--- a/pointer.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <xcb/xcb_keysyms.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "query.h"
-#include "settings.h"
-#include "stack.h"
-#include "tree.h"
-#include "monitor.h"
-#include "subscribe.h"
-#include "events.h"
-#include "window.h"
-#include "pointer.h"
-
-void pointer_init(void)
-{
-       num_lock = modfield_from_keysym(XK_Num_Lock);
-       caps_lock = modfield_from_keysym(XK_Caps_Lock);
-       scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
-       if (caps_lock == XCB_NO_SYMBOL) {
-               caps_lock = XCB_MOD_MASK_LOCK;
-       }
-       grabbing = false;
-       grabbed_node = NULL;
-}
-
-void window_grab_buttons(xcb_window_t win)
-{
-       if (click_to_focus) {
-               window_grab_button(win, XCB_BUTTON_INDEX_1, XCB_NONE);
-       }
-       uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
-       for (unsigned int i = 0; i < LENGTH(buttons); i++) {
-               if (pointer_actions[i] != ACTION_NONE) {
-                       window_grab_button(win, buttons[i], pointer_modifier);
-               }
-       }
-}
-
-void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
-{
-#define GRAB(b, m) \
-       xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
-                       XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
-               GRAB(button, modifier);
-               if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
-               }
-               if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | num_lock | caps_lock);
-               }
-               if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | caps_lock | scroll_lock);
-               }
-               if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | num_lock | scroll_lock);
-               }
-               if (num_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | num_lock);
-               }
-               if (caps_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | caps_lock);
-               }
-               if (scroll_lock != XCB_NO_SYMBOL) {
-                       GRAB(button, modifier | scroll_lock);
-               }
-#undef GRAB
-}
-
-void grab_buttons(void)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                               window_grab_buttons(n->id);
-                               if (n->presel != NULL) {
-                                       window_grab_buttons(n->presel->feedback);
-                               }
-                       }
-               }
-       }
-}
-
-void ungrab_buttons(void)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                               xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
-                       }
-               }
-       }
-}
-
-int16_t modfield_from_keysym(xcb_keysym_t keysym)
-{
-       uint16_t modfield = 0;
-       xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
-       xcb_get_modifier_mapping_reply_t *reply = NULL;
-       xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
-
-       if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
-           (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
-           reply->keycodes_per_modifier < 1 ||
-           (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
-               goto end;
-       }
-
-       unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
-       for (unsigned int i = 0; i < num_mod; i++) {
-               for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
-                       xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
-                       if (mk == XCB_NO_SYMBOL) {
-                               continue;
-                       }
-                       for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
-                               if (*k == mk) {
-                                       modfield |= (1 << i);
-                               }
-                       }
-               }
-       }
-
-end:
-       xcb_key_symbols_free(symbols);
-       free(keycodes);
-       free(reply);
-       return modfield;
-}
-
-resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
-{
-       resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
-       xcb_rectangle_t rect = get_rectangle(NULL, n);
-       if (pac == ACTION_RESIZE_SIDE) {
-               float W = rect.width;
-               float H = rect.height;
-               float ratio = W / H;
-               float x = pos.x - rect.x;
-               float y = pos.y - rect.y;
-               float diag_a = ratio * y;
-               float diag_b = W - diag_a;
-               if (x < diag_a) {
-                       if (x < diag_b) {
-                               rh = HANDLE_LEFT;
-                       } else {
-                               rh = HANDLE_BOTTOM;
-                       }
-               } else {
-                       if (x < diag_b) {
-                               rh = HANDLE_TOP;
-                       } else {
-                               rh = HANDLE_RIGHT;
-                       }
-               }
-       } else if (pac == ACTION_RESIZE_CORNER) {
-               int16_t mid_x = rect.x + (rect.width / 2);
-               int16_t mid_y = rect.y + (rect.height / 2);
-               if (pos.x > mid_x) {
-                       if (pos.y > mid_y) {
-                               rh = HANDLE_BOTTOM_RIGHT;
-                       } else {
-                               rh = HANDLE_TOP_RIGHT;
-                       }
-               } else {
-                       if (pos.y > mid_y) {
-                               rh = HANDLE_BOTTOM_LEFT;
-                       } else {
-                               rh = HANDLE_TOP_LEFT;
-                       }
-               }
-       }
-       return rh;
-}
-
-bool grab_pointer(pointer_action_t pac)
-{
-       xcb_window_t win = XCB_NONE;
-       xcb_point_t pos;
-
-       query_pointer(&win, &pos);
-
-       coordinates_t loc;
-
-       if (!locate_window(win, &loc)) {
-               if (pac == ACTION_FOCUS) {
-                       monitor_t *m = monitor_from_point(pos);
-                       if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
-                               focus_node(m, m->desk, m->desk->focus);
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       if (pac == ACTION_FOCUS) {
-               if (loc.node != mon->desk->focus) {
-                       focus_node(loc.monitor, loc.desktop, loc.node);
-                       return true;
-               } else if (focus_follows_pointer) {
-                       stack(loc.desktop, loc.node, true);
-               }
-               return false;
-       }
-
-       if (loc.node->client->state == STATE_FULLSCREEN) {
-               return true;
-       }
-
-       xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(dpy, xcb_grab_pointer(dpy, 0, root, XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME), NULL);
-
-       if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
-               free(reply);
-               return true;
-       }
-       free(reply);
-
-       if (pac == ACTION_MOVE) {
-               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
-       } else if (pac == ACTION_RESIZE_CORNER) {
-               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
-       } else if (pac == ACTION_RESIZE_SIDE) {
-               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
-       }
-
-       track_pointer(loc, pac, pos);
-
-       return true;
-}
-
-void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
-{
-       node_t *n = loc.node;
-       resize_handle_t rh = get_handle(loc.node, pos, pac);
-
-       uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
-       xcb_timestamp_t last_motion_time = 0;
-
-       xcb_generic_event_t *evt = NULL;
-
-       grabbing = true;
-       grabbed_node = n;
-
-       do {
-               free(evt);
-               while ((evt = xcb_wait_for_event(dpy)) == NULL) {
-                       xcb_flush(dpy);
-               }
-               uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
-               if (resp_type == XCB_MOTION_NOTIFY) {
-                       xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
-                       uint32_t dtime = e->time - last_motion_time;
-                       if (dtime < pointer_motion_interval) {
-                               continue;
-                       }
-                       last_motion_time = e->time;
-                       int16_t dx = e->root_x - last_motion_x;
-                       int16_t dy = e->root_y - last_motion_y;
-                       if (pac == ACTION_MOVE) {
-                               move_client(&loc, dx, dy);
-                       } else {
-                               resize_client(&loc, rh, dx, dy);
-                       }
-                       last_motion_x = e->root_x;
-                       last_motion_y = e->root_y;
-                       xcb_flush(dpy);
-               } else if (resp_type == XCB_BUTTON_RELEASE) {
-                       grabbing = false;
-               } else {
-                       handle_event(evt);
-               }
-       } while (grabbing && grabbed_node != NULL);
-       free(evt);
-
-       xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
-
-       if (grabbed_node == NULL) {
-               grabbing = false;
-               return;
-       }
-
-       if (pac == ACTION_MOVE) {
-               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move end\n", loc.monitor->id, loc.desktop->id, n->id);
-       } else if (pac == ACTION_RESIZE_CORNER) {
-               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner end\n", loc.monitor->id, loc.desktop->id, n->id);
-       } else if (pac == ACTION_RESIZE_SIDE) {
-               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side end\n", loc.monitor->id, loc.desktop->id, n->id);
-       }
-
-       xcb_rectangle_t r = get_rectangle(NULL, n);
-
-       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, loc.node->id, r.width, r.height, r.x, r.y);
-
-       if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
-           ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
-            n->client->state == STATE_TILED)) {
-               for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
-                       if (f == n || f->client == NULL || !IS_TILED(f->client)) {
-                               continue;
-                       }
-                       xcb_rectangle_t r = f->client->tiled_rectangle;
-                       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, f->id, r.width, r.height, r.x, r.y);
-               }
-       }
-}
diff --git a/pointer.h b/pointer.h
deleted file mode 100644 (file)
index 4930e41..0000000
--- a/pointer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_POINTER_H
-#define BSPWM_POINTER_H
-
-#define XK_Num_Lock     0xff7f
-#define XK_Caps_Lock    0xffe5
-#define XK_Scroll_Lock  0xff14
-
-uint16_t num_lock;
-uint16_t caps_lock;
-uint16_t scroll_lock;
-
-bool grabbing;
-node_t *grabbed_node;
-
-void pointer_init(void);
-void window_grab_buttons(xcb_window_t win);
-void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier);
-void grab_buttons(void);
-void ungrab_buttons(void);
-int16_t modfield_from_keysym(xcb_keysym_t keysym);
-resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac);
-bool grab_pointer(pointer_action_t pac);
-void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos);
-
-#endif
diff --git a/query.c b/query.c
deleted file mode 100644 (file)
index bc51a4a..0000000
--- a/query.c
+++ /dev/null
@@ -1,1032 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "history.h"
-#include "parse.h"
-#include "monitor.h"
-#include "window.h"
-#include "tree.h"
-#include "query.h"
-
-void query_tree(FILE *rsp)
-{
-       fprintf(rsp, "{");
-       fprintf(rsp, "\"focusedMonitorId\":%u,", mon->id);
-       if (pri_mon != NULL) {
-               fprintf(rsp, "\"primaryMonitorId\":%u,", pri_mon->id);
-       }
-       fprintf(rsp, "\"clientsCount\":%i,", clients_count);
-       fprintf(rsp, "\"monitors\":");
-       fprintf(rsp, "[");
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               query_monitor(m, rsp);
-               if (m->next != NULL) {
-                       fprintf(rsp, ",");
-               }
-       }
-       fprintf(rsp, "]");
-       fprintf(rsp,",");
-       fprintf(rsp, "\"focusHistory\":");
-       query_history(rsp);
-       fprintf(rsp,",");
-       fprintf(rsp, "\"stackingList\":");
-       query_stack(rsp);
-       fprintf(rsp, "}");
-
-}
-
-void query_monitor(monitor_t *m, FILE *rsp)
-{
-       fprintf(rsp, "{");
-       fprintf(rsp, "\"name\":\"%s\",", m->name);
-       fprintf(rsp, "\"id\":%u,", m->id);
-       fprintf(rsp, "\"randrId\":%u,", m->randr_id);
-       fprintf(rsp, "\"wired\":%s,", BOOL_STR(m->wired));
-       fprintf(rsp, "\"stickyCount\":%i,", m->sticky_count);
-       fprintf(rsp, "\"windowGap\":%i,", m->window_gap);
-       fprintf(rsp, "\"borderWidth\":%u,", m->border_width);
-       fprintf(rsp, "\"focusedDesktopId\":%u,", m->desk->id);
-       fprintf(rsp, "\"padding\":");
-       query_padding(m->padding, rsp);
-       fprintf(rsp,",");
-       fprintf(rsp, "\"rectangle\":");
-       query_rectangle(m->rectangle, rsp);
-       fprintf(rsp,",");
-       fprintf(rsp, "\"desktops\":");
-       fprintf(rsp, "[");
-       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-               query_desktop(d, rsp);
-               if (d->next != NULL) {
-                       fprintf(rsp,",");
-               }
-       }
-       fprintf(rsp, "]");
-       fprintf(rsp, "}");
-}
-
-void query_desktop(desktop_t *d, FILE *rsp)
-{
-       fprintf(rsp, "{");
-       fprintf(rsp, "\"name\":\"%s\",", d->name);
-       fprintf(rsp, "\"id\":%u,", d->id);
-       fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout));
-       fprintf(rsp, "\"windowGap\":%i,", d->window_gap);
-       fprintf(rsp, "\"borderWidth\":%u,", d->border_width);
-       fprintf(rsp, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0);
-       fprintf(rsp, "\"padding\":");
-       query_padding(d->padding, rsp);
-       fprintf(rsp,",");
-       fprintf(rsp, "\"root\":");
-       query_node(d->root, rsp);
-       fprintf(rsp, "}");
-}
-
-void query_node(node_t *n, FILE *rsp)
-{
-       if (n == NULL) {
-               fprintf(rsp, "null");
-       } else {
-               fprintf(rsp, "{");
-               fprintf(rsp, "\"id\":%u,", n->id);
-               fprintf(rsp, "\"splitType\":\"%s\",", SPLIT_TYPE_STR(n->split_type));
-               fprintf(rsp, "\"splitRatio\":%lf,", n->split_ratio);
-               fprintf(rsp, "\"birthRotation\":%i,", n->birth_rotation);
-               fprintf(rsp, "\"vacant\":%s,", BOOL_STR(n->vacant));
-               fprintf(rsp, "\"hidden\":%s,", BOOL_STR(n->hidden));
-               fprintf(rsp, "\"sticky\":%s,", BOOL_STR(n->sticky));
-               fprintf(rsp, "\"private\":%s,", BOOL_STR(n->private));
-               fprintf(rsp, "\"locked\":%s,", BOOL_STR(n->locked));
-               fprintf(rsp, "\"presel\":");
-               query_presel(n->presel, rsp);
-               fprintf(rsp,",");
-               fprintf(rsp, "\"rectangle\":");
-               query_rectangle(n->rectangle, rsp);
-               fprintf(rsp,",");
-               fprintf(rsp, "\"firstChild\":");
-               query_node(n->first_child, rsp);
-               fprintf(rsp,",");
-               fprintf(rsp, "\"secondChild\":");
-               query_node(n->second_child, rsp);
-               fprintf(rsp,",");
-               fprintf(rsp, "\"client\":");
-               query_client(n->client, rsp);
-               fprintf(rsp, "}");
-       }
-}
-
-void query_presel(presel_t *p, FILE *rsp)
-{
-       if (p == NULL) {
-               fprintf(rsp, "null");
-       } else {
-               fprintf(rsp, "{\"splitDir\":\"%s\",\"splitRatio\":%lf}", SPLIT_DIR_STR(p->split_dir), p->split_ratio);
-       }
-}
-
-void query_client(client_t *c, FILE *rsp)
-{
-       if (c == NULL) {
-               fprintf(rsp, "null");
-       } else {
-               fprintf(rsp, "{");
-               fprintf(rsp, "\"className\":\"%s\",", c->class_name);
-               fprintf(rsp, "\"instanceName\":\"%s\",", c->instance_name);
-               fprintf(rsp, "\"borderWidth\":%u,", c->border_width);
-               fprintf(rsp, "\"state\":\"%s\",", STATE_STR(c->state));
-               fprintf(rsp, "\"lastState\":\"%s\",", STATE_STR(c->last_state));
-               fprintf(rsp, "\"layer\":\"%s\",", LAYER_STR(c->layer));
-               fprintf(rsp, "\"lastLayer\":\"%s\",", LAYER_STR(c->last_layer));
-               fprintf(rsp, "\"urgent\":%s,", BOOL_STR(c->urgent));
-               fprintf(rsp, "\"shown\":%s,", BOOL_STR(c->shown));
-               fprintf(rsp, "\"tiledRectangle\":");
-               query_rectangle(c->tiled_rectangle, rsp);
-               fprintf(rsp,",");
-               fprintf(rsp, "\"floatingRectangle\":");
-               query_rectangle(c->floating_rectangle, rsp);
-               fprintf(rsp, "}");
-       }
-}
-
-void query_rectangle(xcb_rectangle_t r, FILE *rsp)
-{
-       fprintf(rsp, "{\"x\":%i,\"y\":%i,\"width\":%u,\"height\":%u}", r.x, r.y, r.width, r.height);
-}
-
-void query_padding(padding_t p, FILE *rsp)
-{
-       fprintf(rsp, "{\"top\":%i,\"right\":%i,\"bottom\":%i,\"left\":%i}", p.top, p.right, p.bottom, p.left);
-}
-
-void query_history(FILE *rsp)
-{
-       fprintf(rsp, "[");
-       for (history_t *h = history_head; h != NULL; h = h->next) {
-               query_coordinates(&h->loc, rsp);
-               if (h->next != NULL) {
-                       fprintf(rsp, ",");
-               }
-       }
-       fprintf(rsp, "]");
-}
-
-void query_coordinates(coordinates_t *loc, FILE *rsp)
-{
-       fprintf(rsp, "{\"monitorId\":%u,\"desktopId\":%u,\"nodeId\":%u}", loc->monitor->id, loc->desktop->id, loc->node!=NULL?loc->node->id:0);
-}
-
-void query_stack(FILE *rsp)
-{
-       fprintf(rsp, "[");
-       for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
-               fprintf(rsp, "%u", s->node->id);
-               if (s->next != NULL) {
-                       fprintf(rsp, ",");
-               }
-       }
-       fprintf(rsp, "]");
-}
-
-int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
-{
-       int count = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (trg->monitor != NULL && m != trg->monitor) {
-                       continue;
-               }
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       if (trg->desktop != NULL && d != trg->desktop) {
-                               continue;
-                       }
-                       count += query_node_ids_in(d->root, d, m, ref, trg, sel, rsp);
-               }
-       }
-       return count;
-}
-
-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)
-{
-       int count = 0;
-       if (n == NULL) {
-               return 0;
-       } else {
-               coordinates_t loc = {m, d, n};
-               if ((trg->node == NULL || n == trg->node) &&
-                   (sel == NULL || node_matches(&loc, ref, *sel))) {
-                       fprintf(rsp, "0x%08X\n", n->id);
-                       count++;
-               }
-               count += query_node_ids_in(n->first_child, d, m, ref, trg, sel, rsp);
-               count += query_node_ids_in(n->second_child, d, m, ref, trg, sel, rsp);
-       }
-       return count;
-}
-
-int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp)
-{
-       int count = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (trg->monitor != NULL && m != trg->monitor) {
-                       continue;
-               }
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       coordinates_t loc = {m, d, NULL};
-                       if ((trg->desktop != NULL && d != trg->desktop) ||
-                           (sel != NULL && !desktop_matches(&loc, ref, *sel))) {
-                               continue;
-                       }
-                       printer(d, rsp);
-                       count++;
-               }
-       }
-       return count;
-}
-
-int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp)
-{
-       int count = 0;
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               coordinates_t loc = {m, NULL, NULL};
-               if ((trg->monitor != NULL && m != trg->monitor) ||
-                       (sel != NULL && !monitor_matches(&loc, ref, *sel))) {
-                       continue;
-               }
-               printer(m, rsp);
-               count++;
-       }
-       return count;
-}
-
-void fprint_monitor_id(monitor_t *m, FILE *rsp)
-{
-       fprintf(rsp, "0x%08X\n", m->id);
-}
-
-void fprint_monitor_name(monitor_t *m, FILE *rsp)
-{
-       fprintf(rsp, "%s\n", m->name);
-}
-
-void fprint_desktop_id(desktop_t *d, FILE *rsp)
-{
-       fprintf(rsp, "0x%08X\n", d->id);
-}
-
-void fprint_desktop_name(desktop_t *d, FILE *rsp)
-{
-       fprintf(rsp, "%s\n", d->name);
-}
-
-void print_modifier_mask(uint16_t m, FILE *rsp)
-{
-       switch (m) {
-               case XCB_MOD_MASK_SHIFT:
-                       fprintf(rsp, "shift");
-                       break;
-               case XCB_MOD_MASK_CONTROL:
-                       fprintf(rsp, "control");
-                       break;
-               case XCB_MOD_MASK_LOCK:
-                       fprintf(rsp, "lock");
-                       break;
-               case XCB_MOD_MASK_1:
-                       fprintf(rsp, "mod1");
-                       break;
-               case XCB_MOD_MASK_2:
-                       fprintf(rsp, "mod2");
-                       break;
-               case XCB_MOD_MASK_3:
-                       fprintf(rsp, "mod3");
-                       break;
-               case XCB_MOD_MASK_4:
-                       fprintf(rsp, "mod4");
-                       break;
-               case XCB_MOD_MASK_5:
-                       fprintf(rsp, "mod5");
-                       break;
-       }
-}
-
-void print_pointer_action(pointer_action_t a, FILE *rsp)
-{
-       switch (a) {
-               case ACTION_MOVE:
-                       fprintf(rsp, "move");
-                       break;
-               case ACTION_RESIZE_SIDE:
-                       fprintf(rsp, "resize_side");
-                       break;
-               case ACTION_RESIZE_CORNER:
-                       fprintf(rsp, "resize_corner");
-                       break;
-               case ACTION_FOCUS:
-                       fprintf(rsp, "focus");
-                       break;
-               case ACTION_NONE:
-                       fprintf(rsp, "none");
-                       break;
-       }
-}
-
-node_select_t make_node_select(void)
-{
-       node_select_t sel = {
-               .automatic = OPTION_NONE,
-               .focused = OPTION_NONE,
-               .local = OPTION_NONE,
-               .active = OPTION_NONE,
-               .leaf = OPTION_NONE,
-               .window = OPTION_NONE,
-               .tiled = OPTION_NONE,
-               .pseudo_tiled = OPTION_NONE,
-               .floating = OPTION_NONE,
-               .fullscreen = OPTION_NONE,
-               .hidden = OPTION_NONE,
-               .sticky = OPTION_NONE,
-               .private = OPTION_NONE,
-               .locked = OPTION_NONE,
-               .urgent = OPTION_NONE,
-               .same_class = OPTION_NONE,
-               .descendant_of = OPTION_NONE,
-               .ancestor_of = OPTION_NONE,
-               .below = OPTION_NONE,
-               .normal = OPTION_NONE,
-               .above = OPTION_NONE
-       };
-       return sel;
-}
-
-desktop_select_t make_desktop_select(void)
-{
-       desktop_select_t sel = {
-               .occupied = OPTION_NONE,
-               .focused = OPTION_NONE,
-               .urgent = OPTION_NONE,
-               .local = OPTION_NONE
-       };
-       return sel;
-}
-
-monitor_select_t make_monitor_select(void)
-{
-       monitor_select_t sel = {
-               .occupied = OPTION_NONE,
-               .focused = OPTION_NONE
-       };
-       return sel;
-}
-
-int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
-{
-       coordinates_t ref_copy = *ref;
-       ref = &ref_copy;
-       char *desc_copy = copy_string(desc, strlen(desc));
-       desc = desc_copy;
-
-       char *hash = NULL;
-       char *path = strrchr(desc, '@');
-       if (path == NULL) {
-               hash = strrchr(desc, '#');
-       } else {
-               if (path != desc && *(path - 1) == '#') {
-                       hash = path - 1;
-               }
-       }
-
-       if (hash != NULL) {
-               *hash = '\0';
-               int ret;
-               coordinates_t tmp = {mon, mon->desk, mon->desk->focus};
-               if ((ret = node_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
-                       desc = hash + 1;
-               } else {
-                       free(desc_copy);
-                       return ret;
-               }
-       }
-
-       node_select_t sel = make_node_select();
-       char *colon = strrchr(desc, ':');
-
-       if (!parse_node_modifiers(colon != NULL ? colon : desc, &sel)) {
-               free(desc_copy);
-               return SELECTOR_BAD_MODIFIERS;
-       }
-
-       dst->node = NULL;
-
-       direction_t dir;
-       cycle_dir_t cyc;
-       history_dir_t hdi;
-       if (parse_direction(desc, &dir)) {
-               find_nearest_neighbor(ref, dst, dir, sel);
-       } else if (parse_cycle_direction(desc, &cyc)) {
-               find_closest_node(ref, dst, cyc, sel);
-       } else if (parse_history_direction(desc, &hdi)) {
-               history_find_node(hdi, ref, dst, sel);
-       } else if (streq("last", desc)) {
-               history_find_node(HISTORY_OLDER, ref, dst, sel);
-       } else if (streq("biggest", desc)) {
-               find_biggest(ref, dst, sel);
-       } else if (streq("pointed", desc)) {
-               xcb_window_t win;
-               query_pointer(&win, NULL);
-               if (locate_window(win, dst) && node_matches(dst, ref, sel)) {
-                       return SELECTOR_OK;
-               } else {
-                       return SELECTOR_INVALID;
-               }
-       } else if (streq("focused", desc)) {
-               coordinates_t loc = {mon, mon->desk, mon->desk->focus};
-               if (node_matches(&loc, ref, sel)) {
-                       *dst = loc;
-               }
-       } else if (*desc == '@') {
-               desc++;
-               *dst = *ref;
-               if (colon != NULL) {
-                       *colon = '\0';
-                       int ret;
-                       if ((ret = desktop_from_desc(desc, ref, dst)) == SELECTOR_OK) {
-                               dst->node = dst->desktop->focus;
-                               desc = colon + 1;
-                       } else {
-                               free(desc_copy);
-                               return ret;
-                       }
-               }
-               if (*desc == '/') {
-                       dst->node = dst->desktop->root;
-               }
-               char *move = strtok(desc, PTH_TOK);
-               while (move != NULL && dst->node != NULL) {
-                       if (streq("first", move) || streq("1", move)) {
-                               dst->node = dst->node->first_child;
-                       } else if (streq("second", move) || streq("2", move)) {
-                               dst->node = dst->node->second_child;
-                       } else if (streq("parent", move)) {
-                               dst->node = dst->node->parent;
-                       } else if (streq("brother", move)) {
-                               dst->node = brother_tree(dst->node);
-                       } else {
-                               direction_t dir;
-                               if (parse_direction(move, &dir)) {
-                                       dst->node = find_fence(dst->node, dir);
-                               } else {
-                                       free(desc_copy);
-                                       return SELECTOR_BAD_DESCRIPTOR;
-                               }
-                       }
-                       move = strtok(NULL, PTH_TOK);
-               }
-               free(desc_copy);
-               if (dst->node != NULL) {
-                       if (node_matches(dst, ref, sel)) {
-                               return SELECTOR_OK;
-                       } else {
-                               return SELECTOR_INVALID;
-                       }
-               } else if (dst->desktop->root != NULL) {
-                       return SELECTOR_INVALID;
-               }
-               return SELECTOR_OK;
-       } else {
-               uint32_t id;
-               if (parse_id(desc, &id)) {
-                       free(desc_copy);
-                       if (find_by_id(id, dst) && node_matches(dst, ref, sel)) {
-                               return SELECTOR_OK;
-                       } else {
-                               return SELECTOR_INVALID;
-                       }
-               } else {
-                       free(desc_copy);
-                       return SELECTOR_BAD_DESCRIPTOR;
-               }
-       }
-
-       free(desc_copy);
-
-       if (dst->node == NULL) {
-               return SELECTOR_INVALID;
-       }
-
-       return SELECTOR_OK;
-}
-
-int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
-{
-       coordinates_t ref_copy = *ref;
-       ref = &ref_copy;
-       char *desc_copy = copy_string(desc, strlen(desc));
-       desc = desc_copy;
-
-       char *hash = strrchr(desc, '#');
-
-       if (hash != NULL) {
-               *hash = '\0';
-               int ret;
-               coordinates_t tmp = {mon, mon->desk, NULL};
-               if ((ret = desktop_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
-                       desc = hash + 1;
-               } else {
-                       free(desc_copy);
-                       return ret;
-               }
-       }
-
-       desktop_select_t sel = make_desktop_select();
-       char *colon = strrchr(desc, ':');
-
-       if (!parse_desktop_modifiers(colon != NULL ? colon : desc, &sel)) {
-               free(desc_copy);
-               return SELECTOR_BAD_MODIFIERS;
-       }
-
-       dst->desktop = NULL;
-
-       cycle_dir_t cyc;
-       history_dir_t hdi;
-       uint16_t idx;
-       uint32_t id;
-       if (parse_cycle_direction(desc, &cyc)) {
-               find_closest_desktop(ref, dst, cyc, sel);
-       } else if (parse_history_direction(desc, &hdi)) {
-               history_find_desktop(hdi, ref, dst, sel);
-       } else if (streq("last", desc)) {
-               history_find_desktop(HISTORY_OLDER, ref, dst, sel);
-       } else if (streq("focused", desc)) {
-               coordinates_t loc = {mon, mon->desk, NULL};
-               if (desktop_matches(&loc, ref, sel)) {
-                       *dst = loc;
-               }
-       } else if (colon != NULL) {
-               *colon = '\0';
-               int ret;
-               if ((ret = monitor_from_desc(desc, ref, dst)) == SELECTOR_OK) {
-                       if (streq("focused", colon + 1)) {
-                               coordinates_t loc = {dst->monitor, dst->monitor->desk, NULL};
-                               if (desktop_matches(&loc, ref, sel)) {
-                                       *dst = loc;
-                               }
-                       } else if (parse_index(colon + 1, &idx)) {
-                               free(desc_copy);
-                               if (desktop_from_index(idx, dst, dst->monitor) && desktop_matches(dst, ref, sel)) {
-                                       return SELECTOR_OK;
-                               } else {
-                                       return SELECTOR_INVALID;
-                               }
-                       } else {
-                               free(desc_copy);
-                               return SELECTOR_BAD_DESCRIPTOR;
-                       }
-               } else {
-                       free(desc_copy);
-                       return ret;
-               }
-       } else if (parse_index(desc, &idx) && desktop_from_index(idx, dst, NULL)) {
-               free(desc_copy);
-               if (desktop_matches(dst, ref, sel)) {
-                       return SELECTOR_OK;
-               } else {
-                       return SELECTOR_INVALID;
-               }
-       } else if (parse_id(desc, &id) && desktop_from_id(id, dst, NULL)) {
-               free(desc_copy);
-               if (desktop_matches(dst, ref, sel)) {
-                       return SELECTOR_OK;
-               } else {
-                       return SELECTOR_INVALID;
-               }
-       } else {
-               if (locate_desktop(desc, dst)) {
-                       free(desc_copy);
-                       if (desktop_matches(dst, ref, sel)) {
-                               return SELECTOR_OK;
-                       } else {
-                               return SELECTOR_INVALID;
-                       }
-               } else {
-                       free(desc_copy);
-                       return SELECTOR_BAD_DESCRIPTOR;
-               }
-       }
-
-       free(desc_copy);
-
-       if (dst->desktop == NULL) {
-               return SELECTOR_INVALID;
-       }
-
-       return SELECTOR_OK;
-}
-
-int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
-{
-       coordinates_t ref_copy = *ref;
-       ref = &ref_copy;
-       char *desc_copy = copy_string(desc, strlen(desc));
-       desc = desc_copy;
-
-       char *hash = strrchr(desc, '#');
-
-       if (hash != NULL) {
-               *hash = '\0';
-               int ret;
-               coordinates_t tmp = {mon, NULL, NULL};
-               if ((ret = monitor_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
-                       desc = hash + 1;
-               } else {
-                       free(desc_copy);
-                       return ret;
-               }
-       }
-
-       monitor_select_t sel = make_monitor_select();
-
-       if (!parse_monitor_modifiers(desc, &sel)) {
-               free(desc_copy);
-               return SELECTOR_BAD_MODIFIERS;
-       }
-
-       dst->monitor = NULL;
-
-       direction_t dir;
-       cycle_dir_t cyc;
-       history_dir_t hdi;
-       uint16_t idx;
-       uint32_t id;
-       if (parse_direction(desc, &dir)) {
-               dst->monitor = nearest_monitor(ref->monitor, dir, sel);
-       } else if (parse_cycle_direction(desc, &cyc)) {
-               dst->monitor = closest_monitor(ref->monitor, cyc, sel);
-       } else if (parse_history_direction(desc, &hdi)) {
-               history_find_monitor(hdi, ref, dst, sel);
-       } else if (streq("last", desc)) {
-               history_find_monitor(HISTORY_OLDER, ref, dst, sel);
-       } else if (streq("primary", desc)) {
-               if (pri_mon != NULL) {
-                       coordinates_t loc = {pri_mon, NULL, NULL};
-                       if (monitor_matches(&loc, ref, sel)) {
-                               dst->monitor = pri_mon;
-                       }
-               }
-       } else if (streq("focused", desc)) {
-               coordinates_t loc = {mon, NULL, NULL};
-               if (monitor_matches(&loc, ref, sel)) {
-                       dst->monitor = mon;
-               }
-       } else if (parse_index(desc, &idx) && monitor_from_index(idx, dst)) {
-               free(desc_copy);
-               if (monitor_matches(dst, ref, sel)) {
-                       return SELECTOR_OK;
-               } else {
-                       return SELECTOR_INVALID;
-               }
-       } else if (parse_id(desc, &id) && monitor_from_id(id, dst)) {
-               free(desc_copy);
-               if (monitor_matches(dst, ref, sel)) {
-                       return SELECTOR_OK;
-               } else {
-                       return SELECTOR_INVALID;
-               }
-       } else {
-               if (locate_monitor(desc, dst)) {
-                       free(desc_copy);
-                       if (monitor_matches(dst, ref, sel)) {
-                               return SELECTOR_OK;
-                       } else {
-                               return SELECTOR_INVALID;
-                       }
-               } else {
-                       free(desc_copy);
-                       return SELECTOR_BAD_DESCRIPTOR;
-               }
-       }
-
-       free(desc_copy);
-
-       if (dst->monitor == NULL) {
-               return SELECTOR_INVALID;
-       }
-
-       return SELECTOR_OK;
-}
-
-bool locate_window(xcb_window_t win, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                               if (n->client == NULL) {
-                                       continue;
-                               }
-                               if (n->id == win) {
-                                       loc->monitor = m;
-                                       loc->desktop = d;
-                                       loc->node = n;
-                                       return true;
-                               }
-                       }
-               }
-       }
-       return false;
-}
-
-bool locate_desktop(char *name, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       if (streq(d->name, name)) {
-                               loc->monitor = m;
-                               loc->desktop = d;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-bool locate_monitor(char *name, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (streq(m->name, name)) {
-                       loc->monitor = m;
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (mm != NULL && m != mm) {
-                       continue;
-               }
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       if (d->id == id) {
-                               loc->monitor = m;
-                               loc->desktop = d;
-                               loc->node = NULL;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (mm != NULL && m != mm) {
-                       continue;
-               }
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, idx--) {
-                       if (idx == 1) {
-                               loc->monitor = m;
-                               loc->desktop = d;
-                               loc->node = NULL;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-bool monitor_from_id(uint32_t id, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               if (m->id == id) {
-                       loc->monitor = m;
-                       loc->desktop = NULL;
-                       loc->node = NULL;
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool monitor_from_index(int idx, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next, idx--) {
-               if (idx == 1) {
-                       loc->monitor = m;
-                       loc->desktop = NULL;
-                       loc->node = NULL;
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
-{
-       if (loc->node == NULL) {
-               return false;
-       }
-
-       if (sel.focused != OPTION_NONE &&
-           loc->node != loc->desktop->focus
-           ? sel.focused == OPTION_TRUE
-           : sel.focused == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.automatic != OPTION_NONE &&
-           loc->node->presel != NULL
-           ? sel.automatic == OPTION_TRUE
-           : sel.automatic == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.local != OPTION_NONE &&
-           loc->desktop != ref->desktop
-           ? sel.local == OPTION_TRUE
-           : sel.local == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.active != OPTION_NONE &&
-           loc->desktop != loc->monitor->desk
-           ? sel.active == OPTION_TRUE
-           : sel.active == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.leaf != OPTION_NONE &&
-           !is_leaf(loc->node)
-           ? sel.leaf == OPTION_TRUE
-           : sel.leaf == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.window != OPTION_NONE &&
-           loc->node->client == NULL
-           ? sel.window == OPTION_TRUE
-           : sel.window == OPTION_FALSE) {
-               return false;
-       }
-
-#define NFLAG(p) \
-       if (sel.p != OPTION_NONE && \
-           !loc->node->p \
-           ? sel.p == OPTION_TRUE \
-           : sel.p == OPTION_FALSE) { \
-               return false; \
-       }
-       NFLAG(hidden)
-       NFLAG(sticky)
-       NFLAG(private)
-       NFLAG(locked)
-#undef WFLAG
-
-       if (loc->node->client == NULL &&
-               (sel.same_class != OPTION_NONE ||
-                sel.tiled != OPTION_NONE ||
-                sel.pseudo_tiled != OPTION_NONE ||
-                sel.floating != OPTION_NONE ||
-                sel.fullscreen != OPTION_NONE ||
-                sel.below != OPTION_NONE ||
-                sel.normal != OPTION_NONE ||
-                sel.above != OPTION_NONE ||
-                sel.urgent != OPTION_NONE)) {
-               return false;
-       }
-
-       if (ref->node != NULL && ref->node->client != NULL &&
-           sel.same_class != OPTION_NONE &&
-           streq(loc->node->client->class_name, ref->node->client->class_name)
-           ? sel.same_class == OPTION_FALSE
-           : sel.same_class == OPTION_TRUE) {
-               return false;
-       }
-
-       if (sel.descendant_of != OPTION_NONE &&
-           !is_descendant(loc->node, ref->node)
-           ? sel.descendant_of == OPTION_TRUE
-           : sel.descendant_of == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.ancestor_of != OPTION_NONE &&
-           !is_descendant(ref->node, loc->node)
-           ? sel.ancestor_of == OPTION_TRUE
-           : sel.ancestor_of == OPTION_FALSE) {
-               return false;
-       }
-
-#define WSTATE(p, e) \
-       if (sel.p != OPTION_NONE && \
-           loc->node->client->state != e \
-           ? sel.p == OPTION_TRUE \
-           : sel.p == OPTION_FALSE) { \
-               return false; \
-       }
-       WSTATE(tiled, STATE_TILED)
-       WSTATE(pseudo_tiled, STATE_PSEUDO_TILED)
-       WSTATE(floating, STATE_FLOATING)
-       WSTATE(fullscreen, STATE_FULLSCREEN)
-#undef WSTATE
-
-#define WLAYER(p, e) \
-       if (sel.p != OPTION_NONE && \
-           loc->node->client->layer != e \
-           ? sel.p == OPTION_TRUE \
-           : sel.p == OPTION_FALSE) { \
-               return false; \
-       }
-       WLAYER(below, LAYER_BELOW)
-       WLAYER(normal, LAYER_NORMAL)
-       WLAYER(above, LAYER_ABOVE)
-#undef WLAYER
-
-#define WFLAG(p) \
-       if (sel.p != OPTION_NONE && \
-           !loc->node->client->p \
-           ? sel.p == OPTION_TRUE \
-           : sel.p == OPTION_FALSE) { \
-               return false; \
-       }
-       WFLAG(urgent)
-#undef WFLAG
-
-       return true;
-}
-
-bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel)
-{
-       if (sel.occupied != OPTION_NONE &&
-           loc->desktop->root == NULL
-           ? sel.occupied == OPTION_TRUE
-           : sel.occupied == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.focused != OPTION_NONE &&
-           loc->desktop != loc->monitor->desk
-           ? sel.focused == OPTION_TRUE
-           : sel.focused == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.urgent != OPTION_NONE &&
-           !is_urgent(loc->desktop)
-           ? sel.urgent == OPTION_TRUE
-           : sel.urgent == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.local != OPTION_NONE &&
-           ref->monitor != loc->monitor
-           ? sel.local == OPTION_TRUE
-           : sel.local == OPTION_FALSE) {
-               return false;
-       }
-
-       return true;
-}
-
-bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel)
-{
-       if (sel.occupied != OPTION_NONE &&
-           loc->monitor->desk->root == NULL
-           ? sel.occupied == OPTION_TRUE
-           : sel.occupied == OPTION_FALSE) {
-               return false;
-       }
-
-       if (sel.focused != OPTION_NONE &&
-           mon != loc->monitor
-           ? sel.focused == OPTION_TRUE
-           : sel.focused == OPTION_FALSE) {
-               return false;
-       }
-
-       return true;
-}
diff --git a/query.h b/query.h
deleted file mode 100644 (file)
index 2ff20d7..0000000
--- a/query.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_QUERY_H
-#define BSPWM_QUERY_H
-
-#define PTH_TOK  "/"
-
-typedef enum {
-       DOMAIN_TREE,
-       DOMAIN_MONITOR,
-       DOMAIN_DESKTOP,
-       DOMAIN_NODE
-} domain_t;
-
-enum {
-       SELECTOR_OK,
-       SELECTOR_INVALID,
-       SELECTOR_BAD_MODIFIERS,
-       SELECTOR_BAD_DESCRIPTOR
-};
-
-typedef void (*monitor_printer_t)(monitor_t *m, FILE *rsp);
-typedef void (*desktop_printer_t)(desktop_t *m, FILE *rsp);
-
-void query_tree(FILE *rsp);
-void query_monitor(monitor_t *m, FILE *rsp);
-void query_desktop(desktop_t *d, FILE *rsp);
-void query_node(node_t *n, FILE *rsp);
-void query_presel(presel_t *p, FILE *rsp);
-void query_client(client_t *c, FILE *rsp);
-void query_rectangle(xcb_rectangle_t r, FILE *rsp);
-void query_padding(padding_t p, FILE *rsp);
-void query_history(FILE *rsp);
-void query_coordinates(coordinates_t *loc, FILE *rsp);
-void query_stack(FILE *rsp);
-int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
-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);
-int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp);
-int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp);
-void fprint_monitor_id(monitor_t *m, FILE *rsp);
-void fprint_monitor_name(monitor_t *m, FILE *rsp);
-void fprint_desktop_id(desktop_t *d, FILE *rsp);
-void fprint_desktop_name(desktop_t *d, FILE *rsp);
-void print_modifier_mask(uint16_t m, FILE *rsp);
-void print_pointer_action(pointer_action_t a, FILE *rsp);
-node_select_t make_node_select(void);
-desktop_select_t make_desktop_select(void);
-monitor_select_t make_monitor_select(void);
-int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
-int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
-int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
-bool locate_window(xcb_window_t win, coordinates_t *loc);
-bool locate_desktop(char *name, coordinates_t *loc);
-bool locate_monitor(char *name, coordinates_t *loc);
-bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm);
-bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm);
-bool monitor_from_id(uint32_t id, coordinates_t *loc);
-bool monitor_from_index(int idx, coordinates_t *loc);
-bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel);
-bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
-bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel);
-
-#endif
diff --git a/restore.c b/restore.c
deleted file mode 100644 (file)
index de51f6c..0000000
--- a/restore.c
+++ /dev/null
@@ -1,562 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "ewmh.h"
-#include "history.h"
-#include "pointer.h"
-#include "monitor.h"
-#include "query.h"
-#include "stack.h"
-#include "tree.h"
-#include "settings.h"
-#include "restore.h"
-#include "window.h"
-#include "parse.h"
-
-bool restore_tree(const char *file_path)
-{
-       size_t jslen;
-       char *json = read_string(file_path, &jslen);
-
-       if (json == NULL) {
-               return false;
-       }
-
-       int nbtok = 256;
-       jsmn_parser parser;
-       jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
-
-       if (tokens == NULL) {
-               perror("Restore tree: malloc");
-               free(json);
-               return false;
-       }
-
-       jsmn_init(&parser);
-       int ret;
-
-       while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
-               nbtok *= 2;
-               jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
-               if (rtokens == NULL) {
-                       perror("Restore tree: realloc");
-                       free(tokens);
-                       free(json);
-                       return false;
-               } else {
-                       tokens = rtokens;
-               }
-       }
-
-       if (ret < 0) {
-               warn("Restore tree: jsmn_parse: ");
-               switch (ret) {
-                       case JSMN_ERROR_NOMEM:
-                               warn("not enough memory.\n");
-                               break;
-                       case JSMN_ERROR_INVAL:
-                               warn("found invalid character inside JSON string.\n");
-                               break;
-                       case JSMN_ERROR_PART:
-                               warn("not a full JSON packet.\n");
-                               break;
-                       default:
-                               warn("unknown error.\n");
-                               break;
-               }
-
-               free(tokens);
-               free(json);
-
-               return false;
-       }
-
-       int num = tokens[0].size;
-
-       if (num < 1) {
-               free(tokens);
-               free(json);
-
-               return false;
-       }
-
-       mon = NULL;
-       while (mon_head != NULL) {
-               remove_monitor(mon_head);
-       }
-
-       jsmntok_t *t = tokens + 1;
-       uint32_t focused_monitor_id = 0, primary_monitor_id = 0;
-
-       for (int i = 0; i < num; i++) {
-               if (keyeq("focusedMonitorId", t, json)) {
-                       t++;
-                       sscanf(json + t->start, "%u", &focused_monitor_id);
-               } else if (keyeq("primaryMonitorId", t, json)) {
-                       t++;
-                       sscanf(json + t->start, "%u", &primary_monitor_id);
-               } else if (keyeq("clientsCount", t, json)) {
-                       t++;
-                       sscanf(json + t->start, "%u", &clients_count);
-               } else if (keyeq("monitors", t, json)) {
-                       t++;
-                       int s = t->size;
-                       t++;
-                       for (int j = 0; j < s; j++) {
-                               monitor_t *m = restore_monitor(&t, json);
-                               if (m->desk == NULL) {
-                                       add_desktop(m, make_desktop(NULL, XCB_NONE));
-                               }
-                               add_monitor(m);
-                       }
-                       continue;
-               } else if (keyeq("focusHistory", t, json)) {
-                       t++;
-                       restore_history(&t, json);
-                       continue;
-               } else if (keyeq("stackingList", t, json)) {
-                       t++;
-                       restore_stack(&t, json);
-                       continue;
-               }
-               t++;
-       }
-
-       if (focused_monitor_id != 0) {
-               coordinates_t loc;
-               if (monitor_from_id(focused_monitor_id, &loc)) {
-                       mon = loc.monitor;
-               }
-       }
-
-       if (primary_monitor_id != 0) {
-               coordinates_t loc;
-               if (monitor_from_id(primary_monitor_id, &loc)) {
-                       pri_mon = loc.monitor;
-               }
-       }
-
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       refresh_presel_feedbacks(m, d, d->root);
-                       restack_presel_feedbacks(d);
-                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-                               if (n->client == NULL) {
-                                       continue;
-                               }
-                               initialize_client(n);
-                               uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
-                               xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
-                               window_grab_buttons(n->id);
-                       }
-               }
-       }
-
-       ewmh_update_client_list(false);
-       ewmh_update_client_list(true);
-       ewmh_update_active_window();
-       ewmh_update_number_of_desktops();
-       ewmh_update_current_desktop();
-       ewmh_update_desktop_names();
-       ewmh_update_desktop_viewport();
-
-       free(tokens);
-       free(json);
-
-       return true;
-}
-
-#define RESTORE_INT(k, p) \
-       } else if (keyeq(#k, *t, json)) { \
-               (*t)++; \
-               sscanf(json + (*t)->start, "%i", p);
-
-#define RESTORE_UINT(k, p) \
-       } else if (keyeq(#k, *t, json)) { \
-               (*t)++; \
-               sscanf(json + (*t)->start, "%u", p);
-
-#define RESTORE_USINT(k, p) \
-       } else if (keyeq(#k, *t, json)) { \
-               (*t)++; \
-               sscanf(json + (*t)->start, "%hu", p);
-
-#define RESTORE_DOUBLE(k, p) \
-       } else if (keyeq(#k, *t, json)) { \
-               (*t)++; \
-               sscanf(json + (*t)->start, "%lf", p);
-
-#define RESTORE_ANY(k, p, f) \
-       } else if (keyeq(#k, *t, json)) { \
-               (*t)++; \
-               char *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \
-               f(val, p); \
-               free(val);
-
-#define RESTORE_BOOL(k, p)  RESTORE_ANY(k, p, parse_bool)
-
-monitor_t *restore_monitor(jsmntok_t **t, char *json)
-{
-       int num = (*t)->size;
-       (*t)++;
-       monitor_t *m = make_monitor(NULL, NULL, UINT32_MAX);
-       uint32_t focused_desktop_id = 0;
-
-       for (int i = 0; i < num; i++) {
-               if (keyeq("name", *t, json)) {
-                       (*t)++;
-                       snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
-               RESTORE_UINT(id, &m->id)
-               RESTORE_UINT(randrId, &m->randr_id)
-               RESTORE_BOOL(wired, &m->wired)
-               RESTORE_UINT(stickyCount, &m->sticky_count)
-               RESTORE_INT(windowGap, &m->window_gap)
-               RESTORE_UINT(borderWidth, &m->border_width)
-               RESTORE_UINT(focusedDesktopId, &focused_desktop_id)
-               } else if (keyeq("padding", *t, json)) {
-                       (*t)++;
-                       restore_padding(&m->padding, t, json);
-                       continue;
-               } else if (keyeq("rectangle", *t, json)) {
-                       (*t)++;
-                       restore_rectangle(&m->rectangle, t, json);
-                       update_root(m, &m->rectangle);
-                       continue;
-               } else if (keyeq("desktops", *t, json)) {
-                       (*t)++;
-                       int s = (*t)->size;
-                       (*t)++;
-                       for (int j = 0; j < s; j++) {
-                               desktop_t *d = restore_desktop(t, json);
-                               add_desktop(m, d);
-                       }
-                       continue;
-               } else {
-                       warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
-                       (*t)++;
-               }
-               (*t)++;
-       }
-
-       if (focused_desktop_id != 0) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       if (d->id == focused_desktop_id) {
-                               m->desk = d;
-                               break;
-                       }
-               }
-       }
-
-       return m;
-}
-
-desktop_t *restore_desktop(jsmntok_t **t, char *json)
-{
-       int s = (*t)->size;
-       (*t)++;
-       desktop_t *d = make_desktop(NULL, UINT32_MAX);
-       xcb_window_t focusedNodeId = XCB_NONE;
-
-       for (int i = 0; i < s; i++) {
-               if (keyeq("name", *t, json)) {
-                       (*t)++;
-                       snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
-               RESTORE_UINT(id, &d->id)
-               RESTORE_ANY(layout, &d->layout, parse_layout)
-               RESTORE_INT(windowGap, &d->window_gap)
-               RESTORE_UINT(borderWidth, &d->border_width)
-               } else if (keyeq("focusedNodeId", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%u", &focusedNodeId);
-               } else if (keyeq("padding", *t, json)) {
-                       (*t)++;
-                       restore_padding(&d->padding, t, json);
-                       continue;
-               } else if (keyeq("root", *t, json)) {
-                       (*t)++;
-                       d->root = restore_node(t, json);
-                       continue;
-               } else {
-                       warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
-                       (*t)++;
-               }
-               (*t)++;
-       }
-
-       if (focusedNodeId != XCB_NONE) {
-               d->focus = find_by_id_in(d->root, focusedNodeId);
-       }
-
-       return d;
-}
-
-node_t *restore_node(jsmntok_t **t, char *json)
-{
-       if ((*t)->type == JSMN_PRIMITIVE) {
-               (*t)++;
-               return NULL;
-       } else {
-               int s = (*t)->size;
-               (*t)++;
-               /* hack to prevent a new ID from being generated */
-               node_t *n = make_node(UINT32_MAX);
-
-               for (int i = 0; i < s; i++) {
-                       if (keyeq("id", *t, json)) {
-                               (*t)++;
-                               sscanf(json + (*t)->start, "%u", &n->id);
-                       RESTORE_ANY(splitType, &n->split_type, parse_split_type)
-                       RESTORE_DOUBLE(splitRatio, &n->split_ratio)
-                       RESTORE_INT(birthRotation, &n->birth_rotation)
-                       RESTORE_BOOL(vacant, &n->vacant)
-                       RESTORE_BOOL(hidden, &n->hidden)
-                       RESTORE_BOOL(sticky, &n->sticky)
-                       RESTORE_BOOL(private, &n->private)
-                       RESTORE_BOOL(locked, &n->locked)
-                       } else if (keyeq("presel", *t, json)) {
-                               (*t)++;
-                               n->presel = restore_presel(t, json);
-                               continue;
-                       } else if (keyeq("rectangle", *t, json)) {
-                               (*t)++;
-                               restore_rectangle(&n->rectangle, t, json);
-                               continue;
-                       } else if (keyeq("firstChild", *t, json)) {
-                               (*t)++;
-                               node_t *fc = restore_node(t, json);
-                               n->first_child = fc;
-                               if (fc != NULL) {
-                                       fc->parent = n;
-                               }
-                               continue;
-                       } else if (keyeq("secondChild", *t, json)) {
-                               (*t)++;
-                               node_t *sc = restore_node(t, json);
-                               n->second_child = sc;
-                               if (sc != NULL) {
-                                       sc->parent = n;
-                               }
-                               continue;
-                       } else if (keyeq("client", *t, json)) {
-                               (*t)++;
-                               n->client = restore_client(t, json);
-                               continue;
-                       } else {
-                               warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
-                               (*t)++;
-                       }
-                       (*t)++;
-               }
-
-               return n;
-       }
-}
-
-presel_t *restore_presel(jsmntok_t **t, char *json)
-{
-       if ((*t)->type == JSMN_PRIMITIVE) {
-               (*t)++;
-               return NULL;
-       } else {
-               int s = (*t)->size;
-               (*t)++;
-               presel_t *p = make_presel();
-
-               for (int i = 0; i < s; i++) {
-                       if (keyeq("splitRatio", *t, json)) {
-                               (*t)++;
-                               sscanf(json + (*t)->start, "%lf", &p->split_ratio);
-                       RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
-                       }
-
-                       (*t)++;
-               }
-
-               return p;
-       }
-}
-
-
-client_t *restore_client(jsmntok_t **t, char *json)
-{
-       if ((*t)->type == JSMN_PRIMITIVE) {
-               (*t)++;
-               return NULL;
-       } else {
-               int s = (*t)->size;
-               (*t)++;
-               client_t *c = make_client();
-
-               for (int i = 0; i < s; i++) {
-                       if (keyeq("className", *t, json)) {
-                               (*t)++;
-                               snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
-                       } else if (keyeq("instanceName", *t, json)) {
-                               (*t)++;
-                               snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
-                       RESTORE_ANY(state, &c->state, parse_client_state)
-                       RESTORE_ANY(lastState, &c->last_state, parse_client_state)
-                       RESTORE_ANY(layer, &c->layer, parse_stack_layer)
-                       RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
-                       RESTORE_UINT(borderWidth, &c->border_width)
-                       RESTORE_BOOL(urgent, &c->urgent)
-                       RESTORE_BOOL(shown, &c->shown)
-                       } else if (keyeq("tiledRectangle", *t, json)) {
-                               (*t)++;
-                               restore_rectangle(&c->tiled_rectangle, t, json);
-                               continue;
-                       } else if (keyeq("floatingRectangle", *t, json)) {
-                               (*t)++;
-                               restore_rectangle(&c->floating_rectangle, t, json);
-                               continue;
-                       } else {
-                               warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
-                               (*t)++;
-                       }
-
-                       (*t)++;
-               }
-
-               return c;
-       }
-}
-
-void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
-{
-       int s = (*t)->size;
-       (*t)++;
-
-       for (int i = 0; i < s; i++) {
-               if (keyeq("x", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%hi", &r->x);
-               } else if (keyeq("y", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%hi", &r->y);
-               } else if (keyeq("width", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%hu", &r->width);
-               } else if (keyeq("height", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%hu", &r->height);
-               }
-               (*t)++;
-       }
-}
-
-void restore_padding(padding_t *p, jsmntok_t **t, char *json)
-{
-       int s = (*t)->size;
-       (*t)++;
-
-       for (int i = 0; i < s; i++) {
-               if (keyeq("top", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%i", &p->top);
-               } else if (keyeq("right", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%i", &p->right);
-               } else if (keyeq("bottom", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%i", &p->bottom);
-               } else if (keyeq("left", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%i", &p->left);
-               }
-               (*t)++;
-       }
-}
-
-void restore_history(jsmntok_t **t, char *json)
-{
-       int s = (*t)->size;
-       (*t)++;
-
-       for (int i = 0; i < s; i++) {
-               coordinates_t loc = {NULL, NULL, NULL};
-               restore_coordinates(&loc, t, json);
-               if (loc.monitor != NULL && loc.desktop != NULL) {
-                       history_add(loc.monitor, loc.desktop, loc.node);
-               }
-       }
-}
-
-void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
-{
-       int s = (*t)->size;
-       (*t)++;
-       uint32_t id = 0;
-
-       for (int i = 0; i < s; i++) {
-               if (keyeq("monitorId", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%u", &id);
-                       loc->monitor = find_monitor(id);
-               } else if (keyeq("desktopId", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%u", &id);
-                       loc->desktop = find_desktop_in(id, loc->monitor);
-               } else if (keyeq("nodeId", *t, json)) {
-                       (*t)++;
-                       sscanf(json + (*t)->start, "%u", &id);
-                       loc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id);
-               }
-               (*t)++;
-       }
-}
-
-void restore_stack(jsmntok_t **t, char *json)
-{
-       int s = (*t)->size;
-       (*t)++;
-
-       for (int i = 0; i < s; i++) {
-               uint32_t id;
-               sscanf(json + (*t)->start, "%u", &id);
-               coordinates_t loc;
-               if (locate_window(id, &loc)) {
-                       stack_insert_after(stack_tail, loc.node);
-               }
-               (*t)++;
-       }
-}
-
-#undef RESTORE_INT
-#undef RESTORE_UINT
-#undef RESTORE_USINT
-#undef RESTORE_DOUBLE
-#undef RESTORE_ANY
-#undef RESTORE_BOOL
-
-bool keyeq(char *s, jsmntok_t *key, char *json)
-{
-       size_t n = key->end - key->start;
-       return (strlen(s) == n && strncmp(s, json + key->start, n) == 0);
-}
diff --git a/restore.h b/restore.h
deleted file mode 100644 (file)
index 0420ec5..0000000
--- a/restore.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_RESTORE_H
-#define BSPWM_RESTORE_H
-
-#include "jsmn.h"
-
-bool restore_tree(const char *file_path);
-monitor_t *restore_monitor(jsmntok_t **t, char *json);
-desktop_t *restore_desktop(jsmntok_t **t, char *json);
-node_t *restore_node(jsmntok_t **t, char *json);
-presel_t *restore_presel(jsmntok_t **t, char *json);
-client_t *restore_client(jsmntok_t **t, char *json);
-void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json);
-void restore_padding(padding_t *p, jsmntok_t **t, char *json);
-void restore_history(jsmntok_t **t, char *json);
-void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);
-void restore_stack(jsmntok_t **t, char *json);
-void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json);
-bool keyeq(char *s, jsmntok_t *key, char *json);
-
-#endif
diff --git a/rule.c b/rule.c
deleted file mode 100644 (file)
index 75074e3..0000000
--- a/rule.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <sys/types.h>
-#include <string.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "window.h"
-#include "parse.h"
-#include "settings.h"
-#include "rule.h"
-
-rule_t *make_rule(void)
-{
-       rule_t *r = calloc(1, sizeof(rule_t));
-       r->class_name[0] = r->instance_name[0] = r->effect[0] = '\0';
-       r->next = r->prev = NULL;
-       r->one_shot = false;
-       return r;
-}
-
-void add_rule(rule_t *r)
- {
-       if (rule_head == NULL) {
-               rule_head = rule_tail = r;
-       } else {
-               rule_tail->next = r;
-               r->prev = rule_tail;
-               rule_tail = r;
-       }
-}
-
-void remove_rule(rule_t *r)
-{
-       if (r == NULL)
-               return;
-       rule_t *prev = r->prev;
-       rule_t *next = r->next;
-       if (prev != NULL)
-               prev->next = next;
-       if (next != NULL)
-               next->prev = prev;
-       if (r == rule_head)
-               rule_head = next;
-       if (r == rule_tail)
-               rule_tail = prev;
-       free(r);
-}
-
-void remove_rule_by_cause(char *cause)
-{
-       rule_t *r = rule_head;
-       char *class_name = strtok(cause, COL_TOK);
-       char *instance_name = strtok(NULL, COL_TOK);
-       while (r != NULL) {
-               rule_t *next = r->next;
-               if ((streq(class_name, MATCH_ANY) || streq(r->class_name, class_name)) &&
-                   (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name))) {
-                       remove_rule(r);
-               }
-               r = next;
-       }
-}
-
-bool remove_rule_by_index(int idx)
-{
-       for (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {
-               if (idx == 0) {
-                       remove_rule(r);
-                       return true;
-               }
-       }
-       return false;
-}
-
-rule_consequence_t *make_rule_conquence(void)
-{
-       rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
-       rc->manage = rc->focus = rc->border = true;
-       rc->layer = NULL;
-       rc->state = NULL;
-       return rc;
-}
-
-pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
-{
-       pending_rule_t *pr = calloc(1, sizeof(pending_rule_t));
-       pr->prev = pr->next = NULL;
-       pr->fd = fd;
-       pr->win = win;
-       pr->csq = csq;
-       return pr;
-}
-
-void add_pending_rule(pending_rule_t *pr)
-{
-       if (pr == NULL) {
-               return;
-       }
-       if (pending_rule_head == NULL) {
-               pending_rule_head = pending_rule_tail = pr;
-       } else {
-               pending_rule_tail->next = pr;
-               pr->prev = pending_rule_tail;
-               pending_rule_tail = pr;
-       }
-}
-
-void remove_pending_rule(pending_rule_t *pr)
-{
-       if (pr == NULL) {
-               return;
-       }
-       pending_rule_t *a = pr->prev;
-       pending_rule_t *b = pr->next;
-       if (a != NULL) {
-               a->next = b;
-       }
-       if (b != NULL) {
-               b->prev = a;
-       }
-       if (pr == pending_rule_head) {
-               pending_rule_head = b;
-       }
-       if (pr == pending_rule_tail) {
-               pending_rule_tail = a;
-       }
-       close(pr->fd);
-       free(pr->csq);
-       free(pr);
-}
-
-void apply_rules(xcb_window_t win, rule_consequence_t *csq)
-{
-       xcb_ewmh_get_atoms_reply_t win_type;
-
-       if (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {
-               for (unsigned int i = 0; i < win_type.atoms_len; i++) {
-                       xcb_atom_t a = win_type.atoms[i];
-                       if (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR ||
-                           a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {
-                               csq->focus = false;
-                       } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {
-                               if (csq->state == NULL) {
-                                       csq->state = calloc(1, sizeof(client_state_t));
-                               }
-                               *(csq->state) = STATE_FLOATING;
-                               csq->center = true;
-                       } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK ||
-                                  a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP ||
-                                  a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {
-                               csq->manage = false;
-                               if (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP) {
-                                       window_lower(win);
-                               }
-                       }
-               }
-               xcb_ewmh_get_atoms_reply_wipe(&win_type);
-       }
-
-       xcb_ewmh_get_atoms_reply_t win_state;
-
-       if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {
-               for (unsigned int i = 0; i < win_state.atoms_len; i++) {
-                       xcb_atom_t a = win_state.atoms[i];
-                       if (a == ewmh->_NET_WM_STATE_FULLSCREEN) {
-                               if (csq->state == NULL) {
-                                       csq->state = calloc(1, sizeof(client_state_t));
-                               }
-                               *(csq->state) = STATE_FULLSCREEN;
-                       } else if (a == ewmh->_NET_WM_STATE_BELOW) {
-                               if (csq->layer == NULL) {
-                                       csq->layer = calloc(1, sizeof(stack_layer_t));
-                               }
-                               *(csq->layer) = LAYER_BELOW;
-                       } else if (a == ewmh->_NET_WM_STATE_ABOVE) {
-                               if (csq->layer == NULL) {
-                                       csq->layer = calloc(1, sizeof(stack_layer_t));
-                               }
-                               *(csq->layer) = LAYER_ABOVE;
-                       } else if (a == ewmh->_NET_WM_STATE_STICKY) {
-                               csq->sticky = true;
-                       }
-               }
-               xcb_ewmh_get_atoms_reply_wipe(&win_state);
-       }
-
-       xcb_window_t transient_for = XCB_NONE;
-       xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
-       if (transient_for != XCB_NONE) {
-               if (csq->state == NULL) {
-                       csq->state = calloc(1, sizeof(client_state_t));
-               }
-               *(csq->state) = STATE_FLOATING;
-       }
-
-       xcb_size_hints_t size_hints;
-       if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {
-               if ((size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE|XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) &&
-                   size_hints.min_width == size_hints.max_width && size_hints.min_height == size_hints.max_height) {
-                       if (csq->state == NULL) {
-                               csq->state = calloc(1, sizeof(client_state_t));
-                       }
-                       *(csq->state) = STATE_FLOATING;
-               }
-       }
-
-       xcb_icccm_get_wm_class_reply_t reply;
-       if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
-               snprintf(csq->class_name, sizeof(csq->class_name), "%s", reply.class_name);
-               snprintf(csq->instance_name, sizeof(csq->instance_name), "%s", reply.instance_name);
-               xcb_icccm_get_wm_class_reply_wipe(&reply);
-       }
-
-       rule_t *rule = rule_head;
-       while (rule != NULL) {
-               rule_t *next = rule->next;
-               if ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&
-                   (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name))) {
-                       char effect[MAXLEN];
-                       snprintf(effect, sizeof(effect), "%s", rule->effect);
-                       char *key = strtok(effect, CSQ_BLK);
-                       char *value = strtok(NULL, CSQ_BLK);
-                       while (key != NULL && value != NULL) {
-                               parse_key_value(key, value, csq);
-                               key = strtok(NULL, CSQ_BLK);
-                               value = strtok(NULL, CSQ_BLK);
-                       }
-                       if (rule->one_shot) {
-                               remove_rule(rule);
-                               break;
-                       }
-               }
-               rule = next;
-       }
-}
-
-bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
-{
-       if (external_rules_command[0] == '\0') {
-               return false;
-       }
-       int fds[2];
-       if (pipe(fds) == -1) {
-               return false;
-       }
-       pid_t pid = fork();
-       if (pid == 0) {
-               if (dpy != NULL) {
-                       close(xcb_get_file_descriptor(dpy));
-               }
-               dup2(fds[1], 1);
-               close(fds[0]);
-               char wid[SMALEN];
-               snprintf(wid, sizeof(wid), "%i", win);
-               setsid();
-               execl(external_rules_command, external_rules_command, wid, csq->class_name, csq->instance_name, csq->monitor_desc, csq->desktop_desc, csq->node_desc, NULL);
-               err("Couldn't spawn rule command.\n");
-       } else if (pid > 0) {
-               close(fds[1]);
-               pending_rule_t *pr = make_pending_rule(fds[0], win, csq);
-               add_pending_rule(pr);
-       }
-       return (pid != -1);
-}
-
-void parse_rule_consequence(int fd, rule_consequence_t *csq)
-{
-       if (fd == -1) {
-               return;
-       }
-       char data[BUFSIZ];
-       int nb;
-       while ((nb = read(fd, data, sizeof(data))) > 0) {
-               int end = MIN(nb, (int) sizeof(data) - 1);
-               data[end] = '\0';
-               char *key = strtok(data, CSQ_BLK);
-               char *value = strtok(NULL, CSQ_BLK);
-               while (key != NULL && value != NULL) {
-                       parse_key_value(key, value, csq);
-                       key = strtok(NULL, CSQ_BLK);
-                       value = strtok(NULL, CSQ_BLK);
-               }
-       }
-}
-
-void parse_key_value(char *key, char *value, rule_consequence_t *csq)
-{
-       bool v;
-       if (streq("monitor", key)) {
-               snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
-       } else if (streq("desktop", key)) {
-               snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
-       } else if (streq("node", key)) {
-               snprintf(csq->node_desc, sizeof(csq->node_desc), "%s", value);
-       } else if (streq("split_dir", key)) {
-               snprintf(csq->split_dir, sizeof(csq->split_dir), "%s", value);
-       } else if (streq("state", key)) {
-               client_state_t cst;
-               if (parse_client_state(value, &cst)) {
-                       if (csq->state == NULL) {
-                               csq->state = calloc(1, sizeof(client_state_t));
-                       }
-                       *(csq->state) = cst;
-               }
-       } else if (streq("layer", key)) {
-               stack_layer_t lyr;
-               if (parse_stack_layer(value, &lyr)) {
-                       if (csq->layer == NULL) {
-                               csq->layer = calloc(1, sizeof(stack_layer_t));
-                       }
-                       *(csq->layer) = lyr;
-               }
-       } else if (streq("split_ratio", key)) {
-               double rat;
-               if (sscanf(value, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
-                       csq->split_ratio = rat;
-               }
-       } else if (parse_bool(value, &v)) {
-               if (streq("hidden", key))
-                       csq->hidden = true;
-#define SETCSQ(name) \
-               else if (streq(#name, key)) \
-                       csq->name = v;
-               SETCSQ(sticky)
-               SETCSQ(private)
-               SETCSQ(locked)
-               SETCSQ(center)
-               SETCSQ(follow)
-               SETCSQ(manage)
-               SETCSQ(focus)
-               SETCSQ(border)
-#undef SETCSQ
-       }
-}
-
-void list_rules(FILE *rsp)
-{
-       for (rule_t *r = rule_head; r != NULL; r = r->next) {
-               fprintf(rsp, "%s:%s %c> %s\n", r->class_name, r->instance_name, r->one_shot?'-':'=', r->effect);
-       }
-}
diff --git a/rule.h b/rule.h
deleted file mode 100644 (file)
index 0ccf396..0000000
--- a/rule.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_RULE_H
-#define BSPWM_RULE_H
-
-#define MATCH_ANY  "*"
-#define CSQ_BLK    " =,\n"
-
-rule_t *make_rule(void);
-void add_rule(rule_t *r);
-void remove_rule(rule_t *r);
-void remove_rule_by_cause(char *cause);
-bool remove_rule_by_index(int idx);
-rule_consequence_t *make_rule_conquence(void);
-pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);
-void add_pending_rule(pending_rule_t *pr);
-void remove_pending_rule(pending_rule_t *pr);
-void apply_rules(xcb_window_t win, rule_consequence_t *csq);
-bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
-void parse_rule_consequence(int fd, rule_consequence_t *csq);
-void parse_key_value(char *key, char *value, rule_consequence_t *csq);
-void list_rules(FILE *rsp);
-
-#endif
diff --git a/settings.c b/settings.c
deleted file mode 100644 (file)
index a17cff5..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include "bspwm.h"
-#include "settings.h"
-
-void run_config(void)
-{
-       if (fork() == 0) {
-               if (dpy != NULL) {
-                       close(xcb_get_file_descriptor(dpy));
-               }
-               setsid();
-               execl(config_path, config_path, NULL);
-               err("Couldn't execute the configuration file.\n");
-       }
-}
-
-void load_settings(void)
-{
-       snprintf(external_rules_command, sizeof(external_rules_command), "%s", EXTERNAL_RULES_COMMAND);
-       snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
-
-       snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
-       snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
-       snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
-       snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR);
-
-       padding = (padding_t) PADDING;
-       window_gap = WINDOW_GAP;
-       border_width = BORDER_WIDTH;
-       split_ratio = SPLIT_RATIO;
-       initial_polarity = FIRST_CHILD;
-       pointer_modifier = POINTER_MODIFIER;
-       pointer_motion_interval = POINTER_MOTION_INTERVAL;
-
-       pointer_actions[0] = ACTION_MOVE;
-       pointer_actions[1] = ACTION_RESIZE_SIDE;
-       pointer_actions[2] = ACTION_RESIZE_CORNER;
-
-       borderless_monocle = BORDERLESS_MONOCLE;
-       gapless_monocle = GAPLESS_MONOCLE;
-       paddingless_monocle = PADDINGLESS_MONOCLE;
-       single_monocle = SINGLE_MONOCLE;
-       focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
-       pointer_follows_focus = POINTER_FOLLOWS_FOCUS;
-       pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
-       ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
-       center_pseudo_tiled = CENTER_PSEUDO_TILED;
-       click_to_focus = CLICK_TO_FOCUS;
-       swallow_first_click = SWALLOW_FIRST_CLICK;
-       honor_size_hints = HONOR_SIZE_HINTS;
-       remove_disabled_monitors = REMOVE_DISABLED_MONITORS;
-       remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;
-       merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;
-}
diff --git a/settings.h b/settings.h
deleted file mode 100644 (file)
index f8ed182..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_SETTINGS_H
-#define BSPWM_SETTINGS_H
-
-#include "types.h"
-
-#define WM_NAME                  "bspwm"
-#define CONFIG_NAME              WM_NAME "rc"
-#define CONFIG_HOME_ENV          "XDG_CONFIG_HOME"
-#define POINTER_MODIFIER         XCB_MOD_MASK_4
-#define POINTER_MOTION_INTERVAL  17
-#define EXTERNAL_RULES_COMMAND   ""
-#define STATUS_PREFIX            "W"
-
-#define NORMAL_BORDER_COLOR           "#30302f"
-#define ACTIVE_BORDER_COLOR           "#474645"
-#define FOCUSED_BORDER_COLOR          "#817f7f"
-#define PRESEL_FEEDBACK_COLOR         "#f4d775"
-
-#define PADDING              {0, 0, 0, 0}
-#define WINDOW_GAP           6
-#define BORDER_WIDTH         1
-#define SPLIT_RATIO          0.5
-
-#define BORDERLESS_MONOCLE          false
-#define GAPLESS_MONOCLE             false
-#define PADDINGLESS_MONOCLE         false
-#define SINGLE_MONOCLE              false
-#define FOCUS_FOLLOWS_POINTER       false
-#define POINTER_FOLLOWS_FOCUS       false
-#define POINTER_FOLLOWS_MONITOR     false
-#define IGNORE_EWMH_FOCUS           false
-#define CENTER_PSEUDO_TILED         true
-#define CLICK_TO_FOCUS              false
-#define SWALLOW_FIRST_CLICK         false
-#define HONOR_SIZE_HINTS            false
-#define REMOVE_DISABLED_MONITORS    false
-#define REMOVE_UNPLUGGED_MONITORS   false
-#define MERGE_OVERLAPPING_MONITORS  false
-
-char external_rules_command[MAXLEN];
-char status_prefix[MAXLEN];
-
-char normal_border_color[MAXLEN];
-char active_border_color[MAXLEN];
-char focused_border_color[MAXLEN];
-char presel_feedback_color[MAXLEN];
-
-padding_t padding;
-int window_gap;
-unsigned int border_width;
-double split_ratio;
-
-child_polarity_t initial_polarity;
-uint16_t pointer_modifier;
-uint32_t pointer_motion_interval;
-pointer_action_t pointer_actions[3];
-
-bool borderless_monocle;
-bool gapless_monocle;
-bool paddingless_monocle;
-bool single_monocle;
-bool focus_follows_pointer;
-bool pointer_follows_focus;
-bool pointer_follows_monitor;
-bool ignore_ewmh_focus;
-bool center_pseudo_tiled;
-bool click_to_focus;
-bool swallow_first_click;
-bool honor_size_hints;
-bool remove_disabled_monitors;
-bool remove_unplugged_monitors;
-bool merge_overlapping_monitors;
-
-void run_config(void);
-void load_settings(void);
-
-#endif
diff --git a/src/bspc.c b/src/bspc.c
new file mode 100644 (file)
index 0000000..fe23c64
--- /dev/null
@@ -0,0 +1,94 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "helpers.h"
+#include "common.h"
+
+int main(int argc, char *argv[])
+{
+       int fd;
+       struct sockaddr_un sock_address;
+       char msg[BUFSIZ], rsp[BUFSIZ];
+
+       if (argc < 2) {
+               err("No arguments given.\n");
+       }
+
+       sock_address.sun_family = AF_UNIX;
+       char *sp;
+
+       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+               err("Failed to create the socket.\n");
+       }
+
+       sp = getenv(SOCKET_ENV_VAR);
+       if (sp != NULL) {
+               snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
+       } else {
+               char *host = NULL;
+               int dn = 0, sn = 0;
+               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
+                       snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);
+               }
+               free(host);
+       }
+
+       if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
+               err("Failed to connect to the socket.\n");
+       }
+
+       argc--, argv++;
+       int msg_len = 0;
+
+       for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) {
+               n = snprintf(msg + offset, rem, "%s%c", *argv, 0);
+               msg_len += n;
+       }
+
+       if (send(fd, msg, msg_len, 0) == -1) {
+               err("Failed to send the data.\n");
+       }
+
+       int ret = EXIT_SUCCESS, nb;
+       while ((nb = recv(fd, rsp, sizeof(rsp)-1, 0)) > 0) {
+               rsp[nb] = '\0';
+               if (rsp[0] == FAILURE_MESSAGE[0]) {
+                       ret = EXIT_FAILURE;
+                       printf("%s", rsp + 1);
+               } else {
+                       printf("%s", rsp);
+               }
+               fflush(stdout);
+       }
+
+       close(fd);
+       return ret;
+}
diff --git a/src/bspwm.c b/src/bspwm.c
new file mode 100644 (file)
index 0000000..6a84237
--- /dev/null
@@ -0,0 +1,402 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <xcb/xinerama.h>
+#include "types.h"
+#include "desktop.h"
+#include "monitor.h"
+#include "settings.h"
+#include "messages.h"
+#include "pointer.h"
+#include "events.h"
+#include "common.h"
+#include "window.h"
+#include "history.h"
+#include "ewmh.h"
+#include "rule.h"
+#include "bspwm.h"
+
+int main(int argc, char *argv[])
+{
+       fd_set descriptors;
+       char socket_path[MAXLEN];
+       config_path[0] = '\0';
+       int sock_fd, cli_fd, dpy_fd, max_fd, n;
+       struct sockaddr_un sock_address;
+       char msg[BUFSIZ] = {0};
+       xcb_generic_event_t *event;
+       int opt;
+
+       while ((opt = getopt(argc, argv, "hvc:")) != -1) {
+               switch (opt) {
+                       case 'h':
+                               printf(WM_NAME " [-h|-v|-c CONFIG_PATH]\n");
+                               exit(EXIT_SUCCESS);
+                               break;
+                       case 'v':
+                               printf("%s\n", VERSION);
+                               exit(EXIT_SUCCESS);
+                               break;
+                       case 'c':
+                               snprintf(config_path, sizeof(config_path), "%s", optarg);
+                               break;
+               }
+       }
+
+       if (config_path[0] == '\0') {
+               char *config_home = getenv(CONFIG_HOME_ENV);
+               if (config_home != NULL) {
+                       snprintf(config_path, sizeof(config_path), "%s/%s/%s", config_home, WM_NAME, CONFIG_NAME);
+               } else {
+                       snprintf(config_path, sizeof(config_path), "%s/%s/%s/%s", getenv("HOME"), ".config", WM_NAME, CONFIG_NAME);
+               }
+       }
+
+       dpy = xcb_connect(NULL, &default_screen);
+
+       if (!check_connection(dpy)) {
+               exit(EXIT_FAILURE);
+       }
+
+       load_settings();
+       setup();
+
+       dpy_fd = xcb_get_file_descriptor(dpy);
+
+       char *sp = getenv(SOCKET_ENV_VAR);
+       if (sp != NULL) {
+               snprintf(socket_path, sizeof(socket_path), "%s", sp);
+       } else {
+               char *host = NULL;
+               int dn = 0, sn = 0;
+               if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
+                       snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, host, dn, sn);
+               }
+               free(host);
+       }
+
+       sock_address.sun_family = AF_UNIX;
+       snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path);
+
+       sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+       if (sock_fd == -1) {
+               err("Couldn't create the socket.\n");
+       }
+
+       unlink(socket_path);
+       if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
+               err("Couldn't bind a name to the socket.\n");
+       }
+
+       if (listen(sock_fd, SOMAXCONN) == -1) {
+               err("Couldn't listen to the socket.\n");
+       }
+
+       signal(SIGINT, sig_handler);
+       signal(SIGHUP, sig_handler);
+       signal(SIGTERM, sig_handler);
+       signal(SIGCHLD, sig_handler);
+       signal(SIGPIPE, SIG_IGN);
+       run_config();
+       running = true;
+
+       while (running) {
+
+               xcb_flush(dpy);
+
+               FD_ZERO(&descriptors);
+               FD_SET(sock_fd, &descriptors);
+               FD_SET(dpy_fd, &descriptors);
+               max_fd = MAX(sock_fd, dpy_fd);
+
+               for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+                       FD_SET(pr->fd, &descriptors);
+                       if (pr->fd > max_fd) {
+                               max_fd = pr->fd;
+                       }
+               }
+
+               if (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {
+
+                       pending_rule_t *pr = pending_rule_head;
+                       while (pr != NULL) {
+                               pending_rule_t *next = pr->next;
+                               if (FD_ISSET(pr->fd, &descriptors)) {
+                                       manage_window(pr->win, pr->csq, pr->fd);
+                                       remove_pending_rule(pr);
+                               }
+                               pr = next;
+                       }
+
+                       if (FD_ISSET(sock_fd, &descriptors)) {
+                               cli_fd = accept(sock_fd, NULL, 0);
+                               if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg), 0)) > 0) {
+                                       msg[n] = '\0';
+                                       FILE *rsp = fdopen(cli_fd, "w");
+                                       if (rsp != NULL) {
+                                               handle_message(msg, n, rsp);
+                                       } else {
+                                               warn("Can't open the client socket as file.\n");
+                                               close(cli_fd);
+                                       }
+                               }
+                       }
+
+                       if (FD_ISSET(dpy_fd, &descriptors)) {
+                               while ((event = xcb_poll_for_event(dpy)) != NULL) {
+                                       handle_event(event);
+                                       free(event);
+                               }
+                       }
+
+               }
+
+               if (!check_connection(dpy)) {
+                       running = false;
+               }
+       }
+
+       cleanup();
+       close(sock_fd);
+       unlink(socket_path);
+       ungrab_buttons();
+       xcb_ewmh_connection_wipe(ewmh);
+       xcb_destroy_window(dpy, meta_window);
+       free(ewmh);
+       xcb_flush(dpy);
+       xcb_disconnect(dpy);
+       return exit_status;
+}
+
+void init(void)
+{
+       clients_count = 0;
+       mon = mon_head = mon_tail = pri_mon = NULL;
+       history_head = history_tail = history_needle = NULL;
+       rule_head = rule_tail = NULL;
+       stack_head = stack_tail = NULL;
+       subscribe_head = subscribe_tail = NULL;
+       pending_rule_head = pending_rule_tail = NULL;
+       auto_raise = sticky_still = record_history = true;
+       randr_base = 0;
+       exit_status = 0;
+}
+
+void setup(void)
+{
+       init();
+       ewmh_init();
+       pointer_init();
+
+       screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
+
+       if (screen == NULL) {
+               err("Can't acquire the default screen.\n");
+       }
+
+       root = screen->root;
+       register_events();
+
+       screen_width = screen->width_in_pixels;
+       screen_height = screen->height_in_pixels;
+
+       meta_window = xcb_generate_id(dpy);
+       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, meta_window, root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_NONE, NULL);
+       xcb_icccm_set_wm_class(dpy, meta_window, sizeof(META_WINDOW_IC), META_WINDOW_IC);
+
+       xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
+                                 ewmh->_NET_SUPPORTING_WM_CHECK,
+                                 ewmh->_NET_DESKTOP_NAMES,
+                                 ewmh->_NET_DESKTOP_VIEWPORT,
+                                 ewmh->_NET_NUMBER_OF_DESKTOPS,
+                                 ewmh->_NET_CURRENT_DESKTOP,
+                                 ewmh->_NET_CLIENT_LIST,
+                                 ewmh->_NET_ACTIVE_WINDOW,
+                                 ewmh->_NET_CLOSE_WINDOW,
+                                 ewmh->_NET_WM_STRUT_PARTIAL,
+                                 ewmh->_NET_WM_DESKTOP,
+                                 ewmh->_NET_WM_STATE,
+                                 ewmh->_NET_WM_STATE_HIDDEN,
+                                 ewmh->_NET_WM_STATE_FULLSCREEN,
+                                 ewmh->_NET_WM_STATE_BELOW,
+                                 ewmh->_NET_WM_STATE_ABOVE,
+                                 ewmh->_NET_WM_STATE_STICKY,
+                                 ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
+                                 ewmh->_NET_WM_WINDOW_TYPE,
+                                 ewmh->_NET_WM_WINDOW_TYPE_DOCK,
+                                 ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,
+                                 ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,
+                                 ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
+                                 ewmh->_NET_WM_WINDOW_TYPE_UTILITY,
+                                 ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};
+
+       xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
+       ewmh_set_supporting(meta_window);
+
+#define GETATOM(a) \
+       get_atom(#a, &a);
+       GETATOM(WM_STATE)
+       GETATOM(WM_DELETE_WINDOW)
+       GETATOM(WM_TAKE_FOCUS)
+#undef GETATOM
+
+       const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
+       if (qep->present && update_monitors()) {
+               randr = true;
+               randr_base = qep->first_event;
+               xcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
+       } else {
+               randr = false;
+               warn("Couldn't retrieve monitors via RandR.\n");
+               bool xinerama_is_active = false;
+
+               if (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {
+                       xcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);
+                       if (xia != NULL) {
+                               xinerama_is_active = xia->state;
+                               free(xia);
+                       }
+               }
+
+               if (xinerama_is_active) {
+                       xcb_xinerama_query_screens_reply_t *xsq = xcb_xinerama_query_screens_reply(dpy, xcb_xinerama_query_screens(dpy), NULL);
+                       xcb_xinerama_screen_info_t *xsi = xcb_xinerama_query_screens_screen_info(xsq);
+                       int n = xcb_xinerama_query_screens_screen_info_length(xsq);
+                       for (int i = 0; i < n; i++) {
+                               xcb_xinerama_screen_info_t info = xsi[i];
+                               xcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};
+                               monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
+                               add_monitor(m);
+                               add_desktop(m, make_desktop(NULL, XCB_NONE));
+                       }
+                       free(xsq);
+               } else {
+                       warn("Xinerama is inactive.\n");
+                       xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+                       monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
+                       add_monitor(m);
+                       add_desktop(m, make_desktop(NULL, XCB_NONE));
+               }
+       }
+
+       ewmh_update_number_of_desktops();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+       ewmh_update_current_desktop();
+       xcb_get_input_focus_reply_t *ifo = xcb_get_input_focus_reply(dpy, xcb_get_input_focus(dpy), NULL);
+       if (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE)) {
+               clear_input_focus();
+       }
+       free(ifo);
+}
+
+void register_events(void)
+{
+       uint32_t values[] = {ROOT_EVENT_MASK};
+       xcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));
+       if (e != NULL) {
+               xcb_disconnect(dpy);
+               err("Another window manager is already running.\n");
+       }
+}
+
+void cleanup(void)
+{
+       mon = NULL;
+
+       while (mon_head != NULL) {
+               remove_monitor(mon_head);
+       }
+       while (rule_head != NULL) {
+               remove_rule(rule_head);
+       }
+       while (subscribe_head != NULL) {
+               remove_subscriber(subscribe_head);
+       }
+       while (pending_rule_head != NULL) {
+               remove_pending_rule(pending_rule_head);
+       }
+
+       empty_history();
+}
+
+bool check_connection (xcb_connection_t *dpy)
+{
+       int xerr;
+       if ((xerr = xcb_connection_has_error(dpy)) != 0) {
+               warn("The server closed the connection: ");
+               switch (xerr) {
+                       case XCB_CONN_ERROR:
+                               warn("socket, pipe or stream error.\n");
+                               break;
+                       case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
+                               warn("unsupported extension.\n");
+                               break;
+                       case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
+                               warn("not enough memory.\n");
+                               break;
+                       case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
+                               warn("request length exceeded.\n");
+                               break;
+                       case XCB_CONN_CLOSED_PARSE_ERR:
+                               warn("can't parse display string.\n");
+                               break;
+                       case XCB_CONN_CLOSED_INVALID_SCREEN:
+                               warn("invalid screen.\n");
+                               break;
+                       case XCB_CONN_CLOSED_FDPASSING_FAILED:
+                               warn("failed to pass FD.\n");
+                               break;
+                       default:
+                               warn("unknown error.\n");
+                               break;
+               }
+               return false;
+       } else {
+               return true;
+       }
+}
+
+void sig_handler(int sig)
+{
+       if (sig == SIGCHLD) {
+               signal(sig, sig_handler);
+               while (waitpid(-1, 0, WNOHANG) > 0)
+                       ;
+       } else if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {
+               running = false;
+       }
+}
diff --git a/src/bspwm.h b/src/bspwm.h
new file mode 100644 (file)
index 0000000..aa5ca9e
--- /dev/null
@@ -0,0 +1,80 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_BSPWM_H
+#define BSPWM_BSPWM_H
+
+#include "types.h"
+
+#define ROOT_EVENT_MASK     (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS)
+#define CLIENT_EVENT_MASK   (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
+#define BSPWM_CLASS_NAME    "Bspwm"
+#define META_WINDOW_IC      "wm\0" BSPWM_CLASS_NAME
+#define ROOT_WINDOW_IC      "root\0" BSPWM_CLASS_NAME
+#define PRESEL_FEEDBACK_I   "presel_feedback"
+#define PRESEL_FEEDBACK_IC  PRESEL_FEEDBACK_I "\0" BSPWM_CLASS_NAME
+
+xcb_connection_t *dpy;
+int default_screen, screen_width, screen_height;
+uint32_t clients_count;
+xcb_screen_t *screen;
+xcb_window_t root;
+char config_path[MAXLEN];
+
+monitor_t *mon;
+monitor_t *mon_head;
+monitor_t *mon_tail;
+monitor_t *pri_mon;
+history_t *history_head;
+history_t *history_tail;
+history_t *history_needle;
+rule_t *rule_head;
+rule_t *rule_tail;
+stacking_list_t *stack_head;
+stacking_list_t *stack_tail;
+subscriber_list_t *subscribe_head;
+subscriber_list_t *subscribe_tail;
+pending_rule_t *pending_rule_head;
+pending_rule_t *pending_rule_tail;
+
+xcb_window_t meta_window;
+xcb_atom_t WM_STATE;
+xcb_atom_t WM_TAKE_FOCUS;
+xcb_atom_t WM_DELETE_WINDOW;
+int exit_status;
+
+bool auto_raise;
+bool sticky_still;
+bool record_history;
+bool running;
+bool randr;
+
+void init(void);
+void setup(void);
+void register_events(void);
+void cleanup(void);
+bool check_connection (xcb_connection_t *dpy);
+void sig_handler(int sig);
+
+#endif
diff --git a/src/common.h b/src/common.h
new file mode 100644 (file)
index 0000000..f44eef8
--- /dev/null
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_COMMON_H
+#define BSPWM_COMMON_H
+
+#define SOCKET_PATH_TPL  "/tmp/bspwm%s_%i_%i-socket"
+#define SOCKET_ENV_VAR   "BSPWM_SOCKET"
+
+#define FAILURE_MESSAGE  "\x07"
+
+#endif
diff --git a/src/desktop.c b/src/desktop.c
new file mode 100644 (file)
index 0000000..8f6a5b6
--- /dev/null
@@ -0,0 +1,476 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "history.h"
+#include "monitor.h"
+#include "query.h"
+#include "tree.h"
+#include "window.h"
+#include "desktop.h"
+#include "subscribe.h"
+#include "settings.h"
+
+void focus_desktop(monitor_t *m, desktop_t *d)
+{
+       bool changed = (m != mon || m->desk != d);
+
+       focus_monitor(m);
+
+       if (m->desk != d) {
+               if (focus_follows_pointer) {
+                       listen_enter_notify(d->root, false);
+               }
+               show_desktop(d);
+               hide_desktop(m->desk);
+               if (focus_follows_pointer) {
+                       listen_enter_notify(d->root, true);
+               }
+               m->desk = d;
+       }
+
+       if (changed) {
+               ewmh_update_current_desktop();
+               put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
+       }
+}
+
+bool activate_desktop(monitor_t *m, desktop_t *d)
+{
+       if (m == mon || d == m->desk) {
+               return false;
+       }
+
+       if (m->sticky_count > 0) {
+               sticky_still = false;
+               transfer_sticky_nodes(m, m->desk, d, m->desk->root);
+               sticky_still = true;
+       }
+
+       show_desktop(d);
+       hide_desktop(m->desk);
+
+       m->desk = d;
+
+       put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate 0x%08X 0x%08X\n", m->id, d->id);
+       put_status(SBSC_MASK_REPORT);
+
+       return true;
+}
+
+bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel)
+{
+       monitor_t *m = ref->monitor;
+       desktop_t *d = ref->desktop;
+       desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
+
+#define HANDLE_BOUNDARIES(f)  \
+       if (f == NULL) { \
+               m = (dir == CYCLE_PREV ? m->prev : m->next); \
+               if (m == NULL) { \
+                       m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
+               } \
+               f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
+       }
+       HANDLE_BOUNDARIES(f)
+
+       while (f != d) {
+               coordinates_t loc = {m, f, NULL};
+               if (desktop_matches(&loc, ref, sel)) {
+                       *dst = loc;
+                       return true;
+               }
+               f = (dir == CYCLE_PREV ? f->prev : f->next);
+               HANDLE_BOUNDARIES(f)
+       }
+#undef HANDLE_BOUNDARIES
+
+       return false;
+}
+
+bool set_layout(monitor_t *m, desktop_t *d, layout_t l)
+{
+       if (d->layout == l) {
+               return false;
+       }
+
+       d->layout = l;
+
+       handle_presel_feedbacks(m, d);
+
+       arrange(m, d);
+
+       put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(l));
+
+       if (d == m->desk) {
+               put_status(SBSC_MASK_REPORT);
+       }
+
+       return true;
+}
+
+void handle_presel_feedbacks(monitor_t *m, desktop_t *d)
+{
+       if (m->desk != d) {
+               return;
+       }
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, false);
+       }
+       if (d->layout == LAYOUT_MONOCLE) {
+               hide_presel_feedbacks(m, d, d->root);
+       } else {
+               show_presel_feedbacks(m, d, d->root);
+       }
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, true);
+       }
+}
+
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
+{
+       if (ms == NULL || md == NULL || d == NULL || ms == md) {
+               return false;
+       }
+
+       bool was_active = (d == ms->desk);
+
+       unlink_desktop(ms, d);
+
+       if (md->desk != NULL) {
+               hide_desktop(d);
+       }
+
+       insert_desktop(md, d);
+       history_transfer_desktop(md, d);
+
+       if (was_active) {
+               if (mon == ms) {
+                       focus_node(ms, NULL, NULL);
+               } else {
+                       activate_node(ms, NULL, NULL);
+               }
+       }
+
+       if (ms->sticky_count > 0 && was_active) {
+               sticky_still = false;
+               transfer_sticky_nodes(ms, d, ms->desk, d->root);
+               sticky_still = true;
+       }
+
+       adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
+       arrange(md, d);
+
+       if (md->desk == d) {
+               if (mon == md) {
+                       focus_node(md, d, d->focus);
+               } else {
+                       activate_node(md, d, d->focus);
+               }
+       }
+
+       ewmh_update_wm_desktops();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+       ewmh_update_current_desktop();
+
+       put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
+       put_status(SBSC_MASK_REPORT);
+
+       return true;
+}
+
+desktop_t *make_desktop(const char *name, uint32_t id)
+{
+       desktop_t *d = calloc(1, sizeof(desktop_t));
+       snprintf(d->name, sizeof(d->name), "%s", name == NULL ? DEFAULT_DESK_NAME : name);
+       if (id == XCB_NONE) {
+               d->id = xcb_generate_id(dpy);
+       }
+       d->prev = d->next = NULL;
+       d->root = d->focus = NULL;
+       d->layout = LAYOUT_TILED;
+       d->padding = (padding_t) PADDING;
+       d->window_gap = window_gap;
+       d->border_width = border_width;
+       return d;
+}
+
+void rename_desktop(monitor_t *m, desktop_t *d, const char *name)
+{
+
+       put_status(SBSC_MASK_DESKTOP_RENAME, "desktop_rename 0x%08X 0x%08X %s %s\n", m->id, d->id, d->name, name);
+
+       snprintf(d->name, sizeof(d->name), "%s", name);
+
+       put_status(SBSC_MASK_REPORT);
+       ewmh_update_desktop_names();
+}
+
+void insert_desktop(monitor_t *m, desktop_t *d)
+{
+       if (m->desk == NULL) {
+               m->desk = d;
+               m->desk_head = d;
+               m->desk_tail = d;
+       } else {
+               m->desk_tail->next = d;
+               d->prev = m->desk_tail;
+               m->desk_tail = d;
+       }
+}
+
+void add_desktop(monitor_t *m, desktop_t *d)
+{
+       put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add 0x%08X %s 0x%08X\n", d->id, d->name, m->id);
+
+       d->border_width = m->border_width;
+       d->window_gap = m->window_gap;
+       insert_desktop(m, d);
+       ewmh_update_number_of_desktops();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+       ewmh_update_wm_desktops();
+       put_status(SBSC_MASK_REPORT);
+}
+
+desktop_t *find_desktop_in(uint32_t id, monitor_t *m)
+{
+       if (m == NULL) {
+               return NULL;
+       }
+
+       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+               if (d->id == id) {
+                       return d;
+               }
+       }
+
+       return NULL;
+}
+
+void unlink_desktop(monitor_t *m, desktop_t *d)
+{
+       desktop_t *prev = d->prev;
+       desktop_t *next = d->next;
+
+       if (prev != NULL) {
+               prev->next = next;
+       }
+
+       if (next != NULL) {
+               next->prev = prev;
+       }
+
+       if (m->desk_head == d) {
+               m->desk_head = next;
+       }
+
+       if (m->desk_tail == d) {
+               m->desk_tail = prev;
+       }
+
+       if (m->desk == d) {
+               m->desk = NULL;
+       }
+
+       d->prev = d->next = NULL;
+}
+
+void remove_desktop(monitor_t *m, desktop_t *d)
+{
+       put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);
+
+       remove_node(m, d, d->root);
+       unlink_desktop(m, d);
+       history_remove(d, NULL, false);
+       free(d);
+
+       ewmh_update_current_desktop();
+       ewmh_update_number_of_desktops();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+
+       if (mon != NULL && m->desk == NULL) {
+               if (m == mon) {
+                       focus_node(m, NULL, NULL);
+               } else {
+                       activate_node(m, NULL, NULL);
+               }
+       }
+
+       put_status(SBSC_MASK_REPORT);
+}
+
+void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
+{
+       if (ds == NULL || dd == NULL || ds == dd) {
+               return;
+       }
+       transfer_node(ms, ds, ds->root, md, dd, dd->focus);
+}
+
+bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
+{
+       if (d1 == NULL || d2 == NULL || d1 == d2 ||
+           (m1->desk == d1 && m1->sticky_count > 0) ||
+           (m2->desk == d2 && m2->sticky_count > 0)) {
+               return false;
+       }
+
+       put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, m2->id, d2->id);
+
+       bool d1_focused = (m1->desk == d1);
+       bool d2_focused = (m2->desk == d2);
+
+       if (m1 != m2) {
+               if (m1->desk == d1) {
+                       m1->desk = d2;
+               }
+               if (m1->desk_head == d1) {
+                       m1->desk_head = d2;
+               }
+               if (m1->desk_tail == d1) {
+                       m1->desk_tail = d2;
+               }
+               if (m2->desk == d2) {
+                       m2->desk = d1;
+               }
+               if (m2->desk_head == d2) {
+                       m2->desk_head = d1;
+               }
+               if (m2->desk_tail == d2) {
+                       m2->desk_tail = d1;
+               }
+       } else {
+               if (m1->desk == d1) {
+                       m1->desk = d2;
+               } else if (m1->desk == d2) {
+                       m1->desk = d1;
+               }
+               if (m1->desk_head == d1) {
+                       m1->desk_head = d2;
+               } else if (m1->desk_head == d2) {
+                       m1->desk_head = d1;
+               }
+               if (m1->desk_tail == d1) {
+                       m1->desk_tail = d2;
+               } else if (m1->desk_tail == d2) {
+                       m1->desk_tail = d1;
+               }
+       }
+
+       desktop_t *p1 = d1->prev;
+       desktop_t *n1 = d1->next;
+       desktop_t *p2 = d2->prev;
+       desktop_t *n2 = d2->next;
+
+       if (p1 != NULL && p1 != d2) {
+               p1->next = d2;
+       }
+       if (n1 != NULL && n1 != d2) {
+               n1->prev = d2;
+       }
+       if (p2 != NULL && p2 != d1) {
+               p2->next = d1;
+       }
+       if (n2 != NULL && n2 != d1) {
+               n2->prev = d1;
+       }
+
+       d1->prev = p2 == d1 ? d2 : p2;
+       d1->next = n2 == d1 ? d2 : n2;
+       d2->prev = p1 == d2 ? d1 : p1;
+       d2->next = n1 == d2 ? d1 : n1;
+
+       if (m1 != m2) {
+               adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
+               adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
+               history_swap_desktops(m1, d1, m2, d2);
+               arrange(m1, d2);
+               arrange(m2, d1);
+       }
+
+       if (d1_focused && !d2_focused) {
+               hide_desktop(d1);
+               show_desktop(d2);
+       } else if (!d1_focused && d2_focused) {
+               show_desktop(d1);
+               hide_desktop(d2);
+       }
+
+       if (d1 == mon->desk) {
+               focus_node(m2, d1, d1->focus);
+       } else if (d1 == m2->desk) {
+               activate_node(m2, d1, d1->focus);
+       }
+
+       if (d2 == mon->desk) {
+               focus_node(m1, d2, d2->focus);
+       } else if (d2 == m1->desk) {
+               activate_node(m1, d2, d2->focus);
+       }
+
+       ewmh_update_wm_desktops();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+       ewmh_update_current_desktop();
+
+       put_status(SBSC_MASK_REPORT);
+
+       return true;
+}
+
+void show_desktop(desktop_t *d)
+{
+       if (d == NULL) {
+               return;
+       }
+       show_node(d, d->root);
+}
+
+void hide_desktop(desktop_t *d)
+{
+       if (d == NULL) {
+               return;
+       }
+       hide_node(d, d->root);
+}
+
+bool is_urgent(desktop_t *d)
+{
+       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+               if (n->client == NULL) {
+                       continue;
+               }
+               if (n->client->urgent) {
+                       return true;
+               }
+       }
+       return false;
+}
diff --git a/src/desktop.h b/src/desktop.h
new file mode 100644 (file)
index 0000000..80b33de
--- /dev/null
@@ -0,0 +1,49 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_DESKTOP_H
+#define BSPWM_DESKTOP_H
+
+#define DEFAULT_DESK_NAME    "Desktop"
+
+void focus_desktop(monitor_t *m, desktop_t *d);
+bool activate_desktop(monitor_t *m, desktop_t *d);
+bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel);
+bool set_layout(monitor_t *m, desktop_t *d, layout_t l);
+void handle_presel_feedbacks(monitor_t *m, desktop_t *d);
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
+desktop_t *make_desktop(const char *name, uint32_t id);
+void rename_desktop(monitor_t *m, desktop_t *d, const char *name);
+void insert_desktop(monitor_t *m, desktop_t *d);
+void add_desktop(monitor_t *m, desktop_t *d);
+desktop_t *find_desktop_in(uint32_t id, monitor_t *m);
+void unlink_desktop(monitor_t *m, desktop_t *d);
+void remove_desktop(monitor_t *m, desktop_t *d);
+void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);
+bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
+void show_desktop(desktop_t *d);
+void hide_desktop(desktop_t *d);
+bool is_urgent(desktop_t *d);
+
+#endif
diff --git a/src/events.c b/src/events.c
new file mode 100644 (file)
index 0000000..55656cb
--- /dev/null
@@ -0,0 +1,485 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "monitor.h"
+#include "query.h"
+#include "settings.h"
+#include "subscribe.h"
+#include "tree.h"
+#include "window.h"
+#include "pointer.h"
+#include "events.h"
+
+void handle_event(xcb_generic_event_t *evt)
+{
+       uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
+       switch (resp_type) {
+               case XCB_MAP_REQUEST:
+                       map_request(evt);
+                       break;
+               case XCB_DESTROY_NOTIFY:
+                       destroy_notify(evt);
+                       break;
+               case XCB_UNMAP_NOTIFY:
+                       unmap_notify(evt);
+                       break;
+               case XCB_CLIENT_MESSAGE:
+                       client_message(evt);
+                       break;
+               case XCB_CONFIGURE_REQUEST:
+                       configure_request(evt);
+                       break;
+               case XCB_CONFIGURE_NOTIFY:
+                       configure_notify(evt);
+                       break;
+               case XCB_PROPERTY_NOTIFY:
+                       property_notify(evt);
+                       break;
+               case XCB_ENTER_NOTIFY:
+                       enter_notify(evt);
+                       break;
+               case XCB_BUTTON_PRESS:
+                       button_press(evt);
+                       break;
+               case XCB_FOCUS_IN:
+                       focus_in(evt);
+                       break;
+               case 0:
+                       process_error(evt);
+                       break;
+               default:
+                       if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
+                               update_monitors();
+                       }
+                       break;
+       }
+}
+
+void map_request(xcb_generic_event_t *evt)
+{
+       xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
+
+       schedule_window(e->window);
+}
+
+void configure_request(xcb_generic_event_t *evt)
+{
+       xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
+
+       coordinates_t loc;
+       bool is_managed = locate_window(e->window, &loc);
+       client_t *c = (is_managed ? loc.node->client : NULL);
+       uint16_t width, height;
+
+       if (!is_managed) {
+               uint16_t mask = 0;
+               uint32_t values[7];
+               unsigned short i = 0;
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_X) {
+                       mask |= XCB_CONFIG_WINDOW_X;
+                       values[i++] = e->x;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
+                       mask |= XCB_CONFIG_WINDOW_Y;
+                       values[i++] = e->y;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+                       mask |= XCB_CONFIG_WINDOW_WIDTH;
+                       values[i++] = e->width;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+                       mask |= XCB_CONFIG_WINDOW_HEIGHT;
+                       values[i++] = e->height;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
+                       mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
+                       values[i++] = e->border_width;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
+                       mask |= XCB_CONFIG_WINDOW_SIBLING;
+                       values[i++] = e->sibling;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
+                       mask |= XCB_CONFIG_WINDOW_STACK_MODE;
+                       values[i++] = e->stack_mode;
+               }
+
+               xcb_configure_window(dpy, e->window, mask, values);
+
+       } else if (IS_FLOATING(c)) {
+               width = c->floating_rectangle.width;
+               height = c->floating_rectangle.height;
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_X) {
+                       c->floating_rectangle.x = e->x;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
+                       c->floating_rectangle.y = e->y;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+                       width = e->width;
+               }
+
+               if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+                       height = e->height;
+               }
+
+               apply_size_hints(c, &width, &height);
+               c->floating_rectangle.width = width;
+               c->floating_rectangle.height = height;
+               xcb_rectangle_t r = c->floating_rectangle;
+
+               if (focus_follows_pointer) {
+                       listen_enter_notify(loc.desktop->root, false);
+               }
+
+               window_move_resize(e->window, r.x, r.y, r.width, r.height);
+
+               if (focus_follows_pointer) {
+                       listen_enter_notify(loc.desktop->root, true);
+               }
+
+               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, e->window, r.width, r.height, r.x, r.y);
+
+               monitor_t *m = monitor_from_client(c);
+               if (m != loc.monitor) {
+                       transfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus);
+               }
+       } else {
+               if (c->state == STATE_PSEUDO_TILED) {
+                       width = c->floating_rectangle.width;
+                       height = c->floating_rectangle.height;
+                       if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+                               width = e->width;
+                       }
+                       if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+                               height = e->height;
+                       }
+                       apply_size_hints(c, &width, &height);
+                       if (width != c->floating_rectangle.width || height != c->floating_rectangle.height) {
+                               c->floating_rectangle.width = width;
+                               c->floating_rectangle.height = height;
+                               arrange(loc.monitor, loc.desktop);
+                       }
+               }
+
+
+               xcb_configure_notify_event_t evt;
+               unsigned int bw = c->border_width;
+
+               xcb_rectangle_t r = IS_FULLSCREEN(c) ? loc.monitor->rectangle : c->tiled_rectangle;
+
+               evt.response_type = XCB_CONFIGURE_NOTIFY;
+               evt.event = e->window;
+               evt.window = e->window;
+               evt.above_sibling = XCB_NONE;
+               evt.x = r.x;
+               evt.y = r.y;
+               evt.width = r.width;
+               evt.height = r.height;
+               evt.border_width = bw;
+               evt.override_redirect = false;
+
+               xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
+       }
+}
+
+void configure_notify(xcb_generic_event_t *evt)
+{
+       xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *) evt;
+
+       if (e->window == root) {
+               screen_width = e->width;
+               screen_height = e->height;
+       }
+}
+
+void destroy_notify(xcb_generic_event_t *evt)
+{
+       xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
+
+       unmanage_window(e->window);
+}
+
+void unmap_notify(xcb_generic_event_t *evt)
+{
+       xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
+
+       unmanage_window(e->window);
+}
+
+void property_notify(xcb_generic_event_t *evt)
+{
+       xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
+
+       if (e->atom == ewmh->_NET_WM_STRUT_PARTIAL && ewmh_handle_struts(e->window)) {
+               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+                       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                               arrange(m, d);
+                       }
+               }
+       }
+
+       if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {
+               return;
+       }
+
+       coordinates_t loc;
+       if (!locate_window(e->window, &loc)) {
+                       return;
+       }
+
+       if (e->atom == XCB_ATOM_WM_HINTS) {
+               xcb_icccm_wm_hints_t hints;
+               if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
+                   (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
+                       set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
+       } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
+               client_t *c = loc.node->client;
+               if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &c->size_hints, NULL) == 1) {
+                       arrange(loc.monitor, loc.desktop);
+               }
+       }
+}
+
+void client_message(xcb_generic_event_t *evt)
+{
+       xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
+
+       if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
+               coordinates_t loc;
+               if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
+                       focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
+               }
+               return;
+       }
+
+       coordinates_t loc;
+       if (!locate_window(e->window, &loc)) {
+               return;
+       }
+
+       if (e->type == ewmh->_NET_WM_STATE) {
+               handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
+               handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
+       } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
+               if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
+                   loc.node == mon->desk->focus) {
+                       return;
+               }
+               focus_node(loc.monitor, loc.desktop, loc.node);
+       } else if (e->type == ewmh->_NET_WM_DESKTOP) {
+               coordinates_t dloc;
+               if (ewmh_locate_desktop(e->data.data32[0], &dloc)) {
+                       transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
+               }
+       } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
+               close_node(loc.node);
+       }
+}
+
+void focus_in(xcb_generic_event_t *evt)
+{
+       xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
+
+       if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB
+           || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT
+           || e->detail == XCB_NOTIFY_DETAIL_NONE) {
+               return;
+       }
+
+       if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) {
+               return;
+       }
+
+       coordinates_t loc;
+       if (locate_window(e->event, &loc)) {
+               // prevent input focus stealing
+               update_input_focus();
+       }
+}
+
+void button_press(xcb_generic_event_t *evt)
+{
+       xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
+       bool replay = false;
+       switch (e->detail) {
+               case XCB_BUTTON_INDEX_1:
+                       if (click_to_focus && cleaned_mask(e->state) == XCB_NONE) {
+                               bool pff = pointer_follows_focus;
+                               bool pfm = pointer_follows_monitor;
+                               pointer_follows_focus = false;
+                               pointer_follows_monitor = false;
+                               replay = !grab_pointer(ACTION_FOCUS) || !swallow_first_click;
+                               pointer_follows_focus = pff;
+                               pointer_follows_monitor = pfm;
+                       } else {
+                               grab_pointer(pointer_actions[0]);
+                       }
+                       break;
+               case XCB_BUTTON_INDEX_2:
+                       grab_pointer(pointer_actions[1]);
+                       break;
+               case XCB_BUTTON_INDEX_3:
+                       grab_pointer(pointer_actions[2]);
+                       break;
+       }
+       xcb_allow_events(dpy, replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, e->time);
+       xcb_flush(dpy);
+}
+
+void enter_notify(xcb_generic_event_t *evt)
+{
+       xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
+       xcb_window_t win = e->event;
+
+       if (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
+               return;
+       }
+
+       xcb_point_t pt = {e->root_x, e->root_y};
+       monitor_t *m = monitor_from_point(pt);
+
+       if (m == NULL) {
+               return;
+       }
+
+       bool pff = pointer_follows_focus;
+       bool pfm = pointer_follows_monitor;
+       pointer_follows_focus = false;
+       pointer_follows_monitor = false;
+       auto_raise = false;
+
+       desktop_t *d = m->desk;
+       node_t *n = NULL;
+
+       for (n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+               if (n->id == win || (n->presel != NULL && n->presel->feedback == win)) {
+                       break;
+               }
+       }
+
+       if (n != mon->desk->focus || mon->desk->focus == NULL) {
+               focus_node(m, d, n == NULL ? d->focus : n);
+       }
+
+       pointer_follows_focus = pff;
+       pointer_follows_monitor = pfm;
+       auto_raise = true;
+}
+
+void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
+{
+       if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
+               if (action == XCB_EWMH_WM_STATE_ADD) {
+                       set_state(m, d, n, STATE_FULLSCREEN);
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+                       if (n->client->state == STATE_FULLSCREEN) {
+                               set_state(m, d, n, n->client->last_state);
+                       }
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+                       set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN);
+               }
+               arrange(m, d);
+       } else if (state == ewmh->_NET_WM_STATE_BELOW) {
+               if (action == XCB_EWMH_WM_STATE_ADD) {
+                       set_layer(m, d, n, LAYER_BELOW);
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+                       if (n->client->layer == LAYER_BELOW) {
+                               set_layer(m, d, n, n->client->last_layer);
+                       }
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+                       set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);
+               }
+       } else if (state == ewmh->_NET_WM_STATE_ABOVE) {
+               if (action == XCB_EWMH_WM_STATE_ADD) {
+                       set_layer(m, d, n, LAYER_ABOVE);
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+                       if (n->client->layer == LAYER_ABOVE) {
+                               set_layer(m, d, n, n->client->last_layer);
+                       }
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+                       set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);
+               }
+       } else if (state == ewmh->_NET_WM_STATE_HIDDEN) {
+               if (action == XCB_EWMH_WM_STATE_ADD) {
+                       set_hidden(m, d, n, true);
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+                       set_hidden(m, d, n, false);
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+                       set_hidden(m, d, n, !n->hidden);
+               }
+       } else if (state == ewmh->_NET_WM_STATE_STICKY) {
+               if (action == XCB_EWMH_WM_STATE_ADD) {
+                       set_sticky(m, d, n, true);
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+                       set_sticky(m, d, n, false);
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+                       set_sticky(m, d, n, !n->sticky);
+               }
+       } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
+               if (action == XCB_EWMH_WM_STATE_ADD) {
+                       set_urgent(m, d, n, true);
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+                       set_urgent(m, d, n, false);
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+                       set_urgent(m, d, n, !n->client->urgent);
+               }
+#define HANDLE_WM_STATE(s)  \
+       } else if (state == ewmh->_NET_WM_STATE_##s) { \
+               if (action == XCB_EWMH_WM_STATE_ADD) { \
+                       n->client->wm_flags |= WM_FLAG_##s; \
+               } else if (action == XCB_EWMH_WM_STATE_REMOVE) { \
+                       n->client->wm_flags &= ~WM_FLAG_##s; \
+               } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { \
+                       n->client->wm_flags ^= WM_FLAG_##s; \
+               } \
+               ewmh_wm_state_update(n);
+       HANDLE_WM_STATE(MODAL)
+       HANDLE_WM_STATE(MAXIMIZED_VERT)
+       HANDLE_WM_STATE(MAXIMIZED_HORZ)
+       HANDLE_WM_STATE(SHADED)
+       HANDLE_WM_STATE(SKIP_TASKBAR)
+       HANDLE_WM_STATE(SKIP_PAGER)
+       }
+#undef HANDLE_WM_STATE
+}
+
+void process_error(xcb_generic_event_t *evt)
+{
+       xcb_request_error_t *e = (xcb_request_error_t *) evt;
+       warn("Failed request: %s, %s: 0x%08X.\n", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value);
+}
diff --git a/src/events.h b/src/events.h
new file mode 100644 (file)
index 0000000..cb26b4a
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_EVENTS_H
+#define BSPWM_EVENTS_H
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_event.h>
+
+uint8_t randr_base;
+
+void handle_event(xcb_generic_event_t *evt);
+void map_request(xcb_generic_event_t *evt);
+void configure_request(xcb_generic_event_t *evt);
+void configure_notify(xcb_generic_event_t *evt);
+void destroy_notify(xcb_generic_event_t *evt);
+void unmap_notify(xcb_generic_event_t *evt);
+void property_notify(xcb_generic_event_t *evt);
+void client_message(xcb_generic_event_t *evt);
+void focus_in(xcb_generic_event_t *evt);
+void button_press(xcb_generic_event_t *evt);
+void enter_notify(xcb_generic_event_t *evt);
+void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action);
+void process_error(xcb_generic_event_t *evt);
+
+#endif
diff --git a/src/ewmh.c b/src/ewmh.c
new file mode 100644 (file)
index 0000000..869d406
--- /dev/null
@@ -0,0 +1,293 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "settings.h"
+#include "tree.h"
+#include "ewmh.h"
+
+void ewmh_init(void)
+{
+       ewmh = calloc(1, sizeof(xcb_ewmh_connection_t));
+       if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
+               err("Can't initialize EWMH atoms.\n");
+       }
+}
+
+void ewmh_update_active_window(void)
+{
+       xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
+       xcb_ewmh_set_active_window(ewmh, default_screen, win);
+}
+
+void ewmh_update_number_of_desktops(void)
+{
+       uint32_t desktops_count = 0;
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       desktops_count++;
+               }
+       }
+
+       xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
+}
+
+uint32_t ewmh_get_desktop_index(desktop_t *d)
+{
+       uint32_t i = 0;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {
+                       if (d == cd) {
+                               return i;
+                       }
+               }
+       }
+       return 0;
+}
+
+bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
+                       if (i == 0) {
+                               loc->monitor = m;
+                               loc->desktop = d;
+                               loc->node = NULL;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+void ewmh_update_current_desktop(void)
+{
+       if (mon == NULL) {
+               return;
+       }
+       uint32_t i = ewmh_get_desktop_index(mon->desk);
+       xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
+}
+
+void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
+{
+       uint32_t i = ewmh_get_desktop_index(d);
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client == NULL) {
+                       continue;
+               }
+               xcb_ewmh_set_wm_desktop(ewmh, f->id, i);
+       }
+}
+
+void ewmh_update_wm_desktops(void)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       uint32_t i = ewmh_get_desktop_index(d);
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                               if (n->client == NULL) {
+                                       continue;
+                               }
+                               xcb_ewmh_set_wm_desktop(ewmh, n->id, i);
+                       }
+               }
+       }
+}
+
+void ewmh_update_desktop_names(void)
+{
+       char names[MAXLEN];
+       unsigned int i, j;
+       uint32_t names_len;
+       i = 0;
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) {
+                               names[i + j] = d->name[j];
+                       }
+                       i += j;
+                       if (i < sizeof(names)) {
+                               names[i++] = '\0';
+                       }
+               }
+       }
+
+       if (i < 1) {
+               xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);
+               return;
+       }
+
+       names_len = i - 1;
+       xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
+}
+
+void ewmh_update_desktop_viewport(void)
+{
+       uint32_t desktops_count = 0;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       desktops_count++;
+               }
+       }
+       xcb_ewmh_coordinates_t coords[desktops_count];
+       uint16_t desktop = 0;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       coords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y};
+               }
+       }
+       xcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords);
+}
+
+bool ewmh_handle_struts(xcb_window_t win)
+{
+       xcb_ewmh_wm_strut_partial_t struts;
+       bool changed = false;
+       if (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) {
+               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+                       xcb_rectangle_t rect = m->rectangle;
+                       if (rect.x < (int16_t) struts.left &&
+                           (int16_t) struts.left < (rect.x + rect.width - 1) &&
+                           (int16_t) struts.left_end_y >= rect.y &&
+                           (int16_t) struts.left_start_y < (rect.y + rect.height)) {
+                               int dx = struts.left - rect.x;
+                               if (m->padding.left < 0) {
+                                       m->padding.left += dx;
+                               } else {
+                                       m->padding.left = MAX(dx, m->padding.left);
+                               }
+                               changed = true;
+                       }
+                       if ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) &&
+                           (int16_t) (screen_width - struts.right) > rect.x &&
+                           (int16_t) struts.right_end_y >= rect.y &&
+                           (int16_t) struts.right_start_y < (rect.y + rect.height)) {
+                               int dx = (rect.x + rect.width) - screen_width + struts.right;
+                               if (m->padding.right < 0) {
+                                       m->padding.right += dx;
+                               } else {
+                                       m->padding.right = MAX(dx, m->padding.right);
+                               }
+                               changed = true;
+                       }
+                       if (rect.y < (int16_t) struts.top &&
+                           (int16_t) struts.top < (rect.y + rect.height - 1) &&
+                           (int16_t) struts.top_end_x >= rect.x &&
+                           (int16_t) struts.top_start_x < (rect.x + rect.width)) {
+                               int dy = struts.top - rect.y;
+                               if (m->padding.top < 0) {
+                                       m->padding.top += dy;
+                               } else {
+                                       m->padding.top = MAX(dy, m->padding.top);
+                               }
+                               changed = true;
+                       }
+                       if ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) &&
+                           (int16_t) (screen_height - struts.bottom) > rect.y &&
+                           (int16_t) struts.bottom_end_x >= rect.x &&
+                           (int16_t) struts.bottom_start_x < (rect.x + rect.width)) {
+                               int dy = (rect.y + rect.height) - screen_height + struts.bottom;
+                               if (m->padding.bottom < 0) {
+                                       m->padding.bottom += dy;
+                               } else {
+                                       m->padding.bottom = MAX(dy, m->padding.bottom);
+                               }
+                               changed = true;
+                       }
+               }
+       }
+       return changed;
+}
+
+void ewmh_update_client_list(bool stacking)
+{
+       if (clients_count == 0) {
+               xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);
+               xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);
+               return;
+       }
+
+       xcb_window_t wins[clients_count];
+       unsigned int i = 0;
+
+       if (stacking) {
+               for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+                       wins[i++] = s->node->id;
+               }
+               xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);
+       } else {
+               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+                       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                               for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                                       if (n->client == NULL) {
+                                               continue;
+                                       }
+                                       wins[i++] = n->id;
+                               }
+                       }
+               }
+               xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);
+       }
+}
+
+void ewmh_wm_state_update(node_t *n)
+{
+       client_t *c = n->client;
+       size_t count = 0;
+       uint32_t values[12];
+#define HANDLE_WM_STATE(s)  \
+       if (WM_FLAG_##s & c->wm_flags) { \
+               values[count++] = ewmh->_NET_WM_STATE_##s; \
+       }
+       HANDLE_WM_STATE(MODAL)
+       HANDLE_WM_STATE(STICKY)
+       HANDLE_WM_STATE(MAXIMIZED_VERT)
+       HANDLE_WM_STATE(MAXIMIZED_HORZ)
+       HANDLE_WM_STATE(SHADED)
+       HANDLE_WM_STATE(SKIP_TASKBAR)
+       HANDLE_WM_STATE(SKIP_PAGER)
+       HANDLE_WM_STATE(HIDDEN)
+       HANDLE_WM_STATE(FULLSCREEN)
+       HANDLE_WM_STATE(ABOVE)
+       HANDLE_WM_STATE(BELOW)
+       HANDLE_WM_STATE(DEMANDS_ATTENTION)
+#undef HANDLE_WM_STATE
+       xcb_ewmh_set_wm_state(ewmh, n->id, count, values);
+}
+
+void ewmh_set_supporting(xcb_window_t win)
+{
+       pid_t wm_pid = getpid();
+       xcb_ewmh_set_supporting_wm_check(ewmh, root, win);
+       xcb_ewmh_set_supporting_wm_check(ewmh, win, win);
+       xcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME);
+       xcb_ewmh_set_wm_pid(ewmh, win, wm_pid);
+}
diff --git a/src/ewmh.h b/src/ewmh.h
new file mode 100644 (file)
index 0000000..5bf3e18
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_EWMH_H
+#define BSPWM_EWMH_H
+
+#include <xcb/xcb_ewmh.h>
+
+xcb_ewmh_connection_t *ewmh;
+
+void ewmh_init(void);
+void ewmh_update_active_window(void);
+void ewmh_update_number_of_desktops(void);
+uint32_t ewmh_get_desktop_index(desktop_t *d);
+bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc);
+void ewmh_update_current_desktop(void);
+void ewmh_set_wm_desktop(node_t *n, desktop_t *d);
+void ewmh_update_wm_desktops(void);
+void ewmh_update_desktop_names(void);
+void ewmh_update_desktop_viewport(void);
+bool ewmh_handle_struts(xcb_window_t win);
+void ewmh_update_client_list(bool stacking);
+void ewmh_wm_state_update(node_t *n);
+void ewmh_set_supporting(xcb_window_t win);
+
+#endif
diff --git a/src/geometry.c b/src/geometry.c
new file mode 100644 (file)
index 0000000..7976a4e
--- /dev/null
@@ -0,0 +1,124 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include "types.h"
+#include "geometry.h"
+
+bool is_inside(xcb_point_t p, xcb_rectangle_t r)
+{
+       return (p.x >= r.x && p.x < (r.x + r.width) &&
+               p.y >= r.y && p.y < (r.y + r.height));
+}
+
+unsigned int area(xcb_rectangle_t r)
+{
+       return r.width * r.height;
+}
+
+
+dpoint_t center(xcb_rectangle_t r)
+{
+       return (dpoint_t) {(double)r.x + ((double)r.width / 2), (double)r.y + ((double)r.height / 2)};
+}
+
+double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2)
+{
+       dpoint_t r1_center = center(r1);
+       dpoint_t r2_center = center(r2);
+       return hypot(r1_center.x - r2_center.x, r1_center.y - r2_center.y);
+}
+
+bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)
+{
+       dpoint_t r1_max = {r1.x + r1.width, r1.y + r1.height};
+       dpoint_t r2_max = {r2.x + r2.width, r2.y + r2.height};
+       dpoint_t r1_center = center(r1);
+       dpoint_t r2_center = center(r2);
+
+       switch (dir) {
+               case DIR_NORTH:
+                       if (r2_center.y >= r1_center.y)
+                               return false;
+                       break;
+               case DIR_WEST:
+                       if (r2_center.x >= r1_center.x)
+                               return false;
+                       break;
+               case DIR_SOUTH:
+                       if (r1_center.y >= r2_center.y)
+                               return false;
+                       break;
+               case DIR_EAST:
+                       if (r1_center.x >= r2_center.x)
+                               return false;
+                       break;
+               default:
+                       return false;
+       }
+
+       switch (dir) {
+               case DIR_NORTH:
+               case DIR_SOUTH:
+                       return
+                               (r2.x >= r1.x && r2.x <= r1_max.x) ||
+                               (r2_max.x >= r1.x && r2_max.x <= r1_max.x) ||
+                               (r1.x >= r2.x && r1.x <= r2_max.x) ||
+                               (r1_max.x >= r2.x && r1_max.x <= r2_max.x);
+                       break;
+               case DIR_WEST:
+               case DIR_EAST:
+                       return
+                               (r2.y >= r1.y && r2.y <= r1_max.y) ||
+                               (r2_max.y >= r1.y && r2_max.y <= r1_max.y) ||
+                               (r1.y >= r2.y && r1.y <= r2_max.y) ||
+                               (r1_max.y >= r2.y && r1_max.y <= r2_max.y);
+                       break;
+               default:
+                       return false;
+       }
+}
+
+bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b)
+{
+       return (a.x == b.x && a.y == b.y &&
+               a.width == b.width && a.height == b.height);
+}
+
+int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2)
+{
+       if (r1.y >= (r2.y + r2.height)) {
+               return 1;
+       } else if (r2.y >= (r1.y + r1.height)) {
+               return -1;
+       } else {
+               if (r1.x >= (r2.x + r2.width)) {
+                       return 1;
+               } else if (r2.x >= (r1.x + r1.width)) {
+                       return -1;
+               } else {
+                       return area(r1) - area(r2);
+               }
+       }
+}
diff --git a/src/geometry.h b/src/geometry.h
new file mode 100644 (file)
index 0000000..0e1a760
--- /dev/null
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_GEOMETRY_H
+#define BSPWM_GEOMETRY_H
+
+#include <stdbool.h>
+#include <xcb/xcb.h>
+
+bool is_inside(xcb_point_t p, xcb_rectangle_t r);
+unsigned int area(xcb_rectangle_t r);
+dpoint_t center(xcb_rectangle_t r);
+double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2);
+bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);
+bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b);
+int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2);
+
+#endif
diff --git a/src/helpers.c b/src/helpers.c
new file mode 100644 (file)
index 0000000..39d0a49
--- /dev/null
@@ -0,0 +1,146 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "bspwm.h"
+
+void warn(char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
+
+__attribute__((noreturn))
+void err(char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       exit(EXIT_FAILURE);
+}
+
+char *read_string(const char *file_path, size_t *tlen)
+{
+       if (file_path == NULL) {
+               return NULL;
+       }
+
+       int fd = open(file_path, O_RDONLY);
+
+       if (fd == -1) {
+               perror("Read file: open");
+               return NULL;
+       }
+
+       char buf[BUFSIZ], *content = NULL;
+       size_t len = sizeof(buf);
+
+       if ((content = calloc(len, sizeof(char))) == NULL) {
+               perror("Read file: calloc");
+               goto end;
+       }
+
+       int nb;
+       *tlen = 0;
+
+       while (true) {
+               nb = read(fd, buf, sizeof(buf));
+               if (nb < 0) {
+                       perror("Restore tree: read");
+                       free(content);
+                       content = NULL;
+                       goto end;
+               } else if (nb == 0) {
+                       break;
+               } else {
+                       *tlen += nb;
+                       if (*tlen > len) {
+                               len *= 2;
+                               char *rcontent = realloc(content, len * sizeof(char));
+                               if (rcontent == NULL) {
+                                       perror("Read file: realloc");
+                                       free(content);
+                                       content = NULL;
+                                       goto end;
+                               } else {
+                                       content = rcontent;
+                               }
+                       }
+                       strncpy(content + (*tlen - nb), buf, nb);
+               }
+       }
+
+end:
+       close(fd);
+       return content;
+}
+
+char *copy_string(char *str, size_t len)
+{
+       char *cpy = calloc(1, ((len+1) * sizeof(char)));
+       if (cpy == NULL) {
+               perror("Copy string: calloc");
+               return NULL;
+       }
+       strncpy(cpy, str, len);
+       cpy[len] = '\0';
+       return cpy;
+}
+
+/* Adapted from i3wm */
+uint32_t get_color_pixel(const char *color)
+{
+       unsigned int red, green, blue;
+       if (sscanf(color + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
+               /* We set the first 8 bits high to have 100% opacity in case of a 32 bit
+                * color depth visual. */
+               return (0xFF << 24) | (red << 16 | green << 8 | blue);
+       } else {
+               return screen->black_pixel;
+       }
+}
+
+bool is_hex_color(const char *color)
+{
+       if (color[0] != '#' || strlen(color) != 7) {
+               return false;
+       }
+       for (int i = 1; i < 7; i++) {
+               if (!isxdigit(color[i])) {
+                       return false;
+               }
+       }
+       return true;
+}
diff --git a/src/helpers.h b/src/helpers.h
new file mode 100644 (file)
index 0000000..23d29fb
--- /dev/null
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_HELPERS_H
+#define BSPWM_HELPERS_H
+
+#include <xcb/xcb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <float.h>
+
+#define LENGTH(x)         (sizeof(x) / sizeof(*x))
+#define MAX(A, B)         ((A) > (B) ? (A) : (B))
+#define MIN(A, B)         ((A) < (B) ? (A) : (B))
+
+#define IS_TILED(c)       (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED)
+#define IS_FLOATING(c)    (c->state == STATE_FLOATING)
+#define IS_FULLSCREEN(c)  (c->state == STATE_FULLSCREEN)
+#define IS_RECEPTACLE(n)  (is_leaf(n) && n->client == NULL)
+#define IS_MONOCLE(d)     (d->layout == LAYOUT_MONOCLE || (single_monocle && tiled_count(d->root) <= 1))
+
+#define BOOL_STR(A)       ((A) ? "true" : "false")
+#define ON_OFF_STR(A)     ((A) ? "on" : "off")
+#define LAYOUT_STR(A)     ((A) == LAYOUT_TILED ? "tiled" : "monocle")
+#define LAYOUT_CHR(A)     ((A) == LAYOUT_TILED ? 'T' : 'M')
+#define CHILD_POL_STR(A)  ((A) == FIRST_CHILD ? "first_child" : "second_child")
+#define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical")
+#define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual")
+#define SPLIT_DIR_STR(A)  ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east")))
+#define STATE_STR(A)      ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
+#define STATE_CHR(A)      ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))
+#define LAYER_STR(A)      ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
+
+#define XCB_CONFIG_WINDOW_X_Y               (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y)
+#define XCB_CONFIG_WINDOW_WIDTH_HEIGHT      (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
+#define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT  (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
+
+#define MAXLEN    256
+#define SMALEN     32
+#define INIT_CAP    8
+
+#define cleaned_mask(m)   (m & ~(num_lock | scroll_lock | caps_lock))
+#define streq(s1, s2)     (strcmp((s1), (s2)) == 0)
+#define unsigned_subtract(a, b)  \
+       do {                         \
+               if (b > a) {             \
+                       a = 0;               \
+               } else {                 \
+                       a -= b;              \
+               }                        \
+       } while (false)
+
+
+void warn(char *fmt, ...);
+void err(char *fmt, ...);
+char *read_string(const char *file_path, size_t *tlen);
+char *copy_string(char *str, size_t len);
+uint32_t get_color_pixel(const char *color);
+bool is_hex_color(const char *color);
+
+#endif
diff --git a/src/history.c b/src/history.c
new file mode 100644 (file)
index 0000000..b20d01c
--- /dev/null
@@ -0,0 +1,277 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "tree.h"
+#include "query.h"
+
+history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
+{
+       history_t *h = calloc(1, sizeof(history_t));
+       h->loc = (coordinates_t) {m, d, n};
+       h->prev = h->next = NULL;
+       h->latest = true;
+       return h;
+}
+
+void history_add(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (!record_history) {
+               return;
+       }
+       history_needle = NULL;
+       history_t *h = make_history(m, d, n);
+       if (history_head == NULL) {
+               history_head = history_tail = h;
+       } else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) {
+               for (history_t *hh = history_tail; hh != NULL; hh = hh->prev) {
+                       if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) {
+                               hh->latest = false;
+                       }
+               }
+               history_tail->next = h;
+               h->prev = history_tail;
+               history_tail = h;
+       } else {
+               free(h);
+       }
+}
+
+void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (is_descendant(h->loc.node, n)) {
+                       h->loc.monitor = m;
+                       h->loc.desktop = d;
+               }
+       }
+}
+
+void history_transfer_desktop(monitor_t *m, desktop_t *d)
+{
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (h->loc.desktop == d) {
+                       h->loc.monitor = m;
+               }
+       }
+}
+
+void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
+{
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (is_descendant(h->loc.node, n1)) {
+                       h->loc.monitor = m2;
+                       h->loc.desktop = d2;
+               } else if (is_descendant(h->loc.node, n2)) {
+                       h->loc.monitor = m1;
+                       h->loc.desktop = d1;
+               }
+       }
+}
+
+void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
+{
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               if (h->loc.desktop == d1) {
+                       h->loc.monitor = m2;
+               } else if (h->loc.desktop == d2) {
+                       h->loc.monitor = m1;
+               }
+       }
+}
+
+void history_remove(desktop_t *d, node_t *n, bool deep)
+{
+       /* removing from the newest to the oldest is required */
+       /* for maintaining the *latest* attribute */
+       history_t *b = history_tail;
+       while (b != NULL) {
+               if ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) ||
+                   (n == NULL && d == b->loc.desktop)) {
+                       history_t *a = b->next;
+                       history_t *c = b->prev;
+                       if (a != NULL) {
+                               /* remove duplicate entries */
+                               while (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) ||
+                                      (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) {
+                                       history_t *p = c->prev;
+                                       if (history_head == c) {
+                                               history_head = history_tail;
+                                       }
+                                       if (history_needle == c) {
+                                               history_needle = history_tail;
+                                       }
+                                       free(c);
+                                       c = p;
+                               }
+                               a->prev = c;
+                       }
+                       if (c != NULL) {
+                               c->next = a;
+                       }
+                       if (history_tail == b) {
+                               history_tail = c;
+                       }
+                       if (history_head == b) {
+                               history_head = a;
+                       }
+                       if (history_needle == b) {
+                               history_needle = c;
+                       }
+                       free(b);
+                       b = c;
+               } else {
+                       b = b->prev;
+               }
+       }
+}
+
+void empty_history(void)
+{
+       history_t *h = history_head;
+       while (h != NULL) {
+               history_t *next = h->next;
+               free(h);
+               h = next;
+       }
+       history_head = history_tail = NULL;
+}
+
+node_t *history_last_node(desktop_t *d, node_t *n)
+{
+       for (history_t *h = history_tail; h != NULL; h = h->prev) {
+               if (h->latest && h->loc.node != NULL && !h->loc.node->hidden &&
+                   !is_descendant(h->loc.node, n) && h->loc.desktop == d) {
+                       return h->loc.node;
+               }
+       }
+       return NULL;
+}
+
+desktop_t *history_last_desktop(monitor_t *m, desktop_t *d)
+{
+       for (history_t *h = history_tail; h != NULL; h = h->prev) {
+               if (h->latest && h->loc.desktop != d && h->loc.monitor == m) {
+                       return h->loc.desktop;
+               }
+       }
+       return NULL;
+}
+
+monitor_t *history_last_monitor(monitor_t *m)
+{
+       for (history_t *h = history_tail; h != NULL; h = h->prev) {
+               if (h->latest && h->loc.monitor != m) {
+                       return h->loc.monitor;
+               }
+       }
+       return NULL;
+}
+
+bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel)
+{
+       if (history_needle == NULL || record_history) {
+               history_needle = history_tail;
+       }
+
+       history_t *h;
+       for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
+               if (!h->latest ||
+                   h->loc.node == NULL ||
+                   h->loc.node == ref->node ||
+                   h->loc.node->hidden ||
+                   !node_matches(&h->loc, ref, sel)) {
+                       continue;
+               }
+               if (!record_history) {
+                       history_needle = h;
+               }
+               *dst = h->loc;
+               return true;
+       }
+       return false;
+}
+
+bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel)
+{
+       if (history_needle == NULL || record_history) {
+               history_needle = history_tail;
+       }
+
+       history_t *h;
+       for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
+               if (!h->latest ||
+                   h->loc.desktop == ref->desktop ||
+                   !desktop_matches(&h->loc, ref, sel)) {
+                       continue;
+               }
+               if (!record_history) {
+                       history_needle = h;
+               }
+               *dst = h->loc;
+               return true;
+       }
+       return false;
+}
+
+bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel)
+{
+       if (history_needle == NULL || record_history) {
+               history_needle = history_tail;
+       }
+
+       history_t *h;
+
+       for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
+               if (!h->latest ||
+                   h->loc.monitor == ref->monitor ||
+                   !monitor_matches(&h->loc, ref, sel)) {
+                       continue;
+               }
+               if (!record_history) {
+                       history_needle = h;
+               }
+               *dst = h->loc;
+               return true;
+       }
+
+       return false;
+}
+
+uint32_t history_rank(node_t *n)
+{
+       uint32_t r = 0;
+       history_t *h = history_tail;
+       while (h != NULL && (!h->latest || h->loc.node != n)) {
+               h = h->prev;
+               r++;
+       }
+       if (h == NULL) {
+               return UINT32_MAX;
+       } else {
+               return r;
+       }
+}
diff --git a/src/history.h b/src/history.h
new file mode 100644 (file)
index 0000000..5df4629
--- /dev/null
@@ -0,0 +1,46 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_HISTORY_H
+#define BSPWM_HISTORY_H
+
+#include "types.h"
+
+history_t *make_history(monitor_t *m, desktop_t *d, node_t *n);
+void history_add(monitor_t *m, desktop_t *d, node_t *n);
+void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n);
+void history_transfer_desktop(monitor_t *m, desktop_t *d);
+void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
+void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
+void history_remove(desktop_t *d, node_t *n, bool deep);
+void empty_history(void);
+node_t *history_last_node(desktop_t *d, node_t *n);
+desktop_t *history_last_desktop(monitor_t *m, desktop_t *d);
+monitor_t *history_last_monitor(monitor_t *m);
+bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel);
+bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel);
+bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel);
+uint32_t history_rank(node_t *n);
+
+#endif
diff --git a/src/jsmn.c b/src/jsmn.c
new file mode 100644 (file)
index 0000000..bbf013d
--- /dev/null
@@ -0,0 +1,311 @@
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+               jsmntok_t *tokens, size_t num_tokens) {
+       jsmntok_t *tok;
+       if (parser->toknext >= num_tokens) {
+               return NULL;
+       }
+       tok = &tokens[parser->toknext++];
+       tok->start = tok->end = -1;
+       tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+       tok->parent = -1;
+#endif
+       return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+                            int start, int end) {
+       token->type = type;
+       token->start = start;
+       token->end = end;
+       token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+               size_t len, jsmntok_t *tokens, size_t num_tokens) {
+       jsmntok_t *token;
+       int start;
+
+       start = parser->pos;
+
+       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+               switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+                       /* In strict mode primitive must be followed by "," or "}" or "]" */
+                       case ':':
+#endif
+                       case '\t' : case '\r' : case '\n' : case ' ' :
+                       case ','  : case ']'  : case '}' :
+                               goto found;
+               }
+               if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+                       parser->pos = start;
+                       return JSMN_ERROR_INVAL;
+               }
+       }
+#ifdef JSMN_STRICT
+       /* In strict mode primitive must be followed by a comma/object/array */
+       parser->pos = start;
+       return JSMN_ERROR_PART;
+#endif
+
+found:
+       if (tokens == NULL) {
+               parser->pos--;
+               return 0;
+       }
+       token = jsmn_alloc_token(parser, tokens, num_tokens);
+       if (token == NULL) {
+               parser->pos = start;
+               return JSMN_ERROR_NOMEM;
+       }
+       jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+       token->parent = parser->toksuper;
+#endif
+       parser->pos--;
+       return 0;
+}
+
+/**
+ * Filsl next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+               size_t len, jsmntok_t *tokens, size_t num_tokens) {
+       jsmntok_t *token;
+
+       int start = parser->pos;
+
+       parser->pos++;
+
+       /* Skip starting quote */
+       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+               char c = js[parser->pos];
+
+               /* Quote: end of string */
+               if (c == '\"') {
+                       if (tokens == NULL) {
+                               return 0;
+                       }
+                       token = jsmn_alloc_token(parser, tokens, num_tokens);
+                       if (token == NULL) {
+                               parser->pos = start;
+                               return JSMN_ERROR_NOMEM;
+                       }
+                       jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+                       token->parent = parser->toksuper;
+#endif
+                       return 0;
+               }
+
+               /* Backslash: Quoted symbol expected */
+               if (c == '\\' && parser->pos + 1 < len) {
+                       int i;
+                       parser->pos++;
+                       switch (js[parser->pos]) {
+                               /* Allowed escaped symbols */
+                               case '\"': case '/' : case '\\' : case 'b' :
+                               case 'f' : case 'r' : case 'n'  : case 't' :
+                                       break;
+                               /* Allows escaped symbol \uXXXX */
+                               case 'u':
+                                       parser->pos++;
+                                       for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
+                                               /* If it isn't a hex character we have an error */
+                                               if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+                                                                       (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+                                                                       (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+                                                       parser->pos = start;
+                                                       return JSMN_ERROR_INVAL;
+                                               }
+                                               parser->pos++;
+                                       }
+                                       parser->pos--;
+                                       break;
+                               /* Unexpected symbol */
+                               default:
+                                       parser->pos = start;
+                                       return JSMN_ERROR_INVAL;
+                       }
+               }
+       }
+       parser->pos = start;
+       return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+               jsmntok_t *tokens, unsigned int num_tokens) {
+       int r;
+       int i;
+       jsmntok_t *token;
+       int count = parser->toknext;
+
+       for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+               char c;
+               jsmntype_t type;
+
+               c = js[parser->pos];
+               switch (c) {
+                       case '{': case '[':
+                               count++;
+                               if (tokens == NULL) {
+                                       break;
+                               }
+                               token = jsmn_alloc_token(parser, tokens, num_tokens);
+                               if (token == NULL)
+                                       return JSMN_ERROR_NOMEM;
+                               if (parser->toksuper != -1) {
+                                       tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+                                       token->parent = parser->toksuper;
+#endif
+                               }
+                               token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+                               token->start = parser->pos;
+                               parser->toksuper = parser->toknext - 1;
+                               break;
+                       case '}': case ']':
+                               if (tokens == NULL)
+                                       break;
+                               type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+                               if (parser->toknext < 1) {
+                                       return JSMN_ERROR_INVAL;
+                               }
+                               token = &tokens[parser->toknext - 1];
+                               for (;;) {
+                                       if (token->start != -1 && token->end == -1) {
+                                               if (token->type != type) {
+                                                       return JSMN_ERROR_INVAL;
+                                               }
+                                               token->end = parser->pos + 1;
+                                               parser->toksuper = token->parent;
+                                               break;
+                                       }
+                                       if (token->parent == -1) {
+                                               break;
+                                       }
+                                       token = &tokens[token->parent];
+                               }
+#else
+                               for (i = parser->toknext - 1; i >= 0; i--) {
+                                       token = &tokens[i];
+                                       if (token->start != -1 && token->end == -1) {
+                                               if (token->type != type) {
+                                                       return JSMN_ERROR_INVAL;
+                                               }
+                                               parser->toksuper = -1;
+                                               token->end = parser->pos + 1;
+                                               break;
+                                       }
+                               }
+                               /* Error if unmatched closing bracket */
+                               if (i == -1) return JSMN_ERROR_INVAL;
+                               for (; i >= 0; i--) {
+                                       token = &tokens[i];
+                                       if (token->start != -1 && token->end == -1) {
+                                               parser->toksuper = i;
+                                               break;
+                                       }
+                               }
+#endif
+                               break;
+                       case '\"':
+                               r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+                               if (r < 0) return r;
+                               count++;
+                               if (parser->toksuper != -1 && tokens != NULL)
+                                       tokens[parser->toksuper].size++;
+                               break;
+                       case '\t' : case '\r' : case '\n' : case ' ':
+                               break;
+                       case ':':
+                               parser->toksuper = parser->toknext - 1;
+                               break;
+                       case ',':
+                               if (tokens != NULL && parser->toksuper != -1 &&
+                                               tokens[parser->toksuper].type != JSMN_ARRAY &&
+                                               tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+                                       parser->toksuper = tokens[parser->toksuper].parent;
+#else
+                                       for (i = parser->toknext - 1; i >= 0; i--) {
+                                               if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+                                                       if (tokens[i].start != -1 && tokens[i].end == -1) {
+                                                               parser->toksuper = i;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+#endif
+                               }
+                               break;
+#ifdef JSMN_STRICT
+                       /* In strict mode primitives are: numbers and booleans */
+                       case '-': case '0': case '1' : case '2': case '3' : case '4':
+                       case '5': case '6': case '7' : case '8': case '9':
+                       case 't': case 'f': case 'n' :
+                               /* And they must not be keys of the object */
+                               if (tokens != NULL && parser->toksuper != -1) {
+                                       jsmntok_t *t = &tokens[parser->toksuper];
+                                       if (t->type == JSMN_OBJECT ||
+                                                       (t->type == JSMN_STRING && t->size != 0)) {
+                                               return JSMN_ERROR_INVAL;
+                                       }
+                               }
+#else
+                       /* In non-strict mode every unquoted value is a primitive */
+                       default:
+#endif
+                               r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+                               if (r < 0) return r;
+                               count++;
+                               if (parser->toksuper != -1 && tokens != NULL)
+                                       tokens[parser->toksuper].size++;
+                               break;
+
+#ifdef JSMN_STRICT
+                       /* Unexpected char in strict mode */
+                       default:
+                               return JSMN_ERROR_INVAL;
+#endif
+               }
+       }
+
+       if (tokens != NULL) {
+               for (i = parser->toknext - 1; i >= 0; i--) {
+                       /* Unmatched opened object or array */
+                       if (tokens[i].start != -1 && tokens[i].end == -1) {
+                               return JSMN_ERROR_PART;
+                       }
+               }
+       }
+
+       return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+       parser->pos = 0;
+       parser->toknext = 0;
+       parser->toksuper = -1;
+}
+
diff --git a/src/jsmn.h b/src/jsmn.h
new file mode 100644 (file)
index 0000000..01ca99c
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * JSON type identifier. Basic types are:
+ *     o Object
+ *     o Array
+ *     o String
+ *     o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+       JSMN_UNDEFINED = 0,
+       JSMN_OBJECT = 1,
+       JSMN_ARRAY = 2,
+       JSMN_STRING = 3,
+       JSMN_PRIMITIVE = 4
+} jsmntype_t;
+
+enum jsmnerr {
+       /* Not enough tokens were provided */
+       JSMN_ERROR_NOMEM = -1,
+       /* Invalid character inside JSON string */
+       JSMN_ERROR_INVAL = -2,
+       /* The string is not a full JSON packet, more bytes expected */
+       JSMN_ERROR_PART = -3
+};
+
+/**
+ * JSON token description.
+ * @param              type    type (object, array, string etc.)
+ * @param              start   start position in JSON data string
+ * @param              end             end position in JSON data string
+ */
+typedef struct {
+       jsmntype_t type;
+       int start;
+       int end;
+       int size;
+#ifdef JSMN_PARENT_LINKS
+       int parent;
+#endif
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+       unsigned int pos; /* offset in the JSON string */
+       unsigned int toknext; /* next token to allocate */
+       int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+ * a single JSON object.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+               jsmntok_t *tokens, unsigned int num_tokens);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JSMN_H_ */
diff --git a/src/messages.c b/src/messages.c
new file mode 100644 (file)
index 0000000..fccad2e
--- /dev/null
@@ -0,0 +1,1652 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "monitor.h"
+#include "pointer.h"
+#include "query.h"
+#include "rule.h"
+#include "restore.h"
+#include "settings.h"
+#include "tree.h"
+#include "window.h"
+#include "common.h"
+#include "parse.h"
+#include "messages.h"
+
+void handle_message(char *msg, int msg_len, FILE *rsp)
+{
+       int cap = INIT_CAP;
+       int num = 0;
+       char **args = calloc(cap, sizeof(char *));
+
+       if (args == NULL) {
+               perror("Handle message: calloc");
+               return;
+       }
+
+       for (int i = 0, j = 0; i < msg_len; i++) {
+               if (msg[i] == 0) {
+                       args[num++] = msg + j;
+                       j = i + 1;
+               }
+               if (num >= cap) {
+                       cap *= 2;
+                       char **new = realloc(args, cap * sizeof(char *));
+                       if (new == NULL) {
+                               free(args);
+                               perror("Handle message: realloc");
+                               return;
+                       } else {
+                               args = new;
+                       }
+               }
+       }
+
+       if (num < 1) {
+               free(args);
+               fail(rsp, "No arguments given.\n");
+               return;
+       }
+
+       char **args_orig = args;
+       process_message(args, num, rsp);
+       free(args_orig);
+}
+
+void process_message(char **args, int num, FILE *rsp)
+{
+       int ret = SUBSCRIBE_FAILURE;
+
+       if (streq("node", *args)) {
+               cmd_node(++args, --num, rsp);
+       } else if (streq("desktop", *args)) {
+               cmd_desktop(++args, --num, rsp);
+       } else if (streq("monitor", *args)) {
+               cmd_monitor(++args, --num, rsp);
+       } else if (streq("query", *args)) {
+               cmd_query(++args, --num, rsp);
+       } else if (streq("subscribe", *args)) {
+               ret = cmd_subscribe(++args, --num, rsp);
+       } else if (streq("wm", *args)) {
+               cmd_wm(++args, --num, rsp);
+       } else if (streq("rule", *args)) {
+               cmd_rule(++args, --num, rsp);
+       } else if (streq("config", *args)) {
+               cmd_config(++args, --num, rsp);
+       } else if (streq("quit", *args)) {
+               cmd_quit(++args, --num, rsp);
+       } else {
+               fail(rsp, "Unknown domain or command: '%s'.\n", *args);
+       }
+
+       fflush(rsp);
+
+       if (ret != SUBSCRIBE_SUCCESS) {
+               fclose(rsp);
+       }
+}
+
+void cmd_node(char **args, int num, FILE *rsp)
+{
+       if (num < 1) {
+               fail(rsp, "node: Missing commands.\n");
+               return;
+       }
+
+       coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+       coordinates_t trg = ref;
+
+       if ((*args)[0] != OPT_CHR) {
+               int ret;
+               if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
+                       num--, args++;
+               } else {
+                       handle_failure(ret, "node", *args, rsp);
+                       return;
+               }
+       }
+
+       bool changed = false;
+
+       while (num > 0) {
+               if (streq("-f", *args) || streq("--focus", *args)) {
+                       coordinates_t dst = trg;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+                                       handle_failure(ret, "node -f", *args, rsp);
+                                       break;
+                               }
+                       }
+                       if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else if (streq("-a", *args) || streq("--activate", *args)) {
+                       coordinates_t dst = trg;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+                                       handle_failure(ret, "node -a", *args, rsp);
+                                       break;
+                               }
+                       }
+                       if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
+                                       trg.monitor = dst.monitor;
+                                       trg.desktop = dst.desktop;
+                               } else {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               handle_failure(ret, "node -d", *args, rsp);
+                               break;
+                       }
+               } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
+                                       trg.monitor = dst.monitor;
+                                       trg.desktop = dst.monitor->desk;
+                               } else {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               handle_failure(ret, "node -m", *args, rsp);
+                               break;
+                       }
+               } else if (streq("-n", *args) || streq("--to-node", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
+                                       trg.monitor = dst.monitor;
+                                       trg.desktop = dst.desktop;
+                               } else {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               handle_failure(ret, "node -n", *args, rsp);
+                               break;
+                       }
+               } else if (streq("-s", *args) || streq("--swap", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
+                                       trg.monitor = dst.monitor;
+                                       trg.desktop = dst.desktop;
+                               } else {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               handle_failure(ret, "node -s", *args, rsp);
+                               break;
+                       }
+               } else if (streq("-l", *args) || streq("--layer", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       stack_layer_t lyr;
+                       if (parse_stack_layer(*args, &lyr)) {
+                               if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-t", *args) || streq("--state", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       client_state_t cst;
+                       bool alternate = false;
+                       if ((*args)[0] == '~') {
+                               alternate = true;
+                               (*args)++;
+                       }
+                       if (parse_client_state(*args, &cst)) {
+                               if (alternate && trg.node != NULL && trg.node->client != NULL &&
+                                   trg.node->client->state == cst) {
+                                       cst = trg.node->client->last_state;
+                               }
+                               if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                               changed = true;
+                       } else {
+                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-g", *args) || streq("--flag", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       char *key = strtok(*args, EQL_TOK);
+                       char *val = strtok(NULL, EQL_TOK);
+                       alter_state_t a;
+                       bool b;
+                       if (val == NULL) {
+                               a = ALTER_TOGGLE;
+                       } else {
+                               if (parse_bool(val, &b)) {
+                                       a = ALTER_SET;
+                               } else {
+                                       fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
+                                       break;
+                               }
+                       }
+                       if (streq("hidden", key)) {
+                               set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
+                               changed = true;
+                       } else if (streq("sticky", key)) {
+                               set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
+                       } else if (streq("private", key)) {
+                               set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
+                       } else if (streq("locked", key)) {
+                               set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
+                       } else {
+                               fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
+                               break;
+                       }
+               } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL || trg.node->vacant) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       if (streq("cancel", *args)) {
+                               cancel_presel(trg.monitor, trg.desktop, trg.node);
+                       } else {
+                               bool alternate = false;
+                               if ((*args)[0] == '~') {
+                                       alternate = true;
+                                       (*args)++;
+                               }
+                               direction_t dir;
+                               if (parse_direction(*args, &dir)) {
+                                       if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
+                                               cancel_presel(trg.monitor, trg.desktop, trg.node);
+                                       } else {
+                                               presel_dir(trg.monitor, trg.desktop, trg.node, dir);
+                                               if (!IS_RECEPTACLE(trg.node)) {
+                                                       draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
+                                               }
+                                       }
+                               } else {
+                                       fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
+                                       break;
+                               }
+                       }
+               } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL || trg.node->vacant) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       double rat;
+                       if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
+                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       } else {
+                               presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
+                               draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
+                       }
+               } else if (streq("-v", *args) || streq("--move", *args)) {
+                       num--, args++;
+                       if (num < 2) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       int dx = 0, dy = 0;
+                       if (sscanf(*args, "%i", &dx) == 1) {
+                               num--, args++;
+                               if (sscanf(*args, "%i", &dy) == 1) {
+                                       if (!move_client(&trg, dx, dy)) {
+                                               fail(rsp, "");
+                                               break;
+                                       }
+                               } else {
+                                       fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
+                                       break;
+                               }
+                       } else {
+                               fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
+                               break;
+                       }
+               } else if (streq("-z", *args) || streq("--resize", *args)) {
+                       num--, args++;
+                       if (num < 3) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       resize_handle_t rh;
+                       if (parse_resize_handle(*args, &rh)) {
+                               num--, args++;
+                               int dx = 0, dy = 0;
+                               if (sscanf(*args, "%i", &dx) == 1) {
+                                       num--, args++;
+                                       if (sscanf(*args, "%i", &dy) == 1) {
+                                               if (!resize_client(&trg, rh, dx, dy)) {
+                                                       fail(rsp, "");
+                                                       break;
+                                               }
+                                       } else {
+                                               fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
+                                               break;
+                                       }
+                               } else {
+                                       fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
+                                       break;
+                               }
+                       } else {
+                               fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-r", *args) || streq("--ratio", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       if ((*args)[0] == '+' || (*args)[0] == '-') {
+                               int pix;
+                               if (sscanf(*args, "%i", &pix) == 1) {
+                                       int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
+                                       double rat = ((max * trg.node->split_ratio) + pix) / max;
+                                       if (rat > 0 && rat < 1) {
+                                               set_ratio(trg.node, rat);
+                                       } else {
+                                               fail(rsp, "");
+                                               break;
+                                       }
+                               } else {
+                                       fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                                       break;
+                               }
+                       } else {
+                               double rat;
+                               if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+                                       set_ratio(trg.node, rat);
+                               } else {
+                                       fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                                       break;
+                               }
+                       }
+                       changed = true;
+               } else if (streq("-F", *args) || streq("--flip", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       flip_t flp;
+                       if (parse_flip(*args, &flp)) {
+                               flip_tree(trg.node, flp);
+                               changed = true;
+                       } else {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else if (streq("-R", *args) || streq("--rotate", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       int deg;
+                       if (parse_degree(*args, &deg)) {
+                               rotate_tree(trg.node, deg);
+                               changed = true;
+                       } else {
+                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-E", *args) || streq("--equalize", *args)) {
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       equalize_tree(trg.node);
+                       changed = true;
+               } else if (streq("-B", *args) || streq("--balance", *args)) {
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       balance_tree(trg.node);
+                       changed = true;
+               } else if (streq("-C", *args) || streq("--circulate", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       circulate_dir_t cir;
+                       if (parse_circulate_direction(*args, &cir)) {
+                               circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
+                               changed = true;
+                       } else {
+                               fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
+                       insert_receptacle(trg.monitor, trg.desktop, trg.node);
+                       changed = true;
+               } else if (streq("-c", *args) || streq("--close", *args)) {
+                       if (num > 1) {
+                               fail(rsp, "node %s: Trailing commands.\n", *args);
+                               break;
+                       }
+                       if (trg.node == NULL || locked_count(trg.node) > 0) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       close_node(trg.node);
+                       break;
+               } else if (streq("-k", *args) || streq("--kill", *args)) {
+                       if (num > 1) {
+                               fail(rsp, "node %s: Trailing commands.\n", *args);
+                               break;
+                       }
+                       if (trg.node == NULL) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       kill_node(trg.monitor, trg.desktop, trg.node);
+                       changed = true;
+                       break;
+               } else {
+                       fail(rsp, "node: Unknown command: '%s'.\n", *args);
+                       break;
+               }
+
+               num--, args++;
+       }
+
+       if (changed) {
+               arrange(trg.monitor, trg.desktop);
+       }
+}
+
+void cmd_desktop(char **args, int num, FILE *rsp)
+{
+       if (num < 1) {
+               fail(rsp, "desktop: Missing commands.\n");
+               return;
+       }
+
+       coordinates_t ref = {mon, mon->desk, NULL};
+       coordinates_t trg = ref;
+
+       if ((*args)[0] != OPT_CHR) {
+               int ret;
+               if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
+                       num--, args++;
+               } else {
+                       handle_failure(ret, "desktop", *args, rsp);
+                       return;
+               }
+       }
+
+       bool changed = false;
+
+       while (num > 0) {
+               if (streq("-f", *args) || streq("--focus", *args)) {
+                       coordinates_t dst = trg;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+                                       handle_failure(ret, "desktop -f", *args, rsp);
+                                       break;
+                               }
+                       }
+                       focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
+               } else if (streq("-a", *args) || streq("--activate", *args)) {
+                       coordinates_t dst = trg;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+                                       handle_failure(ret, "desktop -a", *args, rsp);
+                                       break;
+                               }
+                       }
+                       if (!activate_desktop(dst.monitor, dst.desktop)) {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (trg.monitor->desk_head == trg.monitor->desk_tail) {
+                               fail(rsp, "");
+                               break;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
+                                       trg.monitor = dst.monitor;
+                               } else {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               handle_failure(ret, "desktop -m", *args, rsp);
+                               break;
+                       }
+               } else if (streq("-s", *args) || streq("--swap", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
+                                       trg.monitor = dst.monitor;
+                               } else {
+                                       fail(rsp, "");
+                                       break;
+                               }
+                       } else {
+                               handle_failure(ret, "desktop -s", *args, rsp);
+                               break;
+                       }
+               } else if (streq("-b", *args) || streq("--bubble", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       cycle_dir_t cyc;
+                       if (parse_cycle_direction(*args, &cyc)) {
+                               desktop_t *d = trg.desktop;
+                               if (cyc == CYCLE_PREV) {
+                                       if (d->prev == NULL) {
+                                               while (d->next != NULL) {
+                                                       swap_desktops(trg.monitor, d, trg.monitor, d->next);
+                                               }
+                                       } else {
+                                               swap_desktops(trg.monitor, d, trg.monitor, d->prev);
+                                       }
+                               } else {
+                                       if (d->next == NULL) {
+                                               while (d->prev != NULL) {
+                                                       swap_desktops(trg.monitor, d, trg.monitor, d->prev);
+                                               }
+                                       } else {
+                                               swap_desktops(trg.monitor, d, trg.monitor, d->next);
+                                       }
+                               }
+                       } else {
+                               fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-l", *args) || streq("--layout", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       bool ret;
+                       layout_t lyt;
+                       cycle_dir_t cyc;
+                       if (parse_cycle_direction(*args, &cyc)) {
+                               ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
+                       } else if (parse_layout(*args, &lyt)) {
+                               ret = set_layout(trg.monitor, trg.desktop, lyt);
+                       } else {
+                               fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+                       if (!ret) {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else if (streq("-n", *args) || streq("--rename", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       rename_desktop(trg.monitor, trg.desktop, *args);
+               } else if (streq("-r", *args) || streq("--remove", *args)) {
+                       if (num > 1) {
+                               fail(rsp, "desktop %s: Trailing commands.\n", *args);
+                               break;
+                       }
+                       if (trg.desktop->root == NULL &&
+                           trg.monitor->desk_head != trg.monitor->desk_tail) {
+                               remove_desktop(trg.monitor, trg.desktop);
+                               return;
+                       } else {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else {
+                       fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
+                       break;
+               }
+               num--, args++;
+       }
+
+       if (changed) {
+               arrange(trg.monitor, trg.desktop);
+       }
+}
+
+void cmd_monitor(char **args, int num, FILE *rsp)
+{
+       if (num < 1) {
+               fail(rsp, "monitor: Missing commands.\n");
+               return;
+       }
+
+       coordinates_t ref = {mon, NULL, NULL};
+       coordinates_t trg = ref;
+
+       if ((*args)[0] != OPT_CHR) {
+               int ret;
+               if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
+                       num--, args++;
+               } else {
+                       handle_failure(ret, "monitor", *args, rsp);
+                       return;
+               }
+       }
+
+       while (num > 0) {
+               if (streq("-f", *args) || streq("--focus", *args)) {
+                       coordinates_t dst = trg;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+                                       handle_failure(ret, "monitor -f", *args, rsp);
+                                       fail(rsp, "");
+                                       return;
+                               }
+                       }
+                       focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
+               } else if (streq("-s", *args) || streq("--swap", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       coordinates_t dst;
+                       int ret;
+                       if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+                               if (!swap_monitors(trg.monitor, dst.monitor)) {
+                                       fail(rsp, "");
+                                       return;
+                               }
+                       } else {
+                               handle_failure(ret, "monitor -s", *args, rsp);
+                               return;
+                       }
+               } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       desktop_t *d = trg.monitor->desk_head;
+                       while (num > 0 && d != NULL) {
+                               rename_desktop(trg.monitor, d, *args);
+                               d = d->next;
+                               num--, args++;
+                       }
+                       put_status(SBSC_MASK_REPORT);
+                       while (num > 0) {
+                               add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
+                               num--, args++;
+                       }
+                       while (d != NULL) {
+                               desktop_t *next = d->next;
+                               if (d == mon->desk) {
+                                       focus_node(trg.monitor, d->prev, d->prev->focus);
+                               }
+                               merge_desktops(trg.monitor, d, mon, mon->desk);
+                               remove_desktop(trg.monitor, d);
+                               d = next;
+                       }
+               } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       while (num > 0) {
+                               add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
+                               num--, args++;
+                       }
+               } else if (streq("-r", *args) || streq("--remove", *args)) {
+                       if (num > 1) {
+                               fail(rsp, "monitor %s: Trailing commands.\n", *args);
+                               return;
+                       }
+                       if (mon_head == mon_tail) {
+                               fail(rsp, "");
+                               return;
+                       }
+                       remove_monitor(trg.monitor);
+                       return;
+               } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       desktop_t *d = trg.monitor->desk_head;
+                       while (d != NULL && num > 0) {
+                               desktop_t *next = d->next;
+                               coordinates_t dst;
+                               if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
+                                       swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
+                                       if (next == dst.desktop) {
+                                               next = d;
+                                       }
+                               }
+                               d = next;
+                               num--, args++;
+                       }
+               } else if (streq("-g", *args) || streq("--rectangle", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       xcb_rectangle_t r;
+                       if (parse_rectangle(*args, &r)) {
+                               update_root(trg.monitor, &r);
+                       } else {
+                               fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               return;
+                       }
+               } else if (streq("-n", *args) || streq("--rename", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       rename_monitor(trg.monitor, *args);
+               } else {
+                       fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
+                       return;
+               }
+               num--, args++;
+       }
+}
+
+void cmd_query(char **args, int num, FILE *rsp)
+{
+       coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+       coordinates_t trg = {NULL, NULL, NULL};
+       monitor_select_t *monitor_sel = NULL;
+       desktop_select_t *desktop_sel = NULL;
+       node_select_t *node_sel = NULL;
+       domain_t dom = DOMAIN_TREE;
+       bool print_ids = true;
+       uint8_t d = 0;
+
+       if (num < 1) {
+               fail(rsp, "query: Not enough arguments.\n");
+               return;
+       }
+
+       while (num > 0) {
+               if (streq("-T", *args) || streq("--tree", *args)) {
+                       dom = DOMAIN_TREE, d++;
+               } else if (streq("-M", *args) || streq("--monitors", *args)) {
+                       dom = DOMAIN_MONITOR, d++;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               coordinates_t tmp = ref;
+                               if ((ret = monitor_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
+                                       handle_failure(ret, "query -M", *args, rsp);
+                                       goto end;
+                               }
+                       }
+               } else if (streq("-D", *args) || streq("--desktops", *args)) {
+                       dom = DOMAIN_DESKTOP, d++;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               coordinates_t tmp = ref;
+                               if ((ret = desktop_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
+                                       handle_failure(ret, "query -D", *args, rsp);
+                                       goto end;
+                               }
+                       }
+               } else if (streq("-N", *args) || streq("--nodes", *args)) {
+                       dom = DOMAIN_NODE, d++;
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               coordinates_t tmp = ref;
+                               if ((ret = node_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
+                                       handle_failure(ret, "query -N", *args, rsp);
+                                       goto end;
+                               }
+                       }
+               } else if (streq("-m", *args) || streq("--monitor", *args)) {
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((*args)[0] == '.') {
+                                       free(monitor_sel);
+                                       monitor_sel = malloc(sizeof(monitor_select_t));
+                                       *monitor_sel = make_monitor_select();
+                                       char *desc = copy_string(*args, strlen(*args));
+                                       if (!parse_monitor_modifiers(desc, monitor_sel)) {
+                                               handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
+                                               free(desc);
+                                               goto end;
+                                       }
+                                       free(desc);
+                               } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+                                       handle_failure(ret, "query -m", *args, rsp);
+                                       goto end;
+                               }
+                       } else {
+                               trg.monitor = ref.monitor;
+                       }
+               } else if (streq("-d", *args) || streq("--desktop", *args)) {
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((*args)[0] == '.') {
+                                       free(desktop_sel);
+                                       desktop_sel = malloc(sizeof(desktop_select_t));
+                                       *desktop_sel = make_desktop_select();
+                                       char *desc = copy_string(*args, strlen(*args));
+                                       if (!parse_desktop_modifiers(desc, desktop_sel)) {
+                                               handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
+                                               free(desc);
+                                               goto end;
+                                       }
+                                       free(desc);
+                               } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+                                       handle_failure(ret, "query -d", *args, rsp);
+                                       goto end;
+                               }
+                       } else {
+                               trg.monitor = ref.monitor;
+                               trg.desktop = ref.desktop;
+                       }
+               } else if (streq("-n", *args) || streq("--node", *args)) {
+                       if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+                               num--, args++;
+                               int ret;
+                               if ((*args)[0] == '.') {
+                                       free(node_sel);
+                                       node_sel = malloc(sizeof(node_select_t));
+                                       *node_sel = make_node_select();
+                                       char *desc = copy_string(*args, strlen(*args));
+                                       if (!parse_node_modifiers(desc, node_sel)) {
+                                               handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
+                                               free(desc);
+                                               goto end;
+                                       }
+                                       free(desc);
+                               } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+                                       handle_failure(ret, "query -n", *args, rsp);
+                                       goto end;
+                               }
+                       } else {
+                               trg = ref;
+                               if (ref.node == NULL) {
+                                       fail(rsp, "");
+                                       goto end;
+                               }
+                       }
+               } else if (streq("--names", *args)) {
+                       print_ids = false;
+               } else {
+                       fail(rsp, "query: Unknown option: '%s'.\n", *args);
+                       goto end;
+               }
+               num--, args++;
+       }
+
+       if (d < 1) {
+               fail(rsp, "query: No commands given.\n");
+               goto end;
+       }
+
+       if (d > 1) {
+               fail(rsp, "query: Multiple commands given.\n");
+               goto end;
+       }
+
+       if (dom == DOMAIN_TREE && trg.monitor == NULL) {
+               fail(rsp, "query -T: No options given.\n");
+               goto end;
+       }
+
+       if (dom == DOMAIN_NODE) {
+               if (query_node_ids(&ref, &trg, node_sel, rsp) < 1) {
+                       fail(rsp, "");
+               }
+       } else if (dom == DOMAIN_DESKTOP) {
+               if (query_desktop_ids(&ref, &trg, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {
+                       fail(rsp, "");
+               }
+       } else if (dom == DOMAIN_MONITOR) {
+               if (query_monitor_ids(&ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {
+                       fail(rsp, "");
+               }
+       } else {
+               if (trg.node != NULL) {
+                       query_node(trg.node, rsp);
+               } else if (trg.desktop != NULL) {
+                       query_desktop(trg.desktop, rsp);
+               } else  {
+                       query_monitor(trg.monitor, rsp);
+               }
+               fprintf(rsp, "\n");
+       }
+
+end:
+       free(monitor_sel);
+       free(desktop_sel);
+       free(node_sel);
+}
+
+void cmd_rule(char **args, int num, FILE *rsp)
+{
+       if (num < 1) {
+               fail(rsp, "rule: Missing commands.\n");
+               return;
+       }
+
+       while (num > 0) {
+               if (streq("-a", *args) || streq("--add", *args)) {
+                       num--, args++;
+                       if (num < 2) {
+                               fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       rule_t *rule = make_rule();
+                       char *class_name = strtok(*args, COL_TOK);
+                       char *instance_name = strtok(NULL, COL_TOK);
+                       snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
+                       snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
+                       num--, args++;
+                       size_t i = 0;
+                       while (num > 0) {
+                               if (streq("-o", *args) || streq("--one-shot", *args)) {
+                                       rule->one_shot = true;
+                               } else {
+                                       for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
+                                               rule->effect[i] = (*args)[j];
+                                       }
+                                       if (num > 1 && i < sizeof(rule->effect)) {
+                                               rule->effect[i++] = ' ';
+                                       }
+                               }
+                               num--, args++;
+                       }
+                       rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
+                       add_rule(rule);
+               } else if (streq("-r", *args) || streq("--remove", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       uint16_t idx;
+                       while (num > 0) {
+                               if (parse_index(*args, &idx)) {
+                                       remove_rule_by_index(idx - 1);
+                               } else if (streq("tail", *args)) {
+                                       remove_rule(rule_tail);
+                               } else if (streq("head", *args)) {
+                                       remove_rule(rule_head);
+                               } else {
+                                       remove_rule_by_cause(*args);
+                               }
+                               num--, args++;
+                       }
+               } else if (streq("-l", *args) || streq("--list", *args)) {
+                       list_rules(rsp);
+               } else {
+                       fail(rsp, "rule: Unknown command: '%s'.\n", *args);
+                       return;
+               }
+               num--, args++;
+       }
+}
+
+void cmd_wm(char **args, int num, FILE *rsp)
+{
+       if (num < 1) {
+               fail(rsp, "wm: Missing commands.\n");
+               return;
+       }
+
+       while (num > 0) {
+               if (streq("-d", *args) || streq("--dump-state", *args)) {
+                       query_tree(rsp);
+                       fprintf(rsp, "\n");
+               } else if (streq("-l", *args) || streq("--load-state", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       if (!restore_tree(*args)) {
+                               fail(rsp, "");
+                               break;
+                       }
+               } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
+                       num--, args++;
+                       if (num < 2) {
+                               fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       char *name = *args;
+                       num--, args++;
+                       xcb_rectangle_t r;
+                       if (parse_rectangle(*args, &r)) {
+                               monitor_t *m = make_monitor(name, &r, XCB_NONE);
+                               add_monitor(m);
+                               add_desktop(m, make_desktop(NULL, XCB_NONE));
+                       } else {
+                               fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
+                       adopt_orphans();
+               } else if (streq("-g", *args) || streq("--get-status", *args)) {
+                       print_report(rsp);
+               } else if (streq("-h", *args) || streq("--record-history", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
+                               break;
+                       }
+                       bool b;
+                       if (parse_bool(*args, &b)) {
+                               record_history = b;
+                       } else {
+                               fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+                               break;
+                       }
+               } else {
+                       fail(rsp, "wm: Unkown command: '%s'.\n", *args);
+                       break;
+               }
+               num--, args++;
+       }
+}
+
+int cmd_subscribe(char **args, int num, FILE *rsp)
+{
+       int field = 0;
+       if (num < 1) {
+               field = SBSC_MASK_REPORT;
+       } else {
+               subscriber_mask_t mask;
+               while (num > 0) {
+                       if (parse_subscriber_mask(*args, &mask)) {
+                               field |= mask;
+                       } else {
+                               fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
+                               return SUBSCRIBE_FAILURE;
+                       }
+                       num--, args++;
+               }
+       }
+
+       add_subscriber(rsp, field);
+       return SUBSCRIBE_SUCCESS;
+}
+
+void cmd_quit(char **args, int num, FILE *rsp)
+{
+       if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
+               fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
+               return;
+       }
+       running = false;
+}
+
+void cmd_config(char **args, int num, FILE *rsp)
+{
+       if (num < 1) {
+               fail(rsp, "config: Missing arguments.\n");
+               return;
+       }
+
+       coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+       coordinates_t trg = {NULL, NULL, NULL};
+
+       while (num > 0 && (*args)[0] == OPT_CHR) {
+               if (streq("-m", *args) || streq("--monitor", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       int ret;
+                       if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+                               handle_failure(ret, "config -m", *args, rsp);
+                               return;
+                       }
+               } else if (streq("-d", *args) || streq("--desktop", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       int ret;
+                       if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+                               handle_failure(ret, "config -d", *args, rsp);
+                               return;
+                       }
+               } else if (streq("-n", *args) || streq("--node", *args)) {
+                       num--, args++;
+                       if (num < 1) {
+                               fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
+                               return;
+                       }
+                       int ret;
+                       if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+                               handle_failure(ret, "config -n", *args, rsp);
+                               return;
+                       }
+               } else {
+                       fail(rsp, "config: Unknown option: '%s'.\n", *args);
+                       return;
+               }
+               num--, args++;
+       }
+       if (num == 2) {
+               set_setting(trg, *args, *(args + 1), rsp);
+       } else if (num == 1) {
+               get_setting(trg, *args, rsp);
+       } else {
+               fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
+       }
+}
+
+void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
+{
+       bool colors_changed = false;
+#define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
+               if (loc.node != NULL) { \
+                       for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \
+                               if (n->client != NULL) { \
+                                       n->client->k = v; \
+                               } \
+                       } \
+               } else if (loc.desktop != NULL) { \
+                       loc.desktop->k = v; \
+                       for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
+                               if (n->client != NULL) { \
+                                       n->client->k = v; \
+                               } \
+                       } \
+               } else if (loc.monitor != NULL) { \
+                       loc.monitor->k = v; \
+                       for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
+                               d->k = v; \
+                               for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
+                                       if (n->client != NULL) { \
+                                               n->client->k = v; \
+                                       } \
+                               } \
+                       } \
+               } else { \
+                       k = v; \
+                       for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
+                               m->k = v; \
+                               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
+                                       d->k = v; \
+                                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
+                                               if (n->client != NULL) { \
+                                                       n->client->k = v; \
+                                               } \
+                                       } \
+                               } \
+                       } \
+               }
+       if (streq("border_width", name)) {
+               unsigned int bw;
+               if (sscanf(value, "%u", &bw) != 1) {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+               SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
+#undef SET_DEF_DEFMON_DEFDESK_WIN
+#define SET_DEF_DEFMON_DESK(k, v) \
+               if (loc.desktop != NULL) { \
+                       loc.desktop->k = v; \
+               } else if (loc.monitor != NULL) { \
+                       loc.monitor->k = v; \
+                       for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
+                               d->k = v; \
+                       } \
+               } else { \
+                       k = v; \
+                       for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
+                               m->k = v; \
+                               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
+                                       d->k = v; \
+                               } \
+                       } \
+               }
+       } else if (streq("window_gap", name)) {
+               int wg;
+               if (sscanf(value, "%i", &wg) != 1) {
+                       fail(rsp, "");
+                       return;
+               }
+               SET_DEF_DEFMON_DESK(window_gap, wg)
+#undef SET_DEF_DEFMON_DESK
+#define SET_DEF_MON_DESK(k, v) \
+               if (loc.desktop != NULL) { \
+                       loc.desktop->k = v; \
+               } else if (loc.monitor != NULL) { \
+                       loc.monitor->k = v; \
+               } else { \
+                       k = v; \
+                       for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
+                               m->k = v; \
+                       } \
+               }
+       } else if (streq("top_padding", name)) {
+               int tp;
+               if (sscanf(value, "%i", &tp) != 1) {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+               SET_DEF_MON_DESK(padding.top, tp)
+       } else if (streq("right_padding", name)) {
+               int rp;
+               if (sscanf(value, "%i", &rp) != 1) {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+               SET_DEF_MON_DESK(padding.right, rp)
+       } else if (streq("bottom_padding", name)) {
+               int bp;
+               if (sscanf(value, "%i", &bp) != 1) {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+               SET_DEF_MON_DESK(padding.bottom, bp)
+       } else if (streq("left_padding", name)) {
+               int lp;
+               if (sscanf(value, "%i", &lp) != 1) {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+               SET_DEF_MON_DESK(padding.left, lp)
+#undef SET_DEF_MON_DESK
+#define SET_STR(s) \
+       } else if (streq(#s, name)) { \
+               if (snprintf(s, sizeof(s), "%s", value) < 0) { \
+                       fail(rsp, ""); \
+                       return; \
+               }
+       SET_STR(external_rules_command)
+       SET_STR(status_prefix)
+#undef SET_STR
+       } else if (streq("split_ratio", name)) {
+               double r;
+               if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
+                       split_ratio = r;
+               } else {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+                       return;
+               }
+               return;
+#define SET_COLOR(s) \
+       } else if (streq(#s, name)) { \
+               if (!is_hex_color(value)) { \
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+                       return; \
+               } else { \
+                       snprintf(s, sizeof(s), "%s", value); \
+                       colors_changed = true; \
+               }
+       SET_COLOR(normal_border_color)
+       SET_COLOR(active_border_color)
+       SET_COLOR(focused_border_color)
+       SET_COLOR(presel_feedback_color)
+#undef SET_COLOR
+       } else if (streq("initial_polarity", name)) {
+               child_polarity_t p;
+               if (parse_child_polarity(value, &p)) {
+                       initial_polarity = p;
+               } else {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+       } else if (streq("pointer_modifier", name)) {
+               if (parse_modifier_mask(value, &pointer_modifier)) {
+                       ungrab_buttons();
+                       grab_buttons();
+               } else {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+       } else if (streq("pointer_motion_interval", name)) {
+               if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+       } else if (streq("pointer_action1", name) ||
+                  streq("pointer_action2", name) ||
+                  streq("pointer_action3", name)) {
+               int index = name[14] - '1';
+               if (parse_pointer_action(value, &pointer_actions[index])) {
+                       ungrab_buttons();
+                       grab_buttons();
+               } else {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+       } else if (streq("click_to_focus", name)) {
+               if (parse_bool(value, &click_to_focus)) {
+                       ungrab_buttons();
+                       grab_buttons();
+               } else {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+       } else if (streq("focus_follows_pointer", name)) {
+               bool b;
+               if (parse_bool(value, &b)) {
+                       if (b == focus_follows_pointer) {
+                               fail(rsp, "");
+                               return;
+                       }
+                       focus_follows_pointer = b;
+                       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+                               if (b) {
+                                       window_show(m->root);
+                               } else {
+                                       window_hide(m->root);
+                               }
+                               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                                       listen_enter_notify(d->root, b);
+                               }
+                       }
+                       return;
+               } else {
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+                       return;
+               }
+#define SET_BOOL(s) \
+       } else if (streq(#s, name)) { \
+               if (!parse_bool(value, &s)) { \
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+                       return; \
+               }
+               SET_BOOL(borderless_monocle)
+               SET_BOOL(gapless_monocle)
+               SET_BOOL(paddingless_monocle)
+               SET_BOOL(single_monocle)
+               SET_BOOL(swallow_first_click)
+               SET_BOOL(pointer_follows_focus)
+               SET_BOOL(pointer_follows_monitor)
+               SET_BOOL(ignore_ewmh_focus)
+               SET_BOOL(center_pseudo_tiled)
+               SET_BOOL(honor_size_hints)
+#undef SET_BOOL
+#define SET_MON_BOOL(s) \
+       } else if (streq(#s, name)) { \
+               if (!parse_bool(value, &s)) { \
+                       fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+                       return; \
+               } \
+               if (s) { \
+                       update_monitors(); \
+               }
+               SET_MON_BOOL(remove_disabled_monitors)
+               SET_MON_BOOL(remove_unplugged_monitors)
+               SET_MON_BOOL(merge_overlapping_monitors)
+#undef SET_MON_BOOL
+       } else {
+               fail(rsp, "config: Unknown setting: '%s'.\n", name);
+               return;
+       }
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       arrange(m, d);
+                       if (colors_changed) {
+                               update_colors_in(d->root, d, m);
+                       }
+               }
+       }
+}
+
+void get_setting(coordinates_t loc, char *name, FILE* rsp)
+{
+       if (streq("split_ratio", name)) {
+               fprintf(rsp, "%lf", split_ratio);
+       } else if (streq("border_width", name)) {
+               if (loc.node != NULL) {
+                       for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
+                               if (n->client != NULL) {
+                                       fprintf(rsp, "%u", n->client->border_width);
+                                       break;
+                               }
+                       }
+               } else if (loc.desktop != NULL) {
+                       fprintf(rsp, "%u", loc.desktop->border_width);
+               } else if (loc.monitor != NULL) {
+                       fprintf(rsp, "%u", loc.monitor->border_width);
+               } else {
+                       fprintf(rsp, "%u", border_width);
+               }
+       } else if (streq("window_gap", name)) {
+               if (loc.desktop != NULL) {
+                       fprintf(rsp, "%i", loc.desktop->window_gap);
+               } else if (loc.monitor != NULL) {
+                       fprintf(rsp, "%i", loc.monitor->window_gap);
+               } else {
+                       fprintf(rsp, "%i", window_gap);
+               }
+#define GET_DEF_MON_DESK(k) \
+               if (loc.desktop != NULL) { \
+                       fprintf(rsp, "%i", loc.desktop->k); \
+               } else if (loc.monitor != NULL) { \
+                       fprintf(rsp, "%i", loc.monitor->k); \
+               } else { \
+                       fprintf(rsp, "%i", k); \
+               }
+       } else if (streq("top_padding", name)) {
+               GET_DEF_MON_DESK(padding.top)
+       } else if (streq("right_padding", name)) {
+               GET_DEF_MON_DESK(padding.right)
+       } else if (streq("bottom_padding", name)) {
+               GET_DEF_MON_DESK(padding.bottom)
+       } else if (streq("left_padding", name)) {
+               GET_DEF_MON_DESK(padding.left)
+#undef GET_DEF_MON_DESK
+       } else if (streq("external_rules_command", name)) {
+               fprintf(rsp, "%s", external_rules_command);
+       } else if (streq("status_prefix", name)) {
+               fprintf(rsp, "%s", status_prefix);
+       } else if (streq("initial_polarity", name)) {
+               fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
+       } else if (streq("pointer_modifier", name)) {
+               print_modifier_mask(pointer_modifier, rsp);
+       } else if (streq("pointer_motion_interval", name)) {
+               fprintf(rsp, "%u", pointer_motion_interval);
+       } else if (streq("pointer_action1", name) ||
+                  streq("pointer_action2", name) ||
+                  streq("pointer_action3", name)) {
+               int index = name[14] - '1';
+               print_pointer_action(pointer_actions[index], rsp);
+#define GET_COLOR(s) \
+       } else if (streq(#s, name)) { \
+               fprintf(rsp, "%s", s);
+       GET_COLOR(normal_border_color)
+       GET_COLOR(active_border_color)
+       GET_COLOR(focused_border_color)
+       GET_COLOR(presel_feedback_color)
+#undef GET_COLOR
+#define GET_BOOL(s) \
+       } else if (streq(#s, name)) { \
+               fprintf(rsp, "%s", BOOL_STR(s));
+       GET_BOOL(borderless_monocle)
+       GET_BOOL(gapless_monocle)
+       GET_BOOL(paddingless_monocle)
+       GET_BOOL(single_monocle)
+       GET_BOOL(click_to_focus)
+       GET_BOOL(swallow_first_click)
+       GET_BOOL(focus_follows_pointer)
+       GET_BOOL(pointer_follows_focus)
+       GET_BOOL(pointer_follows_monitor)
+       GET_BOOL(ignore_ewmh_focus)
+       GET_BOOL(center_pseudo_tiled)
+       GET_BOOL(honor_size_hints)
+       GET_BOOL(remove_disabled_monitors)
+       GET_BOOL(remove_unplugged_monitors)
+       GET_BOOL(merge_overlapping_monitors)
+#undef GET_BOOL
+       } else {
+               fail(rsp, "config: Unknown setting: '%s'.\n", name);
+               return;
+       }
+       fprintf(rsp, "\n");
+}
+
+void handle_failure(int code, char *src, char *val, FILE *rsp)
+{
+       switch (code) {
+               case SELECTOR_BAD_DESCRIPTOR:
+                       fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
+                       break;
+               case SELECTOR_BAD_MODIFIERS:
+                       fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
+                       break;
+               case SELECTOR_INVALID:
+                       fail(rsp, "");
+                       break;
+       }
+}
+
+void fail(FILE *rsp, char *fmt, ...)
+{
+       fprintf(rsp, FAILURE_MESSAGE);
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(rsp, fmt, ap);
+       va_end(ap);
+}
diff --git a/src/messages.h b/src/messages.h
new file mode 100644 (file)
index 0000000..a80ff71
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_MESSAGES_H
+#define BSPWM_MESSAGES_H
+
+#include "types.h"
+#include "subscribe.h"
+
+enum {
+       SUBSCRIBE_SUCCESS,
+       SUBSCRIBE_FAILURE
+};
+
+void handle_message(char *msg, int msg_len, FILE *rsp);
+void process_message(char **args, int num, FILE *rsp);
+void cmd_node(char **args, int num, FILE *rsp);
+void cmd_desktop(char **args, int num, FILE *rsp);
+void cmd_monitor(char **args, int num, FILE *rsp);
+void cmd_query(char **args, int num, FILE *rsp);
+void cmd_rule(char **args, int num, FILE *rsp);
+void cmd_wm(char **args, int num, FILE *rsp);
+int cmd_subscribe(char **args, int num, FILE *rsp);
+void cmd_quit(char **args, int num, FILE *rsp);
+void cmd_config(char **args, int num, FILE *rsp);
+void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp);
+void get_setting(coordinates_t loc, char *name, FILE* rsp);
+void handle_failure(int code, char *src, char *val, FILE *rsp);
+void fail(FILE *rsp, char *fmt, ...);
+
+#endif
diff --git a/src/monitor.c b/src/monitor.c
new file mode 100644 (file)
index 0000000..8e26940
--- /dev/null
@@ -0,0 +1,542 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "ewmh.h"
+#include "query.h"
+#include "pointer.h"
+#include "settings.h"
+#include "geometry.h"
+#include "tree.h"
+#include "subscribe.h"
+#include "window.h"
+#include "monitor.h"
+
+monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id)
+{
+       monitor_t *m = calloc(1, sizeof(monitor_t));
+       if (id == XCB_NONE) {
+               m->id = xcb_generate_id(dpy);
+       }
+       m->randr_id = XCB_NONE;
+       snprintf(m->name, sizeof(m->name), "%s", name == NULL ? DEFAULT_MON_NAME : name);
+       m->padding = padding;
+       m->border_width = border_width;
+       m->window_gap = window_gap;
+       m->root = XCB_NONE;
+       m->prev = m->next = NULL;
+       m->desk = m->desk_head = m->desk_tail = NULL;
+       m->wired = true;
+       m->sticky_count = 0;
+       if (rect != NULL) {
+               update_root(m, rect);
+       } else {
+               m->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+       }
+       return m;
+}
+
+void update_root(monitor_t *m, xcb_rectangle_t *rect)
+{
+       xcb_rectangle_t last_rect = m->rectangle;
+       m->rectangle = *rect;
+       if (m->root == XCB_NONE) {
+               uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
+               m->root = xcb_generate_id(dpy);
+               xcb_create_window(dpy, XCB_COPY_FROM_PARENT, m->root, root,
+                                 rect->x, rect->y, rect->width, rect->height, 0,
+                                 XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
+               xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
+               xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
+               window_lower(m->root);
+               if (focus_follows_pointer) {
+                       window_show(m->root);
+               }
+       } else {
+               window_move_resize(m->root, rect->x, rect->y, rect->width, rect->height);
+               put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry 0x%08X %ux%u+%i+%i\n",
+                          m->id, rect->width, rect->height, rect->x, rect->y);
+       }
+       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+               for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                       if (n->client == NULL) {
+                               continue;
+                       }
+                       adapt_geometry(&last_rect, rect, n);
+               }
+               arrange(m, d);
+       }
+}
+
+void rename_monitor(monitor_t *m, const char *name)
+{
+       put_status(SBSC_MASK_MONITOR_RENAME, "monitor_rename 0x%08X %s %s\n", m->id, m->name, name);
+
+       snprintf(m->name, sizeof(m->name), "%s", name);
+       xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
+
+       put_status(SBSC_MASK_REPORT);
+}
+
+monitor_t *find_monitor(uint32_t id)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (m->id == id) {
+                       return m;
+               }
+       }
+       return NULL;
+}
+
+monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (m->randr_id == id) {
+                       return m;
+               }
+       }
+       return NULL;
+}
+
+void embrace_client(monitor_t *m, client_t *c)
+{
+       if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x) {
+               c->floating_rectangle.x = m->rectangle.x;
+       } else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width)) {
+               c->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;
+       }
+       if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y) {
+               c->floating_rectangle.y = m->rectangle.y;
+       } else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height)) {
+               c->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;
+       }
+}
+
+void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
+{
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client == NULL) {
+                       continue;
+               }
+               client_t *c = f->client;
+               /* Clip the rectangle to fit into the monitor.  Without this, the fitting
+                * algorithm doesn't work as expected. This also conserves the
+                * out-of-bounds regions */
+               int left_adjust = MAX((rs->x - c->floating_rectangle.x), 0);
+               int top_adjust = MAX((rs->y - c->floating_rectangle.y), 0);
+               int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (rs->x + rs->width), 0);
+               int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (rs->y + rs->height), 0);
+               c->floating_rectangle.x += left_adjust;
+               c->floating_rectangle.y += top_adjust;
+               c->floating_rectangle.width -= (left_adjust + right_adjust);
+               c->floating_rectangle.height -= (top_adjust + bottom_adjust);
+
+               int dx_s = c->floating_rectangle.x - rs->x;
+               int dy_s = c->floating_rectangle.y - rs->y;
+
+               int nume_x = dx_s * (rd->width - c->floating_rectangle.width);
+               int nume_y = dy_s * (rd->height - c->floating_rectangle.height);
+
+               int deno_x = rs->width - c->floating_rectangle.width;
+               int deno_y = rs->height - c->floating_rectangle.height;
+
+               int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
+               int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
+
+               /* Translate and undo clipping */
+               c->floating_rectangle.width += left_adjust + right_adjust;
+               c->floating_rectangle.height += top_adjust + bottom_adjust;
+               c->floating_rectangle.x = rd->x + dx_d - left_adjust;
+               c->floating_rectangle.y = rd->y + dy_d - top_adjust;
+       }
+}
+
+void focus_monitor(monitor_t *m)
+{
+       if (mon == m) {
+               return;
+       }
+
+       mon = m;
+
+       if (pointer_follows_monitor) {
+               center_pointer(m->rectangle);
+       }
+
+       put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus 0x%08X\n", m->id);
+}
+
+void add_monitor(monitor_t *m)
+{
+       xcb_rectangle_t r = m->rectangle;
+
+       if (mon == NULL) {
+               mon = m;
+               mon_head = m;
+               mon_tail = m;
+       } else {
+               monitor_t *a = mon_head;
+               while (a != NULL && rect_cmp(m->rectangle, a->rectangle) > 0) {
+                       a = a->next;
+               }
+               if (a != NULL) {
+                       monitor_t *b = a->prev;
+                       if (b != NULL) {
+                               b->next = m;
+                       } else {
+                               mon_head = m;
+                       }
+                       m->prev = b;
+                       m->next = a;
+                       a->prev = m;
+               } else {
+                       mon_tail->next = m;
+                       m->prev = mon_tail;
+                       mon_tail = m;
+               }
+       }
+
+       put_status(SBSC_MASK_MONITOR_ADD, "monitor_add 0x%08X %s %ux%u+%i+%i\n", m->id, m->name, r.width, r.height, r.x, r.y);
+
+       put_status(SBSC_MASK_REPORT);
+}
+
+void unlink_monitor(monitor_t *m)
+{
+       monitor_t *prev = m->prev;
+       monitor_t *next = m->next;
+
+       if (prev != NULL) {
+               prev->next = next;
+       }
+
+       if (next != NULL) {
+               next->prev = prev;
+       }
+
+       if (mon_head == m) {
+               mon_head = next;
+       }
+
+       if (mon_tail == m) {
+               mon_tail = prev;
+       }
+
+       if (pri_mon == m) {
+               pri_mon = NULL;
+       }
+
+       if (mon == m) {
+               mon = NULL;
+       }
+}
+
+void remove_monitor(monitor_t *m)
+{
+       put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove 0x%08X\n", m->id);
+
+       while (m->desk_head != NULL) {
+               remove_desktop(m, m->desk_head);
+       }
+
+       monitor_t *last_mon = mon;
+
+       unlink_monitor(m);
+       xcb_destroy_window(dpy, m->root);
+       free(m);
+
+       if (mon != last_mon) {
+               focus_node(NULL, NULL, NULL);
+       }
+
+       put_status(SBSC_MASK_REPORT);
+}
+
+void merge_monitors(monitor_t *ms, monitor_t *md)
+{
+       if (ms == NULL || md == NULL || ms == md) {
+               return;
+       }
+
+       desktop_t *d = ms->desk_head;
+       while (d != NULL) {
+               desktop_t *next = d->next;
+               transfer_desktop(ms, md, d);
+               d = next;
+       }
+}
+
+bool swap_monitors(monitor_t *m1, monitor_t *m2)
+{
+       if (m1 == NULL || m2 == NULL || m1 == m2) {
+               return false;
+       }
+
+       put_status(SBSC_MASK_MONITOR_SWAP, "monitor_swap 0x%08X 0x%08X\n", m1->id, m2->id);
+
+       if (mon_head == m1) {
+               mon_head = m2;
+       } else if (mon_head == m2) {
+               mon_head = m1;
+       }
+       if (mon_tail == m1) {
+               mon_tail = m2;
+       } else if (mon_tail == m2) {
+               mon_tail = m1;
+       }
+
+       monitor_t *p1 = m1->prev;
+       monitor_t *n1 = m1->next;
+       monitor_t *p2 = m2->prev;
+       monitor_t *n2 = m2->next;
+
+       if (p1 != NULL && p1 != m2) {
+               p1->next = m2;
+       }
+       if (n1 != NULL && n1 != m2) {
+               n1->prev = m2;
+       }
+       if (p2 != NULL && p2 != m1) {
+               p2->next = m1;
+       }
+       if (n2 != NULL && n2 != m1) {
+               n2->prev = m1;
+       }
+
+       m1->prev = p2 == m1 ? m2 : p2;
+       m1->next = n2 == m1 ? m2 : n2;
+       m2->prev = p1 == m2 ? m1 : p1;
+       m2->next = n1 == m2 ? m1 : n1;
+
+       ewmh_update_wm_desktops();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+       ewmh_update_current_desktop();
+
+       put_status(SBSC_MASK_REPORT);
+       return true;
+}
+
+monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel)
+{
+       monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
+
+       if (f == NULL) {
+               f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+       }
+
+       while (f != m) {
+               coordinates_t loc = {f, NULL, NULL};
+               if (monitor_matches(&loc, &loc, sel)) {
+                       return f;
+               }
+               f = (dir == CYCLE_PREV ? f->prev : f->next);
+               if (f == NULL) {
+                       f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+               }
+       }
+
+       return NULL;
+}
+
+bool is_inside_monitor(monitor_t *m, xcb_point_t pt)
+{
+       return is_inside(pt, m->rectangle);
+}
+
+monitor_t *monitor_from_point(xcb_point_t pt)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (is_inside_monitor(m, pt)) {
+                       return m;
+               }
+       }
+       return NULL;
+}
+
+monitor_t *monitor_from_client(client_t *c)
+{
+       int16_t xc = c->floating_rectangle.x + c->floating_rectangle.width/2;
+       int16_t yc = c->floating_rectangle.y + c->floating_rectangle.height/2;
+       xcb_point_t pt = {xc, yc};
+       monitor_t *nearest = monitor_from_point(pt);
+       if (nearest == NULL) {
+               int dmin = INT_MAX;
+               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+                       xcb_rectangle_t r = m->rectangle;
+                       int d = abs((r.x + r.width / 2) - xc) + abs((r.y + r.height / 2) - yc);
+                       if (d < dmin) {
+                               dmin = d;
+                               nearest = m;
+                       }
+               }
+       }
+       return nearest;
+}
+
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
+{
+       double dmin = DBL_MAX;
+       monitor_t *nearest = NULL;
+       xcb_rectangle_t rect = m->rectangle;
+       for (monitor_t *f = mon_head; f != NULL; f = f->next) {
+               coordinates_t loc = {f, NULL, NULL};
+               xcb_rectangle_t r = f->rectangle;
+               if (f == m ||
+                   !monitor_matches(&loc, &loc, sel) ||
+                   !on_dir_side(rect, r, dir)) {
+                       continue;
+               }
+               double d = distance_center(rect, r);
+               if (d < dmin) {
+                       dmin = d;
+                       nearest = f;
+               }
+       }
+       return nearest;
+}
+
+bool update_monitors(void)
+{
+       xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
+       if (sres == NULL) {
+               return false;
+       }
+
+       monitor_t *m, *mm = NULL;
+
+       int len = xcb_randr_get_screen_resources_outputs_length(sres);
+       xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);
+
+       xcb_randr_get_output_info_cookie_t cookies[len];
+       for (int i = 0; i < len; i++) {
+               cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
+       }
+
+       for (m = mon_head; m != NULL; m = m->next) {
+               m->wired = false;
+       }
+
+       for (int i = 0; i < len; i++) {
+               xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
+               if (info != NULL) {
+                       if (info->crtc != XCB_NONE) {
+                               xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
+                               if (cir != NULL) {
+                                       xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
+                                       mm = get_monitor_by_randr_id(outputs[i]);
+                                       if (mm != NULL) {
+                                               update_root(mm, &rect);
+                                               mm->wired = true;
+                                       } else {
+                                               char *name = (char *) xcb_randr_get_output_info_name(info);
+                                               size_t len = (size_t) xcb_randr_get_output_info_name_length(info);
+                                               char *name_copy = copy_string(name, len);
+                                               mm = make_monitor(name_copy, &rect, XCB_NONE);
+                                               free(name_copy);
+                                               mm->randr_id = outputs[i];
+                                               add_monitor(mm);
+                                       }
+                               }
+                               free(cir);
+                       } else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
+                               m = get_monitor_by_randr_id(outputs[i]);
+                               if (m != NULL) {
+                                       m->wired = true;
+                               }
+                       }
+               }
+               free(info);
+       }
+
+       xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
+       if (gpo != NULL) {
+               pri_mon = get_monitor_by_randr_id(gpo->output);
+       }
+       free(gpo);
+
+       /* handle overlapping monitors */
+       if (merge_overlapping_monitors) {
+               m = mon_head;
+               while (m != NULL) {
+                       monitor_t *next = m->next;
+                       if (m->wired) {
+                               monitor_t *mb = mon_head;
+                               while (mb != NULL) {
+                                       monitor_t *mb_next = mb->next;
+                                       if (m != mb && mb->wired && contains(m->rectangle, mb->rectangle)) {
+                                               if (mm == mb) {
+                                                       mm = m;
+                                               }
+                                               if (next == mb) {
+                                                       next = mb_next;
+                                               }
+                                               merge_monitors(mb, m);
+                                               remove_monitor(mb);
+                                       }
+                                       mb = mb_next;
+                               }
+                       }
+                       m = next;
+               }
+       }
+
+       /* merge and remove disconnected monitors */
+       if (remove_unplugged_monitors) {
+               m = mon_head;
+               while (m != NULL) {
+                       monitor_t *next = m->next;
+                       if (!m->wired) {
+                               merge_monitors(m, mm);
+                               remove_monitor(m);
+                       }
+                       m = next;
+               }
+       }
+
+       /* add one desktop to each new monitor */
+       for (m = mon_head; m != NULL; m = m->next) {
+               if (m->desk == NULL) {
+                       add_desktop(m, make_desktop(NULL, XCB_NONE));
+               }
+       }
+
+       if (!running && mon != NULL) {
+               if (pri_mon != NULL) {
+                       mon = pri_mon;
+               }
+               center_pointer(mon->rectangle);
+               ewmh_update_current_desktop();
+       }
+
+       free(sres);
+
+       return (mon != NULL);
+}
diff --git a/src/monitor.h b/src/monitor.h
new file mode 100644 (file)
index 0000000..d0d3544
--- /dev/null
@@ -0,0 +1,50 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_MONITOR_H
+#define BSPWM_MONITOR_H
+
+#define DEFAULT_MON_NAME     "MONITOR"
+
+monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id);
+void update_root(monitor_t *m, xcb_rectangle_t *rect);
+void rename_monitor(monitor_t *m, const char *name);
+monitor_t *find_monitor(uint32_t id);
+monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id);
+void embrace_client(monitor_t *m, client_t *c);
+void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n);
+void focus_monitor(monitor_t *m);
+void add_monitor(monitor_t *m);
+void unlink_monitor(monitor_t *m);
+void remove_monitor(monitor_t *m);
+void merge_monitors(monitor_t *ms, monitor_t *md);
+bool swap_monitors(monitor_t *m1, monitor_t *m2);
+monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel);
+bool is_inside_monitor(monitor_t *m, xcb_point_t pt);
+monitor_t *monitor_from_point(xcb_point_t pt);
+monitor_t *monitor_from_client(client_t *c);
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel);
+bool update_monitors(void);
+
+#endif
diff --git a/src/parse.c b/src/parse.c
new file mode 100644 (file)
index 0000000..c5328e8
--- /dev/null
@@ -0,0 +1,468 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include "parse.h"
+
+bool parse_bool(char *value, bool *b)
+{
+       if (streq("true", value) || streq("on", value)) {
+               *b = true;
+               return true;
+       } else if (streq("false", value) || streq("off", value)) {
+               *b = false;
+               return true;
+       }
+       return false;
+}
+
+bool parse_split_type(char *s, split_type_t *t)
+{
+       if (streq("horizontal", s)) {
+               *t = TYPE_HORIZONTAL;
+               return true;
+       } else if (streq("vertical", s)) {
+               *t = TYPE_VERTICAL;
+               return true;
+       }
+       return false;
+}
+
+bool parse_split_mode(char *s, split_mode_t *m)
+{
+       if (streq("automatic", s)) {
+               *m = MODE_AUTOMATIC;
+               return true;
+       } else if (streq("vertical", s)) {
+               *m = MODE_MANUAL;
+               return true;
+       }
+       return false;
+}
+
+bool parse_layout(char *s, layout_t *l)
+{
+       if (streq("monocle", s)) {
+               *l = LAYOUT_MONOCLE;
+               return true;
+       } else if (streq("tiled", s)) {
+               *l = LAYOUT_TILED;
+               return true;
+       }
+       return false;
+}
+
+bool parse_client_state(char *s, client_state_t *t)
+{
+       if (streq("tiled", s)) {
+               *t = STATE_TILED;
+               return true;
+       } else if (streq("pseudo_tiled", s)) {
+               *t = STATE_PSEUDO_TILED;
+               return true;
+       } else if (streq("floating", s)) {
+               *t = STATE_FLOATING;
+               return true;
+       } else if (streq("fullscreen", s)) {
+               *t = STATE_FULLSCREEN;
+               return true;
+       }
+       return false;
+}
+
+bool parse_stack_layer(char *s, stack_layer_t *l)
+{
+       if (streq("below", s)) {
+               *l = LAYER_BELOW;
+               return true;
+       } else if (streq("normal", s)) {
+               *l = LAYER_NORMAL;
+               return true;
+       } else if (streq("above", s)) {
+               *l = LAYER_ABOVE;
+               return true;
+       }
+       return false;
+}
+
+bool parse_direction(char *s, direction_t *d)
+{
+       if (streq("north", s)) {
+               *d = DIR_NORTH;
+               return true;
+       } else if (streq("west", s)) {
+               *d = DIR_WEST;
+               return true;
+       } else if (streq("south", s)) {
+               *d = DIR_SOUTH;
+               return true;
+       } else if (streq("east", s)) {
+               *d = DIR_EAST;
+               return true;
+       }
+       return false;
+}
+
+bool parse_cycle_direction(char *s, cycle_dir_t *d)
+{
+       if (streq("next", s)) {
+               *d = CYCLE_NEXT;
+               return true;
+       } else if (streq("prev", s)) {
+               *d = CYCLE_PREV;
+               return true;
+       }
+       return false;
+}
+
+bool parse_circulate_direction(char *s, circulate_dir_t *d)
+{
+       if (streq("forward", s)) {
+               *d = CIRCULATE_FORWARD;
+               return true;
+       } else if (streq("backward", s)) {
+               *d = CIRCULATE_BACKWARD;
+               return true;
+       }
+       return false;
+}
+
+bool parse_history_direction(char *s, history_dir_t *d)
+{
+       if (streq("older", s)) {
+               *d = HISTORY_OLDER;
+               return true;
+       } else if (streq("newer", s)) {
+               *d = HISTORY_NEWER;
+               return true;
+       }
+       return false;
+}
+
+
+bool parse_flip(char *s, flip_t *f)
+{
+       if (streq("horizontal", s)) {
+               *f = FLIP_HORIZONTAL;
+               return true;
+       } else if (streq("vertical", s)) {
+               *f = FLIP_VERTICAL;
+               return true;
+       }
+       return false;
+}
+
+bool parse_resize_handle(char *s, resize_handle_t *h)
+{
+       if (streq("left", s)) {
+               *h = HANDLE_LEFT;
+               return true;
+       } else if (streq("top", s)) {
+               *h = HANDLE_TOP;
+               return true;
+       } else if (streq("right", s)) {
+               *h = HANDLE_RIGHT;
+               return true;
+       } else if (streq("bottom", s)) {
+               *h = HANDLE_BOTTOM;
+               return true;
+       } else if (streq("top_left", s)) {
+               *h = HANDLE_TOP_LEFT;
+               return true;
+       } else if (streq("top_right", s)) {
+               *h = HANDLE_TOP_RIGHT;
+               return true;
+       } else if (streq("bottom_right", s)) {
+               *h = HANDLE_BOTTOM_RIGHT;
+               return true;
+       } else if (streq("bottom_left", s)) {
+               *h = HANDLE_BOTTOM_LEFT;
+               return true;
+       }
+       return false;
+}
+
+bool parse_modifier_mask(char *s, uint16_t *m)
+{
+       if (strcmp(s, "shift") == 0) {
+               *m = XCB_MOD_MASK_SHIFT;
+               return true;
+       } else if (strcmp(s, "control") == 0) {
+               *m = XCB_MOD_MASK_CONTROL;
+               return true;
+       } else if (strcmp(s, "lock") == 0) {
+               *m = XCB_MOD_MASK_LOCK;
+               return true;
+       } else if (strcmp(s, "mod1") == 0) {
+               *m = XCB_MOD_MASK_1;
+               return true;
+       } else if (strcmp(s, "mod2") == 0) {
+               *m = XCB_MOD_MASK_2;
+               return true;
+       } else if (strcmp(s, "mod3") == 0) {
+               *m = XCB_MOD_MASK_3;
+               return true;
+       } else if (strcmp(s, "mod4") == 0) {
+               *m = XCB_MOD_MASK_4;
+               return true;
+       } else if (strcmp(s, "mod5") == 0) {
+               *m = XCB_MOD_MASK_5;
+               return true;
+       }
+       return false;
+}
+
+bool parse_pointer_action(char *s, pointer_action_t *a)
+{
+       if (streq("move", s)) {
+               *a = ACTION_MOVE;
+               return true;
+       } else if (streq("resize_corner", s)) {
+               *a = ACTION_RESIZE_CORNER;
+               return true;
+       } else if (streq("resize_side", s)) {
+               *a = ACTION_RESIZE_SIDE;
+               return true;
+       } else if (streq("focus", s)) {
+               *a = ACTION_FOCUS;
+               return true;
+       } else if (streq("none", s)) {
+               *a = ACTION_NONE;
+               return true;
+       }
+       return false;
+}
+
+bool parse_child_polarity(char *s, child_polarity_t *p)
+{
+       if (streq("first_child", s)) {
+               *p = FIRST_CHILD;
+               return true;
+       } else if (streq("second_child", s)) {
+               *p = SECOND_CHILD;
+               return true;
+       }
+       return false;
+}
+
+bool parse_degree(char *s, int *d)
+{
+       int i = atoi(s);
+       while (i < 0)
+               i += 360;
+       while (i > 359)
+               i -= 360;
+       if ((i % 90) != 0) {
+               return false;
+       } else {
+               *d = i;
+               return true;
+       }
+}
+
+bool parse_id(char *s, uint32_t *id)
+{
+       char *end;
+       errno = 0;
+       uint32_t v = strtol(s, &end, 0);
+       if (errno != 0 || *end != '\0') {
+               return false;
+       }
+       *id = v;
+       return true;
+}
+
+bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)
+{
+       *key = strtok(s, EQL_TOK);
+       char *v = strtok(NULL, EQL_TOK);
+       if (v == NULL) {
+               *state = ALTER_TOGGLE;
+               return true;
+       } else {
+               if (parse_bool(v, value)) {
+                       *state = ALTER_SET;
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+       return false;
+}
+
+bool parse_index(char *s, uint16_t *idx)
+{
+       return (sscanf(s, "^%hu", idx) == 1);
+}
+
+bool parse_rectangle(char *s, xcb_rectangle_t *r)
+{
+       uint16_t w, h;
+       int16_t x, y;
+       if (sscanf(s, "%hux%hu+%hi+%hi", &w, &h, &x, &y) != 4) {
+               return false;
+       }
+       r->width = w;
+       r->height = h;
+       r->x = x;
+       r->y = y;
+       return true;
+}
+
+bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
+{
+       if (streq("all", s)) {
+               *mask = SBSC_MASK_ALL;
+       } else if (streq("node", s)) {
+               *mask = SBSC_MASK_NODE;
+       } else if (streq("desktop", s)) {
+               *mask = SBSC_MASK_DESKTOP;
+       } else if (streq("monitor", s)) {
+               *mask = SBSC_MASK_MONITOR;
+       } else if (streq("pointer_action", s)) {
+               *mask = SBSC_MASK_POINTER_ACTION;
+       } else if (streq("node_manage", s)) {
+               *mask = SBSC_MASK_NODE_MANAGE;
+       } else if (streq("node_unmanage", s)) {
+               *mask = SBSC_MASK_NODE_UNMANAGE;
+       } else if (streq("node_swap", s)) {
+               *mask = SBSC_MASK_NODE_SWAP;
+       } else if (streq("node_transfer", s)) {
+               *mask = SBSC_MASK_NODE_TRANSFER;
+       } else if (streq("node_focus", s)) {
+               *mask = SBSC_MASK_NODE_FOCUS;
+       } else if (streq("node_presel", s)) {
+               *mask = SBSC_MASK_NODE_PRESEL;
+       } else if (streq("node_stack", s)) {
+               *mask = SBSC_MASK_NODE_STACK;
+       } else if (streq("node_activate", s)) {
+               *mask = SBSC_MASK_NODE_ACTIVATE;
+       } else if (streq("node_geometry", s)) {
+               *mask = SBSC_MASK_NODE_GEOMETRY;
+       } else if (streq("node_state", s)) {
+               *mask = SBSC_MASK_NODE_STATE;
+       } else if (streq("node_flag", s)) {
+               *mask = SBSC_MASK_NODE_FLAG;
+       } else if (streq("node_layer", s)) {
+               *mask = SBSC_MASK_NODE_LAYER;
+       } else if (streq("desktop_add", s)) {
+               *mask = SBSC_MASK_DESKTOP_ADD;
+       } else if (streq("desktop_rename", s)) {
+               *mask = SBSC_MASK_DESKTOP_RENAME;
+       } else if (streq("desktop_remove", s)) {
+               *mask = SBSC_MASK_DESKTOP_REMOVE;
+       } else if (streq("desktop_swap", s)) {
+               *mask = SBSC_MASK_DESKTOP_SWAP;
+       } else if (streq("desktop_transfer", s)) {
+               *mask = SBSC_MASK_DESKTOP_TRANSFER;
+       } else if (streq("desktop_focus", s)) {
+               *mask = SBSC_MASK_DESKTOP_FOCUS;
+       } else if (streq("desktop_activate", s)) {
+               *mask = SBSC_MASK_DESKTOP_ACTIVATE;
+       } else if (streq("desktop_layout", s)) {
+               *mask = SBSC_MASK_DESKTOP_LAYOUT;
+       } else if (streq("monitor_add", s)) {
+               *mask = SBSC_MASK_MONITOR_ADD;
+       } else if (streq("monitor_rename", s)) {
+               *mask = SBSC_MASK_MONITOR_RENAME;
+       } else if (streq("monitor_remove", s)) {
+               *mask = SBSC_MASK_MONITOR_REMOVE;
+       } else if (streq("monitor_swap", s)) {
+               *mask = SBSC_MASK_MONITOR_SWAP;
+       } else if (streq("monitor_focus", s)) {
+               *mask = SBSC_MASK_MONITOR_FOCUS;
+       } else if (streq("monitor_geometry", s)) {
+               *mask = SBSC_MASK_MONITOR_GEOMETRY;
+       } else if (streq("report", s)) {
+               *mask = SBSC_MASK_REPORT;
+       } else {
+               return false;
+       }
+       return true;
+}
+
+
+#define GET_MOD(k) \
+       } else if (streq(#k, tok)) { \
+               sel->k = OPTION_TRUE; \
+       } else if (streq("!" #k, tok)) { \
+               sel->k = OPTION_FALSE;
+
+bool parse_monitor_modifiers(char *desc, monitor_select_t *sel)
+{
+       char *tok;
+       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+               tok[0] = '\0';
+               tok++;
+               if (streq("occupied", tok)) {
+                       sel->occupied = OPTION_TRUE;
+               } else if (streq("!occupied", tok)) {
+                       sel->occupied = OPTION_FALSE;
+               GET_MOD(focused)
+               } else {
+                       return false;
+               }
+       }
+       return true;
+}
+
+bool parse_desktop_modifiers(char *desc, desktop_select_t *sel)
+{
+       char *tok;
+       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+               tok[0] = '\0';
+               tok++;
+               if (streq("occupied", tok)) {
+                       sel->occupied = OPTION_TRUE;
+               } else if (streq("!occupied", tok)) {
+                       sel->occupied = OPTION_FALSE;
+               GET_MOD(focused)
+               GET_MOD(urgent)
+               GET_MOD(local)
+               } else {
+                       return false;
+               }
+       }
+       return true;
+
+}
+
+bool parse_node_modifiers(char *desc, node_select_t *sel)
+{
+       char *tok;
+       while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+               tok[0] = '\0';
+               tok++;
+               if (streq("tiled", tok)) {
+                       sel->tiled = OPTION_TRUE;
+               } else if (streq("!tiled", tok)) {
+                       sel->tiled = OPTION_FALSE;
+               GET_MOD(automatic)
+               GET_MOD(focused)
+               GET_MOD(local)
+               GET_MOD(active)
+               GET_MOD(leaf)
+               GET_MOD(window)
+               GET_MOD(pseudo_tiled)
+               GET_MOD(floating)
+               GET_MOD(fullscreen)
+               GET_MOD(hidden)
+               GET_MOD(sticky)
+               GET_MOD(private)
+               GET_MOD(locked)
+               GET_MOD(urgent)
+               GET_MOD(same_class)
+               GET_MOD(descendant_of)
+               GET_MOD(ancestor_of)
+               GET_MOD(below)
+               GET_MOD(normal)
+               GET_MOD(above)
+               } else {
+                       return false;
+               }
+       }
+       return true;
+}
+
+#undef GET_MOD
diff --git a/src/parse.h b/src/parse.h
new file mode 100644 (file)
index 0000000..bb25799
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef BSPWM_PARSE_H
+#define BSPWM_PARSE_H
+
+#include "types.h"
+#include "subscribe.h"
+
+#define OPT_CHR  '-'
+#define CAT_CHR  '.'
+#define EQL_TOK  "="
+#define COL_TOK  ":"
+
+bool parse_bool(char *value, bool *b);
+bool parse_split_type(char *s, split_type_t *t);
+bool parse_split_mode(char *s, split_mode_t *m);
+bool parse_layout(char *s, layout_t *l);
+bool parse_client_state(char *s, client_state_t *t);
+bool parse_stack_layer(char *s, stack_layer_t *l);
+bool parse_direction(char *s, direction_t *d);
+bool parse_cycle_direction(char *s, cycle_dir_t *d);
+bool parse_circulate_direction(char *s, circulate_dir_t *d);
+bool parse_history_direction(char *s, history_dir_t *d);
+bool parse_flip(char *s, flip_t *f);
+bool parse_resize_handle(char *s, resize_handle_t *h);
+bool parse_modifier_mask(char *s, uint16_t *m);
+bool parse_pointer_action(char *s, pointer_action_t *a);
+bool parse_child_polarity(char *s, child_polarity_t *p);
+bool parse_degree(char *s, int *d);
+bool parse_id(char *s, uint32_t *id);
+bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
+bool parse_index(char *s, uint16_t *idx);
+bool parse_rectangle(char *s, xcb_rectangle_t *r);
+bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
+bool parse_monitor_modifiers(char *desc, monitor_select_t *sel);
+bool parse_desktop_modifiers(char *desc, desktop_select_t *sel);
+bool parse_node_modifiers(char *desc, node_select_t *sel);
+
+#endif
diff --git a/src/pointer.c b/src/pointer.c
new file mode 100644 (file)
index 0000000..3f4be8a
--- /dev/null
@@ -0,0 +1,329 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <xcb/xcb_keysyms.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "query.h"
+#include "settings.h"
+#include "stack.h"
+#include "tree.h"
+#include "monitor.h"
+#include "subscribe.h"
+#include "events.h"
+#include "window.h"
+#include "pointer.h"
+
+void pointer_init(void)
+{
+       num_lock = modfield_from_keysym(XK_Num_Lock);
+       caps_lock = modfield_from_keysym(XK_Caps_Lock);
+       scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
+       if (caps_lock == XCB_NO_SYMBOL) {
+               caps_lock = XCB_MOD_MASK_LOCK;
+       }
+       grabbing = false;
+       grabbed_node = NULL;
+}
+
+void window_grab_buttons(xcb_window_t win)
+{
+       if (click_to_focus) {
+               window_grab_button(win, XCB_BUTTON_INDEX_1, XCB_NONE);
+       }
+       uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
+       for (unsigned int i = 0; i < LENGTH(buttons); i++) {
+               if (pointer_actions[i] != ACTION_NONE) {
+                       window_grab_button(win, buttons[i], pointer_modifier);
+               }
+       }
+}
+
+void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
+{
+#define GRAB(b, m) \
+       xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
+                       XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
+               GRAB(button, modifier);
+               if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
+               }
+               if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | num_lock | caps_lock);
+               }
+               if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | caps_lock | scroll_lock);
+               }
+               if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | num_lock | scroll_lock);
+               }
+               if (num_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | num_lock);
+               }
+               if (caps_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | caps_lock);
+               }
+               if (scroll_lock != XCB_NO_SYMBOL) {
+                       GRAB(button, modifier | scroll_lock);
+               }
+#undef GRAB
+}
+
+void grab_buttons(void)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                               window_grab_buttons(n->id);
+                               if (n->presel != NULL) {
+                                       window_grab_buttons(n->presel->feedback);
+                               }
+                       }
+               }
+       }
+}
+
+void ungrab_buttons(void)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                               xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
+                       }
+               }
+       }
+}
+
+int16_t modfield_from_keysym(xcb_keysym_t keysym)
+{
+       uint16_t modfield = 0;
+       xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
+       xcb_get_modifier_mapping_reply_t *reply = NULL;
+       xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
+
+       if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
+           (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
+           reply->keycodes_per_modifier < 1 ||
+           (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
+               goto end;
+       }
+
+       unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
+       for (unsigned int i = 0; i < num_mod; i++) {
+               for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
+                       xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
+                       if (mk == XCB_NO_SYMBOL) {
+                               continue;
+                       }
+                       for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
+                               if (*k == mk) {
+                                       modfield |= (1 << i);
+                               }
+                       }
+               }
+       }
+
+end:
+       xcb_key_symbols_free(symbols);
+       free(keycodes);
+       free(reply);
+       return modfield;
+}
+
+resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
+{
+       resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
+       xcb_rectangle_t rect = get_rectangle(NULL, n);
+       if (pac == ACTION_RESIZE_SIDE) {
+               float W = rect.width;
+               float H = rect.height;
+               float ratio = W / H;
+               float x = pos.x - rect.x;
+               float y = pos.y - rect.y;
+               float diag_a = ratio * y;
+               float diag_b = W - diag_a;
+               if (x < diag_a) {
+                       if (x < diag_b) {
+                               rh = HANDLE_LEFT;
+                       } else {
+                               rh = HANDLE_BOTTOM;
+                       }
+               } else {
+                       if (x < diag_b) {
+                               rh = HANDLE_TOP;
+                       } else {
+                               rh = HANDLE_RIGHT;
+                       }
+               }
+       } else if (pac == ACTION_RESIZE_CORNER) {
+               int16_t mid_x = rect.x + (rect.width / 2);
+               int16_t mid_y = rect.y + (rect.height / 2);
+               if (pos.x > mid_x) {
+                       if (pos.y > mid_y) {
+                               rh = HANDLE_BOTTOM_RIGHT;
+                       } else {
+                               rh = HANDLE_TOP_RIGHT;
+                       }
+               } else {
+                       if (pos.y > mid_y) {
+                               rh = HANDLE_BOTTOM_LEFT;
+                       } else {
+                               rh = HANDLE_TOP_LEFT;
+                       }
+               }
+       }
+       return rh;
+}
+
+bool grab_pointer(pointer_action_t pac)
+{
+       xcb_window_t win = XCB_NONE;
+       xcb_point_t pos;
+
+       query_pointer(&win, &pos);
+
+       coordinates_t loc;
+
+       if (!locate_window(win, &loc)) {
+               if (pac == ACTION_FOCUS) {
+                       monitor_t *m = monitor_from_point(pos);
+                       if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
+                               focus_node(m, m->desk, m->desk->focus);
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       if (pac == ACTION_FOCUS) {
+               if (loc.node != mon->desk->focus) {
+                       focus_node(loc.monitor, loc.desktop, loc.node);
+                       return true;
+               } else if (focus_follows_pointer) {
+                       stack(loc.desktop, loc.node, true);
+               }
+               return false;
+       }
+
+       if (loc.node->client->state == STATE_FULLSCREEN) {
+               return true;
+       }
+
+       xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(dpy, xcb_grab_pointer(dpy, 0, root, XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME), NULL);
+
+       if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
+               free(reply);
+               return true;
+       }
+       free(reply);
+
+       if (pac == ACTION_MOVE) {
+               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
+       } else if (pac == ACTION_RESIZE_CORNER) {
+               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
+       } else if (pac == ACTION_RESIZE_SIDE) {
+               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
+       }
+
+       track_pointer(loc, pac, pos);
+
+       return true;
+}
+
+void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
+{
+       node_t *n = loc.node;
+       resize_handle_t rh = get_handle(loc.node, pos, pac);
+
+       uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
+       xcb_timestamp_t last_motion_time = 0;
+
+       xcb_generic_event_t *evt = NULL;
+
+       grabbing = true;
+       grabbed_node = n;
+
+       do {
+               free(evt);
+               while ((evt = xcb_wait_for_event(dpy)) == NULL) {
+                       xcb_flush(dpy);
+               }
+               uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
+               if (resp_type == XCB_MOTION_NOTIFY) {
+                       xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
+                       uint32_t dtime = e->time - last_motion_time;
+                       if (dtime < pointer_motion_interval) {
+                               continue;
+                       }
+                       last_motion_time = e->time;
+                       int16_t dx = e->root_x - last_motion_x;
+                       int16_t dy = e->root_y - last_motion_y;
+                       if (pac == ACTION_MOVE) {
+                               move_client(&loc, dx, dy);
+                       } else {
+                               resize_client(&loc, rh, dx, dy);
+                       }
+                       last_motion_x = e->root_x;
+                       last_motion_y = e->root_y;
+                       xcb_flush(dpy);
+               } else if (resp_type == XCB_BUTTON_RELEASE) {
+                       grabbing = false;
+               } else {
+                       handle_event(evt);
+               }
+       } while (grabbing && grabbed_node != NULL);
+       free(evt);
+
+       xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
+
+       if (grabbed_node == NULL) {
+               grabbing = false;
+               return;
+       }
+
+       if (pac == ACTION_MOVE) {
+               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move end\n", loc.monitor->id, loc.desktop->id, n->id);
+       } else if (pac == ACTION_RESIZE_CORNER) {
+               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner end\n", loc.monitor->id, loc.desktop->id, n->id);
+       } else if (pac == ACTION_RESIZE_SIDE) {
+               put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side end\n", loc.monitor->id, loc.desktop->id, n->id);
+       }
+
+       xcb_rectangle_t r = get_rectangle(NULL, n);
+
+       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, loc.node->id, r.width, r.height, r.x, r.y);
+
+       if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
+           ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
+            n->client->state == STATE_TILED)) {
+               for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
+                       if (f == n || f->client == NULL || !IS_TILED(f->client)) {
+                               continue;
+                       }
+                       xcb_rectangle_t r = f->client->tiled_rectangle;
+                       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, f->id, r.width, r.height, r.x, r.y);
+               }
+       }
+}
diff --git a/src/pointer.h b/src/pointer.h
new file mode 100644 (file)
index 0000000..4930e41
--- /dev/null
@@ -0,0 +1,49 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_POINTER_H
+#define BSPWM_POINTER_H
+
+#define XK_Num_Lock     0xff7f
+#define XK_Caps_Lock    0xffe5
+#define XK_Scroll_Lock  0xff14
+
+uint16_t num_lock;
+uint16_t caps_lock;
+uint16_t scroll_lock;
+
+bool grabbing;
+node_t *grabbed_node;
+
+void pointer_init(void);
+void window_grab_buttons(xcb_window_t win);
+void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier);
+void grab_buttons(void);
+void ungrab_buttons(void);
+int16_t modfield_from_keysym(xcb_keysym_t keysym);
+resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac);
+bool grab_pointer(pointer_action_t pac);
+void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos);
+
+#endif
diff --git a/src/query.c b/src/query.c
new file mode 100644 (file)
index 0000000..bc51a4a
--- /dev/null
@@ -0,0 +1,1032 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "history.h"
+#include "parse.h"
+#include "monitor.h"
+#include "window.h"
+#include "tree.h"
+#include "query.h"
+
+void query_tree(FILE *rsp)
+{
+       fprintf(rsp, "{");
+       fprintf(rsp, "\"focusedMonitorId\":%u,", mon->id);
+       if (pri_mon != NULL) {
+               fprintf(rsp, "\"primaryMonitorId\":%u,", pri_mon->id);
+       }
+       fprintf(rsp, "\"clientsCount\":%i,", clients_count);
+       fprintf(rsp, "\"monitors\":");
+       fprintf(rsp, "[");
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               query_monitor(m, rsp);
+               if (m->next != NULL) {
+                       fprintf(rsp, ",");
+               }
+       }
+       fprintf(rsp, "]");
+       fprintf(rsp,",");
+       fprintf(rsp, "\"focusHistory\":");
+       query_history(rsp);
+       fprintf(rsp,",");
+       fprintf(rsp, "\"stackingList\":");
+       query_stack(rsp);
+       fprintf(rsp, "}");
+
+}
+
+void query_monitor(monitor_t *m, FILE *rsp)
+{
+       fprintf(rsp, "{");
+       fprintf(rsp, "\"name\":\"%s\",", m->name);
+       fprintf(rsp, "\"id\":%u,", m->id);
+       fprintf(rsp, "\"randrId\":%u,", m->randr_id);
+       fprintf(rsp, "\"wired\":%s,", BOOL_STR(m->wired));
+       fprintf(rsp, "\"stickyCount\":%i,", m->sticky_count);
+       fprintf(rsp, "\"windowGap\":%i,", m->window_gap);
+       fprintf(rsp, "\"borderWidth\":%u,", m->border_width);
+       fprintf(rsp, "\"focusedDesktopId\":%u,", m->desk->id);
+       fprintf(rsp, "\"padding\":");
+       query_padding(m->padding, rsp);
+       fprintf(rsp,",");
+       fprintf(rsp, "\"rectangle\":");
+       query_rectangle(m->rectangle, rsp);
+       fprintf(rsp,",");
+       fprintf(rsp, "\"desktops\":");
+       fprintf(rsp, "[");
+       for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+               query_desktop(d, rsp);
+               if (d->next != NULL) {
+                       fprintf(rsp,",");
+               }
+       }
+       fprintf(rsp, "]");
+       fprintf(rsp, "}");
+}
+
+void query_desktop(desktop_t *d, FILE *rsp)
+{
+       fprintf(rsp, "{");
+       fprintf(rsp, "\"name\":\"%s\",", d->name);
+       fprintf(rsp, "\"id\":%u,", d->id);
+       fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout));
+       fprintf(rsp, "\"windowGap\":%i,", d->window_gap);
+       fprintf(rsp, "\"borderWidth\":%u,", d->border_width);
+       fprintf(rsp, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0);
+       fprintf(rsp, "\"padding\":");
+       query_padding(d->padding, rsp);
+       fprintf(rsp,",");
+       fprintf(rsp, "\"root\":");
+       query_node(d->root, rsp);
+       fprintf(rsp, "}");
+}
+
+void query_node(node_t *n, FILE *rsp)
+{
+       if (n == NULL) {
+               fprintf(rsp, "null");
+       } else {
+               fprintf(rsp, "{");
+               fprintf(rsp, "\"id\":%u,", n->id);
+               fprintf(rsp, "\"splitType\":\"%s\",", SPLIT_TYPE_STR(n->split_type));
+               fprintf(rsp, "\"splitRatio\":%lf,", n->split_ratio);
+               fprintf(rsp, "\"birthRotation\":%i,", n->birth_rotation);
+               fprintf(rsp, "\"vacant\":%s,", BOOL_STR(n->vacant));
+               fprintf(rsp, "\"hidden\":%s,", BOOL_STR(n->hidden));
+               fprintf(rsp, "\"sticky\":%s,", BOOL_STR(n->sticky));
+               fprintf(rsp, "\"private\":%s,", BOOL_STR(n->private));
+               fprintf(rsp, "\"locked\":%s,", BOOL_STR(n->locked));
+               fprintf(rsp, "\"presel\":");
+               query_presel(n->presel, rsp);
+               fprintf(rsp,",");
+               fprintf(rsp, "\"rectangle\":");
+               query_rectangle(n->rectangle, rsp);
+               fprintf(rsp,",");
+               fprintf(rsp, "\"firstChild\":");
+               query_node(n->first_child, rsp);
+               fprintf(rsp,",");
+               fprintf(rsp, "\"secondChild\":");
+               query_node(n->second_child, rsp);
+               fprintf(rsp,",");
+               fprintf(rsp, "\"client\":");
+               query_client(n->client, rsp);
+               fprintf(rsp, "}");
+       }
+}
+
+void query_presel(presel_t *p, FILE *rsp)
+{
+       if (p == NULL) {
+               fprintf(rsp, "null");
+       } else {
+               fprintf(rsp, "{\"splitDir\":\"%s\",\"splitRatio\":%lf}", SPLIT_DIR_STR(p->split_dir), p->split_ratio);
+       }
+}
+
+void query_client(client_t *c, FILE *rsp)
+{
+       if (c == NULL) {
+               fprintf(rsp, "null");
+       } else {
+               fprintf(rsp, "{");
+               fprintf(rsp, "\"className\":\"%s\",", c->class_name);
+               fprintf(rsp, "\"instanceName\":\"%s\",", c->instance_name);
+               fprintf(rsp, "\"borderWidth\":%u,", c->border_width);
+               fprintf(rsp, "\"state\":\"%s\",", STATE_STR(c->state));
+               fprintf(rsp, "\"lastState\":\"%s\",", STATE_STR(c->last_state));
+               fprintf(rsp, "\"layer\":\"%s\",", LAYER_STR(c->layer));
+               fprintf(rsp, "\"lastLayer\":\"%s\",", LAYER_STR(c->last_layer));
+               fprintf(rsp, "\"urgent\":%s,", BOOL_STR(c->urgent));
+               fprintf(rsp, "\"shown\":%s,", BOOL_STR(c->shown));
+               fprintf(rsp, "\"tiledRectangle\":");
+               query_rectangle(c->tiled_rectangle, rsp);
+               fprintf(rsp,",");
+               fprintf(rsp, "\"floatingRectangle\":");
+               query_rectangle(c->floating_rectangle, rsp);
+               fprintf(rsp, "}");
+       }
+}
+
+void query_rectangle(xcb_rectangle_t r, FILE *rsp)
+{
+       fprintf(rsp, "{\"x\":%i,\"y\":%i,\"width\":%u,\"height\":%u}", r.x, r.y, r.width, r.height);
+}
+
+void query_padding(padding_t p, FILE *rsp)
+{
+       fprintf(rsp, "{\"top\":%i,\"right\":%i,\"bottom\":%i,\"left\":%i}", p.top, p.right, p.bottom, p.left);
+}
+
+void query_history(FILE *rsp)
+{
+       fprintf(rsp, "[");
+       for (history_t *h = history_head; h != NULL; h = h->next) {
+               query_coordinates(&h->loc, rsp);
+               if (h->next != NULL) {
+                       fprintf(rsp, ",");
+               }
+       }
+       fprintf(rsp, "]");
+}
+
+void query_coordinates(coordinates_t *loc, FILE *rsp)
+{
+       fprintf(rsp, "{\"monitorId\":%u,\"desktopId\":%u,\"nodeId\":%u}", loc->monitor->id, loc->desktop->id, loc->node!=NULL?loc->node->id:0);
+}
+
+void query_stack(FILE *rsp)
+{
+       fprintf(rsp, "[");
+       for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+               fprintf(rsp, "%u", s->node->id);
+               if (s->next != NULL) {
+                       fprintf(rsp, ",");
+               }
+       }
+       fprintf(rsp, "]");
+}
+
+int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
+{
+       int count = 0;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (trg->monitor != NULL && m != trg->monitor) {
+                       continue;
+               }
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       if (trg->desktop != NULL && d != trg->desktop) {
+                               continue;
+                       }
+                       count += query_node_ids_in(d->root, d, m, ref, trg, sel, rsp);
+               }
+       }
+       return count;
+}
+
+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)
+{
+       int count = 0;
+       if (n == NULL) {
+               return 0;
+       } else {
+               coordinates_t loc = {m, d, n};
+               if ((trg->node == NULL || n == trg->node) &&
+                   (sel == NULL || node_matches(&loc, ref, *sel))) {
+                       fprintf(rsp, "0x%08X\n", n->id);
+                       count++;
+               }
+               count += query_node_ids_in(n->first_child, d, m, ref, trg, sel, rsp);
+               count += query_node_ids_in(n->second_child, d, m, ref, trg, sel, rsp);
+       }
+       return count;
+}
+
+int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp)
+{
+       int count = 0;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (trg->monitor != NULL && m != trg->monitor) {
+                       continue;
+               }
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       coordinates_t loc = {m, d, NULL};
+                       if ((trg->desktop != NULL && d != trg->desktop) ||
+                           (sel != NULL && !desktop_matches(&loc, ref, *sel))) {
+                               continue;
+                       }
+                       printer(d, rsp);
+                       count++;
+               }
+       }
+       return count;
+}
+
+int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp)
+{
+       int count = 0;
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               coordinates_t loc = {m, NULL, NULL};
+               if ((trg->monitor != NULL && m != trg->monitor) ||
+                       (sel != NULL && !monitor_matches(&loc, ref, *sel))) {
+                       continue;
+               }
+               printer(m, rsp);
+               count++;
+       }
+       return count;
+}
+
+void fprint_monitor_id(monitor_t *m, FILE *rsp)
+{
+       fprintf(rsp, "0x%08X\n", m->id);
+}
+
+void fprint_monitor_name(monitor_t *m, FILE *rsp)
+{
+       fprintf(rsp, "%s\n", m->name);
+}
+
+void fprint_desktop_id(desktop_t *d, FILE *rsp)
+{
+       fprintf(rsp, "0x%08X\n", d->id);
+}
+
+void fprint_desktop_name(desktop_t *d, FILE *rsp)
+{
+       fprintf(rsp, "%s\n", d->name);
+}
+
+void print_modifier_mask(uint16_t m, FILE *rsp)
+{
+       switch (m) {
+               case XCB_MOD_MASK_SHIFT:
+                       fprintf(rsp, "shift");
+                       break;
+               case XCB_MOD_MASK_CONTROL:
+                       fprintf(rsp, "control");
+                       break;
+               case XCB_MOD_MASK_LOCK:
+                       fprintf(rsp, "lock");
+                       break;
+               case XCB_MOD_MASK_1:
+                       fprintf(rsp, "mod1");
+                       break;
+               case XCB_MOD_MASK_2:
+                       fprintf(rsp, "mod2");
+                       break;
+               case XCB_MOD_MASK_3:
+                       fprintf(rsp, "mod3");
+                       break;
+               case XCB_MOD_MASK_4:
+                       fprintf(rsp, "mod4");
+                       break;
+               case XCB_MOD_MASK_5:
+                       fprintf(rsp, "mod5");
+                       break;
+       }
+}
+
+void print_pointer_action(pointer_action_t a, FILE *rsp)
+{
+       switch (a) {
+               case ACTION_MOVE:
+                       fprintf(rsp, "move");
+                       break;
+               case ACTION_RESIZE_SIDE:
+                       fprintf(rsp, "resize_side");
+                       break;
+               case ACTION_RESIZE_CORNER:
+                       fprintf(rsp, "resize_corner");
+                       break;
+               case ACTION_FOCUS:
+                       fprintf(rsp, "focus");
+                       break;
+               case ACTION_NONE:
+                       fprintf(rsp, "none");
+                       break;
+       }
+}
+
+node_select_t make_node_select(void)
+{
+       node_select_t sel = {
+               .automatic = OPTION_NONE,
+               .focused = OPTION_NONE,
+               .local = OPTION_NONE,
+               .active = OPTION_NONE,
+               .leaf = OPTION_NONE,
+               .window = OPTION_NONE,
+               .tiled = OPTION_NONE,
+               .pseudo_tiled = OPTION_NONE,
+               .floating = OPTION_NONE,
+               .fullscreen = OPTION_NONE,
+               .hidden = OPTION_NONE,
+               .sticky = OPTION_NONE,
+               .private = OPTION_NONE,
+               .locked = OPTION_NONE,
+               .urgent = OPTION_NONE,
+               .same_class = OPTION_NONE,
+               .descendant_of = OPTION_NONE,
+               .ancestor_of = OPTION_NONE,
+               .below = OPTION_NONE,
+               .normal = OPTION_NONE,
+               .above = OPTION_NONE
+       };
+       return sel;
+}
+
+desktop_select_t make_desktop_select(void)
+{
+       desktop_select_t sel = {
+               .occupied = OPTION_NONE,
+               .focused = OPTION_NONE,
+               .urgent = OPTION_NONE,
+               .local = OPTION_NONE
+       };
+       return sel;
+}
+
+monitor_select_t make_monitor_select(void)
+{
+       monitor_select_t sel = {
+               .occupied = OPTION_NONE,
+               .focused = OPTION_NONE
+       };
+       return sel;
+}
+
+int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+       coordinates_t ref_copy = *ref;
+       ref = &ref_copy;
+       char *desc_copy = copy_string(desc, strlen(desc));
+       desc = desc_copy;
+
+       char *hash = NULL;
+       char *path = strrchr(desc, '@');
+       if (path == NULL) {
+               hash = strrchr(desc, '#');
+       } else {
+               if (path != desc && *(path - 1) == '#') {
+                       hash = path - 1;
+               }
+       }
+
+       if (hash != NULL) {
+               *hash = '\0';
+               int ret;
+               coordinates_t tmp = {mon, mon->desk, mon->desk->focus};
+               if ((ret = node_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
+                       desc = hash + 1;
+               } else {
+                       free(desc_copy);
+                       return ret;
+               }
+       }
+
+       node_select_t sel = make_node_select();
+       char *colon = strrchr(desc, ':');
+
+       if (!parse_node_modifiers(colon != NULL ? colon : desc, &sel)) {
+               free(desc_copy);
+               return SELECTOR_BAD_MODIFIERS;
+       }
+
+       dst->node = NULL;
+
+       direction_t dir;
+       cycle_dir_t cyc;
+       history_dir_t hdi;
+       if (parse_direction(desc, &dir)) {
+               find_nearest_neighbor(ref, dst, dir, sel);
+       } else if (parse_cycle_direction(desc, &cyc)) {
+               find_closest_node(ref, dst, cyc, sel);
+       } else if (parse_history_direction(desc, &hdi)) {
+               history_find_node(hdi, ref, dst, sel);
+       } else if (streq("last", desc)) {
+               history_find_node(HISTORY_OLDER, ref, dst, sel);
+       } else if (streq("biggest", desc)) {
+               find_biggest(ref, dst, sel);
+       } else if (streq("pointed", desc)) {
+               xcb_window_t win;
+               query_pointer(&win, NULL);
+               if (locate_window(win, dst) && node_matches(dst, ref, sel)) {
+                       return SELECTOR_OK;
+               } else {
+                       return SELECTOR_INVALID;
+               }
+       } else if (streq("focused", desc)) {
+               coordinates_t loc = {mon, mon->desk, mon->desk->focus};
+               if (node_matches(&loc, ref, sel)) {
+                       *dst = loc;
+               }
+       } else if (*desc == '@') {
+               desc++;
+               *dst = *ref;
+               if (colon != NULL) {
+                       *colon = '\0';
+                       int ret;
+                       if ((ret = desktop_from_desc(desc, ref, dst)) == SELECTOR_OK) {
+                               dst->node = dst->desktop->focus;
+                               desc = colon + 1;
+                       } else {
+                               free(desc_copy);
+                               return ret;
+                       }
+               }
+               if (*desc == '/') {
+                       dst->node = dst->desktop->root;
+               }
+               char *move = strtok(desc, PTH_TOK);
+               while (move != NULL && dst->node != NULL) {
+                       if (streq("first", move) || streq("1", move)) {
+                               dst->node = dst->node->first_child;
+                       } else if (streq("second", move) || streq("2", move)) {
+                               dst->node = dst->node->second_child;
+                       } else if (streq("parent", move)) {
+                               dst->node = dst->node->parent;
+                       } else if (streq("brother", move)) {
+                               dst->node = brother_tree(dst->node);
+                       } else {
+                               direction_t dir;
+                               if (parse_direction(move, &dir)) {
+                                       dst->node = find_fence(dst->node, dir);
+                               } else {
+                                       free(desc_copy);
+                                       return SELECTOR_BAD_DESCRIPTOR;
+                               }
+                       }
+                       move = strtok(NULL, PTH_TOK);
+               }
+               free(desc_copy);
+               if (dst->node != NULL) {
+                       if (node_matches(dst, ref, sel)) {
+                               return SELECTOR_OK;
+                       } else {
+                               return SELECTOR_INVALID;
+                       }
+               } else if (dst->desktop->root != NULL) {
+                       return SELECTOR_INVALID;
+               }
+               return SELECTOR_OK;
+       } else {
+               uint32_t id;
+               if (parse_id(desc, &id)) {
+                       free(desc_copy);
+                       if (find_by_id(id, dst) && node_matches(dst, ref, sel)) {
+                               return SELECTOR_OK;
+                       } else {
+                               return SELECTOR_INVALID;
+                       }
+               } else {
+                       free(desc_copy);
+                       return SELECTOR_BAD_DESCRIPTOR;
+               }
+       }
+
+       free(desc_copy);
+
+       if (dst->node == NULL) {
+               return SELECTOR_INVALID;
+       }
+
+       return SELECTOR_OK;
+}
+
+int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+       coordinates_t ref_copy = *ref;
+       ref = &ref_copy;
+       char *desc_copy = copy_string(desc, strlen(desc));
+       desc = desc_copy;
+
+       char *hash = strrchr(desc, '#');
+
+       if (hash != NULL) {
+               *hash = '\0';
+               int ret;
+               coordinates_t tmp = {mon, mon->desk, NULL};
+               if ((ret = desktop_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
+                       desc = hash + 1;
+               } else {
+                       free(desc_copy);
+                       return ret;
+               }
+       }
+
+       desktop_select_t sel = make_desktop_select();
+       char *colon = strrchr(desc, ':');
+
+       if (!parse_desktop_modifiers(colon != NULL ? colon : desc, &sel)) {
+               free(desc_copy);
+               return SELECTOR_BAD_MODIFIERS;
+       }
+
+       dst->desktop = NULL;
+
+       cycle_dir_t cyc;
+       history_dir_t hdi;
+       uint16_t idx;
+       uint32_t id;
+       if (parse_cycle_direction(desc, &cyc)) {
+               find_closest_desktop(ref, dst, cyc, sel);
+       } else if (parse_history_direction(desc, &hdi)) {
+               history_find_desktop(hdi, ref, dst, sel);
+       } else if (streq("last", desc)) {
+               history_find_desktop(HISTORY_OLDER, ref, dst, sel);
+       } else if (streq("focused", desc)) {
+               coordinates_t loc = {mon, mon->desk, NULL};
+               if (desktop_matches(&loc, ref, sel)) {
+                       *dst = loc;
+               }
+       } else if (colon != NULL) {
+               *colon = '\0';
+               int ret;
+               if ((ret = monitor_from_desc(desc, ref, dst)) == SELECTOR_OK) {
+                       if (streq("focused", colon + 1)) {
+                               coordinates_t loc = {dst->monitor, dst->monitor->desk, NULL};
+                               if (desktop_matches(&loc, ref, sel)) {
+                                       *dst = loc;
+                               }
+                       } else if (parse_index(colon + 1, &idx)) {
+                               free(desc_copy);
+                               if (desktop_from_index(idx, dst, dst->monitor) && desktop_matches(dst, ref, sel)) {
+                                       return SELECTOR_OK;
+                               } else {
+                                       return SELECTOR_INVALID;
+                               }
+                       } else {
+                               free(desc_copy);
+                               return SELECTOR_BAD_DESCRIPTOR;
+                       }
+               } else {
+                       free(desc_copy);
+                       return ret;
+               }
+       } else if (parse_index(desc, &idx) && desktop_from_index(idx, dst, NULL)) {
+               free(desc_copy);
+               if (desktop_matches(dst, ref, sel)) {
+                       return SELECTOR_OK;
+               } else {
+                       return SELECTOR_INVALID;
+               }
+       } else if (parse_id(desc, &id) && desktop_from_id(id, dst, NULL)) {
+               free(desc_copy);
+               if (desktop_matches(dst, ref, sel)) {
+                       return SELECTOR_OK;
+               } else {
+                       return SELECTOR_INVALID;
+               }
+       } else {
+               if (locate_desktop(desc, dst)) {
+                       free(desc_copy);
+                       if (desktop_matches(dst, ref, sel)) {
+                               return SELECTOR_OK;
+                       } else {
+                               return SELECTOR_INVALID;
+                       }
+               } else {
+                       free(desc_copy);
+                       return SELECTOR_BAD_DESCRIPTOR;
+               }
+       }
+
+       free(desc_copy);
+
+       if (dst->desktop == NULL) {
+               return SELECTOR_INVALID;
+       }
+
+       return SELECTOR_OK;
+}
+
+int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+       coordinates_t ref_copy = *ref;
+       ref = &ref_copy;
+       char *desc_copy = copy_string(desc, strlen(desc));
+       desc = desc_copy;
+
+       char *hash = strrchr(desc, '#');
+
+       if (hash != NULL) {
+               *hash = '\0';
+               int ret;
+               coordinates_t tmp = {mon, NULL, NULL};
+               if ((ret = monitor_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
+                       desc = hash + 1;
+               } else {
+                       free(desc_copy);
+                       return ret;
+               }
+       }
+
+       monitor_select_t sel = make_monitor_select();
+
+       if (!parse_monitor_modifiers(desc, &sel)) {
+               free(desc_copy);
+               return SELECTOR_BAD_MODIFIERS;
+       }
+
+       dst->monitor = NULL;
+
+       direction_t dir;
+       cycle_dir_t cyc;
+       history_dir_t hdi;
+       uint16_t idx;
+       uint32_t id;
+       if (parse_direction(desc, &dir)) {
+               dst->monitor = nearest_monitor(ref->monitor, dir, sel);
+       } else if (parse_cycle_direction(desc, &cyc)) {
+               dst->monitor = closest_monitor(ref->monitor, cyc, sel);
+       } else if (parse_history_direction(desc, &hdi)) {
+               history_find_monitor(hdi, ref, dst, sel);
+       } else if (streq("last", desc)) {
+               history_find_monitor(HISTORY_OLDER, ref, dst, sel);
+       } else if (streq("primary", desc)) {
+               if (pri_mon != NULL) {
+                       coordinates_t loc = {pri_mon, NULL, NULL};
+                       if (monitor_matches(&loc, ref, sel)) {
+                               dst->monitor = pri_mon;
+                       }
+               }
+       } else if (streq("focused", desc)) {
+               coordinates_t loc = {mon, NULL, NULL};
+               if (monitor_matches(&loc, ref, sel)) {
+                       dst->monitor = mon;
+               }
+       } else if (parse_index(desc, &idx) && monitor_from_index(idx, dst)) {
+               free(desc_copy);
+               if (monitor_matches(dst, ref, sel)) {
+                       return SELECTOR_OK;
+               } else {
+                       return SELECTOR_INVALID;
+               }
+       } else if (parse_id(desc, &id) && monitor_from_id(id, dst)) {
+               free(desc_copy);
+               if (monitor_matches(dst, ref, sel)) {
+                       return SELECTOR_OK;
+               } else {
+                       return SELECTOR_INVALID;
+               }
+       } else {
+               if (locate_monitor(desc, dst)) {
+                       free(desc_copy);
+                       if (monitor_matches(dst, ref, sel)) {
+                               return SELECTOR_OK;
+                       } else {
+                               return SELECTOR_INVALID;
+                       }
+               } else {
+                       free(desc_copy);
+                       return SELECTOR_BAD_DESCRIPTOR;
+               }
+       }
+
+       free(desc_copy);
+
+       if (dst->monitor == NULL) {
+               return SELECTOR_INVALID;
+       }
+
+       return SELECTOR_OK;
+}
+
+bool locate_window(xcb_window_t win, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                               if (n->client == NULL) {
+                                       continue;
+                               }
+                               if (n->id == win) {
+                                       loc->monitor = m;
+                                       loc->desktop = d;
+                                       loc->node = n;
+                                       return true;
+                               }
+                       }
+               }
+       }
+       return false;
+}
+
+bool locate_desktop(char *name, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       if (streq(d->name, name)) {
+                               loc->monitor = m;
+                               loc->desktop = d;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+bool locate_monitor(char *name, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (streq(m->name, name)) {
+                       loc->monitor = m;
+                       return true;
+               }
+       }
+       return false;
+}
+
+bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (mm != NULL && m != mm) {
+                       continue;
+               }
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       if (d->id == id) {
+                               loc->monitor = m;
+                               loc->desktop = d;
+                               loc->node = NULL;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (mm != NULL && m != mm) {
+                       continue;
+               }
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next, idx--) {
+                       if (idx == 1) {
+                               loc->monitor = m;
+                               loc->desktop = d;
+                               loc->node = NULL;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+bool monitor_from_id(uint32_t id, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               if (m->id == id) {
+                       loc->monitor = m;
+                       loc->desktop = NULL;
+                       loc->node = NULL;
+                       return true;
+               }
+       }
+       return false;
+}
+
+bool monitor_from_index(int idx, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next, idx--) {
+               if (idx == 1) {
+                       loc->monitor = m;
+                       loc->desktop = NULL;
+                       loc->node = NULL;
+                       return true;
+               }
+       }
+       return false;
+}
+
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
+{
+       if (loc->node == NULL) {
+               return false;
+       }
+
+       if (sel.focused != OPTION_NONE &&
+           loc->node != loc->desktop->focus
+           ? sel.focused == OPTION_TRUE
+           : sel.focused == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.automatic != OPTION_NONE &&
+           loc->node->presel != NULL
+           ? sel.automatic == OPTION_TRUE
+           : sel.automatic == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.local != OPTION_NONE &&
+           loc->desktop != ref->desktop
+           ? sel.local == OPTION_TRUE
+           : sel.local == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.active != OPTION_NONE &&
+           loc->desktop != loc->monitor->desk
+           ? sel.active == OPTION_TRUE
+           : sel.active == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.leaf != OPTION_NONE &&
+           !is_leaf(loc->node)
+           ? sel.leaf == OPTION_TRUE
+           : sel.leaf == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.window != OPTION_NONE &&
+           loc->node->client == NULL
+           ? sel.window == OPTION_TRUE
+           : sel.window == OPTION_FALSE) {
+               return false;
+       }
+
+#define NFLAG(p) \
+       if (sel.p != OPTION_NONE && \
+           !loc->node->p \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       NFLAG(hidden)
+       NFLAG(sticky)
+       NFLAG(private)
+       NFLAG(locked)
+#undef WFLAG
+
+       if (loc->node->client == NULL &&
+               (sel.same_class != OPTION_NONE ||
+                sel.tiled != OPTION_NONE ||
+                sel.pseudo_tiled != OPTION_NONE ||
+                sel.floating != OPTION_NONE ||
+                sel.fullscreen != OPTION_NONE ||
+                sel.below != OPTION_NONE ||
+                sel.normal != OPTION_NONE ||
+                sel.above != OPTION_NONE ||
+                sel.urgent != OPTION_NONE)) {
+               return false;
+       }
+
+       if (ref->node != NULL && ref->node->client != NULL &&
+           sel.same_class != OPTION_NONE &&
+           streq(loc->node->client->class_name, ref->node->client->class_name)
+           ? sel.same_class == OPTION_FALSE
+           : sel.same_class == OPTION_TRUE) {
+               return false;
+       }
+
+       if (sel.descendant_of != OPTION_NONE &&
+           !is_descendant(loc->node, ref->node)
+           ? sel.descendant_of == OPTION_TRUE
+           : sel.descendant_of == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.ancestor_of != OPTION_NONE &&
+           !is_descendant(ref->node, loc->node)
+           ? sel.ancestor_of == OPTION_TRUE
+           : sel.ancestor_of == OPTION_FALSE) {
+               return false;
+       }
+
+#define WSTATE(p, e) \
+       if (sel.p != OPTION_NONE && \
+           loc->node->client->state != e \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       WSTATE(tiled, STATE_TILED)
+       WSTATE(pseudo_tiled, STATE_PSEUDO_TILED)
+       WSTATE(floating, STATE_FLOATING)
+       WSTATE(fullscreen, STATE_FULLSCREEN)
+#undef WSTATE
+
+#define WLAYER(p, e) \
+       if (sel.p != OPTION_NONE && \
+           loc->node->client->layer != e \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       WLAYER(below, LAYER_BELOW)
+       WLAYER(normal, LAYER_NORMAL)
+       WLAYER(above, LAYER_ABOVE)
+#undef WLAYER
+
+#define WFLAG(p) \
+       if (sel.p != OPTION_NONE && \
+           !loc->node->client->p \
+           ? sel.p == OPTION_TRUE \
+           : sel.p == OPTION_FALSE) { \
+               return false; \
+       }
+       WFLAG(urgent)
+#undef WFLAG
+
+       return true;
+}
+
+bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel)
+{
+       if (sel.occupied != OPTION_NONE &&
+           loc->desktop->root == NULL
+           ? sel.occupied == OPTION_TRUE
+           : sel.occupied == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.focused != OPTION_NONE &&
+           loc->desktop != loc->monitor->desk
+           ? sel.focused == OPTION_TRUE
+           : sel.focused == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.urgent != OPTION_NONE &&
+           !is_urgent(loc->desktop)
+           ? sel.urgent == OPTION_TRUE
+           : sel.urgent == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.local != OPTION_NONE &&
+           ref->monitor != loc->monitor
+           ? sel.local == OPTION_TRUE
+           : sel.local == OPTION_FALSE) {
+               return false;
+       }
+
+       return true;
+}
+
+bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel)
+{
+       if (sel.occupied != OPTION_NONE &&
+           loc->monitor->desk->root == NULL
+           ? sel.occupied == OPTION_TRUE
+           : sel.occupied == OPTION_FALSE) {
+               return false;
+       }
+
+       if (sel.focused != OPTION_NONE &&
+           mon != loc->monitor
+           ? sel.focused == OPTION_TRUE
+           : sel.focused == OPTION_FALSE) {
+               return false;
+       }
+
+       return true;
+}
diff --git a/src/query.h b/src/query.h
new file mode 100644 (file)
index 0000000..2ff20d7
--- /dev/null
@@ -0,0 +1,85 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_QUERY_H
+#define BSPWM_QUERY_H
+
+#define PTH_TOK  "/"
+
+typedef enum {
+       DOMAIN_TREE,
+       DOMAIN_MONITOR,
+       DOMAIN_DESKTOP,
+       DOMAIN_NODE
+} domain_t;
+
+enum {
+       SELECTOR_OK,
+       SELECTOR_INVALID,
+       SELECTOR_BAD_MODIFIERS,
+       SELECTOR_BAD_DESCRIPTOR
+};
+
+typedef void (*monitor_printer_t)(monitor_t *m, FILE *rsp);
+typedef void (*desktop_printer_t)(desktop_t *m, FILE *rsp);
+
+void query_tree(FILE *rsp);
+void query_monitor(monitor_t *m, FILE *rsp);
+void query_desktop(desktop_t *d, FILE *rsp);
+void query_node(node_t *n, FILE *rsp);
+void query_presel(presel_t *p, FILE *rsp);
+void query_client(client_t *c, FILE *rsp);
+void query_rectangle(xcb_rectangle_t r, FILE *rsp);
+void query_padding(padding_t p, FILE *rsp);
+void query_history(FILE *rsp);
+void query_coordinates(coordinates_t *loc, FILE *rsp);
+void query_stack(FILE *rsp);
+int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
+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);
+int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp);
+int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp);
+void fprint_monitor_id(monitor_t *m, FILE *rsp);
+void fprint_monitor_name(monitor_t *m, FILE *rsp);
+void fprint_desktop_id(desktop_t *d, FILE *rsp);
+void fprint_desktop_name(desktop_t *d, FILE *rsp);
+void print_modifier_mask(uint16_t m, FILE *rsp);
+void print_pointer_action(pointer_action_t a, FILE *rsp);
+node_select_t make_node_select(void);
+desktop_select_t make_desktop_select(void);
+monitor_select_t make_monitor_select(void);
+int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+bool locate_window(xcb_window_t win, coordinates_t *loc);
+bool locate_desktop(char *name, coordinates_t *loc);
+bool locate_monitor(char *name, coordinates_t *loc);
+bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm);
+bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm);
+bool monitor_from_id(uint32_t id, coordinates_t *loc);
+bool monitor_from_index(int idx, coordinates_t *loc);
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel);
+bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
+bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel);
+
+#endif
diff --git a/src/restore.c b/src/restore.c
new file mode 100644 (file)
index 0000000..de51f6c
--- /dev/null
@@ -0,0 +1,562 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "ewmh.h"
+#include "history.h"
+#include "pointer.h"
+#include "monitor.h"
+#include "query.h"
+#include "stack.h"
+#include "tree.h"
+#include "settings.h"
+#include "restore.h"
+#include "window.h"
+#include "parse.h"
+
+bool restore_tree(const char *file_path)
+{
+       size_t jslen;
+       char *json = read_string(file_path, &jslen);
+
+       if (json == NULL) {
+               return false;
+       }
+
+       int nbtok = 256;
+       jsmn_parser parser;
+       jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
+
+       if (tokens == NULL) {
+               perror("Restore tree: malloc");
+               free(json);
+               return false;
+       }
+
+       jsmn_init(&parser);
+       int ret;
+
+       while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
+               nbtok *= 2;
+               jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
+               if (rtokens == NULL) {
+                       perror("Restore tree: realloc");
+                       free(tokens);
+                       free(json);
+                       return false;
+               } else {
+                       tokens = rtokens;
+               }
+       }
+
+       if (ret < 0) {
+               warn("Restore tree: jsmn_parse: ");
+               switch (ret) {
+                       case JSMN_ERROR_NOMEM:
+                               warn("not enough memory.\n");
+                               break;
+                       case JSMN_ERROR_INVAL:
+                               warn("found invalid character inside JSON string.\n");
+                               break;
+                       case JSMN_ERROR_PART:
+                               warn("not a full JSON packet.\n");
+                               break;
+                       default:
+                               warn("unknown error.\n");
+                               break;
+               }
+
+               free(tokens);
+               free(json);
+
+               return false;
+       }
+
+       int num = tokens[0].size;
+
+       if (num < 1) {
+               free(tokens);
+               free(json);
+
+               return false;
+       }
+
+       mon = NULL;
+       while (mon_head != NULL) {
+               remove_monitor(mon_head);
+       }
+
+       jsmntok_t *t = tokens + 1;
+       uint32_t focused_monitor_id = 0, primary_monitor_id = 0;
+
+       for (int i = 0; i < num; i++) {
+               if (keyeq("focusedMonitorId", t, json)) {
+                       t++;
+                       sscanf(json + t->start, "%u", &focused_monitor_id);
+               } else if (keyeq("primaryMonitorId", t, json)) {
+                       t++;
+                       sscanf(json + t->start, "%u", &primary_monitor_id);
+               } else if (keyeq("clientsCount", t, json)) {
+                       t++;
+                       sscanf(json + t->start, "%u", &clients_count);
+               } else if (keyeq("monitors", t, json)) {
+                       t++;
+                       int s = t->size;
+                       t++;
+                       for (int j = 0; j < s; j++) {
+                               monitor_t *m = restore_monitor(&t, json);
+                               if (m->desk == NULL) {
+                                       add_desktop(m, make_desktop(NULL, XCB_NONE));
+                               }
+                               add_monitor(m);
+                       }
+                       continue;
+               } else if (keyeq("focusHistory", t, json)) {
+                       t++;
+                       restore_history(&t, json);
+                       continue;
+               } else if (keyeq("stackingList", t, json)) {
+                       t++;
+                       restore_stack(&t, json);
+                       continue;
+               }
+               t++;
+       }
+
+       if (focused_monitor_id != 0) {
+               coordinates_t loc;
+               if (monitor_from_id(focused_monitor_id, &loc)) {
+                       mon = loc.monitor;
+               }
+       }
+
+       if (primary_monitor_id != 0) {
+               coordinates_t loc;
+               if (monitor_from_id(primary_monitor_id, &loc)) {
+                       pri_mon = loc.monitor;
+               }
+       }
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       refresh_presel_feedbacks(m, d, d->root);
+                       restack_presel_feedbacks(d);
+                       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+                               if (n->client == NULL) {
+                                       continue;
+                               }
+                               initialize_client(n);
+                               uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+                               xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
+                               window_grab_buttons(n->id);
+                       }
+               }
+       }
+
+       ewmh_update_client_list(false);
+       ewmh_update_client_list(true);
+       ewmh_update_active_window();
+       ewmh_update_number_of_desktops();
+       ewmh_update_current_desktop();
+       ewmh_update_desktop_names();
+       ewmh_update_desktop_viewport();
+
+       free(tokens);
+       free(json);
+
+       return true;
+}
+
+#define RESTORE_INT(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%i", p);
+
+#define RESTORE_UINT(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%u", p);
+
+#define RESTORE_USINT(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%hu", p);
+
+#define RESTORE_DOUBLE(k, p) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               sscanf(json + (*t)->start, "%lf", p);
+
+#define RESTORE_ANY(k, p, f) \
+       } else if (keyeq(#k, *t, json)) { \
+               (*t)++; \
+               char *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \
+               f(val, p); \
+               free(val);
+
+#define RESTORE_BOOL(k, p)  RESTORE_ANY(k, p, parse_bool)
+
+monitor_t *restore_monitor(jsmntok_t **t, char *json)
+{
+       int num = (*t)->size;
+       (*t)++;
+       monitor_t *m = make_monitor(NULL, NULL, UINT32_MAX);
+       uint32_t focused_desktop_id = 0;
+
+       for (int i = 0; i < num; i++) {
+               if (keyeq("name", *t, json)) {
+                       (*t)++;
+                       snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+               RESTORE_UINT(id, &m->id)
+               RESTORE_UINT(randrId, &m->randr_id)
+               RESTORE_BOOL(wired, &m->wired)
+               RESTORE_UINT(stickyCount, &m->sticky_count)
+               RESTORE_INT(windowGap, &m->window_gap)
+               RESTORE_UINT(borderWidth, &m->border_width)
+               RESTORE_UINT(focusedDesktopId, &focused_desktop_id)
+               } else if (keyeq("padding", *t, json)) {
+                       (*t)++;
+                       restore_padding(&m->padding, t, json);
+                       continue;
+               } else if (keyeq("rectangle", *t, json)) {
+                       (*t)++;
+                       restore_rectangle(&m->rectangle, t, json);
+                       update_root(m, &m->rectangle);
+                       continue;
+               } else if (keyeq("desktops", *t, json)) {
+                       (*t)++;
+                       int s = (*t)->size;
+                       (*t)++;
+                       for (int j = 0; j < s; j++) {
+                               desktop_t *d = restore_desktop(t, json);
+                               add_desktop(m, d);
+                       }
+                       continue;
+               } else {
+                       warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                       (*t)++;
+               }
+               (*t)++;
+       }
+
+       if (focused_desktop_id != 0) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       if (d->id == focused_desktop_id) {
+                               m->desk = d;
+                               break;
+                       }
+               }
+       }
+
+       return m;
+}
+
+desktop_t *restore_desktop(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+       desktop_t *d = make_desktop(NULL, UINT32_MAX);
+       xcb_window_t focusedNodeId = XCB_NONE;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("name", *t, json)) {
+                       (*t)++;
+                       snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+               RESTORE_UINT(id, &d->id)
+               RESTORE_ANY(layout, &d->layout, parse_layout)
+               RESTORE_INT(windowGap, &d->window_gap)
+               RESTORE_UINT(borderWidth, &d->border_width)
+               } else if (keyeq("focusedNodeId", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%u", &focusedNodeId);
+               } else if (keyeq("padding", *t, json)) {
+                       (*t)++;
+                       restore_padding(&d->padding, t, json);
+                       continue;
+               } else if (keyeq("root", *t, json)) {
+                       (*t)++;
+                       d->root = restore_node(t, json);
+                       continue;
+               } else {
+                       warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                       (*t)++;
+               }
+               (*t)++;
+       }
+
+       if (focusedNodeId != XCB_NONE) {
+               d->focus = find_by_id_in(d->root, focusedNodeId);
+       }
+
+       return d;
+}
+
+node_t *restore_node(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               /* hack to prevent a new ID from being generated */
+               node_t *n = make_node(UINT32_MAX);
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("id", *t, json)) {
+                               (*t)++;
+                               sscanf(json + (*t)->start, "%u", &n->id);
+                       RESTORE_ANY(splitType, &n->split_type, parse_split_type)
+                       RESTORE_DOUBLE(splitRatio, &n->split_ratio)
+                       RESTORE_INT(birthRotation, &n->birth_rotation)
+                       RESTORE_BOOL(vacant, &n->vacant)
+                       RESTORE_BOOL(hidden, &n->hidden)
+                       RESTORE_BOOL(sticky, &n->sticky)
+                       RESTORE_BOOL(private, &n->private)
+                       RESTORE_BOOL(locked, &n->locked)
+                       } else if (keyeq("presel", *t, json)) {
+                               (*t)++;
+                               n->presel = restore_presel(t, json);
+                               continue;
+                       } else if (keyeq("rectangle", *t, json)) {
+                               (*t)++;
+                               restore_rectangle(&n->rectangle, t, json);
+                               continue;
+                       } else if (keyeq("firstChild", *t, json)) {
+                               (*t)++;
+                               node_t *fc = restore_node(t, json);
+                               n->first_child = fc;
+                               if (fc != NULL) {
+                                       fc->parent = n;
+                               }
+                               continue;
+                       } else if (keyeq("secondChild", *t, json)) {
+                               (*t)++;
+                               node_t *sc = restore_node(t, json);
+                               n->second_child = sc;
+                               if (sc != NULL) {
+                                       sc->parent = n;
+                               }
+                               continue;
+                       } else if (keyeq("client", *t, json)) {
+                               (*t)++;
+                               n->client = restore_client(t, json);
+                               continue;
+                       } else {
+                               warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                               (*t)++;
+                       }
+                       (*t)++;
+               }
+
+               return n;
+       }
+}
+
+presel_t *restore_presel(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               presel_t *p = make_presel();
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("splitRatio", *t, json)) {
+                               (*t)++;
+                               sscanf(json + (*t)->start, "%lf", &p->split_ratio);
+                       RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
+                       }
+
+                       (*t)++;
+               }
+
+               return p;
+       }
+}
+
+
+client_t *restore_client(jsmntok_t **t, char *json)
+{
+       if ((*t)->type == JSMN_PRIMITIVE) {
+               (*t)++;
+               return NULL;
+       } else {
+               int s = (*t)->size;
+               (*t)++;
+               client_t *c = make_client();
+
+               for (int i = 0; i < s; i++) {
+                       if (keyeq("className", *t, json)) {
+                               (*t)++;
+                               snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+                       } else if (keyeq("instanceName", *t, json)) {
+                               (*t)++;
+                               snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+                       RESTORE_ANY(state, &c->state, parse_client_state)
+                       RESTORE_ANY(lastState, &c->last_state, parse_client_state)
+                       RESTORE_ANY(layer, &c->layer, parse_stack_layer)
+                       RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
+                       RESTORE_UINT(borderWidth, &c->border_width)
+                       RESTORE_BOOL(urgent, &c->urgent)
+                       RESTORE_BOOL(shown, &c->shown)
+                       } else if (keyeq("tiledRectangle", *t, json)) {
+                               (*t)++;
+                               restore_rectangle(&c->tiled_rectangle, t, json);
+                               continue;
+                       } else if (keyeq("floatingRectangle", *t, json)) {
+                               (*t)++;
+                               restore_rectangle(&c->floating_rectangle, t, json);
+                               continue;
+                       } else {
+                               warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+                               (*t)++;
+                       }
+
+                       (*t)++;
+               }
+
+               return c;
+       }
+}
+
+void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("x", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hi", &r->x);
+               } else if (keyeq("y", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hi", &r->y);
+               } else if (keyeq("width", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hu", &r->width);
+               } else if (keyeq("height", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%hu", &r->height);
+               }
+               (*t)++;
+       }
+}
+
+void restore_padding(padding_t *p, jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("top", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->top);
+               } else if (keyeq("right", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->right);
+               } else if (keyeq("bottom", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->bottom);
+               } else if (keyeq("left", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%i", &p->left);
+               }
+               (*t)++;
+       }
+}
+
+void restore_history(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               coordinates_t loc = {NULL, NULL, NULL};
+               restore_coordinates(&loc, t, json);
+               if (loc.monitor != NULL && loc.desktop != NULL) {
+                       history_add(loc.monitor, loc.desktop, loc.node);
+               }
+       }
+}
+
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+       uint32_t id = 0;
+
+       for (int i = 0; i < s; i++) {
+               if (keyeq("monitorId", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%u", &id);
+                       loc->monitor = find_monitor(id);
+               } else if (keyeq("desktopId", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%u", &id);
+                       loc->desktop = find_desktop_in(id, loc->monitor);
+               } else if (keyeq("nodeId", *t, json)) {
+                       (*t)++;
+                       sscanf(json + (*t)->start, "%u", &id);
+                       loc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id);
+               }
+               (*t)++;
+       }
+}
+
+void restore_stack(jsmntok_t **t, char *json)
+{
+       int s = (*t)->size;
+       (*t)++;
+
+       for (int i = 0; i < s; i++) {
+               uint32_t id;
+               sscanf(json + (*t)->start, "%u", &id);
+               coordinates_t loc;
+               if (locate_window(id, &loc)) {
+                       stack_insert_after(stack_tail, loc.node);
+               }
+               (*t)++;
+       }
+}
+
+#undef RESTORE_INT
+#undef RESTORE_UINT
+#undef RESTORE_USINT
+#undef RESTORE_DOUBLE
+#undef RESTORE_ANY
+#undef RESTORE_BOOL
+
+bool keyeq(char *s, jsmntok_t *key, char *json)
+{
+       size_t n = key->end - key->start;
+       return (strlen(s) == n && strncmp(s, json + key->start, n) == 0);
+}
diff --git a/src/restore.h b/src/restore.h
new file mode 100644 (file)
index 0000000..0420ec5
--- /dev/null
@@ -0,0 +1,44 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_RESTORE_H
+#define BSPWM_RESTORE_H
+
+#include "jsmn.h"
+
+bool restore_tree(const char *file_path);
+monitor_t *restore_monitor(jsmntok_t **t, char *json);
+desktop_t *restore_desktop(jsmntok_t **t, char *json);
+node_t *restore_node(jsmntok_t **t, char *json);
+presel_t *restore_presel(jsmntok_t **t, char *json);
+client_t *restore_client(jsmntok_t **t, char *json);
+void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json);
+void restore_padding(padding_t *p, jsmntok_t **t, char *json);
+void restore_history(jsmntok_t **t, char *json);
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);
+void restore_stack(jsmntok_t **t, char *json);
+void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json);
+bool keyeq(char *s, jsmntok_t *key, char *json);
+
+#endif
diff --git a/src/rule.c b/src/rule.c
new file mode 100644 (file)
index 0000000..75074e3
--- /dev/null
@@ -0,0 +1,367 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "window.h"
+#include "parse.h"
+#include "settings.h"
+#include "rule.h"
+
+rule_t *make_rule(void)
+{
+       rule_t *r = calloc(1, sizeof(rule_t));
+       r->class_name[0] = r->instance_name[0] = r->effect[0] = '\0';
+       r->next = r->prev = NULL;
+       r->one_shot = false;
+       return r;
+}
+
+void add_rule(rule_t *r)
+ {
+       if (rule_head == NULL) {
+               rule_head = rule_tail = r;
+       } else {
+               rule_tail->next = r;
+               r->prev = rule_tail;
+               rule_tail = r;
+       }
+}
+
+void remove_rule(rule_t *r)
+{
+       if (r == NULL)
+               return;
+       rule_t *prev = r->prev;
+       rule_t *next = r->next;
+       if (prev != NULL)
+               prev->next = next;
+       if (next != NULL)
+               next->prev = prev;
+       if (r == rule_head)
+               rule_head = next;
+       if (r == rule_tail)
+               rule_tail = prev;
+       free(r);
+}
+
+void remove_rule_by_cause(char *cause)
+{
+       rule_t *r = rule_head;
+       char *class_name = strtok(cause, COL_TOK);
+       char *instance_name = strtok(NULL, COL_TOK);
+       while (r != NULL) {
+               rule_t *next = r->next;
+               if ((streq(class_name, MATCH_ANY) || streq(r->class_name, class_name)) &&
+                   (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name))) {
+                       remove_rule(r);
+               }
+               r = next;
+       }
+}
+
+bool remove_rule_by_index(int idx)
+{
+       for (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {
+               if (idx == 0) {
+                       remove_rule(r);
+                       return true;
+               }
+       }
+       return false;
+}
+
+rule_consequence_t *make_rule_conquence(void)
+{
+       rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
+       rc->manage = rc->focus = rc->border = true;
+       rc->layer = NULL;
+       rc->state = NULL;
+       return rc;
+}
+
+pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
+{
+       pending_rule_t *pr = calloc(1, sizeof(pending_rule_t));
+       pr->prev = pr->next = NULL;
+       pr->fd = fd;
+       pr->win = win;
+       pr->csq = csq;
+       return pr;
+}
+
+void add_pending_rule(pending_rule_t *pr)
+{
+       if (pr == NULL) {
+               return;
+       }
+       if (pending_rule_head == NULL) {
+               pending_rule_head = pending_rule_tail = pr;
+       } else {
+               pending_rule_tail->next = pr;
+               pr->prev = pending_rule_tail;
+               pending_rule_tail = pr;
+       }
+}
+
+void remove_pending_rule(pending_rule_t *pr)
+{
+       if (pr == NULL) {
+               return;
+       }
+       pending_rule_t *a = pr->prev;
+       pending_rule_t *b = pr->next;
+       if (a != NULL) {
+               a->next = b;
+       }
+       if (b != NULL) {
+               b->prev = a;
+       }
+       if (pr == pending_rule_head) {
+               pending_rule_head = b;
+       }
+       if (pr == pending_rule_tail) {
+               pending_rule_tail = a;
+       }
+       close(pr->fd);
+       free(pr->csq);
+       free(pr);
+}
+
+void apply_rules(xcb_window_t win, rule_consequence_t *csq)
+{
+       xcb_ewmh_get_atoms_reply_t win_type;
+
+       if (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {
+               for (unsigned int i = 0; i < win_type.atoms_len; i++) {
+                       xcb_atom_t a = win_type.atoms[i];
+                       if (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR ||
+                           a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {
+                               csq->focus = false;
+                       } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {
+                               if (csq->state == NULL) {
+                                       csq->state = calloc(1, sizeof(client_state_t));
+                               }
+                               *(csq->state) = STATE_FLOATING;
+                               csq->center = true;
+                       } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK ||
+                                  a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP ||
+                                  a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {
+                               csq->manage = false;
+                               if (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP) {
+                                       window_lower(win);
+                               }
+                       }
+               }
+               xcb_ewmh_get_atoms_reply_wipe(&win_type);
+       }
+
+       xcb_ewmh_get_atoms_reply_t win_state;
+
+       if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {
+               for (unsigned int i = 0; i < win_state.atoms_len; i++) {
+                       xcb_atom_t a = win_state.atoms[i];
+                       if (a == ewmh->_NET_WM_STATE_FULLSCREEN) {
+                               if (csq->state == NULL) {
+                                       csq->state = calloc(1, sizeof(client_state_t));
+                               }
+                               *(csq->state) = STATE_FULLSCREEN;
+                       } else if (a == ewmh->_NET_WM_STATE_BELOW) {
+                               if (csq->layer == NULL) {
+                                       csq->layer = calloc(1, sizeof(stack_layer_t));
+                               }
+                               *(csq->layer) = LAYER_BELOW;
+                       } else if (a == ewmh->_NET_WM_STATE_ABOVE) {
+                               if (csq->layer == NULL) {
+                                       csq->layer = calloc(1, sizeof(stack_layer_t));
+                               }
+                               *(csq->layer) = LAYER_ABOVE;
+                       } else if (a == ewmh->_NET_WM_STATE_STICKY) {
+                               csq->sticky = true;
+                       }
+               }
+               xcb_ewmh_get_atoms_reply_wipe(&win_state);
+       }
+
+       xcb_window_t transient_for = XCB_NONE;
+       xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
+       if (transient_for != XCB_NONE) {
+               if (csq->state == NULL) {
+                       csq->state = calloc(1, sizeof(client_state_t));
+               }
+               *(csq->state) = STATE_FLOATING;
+       }
+
+       xcb_size_hints_t size_hints;
+       if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {
+               if ((size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE|XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) &&
+                   size_hints.min_width == size_hints.max_width && size_hints.min_height == size_hints.max_height) {
+                       if (csq->state == NULL) {
+                               csq->state = calloc(1, sizeof(client_state_t));
+                       }
+                       *(csq->state) = STATE_FLOATING;
+               }
+       }
+
+       xcb_icccm_get_wm_class_reply_t reply;
+       if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
+               snprintf(csq->class_name, sizeof(csq->class_name), "%s", reply.class_name);
+               snprintf(csq->instance_name, sizeof(csq->instance_name), "%s", reply.instance_name);
+               xcb_icccm_get_wm_class_reply_wipe(&reply);
+       }
+
+       rule_t *rule = rule_head;
+       while (rule != NULL) {
+               rule_t *next = rule->next;
+               if ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&
+                   (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name))) {
+                       char effect[MAXLEN];
+                       snprintf(effect, sizeof(effect), "%s", rule->effect);
+                       char *key = strtok(effect, CSQ_BLK);
+                       char *value = strtok(NULL, CSQ_BLK);
+                       while (key != NULL && value != NULL) {
+                               parse_key_value(key, value, csq);
+                               key = strtok(NULL, CSQ_BLK);
+                               value = strtok(NULL, CSQ_BLK);
+                       }
+                       if (rule->one_shot) {
+                               remove_rule(rule);
+                               break;
+                       }
+               }
+               rule = next;
+       }
+}
+
+bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
+{
+       if (external_rules_command[0] == '\0') {
+               return false;
+       }
+       int fds[2];
+       if (pipe(fds) == -1) {
+               return false;
+       }
+       pid_t pid = fork();
+       if (pid == 0) {
+               if (dpy != NULL) {
+                       close(xcb_get_file_descriptor(dpy));
+               }
+               dup2(fds[1], 1);
+               close(fds[0]);
+               char wid[SMALEN];
+               snprintf(wid, sizeof(wid), "%i", win);
+               setsid();
+               execl(external_rules_command, external_rules_command, wid, csq->class_name, csq->instance_name, csq->monitor_desc, csq->desktop_desc, csq->node_desc, NULL);
+               err("Couldn't spawn rule command.\n");
+       } else if (pid > 0) {
+               close(fds[1]);
+               pending_rule_t *pr = make_pending_rule(fds[0], win, csq);
+               add_pending_rule(pr);
+       }
+       return (pid != -1);
+}
+
+void parse_rule_consequence(int fd, rule_consequence_t *csq)
+{
+       if (fd == -1) {
+               return;
+       }
+       char data[BUFSIZ];
+       int nb;
+       while ((nb = read(fd, data, sizeof(data))) > 0) {
+               int end = MIN(nb, (int) sizeof(data) - 1);
+               data[end] = '\0';
+               char *key = strtok(data, CSQ_BLK);
+               char *value = strtok(NULL, CSQ_BLK);
+               while (key != NULL && value != NULL) {
+                       parse_key_value(key, value, csq);
+                       key = strtok(NULL, CSQ_BLK);
+                       value = strtok(NULL, CSQ_BLK);
+               }
+       }
+}
+
+void parse_key_value(char *key, char *value, rule_consequence_t *csq)
+{
+       bool v;
+       if (streq("monitor", key)) {
+               snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
+       } else if (streq("desktop", key)) {
+               snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
+       } else if (streq("node", key)) {
+               snprintf(csq->node_desc, sizeof(csq->node_desc), "%s", value);
+       } else if (streq("split_dir", key)) {
+               snprintf(csq->split_dir, sizeof(csq->split_dir), "%s", value);
+       } else if (streq("state", key)) {
+               client_state_t cst;
+               if (parse_client_state(value, &cst)) {
+                       if (csq->state == NULL) {
+                               csq->state = calloc(1, sizeof(client_state_t));
+                       }
+                       *(csq->state) = cst;
+               }
+       } else if (streq("layer", key)) {
+               stack_layer_t lyr;
+               if (parse_stack_layer(value, &lyr)) {
+                       if (csq->layer == NULL) {
+                               csq->layer = calloc(1, sizeof(stack_layer_t));
+                       }
+                       *(csq->layer) = lyr;
+               }
+       } else if (streq("split_ratio", key)) {
+               double rat;
+               if (sscanf(value, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+                       csq->split_ratio = rat;
+               }
+       } else if (parse_bool(value, &v)) {
+               if (streq("hidden", key))
+                       csq->hidden = true;
+#define SETCSQ(name) \
+               else if (streq(#name, key)) \
+                       csq->name = v;
+               SETCSQ(sticky)
+               SETCSQ(private)
+               SETCSQ(locked)
+               SETCSQ(center)
+               SETCSQ(follow)
+               SETCSQ(manage)
+               SETCSQ(focus)
+               SETCSQ(border)
+#undef SETCSQ
+       }
+}
+
+void list_rules(FILE *rsp)
+{
+       for (rule_t *r = rule_head; r != NULL; r = r->next) {
+               fprintf(rsp, "%s:%s %c> %s\n", r->class_name, r->instance_name, r->one_shot?'-':'=', r->effect);
+       }
+}
diff --git a/src/rule.h b/src/rule.h
new file mode 100644 (file)
index 0000000..0ccf396
--- /dev/null
@@ -0,0 +1,46 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_RULE_H
+#define BSPWM_RULE_H
+
+#define MATCH_ANY  "*"
+#define CSQ_BLK    " =,\n"
+
+rule_t *make_rule(void);
+void add_rule(rule_t *r);
+void remove_rule(rule_t *r);
+void remove_rule_by_cause(char *cause);
+bool remove_rule_by_index(int idx);
+rule_consequence_t *make_rule_conquence(void);
+pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);
+void add_pending_rule(pending_rule_t *pr);
+void remove_pending_rule(pending_rule_t *pr);
+void apply_rules(xcb_window_t win, rule_consequence_t *csq);
+bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
+void parse_rule_consequence(int fd, rule_consequence_t *csq);
+void parse_key_value(char *key, char *value, rule_consequence_t *csq);
+void list_rules(FILE *rsp);
+
+#endif
diff --git a/src/settings.c b/src/settings.c
new file mode 100644 (file)
index 0000000..a17cff5
--- /dev/null
@@ -0,0 +1,80 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "bspwm.h"
+#include "settings.h"
+
+void run_config(void)
+{
+       if (fork() == 0) {
+               if (dpy != NULL) {
+                       close(xcb_get_file_descriptor(dpy));
+               }
+               setsid();
+               execl(config_path, config_path, NULL);
+               err("Couldn't execute the configuration file.\n");
+       }
+}
+
+void load_settings(void)
+{
+       snprintf(external_rules_command, sizeof(external_rules_command), "%s", EXTERNAL_RULES_COMMAND);
+       snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
+
+       snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
+       snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
+       snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
+       snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR);
+
+       padding = (padding_t) PADDING;
+       window_gap = WINDOW_GAP;
+       border_width = BORDER_WIDTH;
+       split_ratio = SPLIT_RATIO;
+       initial_polarity = FIRST_CHILD;
+       pointer_modifier = POINTER_MODIFIER;
+       pointer_motion_interval = POINTER_MOTION_INTERVAL;
+
+       pointer_actions[0] = ACTION_MOVE;
+       pointer_actions[1] = ACTION_RESIZE_SIDE;
+       pointer_actions[2] = ACTION_RESIZE_CORNER;
+
+       borderless_monocle = BORDERLESS_MONOCLE;
+       gapless_monocle = GAPLESS_MONOCLE;
+       paddingless_monocle = PADDINGLESS_MONOCLE;
+       single_monocle = SINGLE_MONOCLE;
+       focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
+       pointer_follows_focus = POINTER_FOLLOWS_FOCUS;
+       pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
+       ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
+       center_pseudo_tiled = CENTER_PSEUDO_TILED;
+       click_to_focus = CLICK_TO_FOCUS;
+       swallow_first_click = SWALLOW_FIRST_CLICK;
+       honor_size_hints = HONOR_SIZE_HINTS;
+       remove_disabled_monitors = REMOVE_DISABLED_MONITORS;
+       remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;
+       merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;
+}
diff --git a/src/settings.h b/src/settings.h
new file mode 100644 (file)
index 0000000..f8ed182
--- /dev/null
@@ -0,0 +1,101 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_SETTINGS_H
+#define BSPWM_SETTINGS_H
+
+#include "types.h"
+
+#define WM_NAME                  "bspwm"
+#define CONFIG_NAME              WM_NAME "rc"
+#define CONFIG_HOME_ENV          "XDG_CONFIG_HOME"
+#define POINTER_MODIFIER         XCB_MOD_MASK_4
+#define POINTER_MOTION_INTERVAL  17
+#define EXTERNAL_RULES_COMMAND   ""
+#define STATUS_PREFIX            "W"
+
+#define NORMAL_BORDER_COLOR           "#30302f"
+#define ACTIVE_BORDER_COLOR           "#474645"
+#define FOCUSED_BORDER_COLOR          "#817f7f"
+#define PRESEL_FEEDBACK_COLOR         "#f4d775"
+
+#define PADDING              {0, 0, 0, 0}
+#define WINDOW_GAP           6
+#define BORDER_WIDTH         1
+#define SPLIT_RATIO          0.5
+
+#define BORDERLESS_MONOCLE          false
+#define GAPLESS_MONOCLE             false
+#define PADDINGLESS_MONOCLE         false
+#define SINGLE_MONOCLE              false
+#define FOCUS_FOLLOWS_POINTER       false
+#define POINTER_FOLLOWS_FOCUS       false
+#define POINTER_FOLLOWS_MONITOR     false
+#define IGNORE_EWMH_FOCUS           false
+#define CENTER_PSEUDO_TILED         true
+#define CLICK_TO_FOCUS              false
+#define SWALLOW_FIRST_CLICK         false
+#define HONOR_SIZE_HINTS            false
+#define REMOVE_DISABLED_MONITORS    false
+#define REMOVE_UNPLUGGED_MONITORS   false
+#define MERGE_OVERLAPPING_MONITORS  false
+
+char external_rules_command[MAXLEN];
+char status_prefix[MAXLEN];
+
+char normal_border_color[MAXLEN];
+char active_border_color[MAXLEN];
+char focused_border_color[MAXLEN];
+char presel_feedback_color[MAXLEN];
+
+padding_t padding;
+int window_gap;
+unsigned int border_width;
+double split_ratio;
+
+child_polarity_t initial_polarity;
+uint16_t pointer_modifier;
+uint32_t pointer_motion_interval;
+pointer_action_t pointer_actions[3];
+
+bool borderless_monocle;
+bool gapless_monocle;
+bool paddingless_monocle;
+bool single_monocle;
+bool focus_follows_pointer;
+bool pointer_follows_focus;
+bool pointer_follows_monitor;
+bool ignore_ewmh_focus;
+bool center_pseudo_tiled;
+bool click_to_focus;
+bool swallow_first_click;
+bool honor_size_hints;
+bool remove_disabled_monitors;
+bool remove_unplugged_monitors;
+bool merge_overlapping_monitors;
+
+void run_config(void);
+void load_settings(void);
+
+#endif
diff --git a/src/stack.c b/src/stack.c
new file mode 100644 (file)
index 0000000..4ec730d
--- /dev/null
@@ -0,0 +1,218 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "bspwm.h"
+#include "window.h"
+#include "subscribe.h"
+#include "ewmh.h"
+#include "tree.h"
+#include "stack.h"
+
+stacking_list_t *make_stack(node_t *n)
+{
+       stacking_list_t *s = calloc(1, sizeof(stacking_list_t));
+       s->node = n;
+       s->prev = s->next = NULL;
+       return s;
+}
+
+void stack_insert_after(stacking_list_t *a, node_t *n)
+{
+       stacking_list_t *s = make_stack(n);
+       if (a == NULL) {
+               stack_head = stack_tail = s;
+       } else {
+               if (a->node == n) {
+                       free(s);
+                       return;
+               }
+               remove_stack_node(n);
+               stacking_list_t *b = a->next;
+               if (b != NULL) {
+                       b->prev = s;
+               }
+               s->next = b;
+               s->prev = a;
+               a->next = s;
+               if (stack_tail == a) {
+                       stack_tail = s;
+               }
+       }
+}
+
+void stack_insert_before(stacking_list_t *a, node_t *n)
+{
+       stacking_list_t *s = make_stack(n);
+       if (a == NULL) {
+               stack_head = stack_tail = s;
+       } else {
+               if (a->node == n) {
+                       free(s);
+                       return;
+               }
+               remove_stack_node(n);
+               stacking_list_t *b = a->prev;
+               if (b != NULL) {
+                       b->next = s;
+               }
+               s->prev = b;
+               s->next = a;
+               a->prev = s;
+               if (stack_head == a) {
+                       stack_head = s;
+               }
+       }
+}
+
+void remove_stack(stacking_list_t *s)
+{
+       if (s == NULL) {
+               return;
+       }
+       stacking_list_t *a = s->prev;
+       stacking_list_t *b = s->next;
+       if (a != NULL) {
+               a->next = b;
+       }
+       if (b != NULL) {
+               b->prev = a;
+       }
+       if (s == stack_head) {
+               stack_head = b;
+       }
+       if (s == stack_tail) {
+               stack_tail = a;
+       }
+       free(s);
+}
+
+void remove_stack_node(node_t *n)
+{
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+                       if (s->node == f) {
+                               remove_stack(s);
+                               break;
+                       }
+               }
+       }
+}
+
+int stack_level(client_t *c)
+{
+       int layer_level = (c->layer == LAYER_NORMAL ? 1 : (c->layer == LAYER_BELOW ? 0 : 2));
+       int state_level = (IS_TILED(c) ? 0 : (IS_FLOATING(c) ? 2 : 1));
+       return 3 * layer_level + state_level;
+}
+
+int stack_cmp(client_t *c1, client_t *c2)
+{
+       return stack_level(c1) - stack_level(c2);
+}
+
+stacking_list_t *limit_above(node_t *n)
+{
+       stacking_list_t *s = stack_head;
+       while (s != NULL && stack_cmp(n->client, s->node->client) >= 0) {
+               s = s->next;
+       }
+       if (s == NULL) {
+               s = stack_tail;
+       }
+       if (s->node == n) {
+               s = s->prev;
+       }
+       return s;
+}
+
+stacking_list_t *limit_below(node_t *n)
+{
+       stacking_list_t *s = stack_tail;
+       while (s != NULL && stack_cmp(n->client, s->node->client) <= 0) {
+               s = s->prev;
+       }
+       if (s == NULL) {
+               s = stack_head;
+       }
+       if (s->node == n) {
+               s = s->next;
+       }
+       return s;
+}
+
+void stack(desktop_t *d, node_t *n, bool focused)
+{
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client == NULL || (IS_FLOATING(f->client) && !auto_raise)) {
+                       continue;
+               }
+
+               if (stack_head == NULL) {
+                       stack_insert_after(NULL, f);
+               } else {
+                       stacking_list_t *s = (focused ? limit_above(f) : limit_below(f));
+                       if (s == NULL) {
+                               continue;
+                       }
+                       int i = stack_cmp(f->client, s->node->client);
+                       if (i < 0 || (i == 0 && !focused)) {
+                               stack_insert_before(s, f);
+                               window_below(f->id, s->node->id);
+                               put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X below 0x%08X\n", f->id, s->node->id);
+                       } else {
+                               stack_insert_after(s, f);
+                               window_above(f->id, s->node->id);
+                               put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X above 0x%08X\n", f->id, s->node->id);
+                       }
+               }
+       }
+
+       ewmh_update_client_list(true);
+       restack_presel_feedbacks(d);
+}
+
+void restack_presel_feedbacks(desktop_t *d)
+{
+       stacking_list_t *s = stack_tail;
+       while (s != NULL && !IS_TILED(s->node->client)) {
+               s = s->prev;
+       }
+       if (s != NULL) {
+               restack_presel_feedbacks_in(d->root, s->node);
+       }
+}
+
+void restack_presel_feedbacks_in(node_t *r, node_t *n)
+{
+       if (r == NULL) {
+               return;
+       } else {
+               if (r->presel != NULL) {
+                       window_above(r->presel->feedback, n->id);
+               }
+               restack_presel_feedbacks_in(r->first_child, n);
+               restack_presel_feedbacks_in(r->second_child, n);
+       }
+}
diff --git a/src/stack.h b/src/stack.h
new file mode 100644 (file)
index 0000000..21bd048
--- /dev/null
@@ -0,0 +1,41 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_STACK_H
+#define BSPWM_STACK_H
+
+stacking_list_t *make_stack(node_t *n);
+void stack_insert_after(stacking_list_t *a, node_t *n);
+void stack_insert_before(stacking_list_t *a, node_t *n);
+void remove_stack(stacking_list_t *s);
+void remove_stack_node(node_t *n);
+int stack_level(client_t *c);
+int stack_cmp(client_t *c1, client_t *c2);
+stacking_list_t *limit_above(node_t *n);
+stacking_list_t *limit_below(node_t *n);
+void stack(desktop_t *d, node_t *n, bool focused);
+void restack_presel_feedbacks(desktop_t *d);
+void restack_presel_feedbacks_in(node_t *r, node_t *n);
+
+#endif
diff --git a/src/subscribe.c b/src/subscribe.c
new file mode 100644 (file)
index 0000000..8a0b2a1
--- /dev/null
@@ -0,0 +1,149 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "settings.h"
+#include "subscribe.h"
+
+subscriber_list_t *make_subscriber_list(FILE *stream, int field)
+{
+       subscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t));
+       sb->prev = sb->next = NULL;
+       sb->stream = stream;
+       sb->field = field;
+       return sb;
+}
+
+void remove_subscriber(subscriber_list_t *sb)
+{
+       if (sb == NULL) {
+               return;
+       }
+       subscriber_list_t *a = sb->prev;
+       subscriber_list_t *b = sb->next;
+       if (a != NULL) {
+               a->next = b;
+       }
+       if (b != NULL) {
+               b->prev = a;
+       }
+       if (sb == subscribe_head) {
+               subscribe_head = b;
+       }
+       if (sb == subscribe_tail) {
+               subscribe_tail = a;
+       }
+       fclose(sb->stream);
+       free(sb);
+}
+
+void add_subscriber(FILE *stream, int field)
+{
+       subscriber_list_t *sb = make_subscriber_list(stream, field);
+       if (subscribe_head == NULL) {
+               subscribe_head = subscribe_tail = sb;
+       } else {
+               subscribe_tail->next = sb;
+               sb->prev = subscribe_tail;
+               subscribe_tail = sb;
+       }
+       if (sb->field & SBSC_MASK_REPORT) {
+               print_report(sb->stream);
+       }
+}
+
+int print_report(FILE *stream)
+{
+       fprintf(stream, "%s", status_prefix);
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name);
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       char c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o'));
+                       if (m->desk == d) {
+                               c = toupper(c);
+                       }
+                       fprintf(stream, ":%c%s", c, d->name);
+               }
+               if (m->desk != NULL) {
+                       fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout));
+                       if (m->desk->focus != NULL) {
+                               node_t *n = m->desk->focus;
+                               if (n->client != NULL) {
+                                       fprintf(stream, ":T%c", STATE_CHR(n->client->state));
+                               } else {
+                                       fprintf(stream, ":T@");
+                               }
+                               int i = 0;
+                               char flags[4];
+                               if (n->sticky) {
+                                       flags[i++] = 'S';
+                               }
+                               if (n->private) {
+                                       flags[i++] = 'P';
+                               }
+                               if (n->locked) {
+                                       flags[i++] = 'L';
+                               }
+                               flags[i] = '\0';
+                               fprintf(stream, ":G%s", flags);
+                       }
+               }
+               if (m != mon_tail) {
+                       fprintf(stream, "%s", ":");
+               }
+       }
+       fprintf(stream, "%s", "\n");
+       return fflush(stream);
+}
+
+void put_status(subscriber_mask_t mask, ...)
+{
+       subscriber_list_t *sb = subscribe_head;
+       int ret;
+       while (sb != NULL) {
+               subscriber_list_t *next = sb->next;
+               if (sb->field & mask) {
+                       if (mask == SBSC_MASK_REPORT) {
+                               ret = print_report(sb->stream);
+                       } else {
+                               char *fmt;
+                               va_list args;
+                               va_start(args, mask);
+                               fmt = va_arg(args, char *);
+                               vfprintf(sb->stream, fmt, args);
+                               va_end(args);
+                               ret = fflush(sb->stream);
+                       }
+                       if (ret != 0) {
+                               remove_subscriber(sb);
+                       }
+               }
+               sb = next;
+       }
+}
diff --git a/src/subscribe.h b/src/subscribe.h
new file mode 100644 (file)
index 0000000..496ce8d
--- /dev/null
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_SUBSCRIBE_H
+#define BSPWM_SUBSCRIBE_H
+
+typedef enum {
+       SBSC_MASK_REPORT = 1 << 0,
+       SBSC_MASK_MONITOR_ADD = 1 << 1,
+       SBSC_MASK_MONITOR_RENAME = 1 << 2,
+       SBSC_MASK_MONITOR_REMOVE = 1 << 3,
+       SBSC_MASK_MONITOR_SWAP = 1 << 4,
+       SBSC_MASK_MONITOR_FOCUS = 1 << 5,
+       SBSC_MASK_MONITOR_GEOMETRY = 1 << 6,
+       SBSC_MASK_DESKTOP_ADD = 1 << 7,
+       SBSC_MASK_DESKTOP_RENAME = 1 << 8,
+       SBSC_MASK_DESKTOP_REMOVE = 1 << 9,
+       SBSC_MASK_DESKTOP_SWAP = 1 << 10,
+       SBSC_MASK_DESKTOP_TRANSFER = 1 << 11,
+       SBSC_MASK_DESKTOP_FOCUS = 1 << 12,
+       SBSC_MASK_DESKTOP_ACTIVATE = 1 << 13,
+       SBSC_MASK_DESKTOP_LAYOUT = 1 << 14,
+       SBSC_MASK_NODE_MANAGE = 1 << 15,
+       SBSC_MASK_NODE_UNMANAGE = 1 << 16,
+       SBSC_MASK_NODE_SWAP = 1 << 17,
+       SBSC_MASK_NODE_TRANSFER = 1 << 18,
+       SBSC_MASK_NODE_FOCUS = 1 << 19,
+       SBSC_MASK_NODE_PRESEL = 1 << 20,
+       SBSC_MASK_NODE_STACK = 1 << 21,
+       SBSC_MASK_NODE_ACTIVATE = 1 << 22,
+       SBSC_MASK_NODE_GEOMETRY = 1 << 23,
+       SBSC_MASK_NODE_STATE = 1 << 24,
+       SBSC_MASK_NODE_FLAG = 1 << 25,
+       SBSC_MASK_NODE_LAYER = 1 << 26,
+       SBSC_MASK_POINTER_ACTION = 1 << 27,
+       SBSC_MASK_MONITOR = (1 << 7) - (1 << 1),
+       SBSC_MASK_DESKTOP = (1 << 15) - (1 << 7),
+       SBSC_MASK_NODE = (1 << 28) - (1 << 15),
+       SBSC_MASK_ALL = (1 << 28) - 1
+} subscriber_mask_t;
+
+subscriber_list_t *make_subscriber_list(FILE *stream, int field);
+void remove_subscriber(subscriber_list_t *sb);
+void add_subscriber(FILE *stream, int field);
+int print_report(FILE *stream);
+void put_status(subscriber_mask_t mask, ...);
+
+#endif
diff --git a/src/tree.c b/src/tree.c
new file mode 100644 (file)
index 0000000..4ffc9ef
--- /dev/null
@@ -0,0 +1,1948 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "ewmh.h"
+#include "history.h"
+#include "monitor.h"
+#include "query.h"
+#include "geometry.h"
+#include "subscribe.h"
+#include "settings.h"
+#include "pointer.h"
+#include "stack.h"
+#include "window.h"
+#include "tree.h"
+
+void arrange(monitor_t *m, desktop_t *d)
+{
+       if (d->root == NULL) {
+               return;
+       }
+
+       layout_t l = d->layout;
+
+       if (single_monocle && tiled_count(d->root) <= 1) {
+               l = LAYOUT_MONOCLE;
+       }
+
+       xcb_rectangle_t rect = m->rectangle;
+
+       if (!paddingless_monocle || l != LAYOUT_MONOCLE) {
+               rect.x += m->padding.left + d->padding.left;
+               rect.y += m->padding.top + d->padding.top;
+               rect.width -= m->padding.left + d->padding.left + d->padding.right + m->padding.right;
+               rect.height -= m->padding.top + d->padding.top + d->padding.bottom + m->padding.bottom;
+       }
+
+       if (!gapless_monocle || l != LAYOUT_MONOCLE) {
+               rect.x += d->window_gap;
+               rect.y += d->window_gap;
+               rect.width -= d->window_gap;
+               rect.height -= d->window_gap;
+       }
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, false);
+       }
+
+       apply_layout(m, d, d->root, l, rect, rect);
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, true);
+       }
+}
+
+void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       n->rectangle = rect;
+
+       if (pointer_follows_focus && mon->desk->focus == n) {
+               xcb_rectangle_t r = rect;
+               r.width -= d->window_gap;
+               r.height -= d->window_gap;
+               center_pointer(r);
+       }
+
+       if (n->presel != NULL) {
+               draw_presel_feedback(m, d, n);
+       }
+
+       if (is_leaf(n)) {
+
+               if (n->client == NULL) {
+                       return;
+               }
+
+               unsigned int bw;
+               if ((borderless_monocle && n->client->state == STATE_TILED && l == LAYOUT_MONOCLE)
+                   || n->client->state == STATE_FULLSCREEN) {
+                       bw = 0;
+               } else {
+                       bw = n->client->border_width;
+               }
+
+               xcb_rectangle_t r;
+               xcb_rectangle_t cr = get_window_rectangle(n);
+               client_state_t s = n->client->state;
+               if (s == STATE_TILED || s == STATE_PSEUDO_TILED) {
+                       int wg = (gapless_monocle && l == LAYOUT_MONOCLE ? 0 : d->window_gap);
+                       /* tiled clients */
+                       if (s == STATE_TILED) {
+                               r = rect;
+                               int bleed = wg + 2 * bw;
+                               r.width = (bleed < r.width ? r.width - bleed : 1);
+                               r.height = (bleed < r.height ? r.height - bleed : 1);
+                       /* pseudo-tiled clients */
+                       } else {
+                               r = n->client->floating_rectangle;
+                               if (center_pseudo_tiled) {
+                                       r.x = rect.x - bw + (rect.width - wg - r.width) / 2;
+                                       r.y = rect.y - bw + (rect.height - wg - r.height) / 2;
+                               } else {
+                                       r.x = rect.x;
+                                       r.y = rect.y;
+                               }
+                       }
+                       n->client->tiled_rectangle = r;
+               /* floating clients */
+               } else if (s == STATE_FLOATING) {
+                       r = n->client->floating_rectangle;
+               /* fullscreen clients */
+               } else {
+                       r = m->rectangle;
+                       n->client->tiled_rectangle = r;
+               }
+
+               apply_size_hints(n->client, &r.width, &r.height);
+
+               if (!rect_eq(r, cr)) {
+                       window_move_resize(n->id, r.x, r.y, r.width, r.height);
+                       if (!grabbing) {
+                               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", m->id, d->id, n->id, r.width, r.height, r.x, r.y);
+                       }
+               }
+
+               window_border_width(n->id, bw);
+
+       } else {
+               xcb_rectangle_t first_rect;
+               xcb_rectangle_t second_rect;
+
+               if (l == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) {
+                       first_rect = second_rect = rect;
+               } else {
+                       unsigned int fence;
+                       if (n->split_type == TYPE_VERTICAL) {
+                               fence = rect.width * n->split_ratio;
+                               first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
+                               second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
+                       } else {
+                               fence = rect.height * n->split_ratio;
+                               first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
+                               second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
+                       }
+               }
+
+               apply_layout(m, d, n->first_child, l, first_rect, root_rect);
+               apply_layout(m, d, n->second_child, l, second_rect, root_rect);
+       }
+}
+
+presel_t *make_presel(void)
+{
+       presel_t *p = calloc(1, sizeof(presel_t));
+       p->split_dir = DIR_EAST;
+       p->split_ratio = split_ratio;
+       p->feedback = XCB_NONE;
+       return p;
+}
+
+void set_ratio(node_t *n, double rat)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       n->split_ratio = rat;
+}
+
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)
+{
+       if (n->presel == NULL) {
+               n->presel = make_presel();
+       }
+
+       n->presel->split_dir = dir;
+
+       put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X dir %s\n", m->id, d->id, n->id, SPLIT_DIR_STR(dir));
+}
+
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)
+{
+       if (n->presel == NULL) {
+               n->presel = make_presel();
+       }
+
+       n->presel->split_ratio = ratio;
+
+       put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X ratio %lf\n", m->id, d->id, n->id, ratio);
+}
+
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n->presel == NULL) {
+               return;
+       }
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(n, false);
+       }
+
+       if (n->presel->feedback != XCB_NONE) {
+               xcb_destroy_window(dpy, n->presel->feedback);
+       }
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(n, true);
+       }
+
+       free(n->presel);
+       n->presel = NULL;
+
+       put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X cancel\n", m->id, d->id, n->id);
+}
+
+void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+       cancel_presel(m, d, n);
+       cancel_presel_in(m, d, n->first_child);
+       cancel_presel_in(m, d, n->second_child);
+}
+
+node_t *find_public(desktop_t *d)
+{
+       unsigned int b_manual_area = 0;
+       unsigned int b_automatic_area = 0;
+       node_t *b_manual = NULL;
+       node_t *b_automatic = NULL;
+       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+               if (n->vacant) {
+                       continue;
+               }
+               unsigned int n_area = node_area(d, n);
+               if (n_area > b_manual_area && (n->presel != NULL || !n->private)) {
+                       b_manual = n;
+                       b_manual_area = n_area;
+               }
+               if (n_area > b_automatic_area &&
+                   n->presel == NULL && !n->private && private_count(n->parent) == 0) {
+                       b_automatic = n;
+                       b_automatic_area = n_area;
+               }
+       }
+       if (b_automatic != NULL) {
+               return b_automatic;
+       } else {
+               return b_manual;
+       }
+}
+
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+{
+       if (d == NULL || n == NULL) {
+               return NULL;
+       }
+
+       /* n: inserted node */
+       /* c: new internal node */
+       /* f: focus or insertion anchor */
+       /* p: parent of focus */
+       /* g: grand parent of focus */
+
+       if (f == NULL) {
+               f = d->root;
+       }
+
+       if (f == NULL) {
+               d->root = n;
+       } else if (IS_RECEPTACLE(f) && f->presel == NULL) {
+               node_t *p = f->parent;
+               if (p != NULL) {
+                       if (is_first_child(f)) {
+                               p->first_child = n;
+                       } else {
+                               p->second_child = n;
+                       }
+               } else {
+                       d->root = n;
+               }
+               n->parent = p;
+               free(f);
+               f = NULL;
+       } else {
+               node_t *c = make_node(XCB_NONE);
+               node_t *p = f->parent;
+               if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+                       node_t *k = find_public(d);
+                       if (k != NULL) {
+                               f = k;
+                               p = f->parent;
+                       }
+                       if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+                               xcb_rectangle_t rect = get_rectangle(d, f);
+                               presel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));
+                       }
+               }
+               if (f->presel == NULL && p != NULL && p->vacant) {
+                       f = p;
+                       p = f->parent;
+               }
+               n->parent = c;
+               c->birth_rotation = f->birth_rotation;
+               if (f->presel == NULL) {
+                       if (p == NULL) {
+                               if (initial_polarity == FIRST_CHILD) {
+                                       c->first_child = n;
+                                       c->second_child = f;
+                               } else {
+                                       c->first_child = f;
+                                       c->second_child = n;
+                               }
+                               if (m->rectangle.width > m->rectangle.height) {
+                                       c->split_type = TYPE_VERTICAL;
+                               } else {
+                                       c->split_type = TYPE_HORIZONTAL;
+                               }
+                               f->parent = c;
+                               d->root = c;
+                       } else {
+                               node_t *g = p->parent;
+                               c->parent = g;
+                               if (g != NULL) {
+                                       if (is_first_child(p)) {
+                                               g->first_child = c;
+                                       } else {
+                                               g->second_child = c;
+                                       }
+                               } else {
+                                       d->root = c;
+                               }
+                               c->split_type = p->split_type;
+                               c->split_ratio = p->split_ratio;
+                               p->parent = c;
+                               int rot;
+                               if (is_first_child(f)) {
+                                       c->first_child = n;
+                                       c->second_child = p;
+                                       rot = 90;
+                               } else {
+                                       c->first_child = p;
+                                       c->second_child = n;
+                                       rot = 270;
+                               }
+                               n->birth_rotation = rot;
+                               if (!n->vacant) {
+                                       rotate_tree(p, rot);
+                               }
+                       }
+               } else {
+                       if (p != NULL) {
+                               if (is_first_child(f)) {
+                                       p->first_child = c;
+                               } else {
+                                       p->second_child = c;
+                               }
+                       }
+                       c->split_ratio = f->presel->split_ratio;
+                       c->parent = p;
+                       f->parent = c;
+                       f->birth_rotation = 0;
+                       switch (f->presel->split_dir) {
+                               case DIR_WEST:
+                                       c->split_type = TYPE_VERTICAL;
+                                       c->first_child = n;
+                                       c->second_child = f;
+                                       break;
+                               case DIR_EAST:
+                                       c->split_type = TYPE_VERTICAL;
+                                       c->first_child = f;
+                                       c->second_child = n;
+                                       break;
+                               case DIR_NORTH:
+                                       c->split_type = TYPE_HORIZONTAL;
+                                       c->first_child = n;
+                                       c->second_child = f;
+                                       break;
+                               case DIR_SOUTH:
+                                       c->split_type = TYPE_HORIZONTAL;
+                                       c->first_child = f;
+                                       c->second_child = n;
+                                       break;
+                       }
+                       if (d->root == f) {
+                               d->root = c;
+                       }
+                       cancel_presel(m, d, f);
+               }
+       }
+
+       propagate_flags_upward(m, d, n);
+
+       if (d->focus == NULL && is_focusable(n)) {
+               d->focus = n;
+       }
+
+       return f;
+}
+
+void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n)
+{
+       node_t *r = make_node(XCB_NONE);
+       insert_node(m, d, r, n);
+}
+
+bool activate_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (d == NULL) {
+               d = history_last_desktop(m, NULL);
+               if (d == NULL) {
+                       d = m->desk_head;
+               }
+       }
+
+       if (d == NULL) {
+               return false;
+       }
+
+       if (n == NULL && d->root != NULL) {
+               n = history_last_node(d, NULL);
+               if (n == NULL) {
+                       n = first_focusable_leaf(d->root);
+               }
+       }
+
+       if (d == mon->desk || (n != NULL && !is_focusable(n))) {
+               return false;
+       }
+
+       if (n != NULL) {
+               if (d->focus != NULL && n != d->focus) {
+                       neutralize_occluding_windows(m, d, n);
+               }
+               stack(d, n, true);
+               if (d->focus != n) {
+                       for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+                               if (f->client != NULL && !is_descendant(f, n)) {
+                                       window_draw_border(f->id, get_border_color(false, (m == mon)));
+                               }
+                       }
+               }
+               draw_border(n, true, (m == mon));
+       }
+
+       activate_desktop(m, d);
+
+       d->focus = n;
+       history_add(m, d, n);
+
+       put_status(SBSC_MASK_REPORT);
+
+       if (n == NULL) {
+               return true;
+       }
+
+       put_status(SBSC_MASK_NODE_ACTIVATE, "node_activate 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
+
+       return true;
+}
+
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else if (n->sticky) {
+               transfer_node(m, ds, n, m, dd, dd->focus);
+       } else {
+               /* we need references to the children because n might be freed after
+                * the first recursive call */
+               node_t *first_child = n->first_child;
+               node_t *second_child = n->second_child;
+               transfer_sticky_nodes(m, ds, dd, first_child);
+               transfer_sticky_nodes(m, ds, dd, second_child);
+       }
+}
+
+bool focus_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (m == NULL) {
+               m = history_last_monitor(NULL);
+               if (m == NULL) {
+                       m = mon_head;
+               }
+       }
+
+       if (m == NULL) {
+               return false;
+       }
+
+       if (d == NULL) {
+               d = history_last_desktop(m, NULL);
+               if (d == NULL) {
+                       d = m->desk_head;
+               }
+       }
+
+       if (d == NULL) {
+               return false;
+       }
+
+       if (n == NULL && d->root != NULL) {
+               n = history_last_node(d, NULL);
+               if (n == NULL) {
+                       n = first_focusable_leaf(d->root);
+               }
+       }
+
+       if (n != NULL && !is_focusable(n)) {
+               return false;
+       }
+
+       if ((mon != NULL && mon->desk != d) || n == NULL || n->client == NULL) {
+               clear_input_focus();
+       }
+
+       if (m->sticky_count > 0 && d != m->desk) {
+               sticky_still = false;
+               transfer_sticky_nodes(m, m->desk, d, m->desk->root);
+               sticky_still = true;
+               if (n == NULL && d->focus != NULL) {
+                       n = d->focus;
+               }
+       }
+
+       if (d->focus != NULL && n != d->focus) {
+               neutralize_occluding_windows(m, d, n);
+       }
+
+       if (n != NULL && n->client != NULL && n->client->urgent) {
+               set_urgent(m, d, n, false);
+       }
+
+       if (mon != m) {
+               if (mon != NULL) {
+                       for (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {
+                               draw_border(e->focus, true, false);
+                       }
+               }
+               for (desktop_t *e = m->desk_head; e != NULL; e = e->next) {
+                       if (e == d) {
+                               continue;
+                       }
+                       draw_border(e->focus, true, true);
+               }
+       }
+
+       if (d->focus != n) {
+               for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+                       if (f->client != NULL && !is_descendant(f, n)) {
+                               window_draw_border(f->id, get_border_color(false, true));
+                       }
+               }
+       }
+
+       draw_border(n, true, true);
+
+       focus_desktop(m, d);
+
+       d->focus = n;
+       ewmh_update_active_window();
+       history_add(m, d, n);
+
+       put_status(SBSC_MASK_REPORT);
+
+       if (n == NULL) {
+               return true;
+       }
+
+       put_status(SBSC_MASK_NODE_FOCUS, "node_focus 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
+
+       stack(d, n, true);
+       set_input_focus(n);
+
+       if (pointer_follows_focus) {
+               center_pointer(get_rectangle(d, n));
+       }
+
+       return true;
+}
+
+void hide_node(desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (!n->hidden) {
+                       if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
+                               window_hide(n->presel->feedback);
+                       }
+                       if (n->client != NULL) {
+                               window_hide(n->id);
+                       }
+               }
+               if (n->client != NULL) {
+                       n->client->shown = false;
+               }
+               hide_node(d, n->first_child);
+               hide_node(d, n->second_child);
+       }
+}
+
+void show_node(desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (!n->hidden) {
+                       if (n->client != NULL) {
+                               window_show(n->id);
+                       }
+                       if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
+                               window_show(n->presel->feedback);
+                       }
+               }
+               if (n->client != NULL) {
+                       n->client->shown = true;
+               }
+               show_node(d, n->first_child);
+               show_node(d, n->second_child);
+       }
+}
+
+node_t *make_node(uint32_t id)
+{
+       if (id == XCB_NONE) {
+               id = xcb_generate_id(dpy);
+       }
+       node_t *n = calloc(1, sizeof(node_t));
+       n->id = id;
+       n->parent = n->first_child = n->second_child = NULL;
+       n->vacant = n->hidden = n->sticky = n->private = n->locked = false;
+       n->split_ratio = split_ratio;
+       n->split_type = TYPE_VERTICAL;
+       n->birth_rotation = 0;
+       n->presel = NULL;
+       n->client = NULL;
+       return n;
+}
+
+client_t *make_client(void)
+{
+       client_t *c = calloc(1, sizeof(client_t));
+       c->state = c->last_state = STATE_TILED;
+       c->layer = c->last_layer = LAYER_NORMAL;
+       snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
+       snprintf(c->instance_name, sizeof(c->instance_name), "%s", MISSING_VALUE);
+       c->border_width = border_width;
+       c->urgent = false;
+       c->shown = false;
+       c->wm_flags = 0;
+       c->icccm_props.input_hint = true;
+       c->icccm_props.take_focus = false;
+       c->icccm_props.delete_window = false;
+       c->size_hints.flags = 0;
+       return c;
+}
+
+void initialize_client(node_t *n)
+{
+       xcb_window_t win = n->id;
+       client_t *c = n->client;
+       xcb_icccm_get_wm_protocols_reply_t protos;
+       if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protos, NULL) == 1) {
+               for (uint32_t i = 0; i < protos.atoms_len; i++) {
+                       if (protos.atoms[i] == WM_TAKE_FOCUS) {
+                               c->icccm_props.take_focus = true;
+                       } else if (protos.atoms[i] == WM_DELETE_WINDOW) {
+                               c->icccm_props.delete_window = true;
+                       }
+               }
+               xcb_icccm_get_wm_protocols_reply_wipe(&protos);
+       }
+       xcb_ewmh_get_atoms_reply_t wm_state;
+       if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
+               for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_WM_STATES; i++) {
+#define HANDLE_WM_STATE(s) \
+                       if (wm_state.atoms[i] == ewmh->_NET_WM_STATE_##s) { \
+                               c->wm_flags |= WM_FLAG_##s; continue; \
+                       }
+                       HANDLE_WM_STATE(MODAL)
+                       HANDLE_WM_STATE(STICKY)
+                       HANDLE_WM_STATE(MAXIMIZED_VERT)
+                       HANDLE_WM_STATE(MAXIMIZED_HORZ)
+                       HANDLE_WM_STATE(SHADED)
+                       HANDLE_WM_STATE(SKIP_TASKBAR)
+                       HANDLE_WM_STATE(SKIP_PAGER)
+                       HANDLE_WM_STATE(HIDDEN)
+                       HANDLE_WM_STATE(FULLSCREEN)
+                       HANDLE_WM_STATE(ABOVE)
+                       HANDLE_WM_STATE(BELOW)
+                       HANDLE_WM_STATE(DEMANDS_ATTENTION)
+#undef HANDLE_WM_STATE
+               }
+               xcb_ewmh_get_atoms_reply_wipe(&wm_state);
+       }
+       xcb_icccm_wm_hints_t hints;
+       if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1
+               && (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {
+               c->icccm_props.input_hint = hints.input;
+       }
+       xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &c->size_hints, NULL);
+}
+
+bool is_focusable(node_t *n)
+{
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client != NULL && !f->hidden) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+bool is_leaf(node_t *n)
+{
+       return (n != NULL && n->first_child == NULL && n->second_child == NULL);
+}
+
+bool is_first_child(node_t *n)
+{
+       return (n != NULL && n->parent != NULL && n->parent->first_child == n);
+}
+
+bool is_second_child(node_t *n)
+{
+       return (n != NULL && n->parent != NULL && n->parent->second_child == n);
+}
+
+unsigned int clients_count_in(node_t *n)
+{
+       if (n == NULL) {
+               return 0;
+       } else {
+               return (n->client != NULL ? 1 : 0) +
+                       clients_count_in(n->first_child) +
+                       clients_count_in(n->second_child);
+       }
+}
+
+node_t *brother_tree(node_t *n)
+{
+       if (n == NULL || n->parent == NULL) {
+               return NULL;
+       }
+       if (is_first_child(n)) {
+               return n->parent->second_child;
+       } else {
+               return n->parent->first_child;
+       }
+}
+
+node_t *first_extrema(node_t *n)
+{
+       if (n == NULL) {
+               return NULL;
+       } else if (n->first_child == NULL) {
+               return n;
+       } else {
+               return first_extrema(n->first_child);
+       }
+}
+
+node_t *second_extrema(node_t *n)
+{
+       if (n == NULL) {
+               return NULL;
+       } else if (n->second_child == NULL) {
+               return n;
+       } else {
+               return second_extrema(n->second_child);
+       }
+}
+
+node_t *first_focusable_leaf(node_t *n)
+{
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client != NULL && !f->hidden) {
+                       return f;
+               }
+       }
+       return NULL;
+}
+
+node_t *next_leaf(node_t *n, node_t *r)
+{
+       if (n == NULL) {
+               return NULL;
+       }
+       node_t *p = n;
+       while (is_second_child(p) && p != r) {
+               p = p->parent;
+       }
+       if (p == r) {
+               return NULL;
+       }
+       return first_extrema(p->parent->second_child);
+}
+
+node_t *prev_leaf(node_t *n, node_t *r)
+{
+       if (n == NULL) {
+               return NULL;
+       }
+       node_t *p = n;
+       while (is_first_child(p) && p != r) {
+               p = p->parent;
+       }
+       if (p == r) {
+               return NULL;
+       }
+       return second_extrema(p->parent->first_child);
+}
+
+node_t *next_tiled_leaf(node_t *n, node_t *r)
+{
+       node_t *next = next_leaf(n, r);
+       if (next == NULL || (next->client != NULL && !next->vacant)) {
+               return next;
+       } else {
+               return next_tiled_leaf(next, r);
+       }
+}
+
+node_t *prev_tiled_leaf(node_t *n, node_t *r)
+{
+       node_t *prev = prev_leaf(n, r);
+       if (prev == NULL || (prev->client != NULL && !prev->vacant)) {
+               return prev;
+       } else {
+               return prev_tiled_leaf(prev, r);
+       }
+}
+
+/* Returns true if *b* is adjacent to *a* in the direction *dir* */
+bool is_adjacent(node_t *a, node_t *b, direction_t dir)
+{
+       switch (dir) {
+               case DIR_EAST:
+                       return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
+                       break;
+               case DIR_SOUTH:
+                       return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
+                       break;
+               case DIR_WEST:
+                       return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
+                       break;
+               case DIR_NORTH:
+                       return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
+                       break;
+       }
+       return false;
+}
+
+node_t *find_fence(node_t *n, direction_t dir)
+{
+       node_t *p;
+
+       if (n == NULL) {
+               return NULL;
+       }
+
+       p = n->parent;
+
+       while (p != NULL) {
+               if ((dir == DIR_NORTH && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
+                   (dir == DIR_WEST && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
+                   (dir == DIR_SOUTH && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
+                   (dir == DIR_EAST && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
+                       return p;
+               p = p->parent;
+       }
+
+       return NULL;
+}
+
+/* returns *true* if *a* is a child of *b* */
+bool is_child(node_t *a, node_t *b)
+{
+       if (a == NULL || b == NULL) {
+               return false;
+       }
+       return (a->parent != NULL && a->parent == b);
+}
+
+/* returns *true* if *a* is a descendant of *b* */
+bool is_descendant(node_t *a, node_t *b)
+{
+       if (a == NULL || b == NULL) {
+               return false;
+       }
+       while (a != b && a != NULL) {
+               a = a->parent;
+       }
+       return a == b;
+}
+
+bool find_by_id(uint32_t id, coordinates_t *loc)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       node_t *n = find_by_id_in(d->root, id);
+                       if (n != NULL) {
+                               loc->monitor = m;
+                               loc->desktop = d;
+                               loc->node = n;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+node_t *find_by_id_in(node_t *r, uint32_t id)
+{
+       if (r == NULL) {
+               return NULL;
+       } else if (r->id == id) {
+               return r;
+       } else {
+               node_t *f = find_by_id_in(r->first_child, id);
+               if (f != NULL) {
+                       return f;
+               } else {
+                       return find_by_id_in(r->second_child, id);
+               }
+       }
+}
+
+void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel)
+{
+       if (ref->node == NULL) {
+               return;
+       }
+
+       xcb_rectangle_t rect = get_rectangle(ref->desktop, ref->node);
+       double md = DBL_MAX, mr = UINT32_MAX;
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               desktop_t *d = m->desk;
+               for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+                       coordinates_t loc = {m, d, f};
+                       xcb_rectangle_t r = get_rectangle(d, f);
+                       if (f == ref->node ||
+                           f->client == NULL ||
+                           f->hidden ||
+                           is_descendant(f, ref->node) ||
+                           !node_matches(&loc, ref, sel) ||
+                           !on_dir_side(rect, r, dir)) {
+                               continue;
+                       }
+                       double fd = distance_center(rect, r);
+                       uint32_t fr = history_rank(f);
+                       if (fd < md || (fd == md && fr < mr)) {
+                               md = fd;
+                               mr = fr;
+                               *dst = loc;
+                       }
+               }
+       }
+}
+
+unsigned int node_area(desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return 0;
+       }
+       return area(get_rectangle(d, n));
+}
+
+int tiled_count(node_t *n)
+{
+       if (n == NULL) {
+               return 0;
+       }
+       int cnt = 0;
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (!f->hidden && f->client != NULL && IS_TILED(f->client)) {
+                       cnt++;
+               }
+       }
+       return cnt;
+}
+
+void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel)
+{
+       unsigned int b_area = 0;
+
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+                               coordinates_t loc = {m, d, f};
+                               if (f->client == NULL || f->vacant || !node_matches(&loc, ref, sel)) {
+                                       continue;
+                               }
+                               unsigned int f_area = node_area(d, f);
+                               if (f_area > b_area) {
+                                       *dst = loc;
+                                       b_area = f_area;
+                               }
+                       }
+               }
+       }
+}
+
+void rotate_tree(node_t *n, int deg)
+{
+       if (n == NULL || is_leaf(n) || deg == 0) {
+               return;
+       }
+
+       node_t *tmp;
+
+       if ((deg == 90 && n->split_type == TYPE_HORIZONTAL) ||
+           (deg == 270 && n->split_type == TYPE_VERTICAL) ||
+           deg == 180) {
+               tmp = n->first_child;
+               n->first_child = n->second_child;
+               n->second_child = tmp;
+               n->split_ratio = 1.0 - n->split_ratio;
+       }
+
+       if (deg != 180) {
+               if (n->split_type == TYPE_HORIZONTAL) {
+                       n->split_type = TYPE_VERTICAL;
+               } else if (n->split_type == TYPE_VERTICAL) {
+                       n->split_type = TYPE_HORIZONTAL;
+               }
+       }
+
+       rotate_tree(n->first_child, deg);
+       rotate_tree(n->second_child, deg);
+}
+
+void rotate_brother(node_t *n)
+{
+       rotate_tree(brother_tree(n), n->birth_rotation);
+}
+
+void unrotate_tree(node_t *n, int rot)
+{
+       if (rot == 0) {
+               return;
+       }
+       rotate_tree(n, 360 - rot);
+}
+
+void unrotate_brother(node_t *n)
+{
+       unrotate_tree(brother_tree(n), n->birth_rotation);
+}
+
+void flip_tree(node_t *n, flip_t flp)
+{
+       if (n == NULL || is_leaf(n)) {
+               return;
+       }
+
+       node_t *tmp;
+
+       if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL) ||
+           (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
+               tmp = n->first_child;
+               n->first_child = n->second_child;
+               n->second_child = tmp;
+               n->split_ratio = 1.0 - n->split_ratio;
+       }
+
+       flip_tree(n->first_child, flp);
+       flip_tree(n->second_child, flp);
+}
+
+void equalize_tree(node_t *n)
+{
+       if (n == NULL || n->vacant) {
+               return;
+       } else {
+               n->split_ratio = split_ratio;
+               equalize_tree(n->first_child);
+               equalize_tree(n->second_child);
+       }
+}
+
+int balance_tree(node_t *n)
+{
+       if (n == NULL || n->vacant) {
+               return 0;
+       } else if (is_leaf(n)) {
+               return 1;
+       } else {
+               int b1 = balance_tree(n->first_child);
+               int b2 = balance_tree(n->second_child);
+               int b = b1 + b2;
+               if (b1 > 0 && b2 > 0) {
+                       n->split_ratio = (double) b1 / b;
+               }
+               return b;
+       }
+}
+
+void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (d == NULL || n == NULL) {
+               return;
+       }
+
+       node_t *p = n->parent;
+
+       if (p == NULL) {
+               d->root = NULL;
+               d->focus = NULL;
+       } else {
+               if (d->focus == p || is_descendant(d->focus, n)) {
+                       d->focus = NULL;
+               }
+
+               history_remove(d, p, false);
+               cancel_presel(m, d, p);
+               if (p->sticky) {
+                       m->sticky_count--;
+               }
+
+               node_t *b = brother_tree(n);
+               node_t *g = p->parent;
+
+               if (!n->vacant) {
+                       unrotate_tree(b, n->birth_rotation);
+               }
+
+               b->parent = g;
+
+               if (g != NULL) {
+                       if (is_first_child(p)) {
+                               g->first_child = b;
+                       } else {
+                               g->second_child = b;
+                       }
+               } else {
+                       d->root = b;
+               }
+
+               b->birth_rotation = p->birth_rotation;
+
+               free(p);
+               n->parent = NULL;
+
+               propagate_flags_upward(m, d, b);
+       }
+}
+
+void close_node(node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else if (n->client != NULL) {
+               if (n->client->icccm_props.delete_window) {
+                       send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
+               } else {
+                       xcb_kill_client(dpy, n->id);
+               }
+       } else {
+               close_node(n->first_child);
+               close_node(n->second_child);
+       }
+}
+
+void kill_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client != NULL) {
+                       xcb_kill_client(dpy, f->id);
+               }
+       }
+
+       remove_node(m, d, n);
+}
+
+void remove_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       unlink_node(m, d, n);
+       history_remove(d, n, true);
+       remove_stack_node(n);
+       cancel_presel_in(m, d, n);
+       if (m->sticky_count > 0) {
+               m->sticky_count -= sticky_count(n);
+       }
+       clients_count -= clients_count_in(n);
+       if (is_descendant(grabbed_node, n)) {
+               grabbed_node = NULL;
+       }
+       free_node(n);
+
+       ewmh_update_client_list(false);
+       ewmh_update_client_list(true);
+
+       if (mon != NULL && d->focus == NULL) {
+               if (d == mon->desk) {
+                       focus_node(m, d, NULL);
+               } else {
+                       activate_node(m, d, NULL);
+               }
+       }
+}
+
+void free_node(node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+       node_t *first_child = n->first_child;
+       node_t *second_child = n->second_child;
+       free(n->client);
+       free(n);
+       free_node(first_child);
+       free_node(second_child);
+}
+
+bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
+{
+       if (n1 == NULL || n2 == NULL || n1 == n2 || is_descendant(n1, n2) || is_descendant(n2, n1) ||
+           (d1 != d2 && ((m1->sticky_count > 0 && sticky_count(n1) > 0) ||
+                         (m2->sticky_count > 0 && sticky_count(n2) > 0)))) {
+               return false;
+       }
+
+       put_status(SBSC_MASK_NODE_SWAP, "node_swap 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, n1->id, m2->id, d2->id, n2->id);
+
+       node_t *pn1 = n1->parent;
+       node_t *pn2 = n2->parent;
+       bool n1_first_child = is_first_child(n1);
+       bool n2_first_child = is_first_child(n2);
+       int br1 = n1->birth_rotation;
+       int br2 = n2->birth_rotation;
+       bool n1_held_focus = is_descendant(d1->focus, n1);
+       bool n2_held_focus = is_descendant(d2->focus, n2);
+       node_t *last_d1_focus = d1->focus;
+       node_t *last_d2_focus = d2->focus;
+
+       if (pn1 != NULL) {
+               if (n1_first_child) {
+                       pn1->first_child = n2;
+               } else {
+                       pn1->second_child = n2;
+               }
+       }
+
+       if (pn2 != NULL) {
+               if (n2_first_child) {
+                       pn2->first_child = n1;
+               } else {
+                       pn2->second_child = n1;
+               }
+       }
+
+       n1->parent = pn2;
+       n2->parent = pn1;
+       n1->birth_rotation = br2;
+       n2->birth_rotation = br1;
+
+       propagate_flags_upward(m2, d2, n1);
+       propagate_flags_upward(m1, d1, n2);
+
+       if (d1 != d2) {
+               if (d1->root == n1) {
+                       d1->root = n2;
+               }
+
+               if (d2->root == n2) {
+                       d2->root = n1;
+               }
+
+               if (n1_held_focus) {
+                       d1->focus = n2_held_focus ? last_d2_focus : n2;
+               }
+
+               if (n2_held_focus) {
+                       d2->focus = n1_held_focus ? last_d1_focus : n1;
+               }
+
+               if (m1 != m2) {
+                       adapt_geometry(&m2->rectangle, &m1->rectangle, n2);
+                       adapt_geometry(&m1->rectangle, &m2->rectangle, n1);
+               }
+
+               ewmh_set_wm_desktop(n1, d2);
+               ewmh_set_wm_desktop(n2, d1);
+
+               history_swap_nodes(m1, d1, n1, m2, d2, n2);
+
+               if (m1->desk != d1 && m2->desk == d2) {
+                       show_node(d2, n1);
+                       hide_node(d2, n2);
+               } else if (m1->desk == d1 && m2->desk != d2) {
+                       hide_node(d1, n1);
+                       show_node(d1, n2);
+               }
+
+               if (n1_held_focus) {
+                       if (d1 == mon->desk) {
+                               focus_node(m1, d1, d1->focus);
+                       } else {
+                               activate_node(m1, d1, d1->focus);
+                       }
+               } else {
+                       draw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));
+               }
+
+               if (n2_held_focus) {
+                       if (d2 == mon->desk) {
+                               focus_node(m2, d2, d2->focus);
+                       } else {
+                               activate_node(m2, d2, d2->focus);
+                       }
+               } else {
+                       draw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));
+               }
+       }
+
+       arrange(m1, d1);
+
+       if (d1 != d2) {
+               arrange(m2, d2);
+       }
+
+       return true;
+}
+
+bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
+{
+       if (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {
+               return false;
+       }
+
+       if (sticky_still && ds != dd && ms->sticky_count > 0 && sticky_count(ns) > 0) {
+               return false;
+       }
+
+       put_status(SBSC_MASK_NODE_TRANSFER, "node_transfer 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", ms->id, ds->id, ns->id, md->id, dd->id, nd!=NULL?nd->id:0);
+
+       bool held_focus = is_descendant(ds->focus, ns);
+       /* avoid ending up with a dangling pointer (because of unlink_node) */
+       node_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;
+
+       if (held_focus && ds == mon->desk) {
+               clear_input_focus();
+       }
+
+       unlink_node(ms, ds, ns);
+       insert_node(md, dd, ns, nd);
+
+       if (md != ms && (ns->client == NULL || monitor_from_client(ns->client) != md)) {
+               adapt_geometry(&ms->rectangle, &md->rectangle, ns);
+       }
+
+       if (ds != dd) {
+               ewmh_set_wm_desktop(ns, dd);
+               if (sticky_still) {
+                       if (ds == ms->desk && dd != md->desk) {
+                               hide_node(ds, ns);
+                       } else if (ds != ms->desk && dd == md->desk) {
+                               show_node(dd, ns);
+                       }
+               }
+       }
+
+       history_transfer_node(md, dd, ns);
+       stack(dd, ns, false);
+
+       if (ds == dd) {
+               if (held_focus) {
+                       if (ds == mon->desk) {
+                               focus_node(ms, ds, last_ds_focus);
+                       } else {
+                               activate_node(ms, ds, last_ds_focus);
+                       }
+               } else {
+                       draw_border(ns, is_descendant(ns, ds->focus), (ms == mon));
+               }
+       } else {
+               if (held_focus) {
+                       if (ds == mon->desk) {
+                               focus_node(ms, ds, ds->focus);
+                       } else {
+                               activate_node(ms, ds, ds->focus);
+                       }
+               }
+               if (dd->focus == ns) {
+                       if (dd == mon->desk) {
+                               focus_node(md, dd, held_focus ? last_ds_focus : dd->focus);
+                       } else {
+                               activate_node(md, dd, held_focus ? last_ds_focus : dd->focus);
+                       }
+               } else {
+                       draw_border(ns, is_descendant(ns, dd->focus), (md == mon));
+               }
+       }
+
+       arrange(ms, ds);
+
+       if (ds != dd) {
+               arrange(md, dd);
+       }
+
+       return true;
+}
+
+bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel)
+{
+       if (ref->node == NULL) {
+               return false;
+       }
+
+       monitor_t *m = ref->monitor;
+       desktop_t *d = ref->desktop;
+       node_t *n = ref->node;
+
+       node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
+
+#define HANDLE_BOUNDARIES(f)  \
+       while (f == NULL) { \
+               d = (dir == CYCLE_PREV ? d->prev : d->next); \
+               if (d == NULL) { \
+                       m = (dir == CYCLE_PREV ? m->prev : m->next); \
+                       if (m == NULL) { \
+                               m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
+                       } \
+                       d = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
+               } \
+               f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); \
+       }
+       HANDLE_BOUNDARIES(f);
+
+       while (f != n) {
+               coordinates_t loc = {m, d, f};
+               if (f->client != NULL && !f->hidden && node_matches(&loc, ref, sel)) {
+                       *dst = loc;
+                       return true;
+               }
+               f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
+               HANDLE_BOUNDARIES(f);
+       }
+#undef HANDLE_EXTREMUM
+       return false;
+}
+
+void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir)
+{
+       if (n == NULL || d->focus == NULL || is_leaf(n)) {
+               return;
+       }
+       node_t *p = d->focus->parent;
+       bool focus_first_child = is_first_child(d->focus);
+       if (dir == CIRCULATE_FORWARD) {
+               node_t *e = second_extrema(n);
+               while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
+                       e = prev_leaf(e, n);
+               }
+               for (node_t *s = e, *f = prev_tiled_leaf(s, n); f != NULL; s = prev_tiled_leaf(f, n), f = prev_tiled_leaf(s, n)) {
+                       swap_nodes(m, d, f, m, d, s);
+               }
+       } else {
+               node_t *e = first_extrema(n);
+               while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
+                       e = next_leaf(e, n);
+               }
+               for (node_t *f = e, *s = next_tiled_leaf(f, n); s != NULL; f = next_tiled_leaf(s, n), s = next_tiled_leaf(f, n)) {
+                       swap_nodes(m, d, f, m, d, s);
+               }
+       }
+       if (focus_first_child) {
+               focus_node(m, d, p->first_child);
+       } else {
+               focus_node(m, d, p->second_child);
+       }
+}
+
+void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n->vacant == value) {
+               return;
+       }
+
+       propagate_vacant_downward(m, d, n, value);
+       propagate_vacant_upward(m, d, n);
+}
+
+void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n->vacant == value) {
+               return;
+       }
+
+       n->vacant = value;
+
+       if (value) {
+               unrotate_brother(n);
+               cancel_presel(m, d, n);
+       } else {
+               rotate_brother(n);
+       }
+}
+
+void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       set_vacant_local(m, d, n, value);
+
+       propagate_vacant_downward(m, d, n->first_child, value);
+       propagate_vacant_downward(m, d, n->second_child, value);
+}
+
+void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       node_t *p = n->parent;
+
+       if (p != NULL) {
+               set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
+       }
+
+       propagate_vacant_upward(m, d, p);
+}
+
+bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
+{
+       if (n == NULL || n->client->layer == l) {
+               return false;
+       }
+
+       n->client->last_layer = n->client->layer;
+       n->client->layer = l;
+
+       if (l == LAYER_ABOVE) {
+               n->client->wm_flags |= WM_FLAG_ABOVE;
+               n->client->wm_flags &= ~WM_FLAG_BELOW;
+       } else if (l == LAYER_BELOW) {
+               n->client->wm_flags |= WM_FLAG_BELOW;
+               n->client->wm_flags &= ~WM_FLAG_ABOVE;
+       } else {
+               n->client->wm_flags &= ~(WM_FLAG_ABOVE | WM_FLAG_BELOW);
+       }
+
+       ewmh_wm_state_update(n);
+
+       put_status(SBSC_MASK_NODE_LAYER, "node_layer 0x%08X 0x%08X 0x%08X %s\n", m->id, d->id, n->id, LAYER_STR(l));
+
+       if (d->focus == n) {
+               neutralize_occluding_windows(m, d, n);
+       }
+
+       stack(d, n, (d->focus == n));
+
+       return true;
+}
+
+bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
+{
+       if (n == NULL || n->client == NULL || n->client->state == s) {
+               return false;
+       }
+
+       client_t *c = n->client;
+
+       c->last_state = c->state;
+       c->state = s;
+
+       switch (c->last_state) {
+               case STATE_TILED:
+               case STATE_PSEUDO_TILED:
+                       break;
+               case STATE_FLOATING:
+                       set_floating(m, d, n, false);
+                       break;
+               case STATE_FULLSCREEN:
+                       set_fullscreen(m, d, n, false);
+                       break;
+       }
+
+       put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s off\n", m->id, d->id, n->id, STATE_STR(c->last_state));
+
+       switch (c->state) {
+               case STATE_TILED:
+               case STATE_PSEUDO_TILED:
+                       break;
+               case STATE_FLOATING:
+                       set_floating(m, d, n, true);
+                       break;
+               case STATE_FULLSCREEN:
+                       set_fullscreen(m, d, n, true);
+                       break;
+       }
+
+       put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s on\n", m->id, d->id, n->id, STATE_STR(c->state));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+
+       return true;
+}
+
+void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       cancel_presel(m, d, n);
+       set_vacant(m, d, n, value);
+
+       if (!value && d->focus == n) {
+               neutralize_occluding_windows(m, d, n);
+       }
+
+       stack(d, n, (d->focus == n));
+}
+
+void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       client_t *c = n->client;
+
+       cancel_presel(m, d, n);
+       set_vacant(m, d, n, value);
+
+       if (value) {
+               c->wm_flags |= WM_FLAG_FULLSCREEN;
+               c->last_layer = c->layer;
+               c->layer = LAYER_ABOVE;
+       } else {
+               c->wm_flags &= ~WM_FLAG_FULLSCREEN;
+               c->layer = c->last_layer;
+               if (d->focus == n) {
+                       neutralize_occluding_windows(m, d, n);
+               }
+       }
+
+       ewmh_wm_state_update(n);
+       stack(d, n, (d->focus == n));
+}
+
+void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n)
+{
+       bool changed = false;
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
+                       if (a != f && a->client != NULL && f->client != NULL &&
+                           IS_FULLSCREEN(a->client) && stack_cmp(f->client, a->client) < 0) {
+                               set_state(m, d, a, a->client->last_state);
+                               changed = true;
+                       }
+               }
+       }
+       if (changed) {
+               arrange(m, d);
+       }
+}
+
+void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       node_t *p = n->parent;
+
+       if (p != NULL) {
+               set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
+               set_hidden_local(m, d, p, (p->first_child->hidden && p->second_child->hidden));
+       }
+
+       propagate_flags_upward(m, d, p);
+}
+
+void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->hidden == value) {
+               return;
+       }
+
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, false);
+       }
+
+       bool held_focus = is_descendant(d->focus, n);
+
+       propagate_hidden_downward(m, d, n, value);
+       propagate_hidden_upward(m, d, n);
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X hidden %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+       if (held_focus || d->focus == NULL) {
+               if (d->focus != NULL) {
+                       d->focus = NULL;
+                       draw_border(n, false, (mon == m));
+               }
+               if (d == mon->desk) {
+                       focus_node(m, d, d->focus);
+               } else {
+                       activate_node(m, d, d->focus);
+               }
+       }
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, true);
+       }
+}
+
+void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n->hidden == value) {
+               return;
+       }
+
+       n->hidden = value;
+
+       if (n->client != NULL) {
+               if (n->client->shown) {
+                       window_set_visibility(n->id, !value);
+               }
+
+               if (IS_TILED(n->client)) {
+                       set_vacant(m, d, n, value);
+               }
+
+               if (value) {
+                       n->client->wm_flags |= WM_FLAG_HIDDEN;
+               } else {
+                       n->client->wm_flags &= ~WM_FLAG_HIDDEN;
+               }
+
+               ewmh_wm_state_update(n);
+       }
+}
+
+void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       set_hidden_local(m, d, n, value);
+
+       propagate_hidden_downward(m, d, n->first_child, value);
+       propagate_hidden_downward(m, d, n->second_child, value);
+}
+
+void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       node_t *p = n->parent;
+
+       if (p != NULL) {
+               set_hidden_local(m, d, p, p->first_child->hidden && p->second_child->hidden);
+       }
+
+       propagate_hidden_upward(m, d, p);
+}
+
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->sticky == value) {
+               return;
+       }
+
+       if (d != m->desk) {
+               transfer_node(m, d, n, m, m->desk, m->desk->focus);
+       }
+
+       n->sticky = value;
+
+       if (value) {
+               m->sticky_count++;
+       } else {
+               m->sticky_count--;
+       }
+
+       if (n->client != NULL) {
+               if (value) {
+                       n->client->wm_flags |= WM_FLAG_STICKY;
+               } else {
+                       n->client->wm_flags &= ~WM_FLAG_STICKY;
+               }
+               ewmh_wm_state_update(n);
+       }
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X sticky %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+}
+
+void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->private == value) {
+               return;
+       }
+
+       n->private = value;
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X private %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+}
+
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (n == NULL || n->locked == value) {
+               return;
+       }
+
+       n->locked = value;
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X locked %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+       if (n == m->desk->focus) {
+               put_status(SBSC_MASK_REPORT);
+       }
+}
+
+void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+       if (value && mon->desk->focus == n) {
+               return;
+       }
+
+       n->client->urgent = value;
+
+       if (value) {
+               n->client->wm_flags |= WM_FLAG_DEMANDS_ATTENTION;
+       } else {
+               n->client->wm_flags &= ~WM_FLAG_DEMANDS_ATTENTION;
+       }
+
+       ewmh_wm_state_update(n);
+
+       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X urgent %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+       put_status(SBSC_MASK_REPORT);
+}
+
+/* Returns true if a contains b */
+bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
+{
+       return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
+               a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
+}
+
+xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n)
+{
+       client_t *c = n->client;
+       if (c != NULL) {
+               if (IS_FLOATING(c)) {
+                       return c->floating_rectangle;
+               } else {
+                       return c->tiled_rectangle;
+               }
+       } else {
+               int wg = (d == NULL ? 0 : (gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap));
+               xcb_rectangle_t rect = n->rectangle;
+               rect.width -= wg;
+               rect.height -= wg;
+               return rect;
+       }
+}
+
+void listen_enter_notify(node_t *n, bool enable)
+{
+       uint32_t mask = CLIENT_EVENT_MASK | (enable ? XCB_EVENT_MASK_ENTER_WINDOW : 0);
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client == NULL) {
+                       continue;
+               }
+               xcb_change_window_attributes(dpy, f->id, XCB_CW_EVENT_MASK, &mask);
+               if (f->presel != NULL) {
+                       xcb_change_window_attributes(dpy, f->presel->feedback, XCB_CW_EVENT_MASK, &mask);
+               }
+       }
+}
+
+#define DEF_FLAG_COUNT(flag) \
+       unsigned int flag##_count(node_t *n) \
+       { \
+               if (n == NULL) { \
+                       return 0; \
+               } else { \
+                       return ((n->flag ? 1 : 0) + \
+                               flag##_count(n->first_child) + \
+                               flag##_count(n->second_child)); \
+               } \
+       }
+       DEF_FLAG_COUNT(sticky)
+       DEF_FLAG_COUNT(private)
+       DEF_FLAG_COUNT(locked)
+#undef DEF_FLAG_COUNT
diff --git a/src/tree.h b/src/tree.h
new file mode 100644 (file)
index 0000000..1eb6764
--- /dev/null
@@ -0,0 +1,112 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_TREE_H
+#define BSPWM_TREE_H
+
+void arrange(monitor_t *m, desktop_t *d);
+void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect);
+presel_t *make_presel(void);
+void set_ratio(node_t *n, double rat);
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n);
+void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n);
+node_t *find_public(desktop_t *d);
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
+void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n);
+bool activate_node(monitor_t *m, desktop_t *d, node_t *n);
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n);
+bool focus_node(monitor_t *m, desktop_t *d, node_t *n);
+void hide_node(desktop_t *d, node_t *n);
+void show_node(desktop_t *d, node_t *n);
+node_t *make_node(uint32_t id);
+client_t *make_client(void);
+void initialize_client(node_t *n);
+bool is_focusable(node_t *n);
+bool is_leaf(node_t *n);
+bool is_first_child(node_t *n);
+bool is_second_child(node_t *n);
+unsigned int clients_count_in(node_t *n);
+node_t *brother_tree(node_t *n);
+node_t *first_extrema(node_t *n);
+node_t *second_extrema(node_t *n);
+node_t *first_focusable_leaf(node_t *n);
+node_t *next_leaf(node_t *n, node_t *r);
+node_t *prev_leaf(node_t *n, node_t *r);
+node_t *next_tiled_leaf(node_t *n, node_t *r);
+node_t *prev_tiled_leaf(node_t *n, node_t *r);
+bool is_adjacent(node_t *a, node_t *b, direction_t dir);
+node_t *find_fence(node_t *n, direction_t dir);
+bool is_child(node_t *a, node_t *b);
+bool is_descendant(node_t *a, node_t *b);
+bool find_by_id(uint32_t id, coordinates_t *loc);
+node_t *find_by_id_in(node_t *r, uint32_t id);
+void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel);
+unsigned int node_area(desktop_t *d, node_t *n);
+int tiled_count(node_t *n);
+void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel);
+void rotate_tree(node_t *n, int deg);
+void rotate_brother(node_t *n);
+void unrotate_tree(node_t *n, int rot);
+void unrotate_brother(node_t *n);
+void flip_tree(node_t *n, flip_t flp);
+void equalize_tree(node_t *n);
+int balance_tree(node_t *n);
+void unlink_node(monitor_t *m, desktop_t *d, node_t *n);
+void close_node(node_t *n);
+void kill_node(monitor_t *m, desktop_t *d, node_t *n);
+void remove_node(monitor_t *m, desktop_t *d, node_t *n);
+void free_node(node_t *n);
+bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
+bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd);
+bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel);
+void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);
+void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n);
+bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
+bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
+void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);
+void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n);
+void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n);
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);
+bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
+xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n);
+void listen_enter_notify(node_t *n, bool enable);
+
+unsigned int sticky_count(node_t *n);
+unsigned int private_count(node_t *n);
+unsigned int locked_count(node_t *n);
+
+#endif
diff --git a/src/types.h b/src/types.h
new file mode 100644 (file)
index 0000000..39b5cf7
--- /dev/null
@@ -0,0 +1,344 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_TYPES_H
+#define BSPWM_TYPES_H
+#include <stdbool.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/randr.h>
+#include <xcb/xcb_event.h>
+#include "helpers.h"
+
+#define MISSING_VALUE        "N/A"
+#define MAX_WM_STATES        4
+
+typedef enum {
+       TYPE_HORIZONTAL,
+       TYPE_VERTICAL
+} split_type_t;
+
+typedef enum {
+       MODE_AUTOMATIC,
+       MODE_MANUAL
+} split_mode_t;
+
+typedef enum {
+       STATE_TILED,
+       STATE_PSEUDO_TILED,
+       STATE_FLOATING,
+       STATE_FULLSCREEN
+} client_state_t;
+
+typedef enum {
+       WM_FLAG_MODAL = 1 << 0,
+       WM_FLAG_STICKY = 1 << 1,
+       WM_FLAG_MAXIMIZED_VERT = 1 << 2,
+       WM_FLAG_MAXIMIZED_HORZ = 1 << 3,
+       WM_FLAG_SHADED = 1 << 4,
+       WM_FLAG_SKIP_TASKBAR = 1 << 5,
+       WM_FLAG_SKIP_PAGER = 1 << 6,
+       WM_FLAG_HIDDEN = 1 << 7,
+       WM_FLAG_FULLSCREEN = 1 << 8,
+       WM_FLAG_ABOVE = 1 << 9,
+       WM_FLAG_BELOW = 1 << 10,
+       WM_FLAG_DEMANDS_ATTENTION = 1 << 11,
+} wm_flags_t;
+
+typedef enum {
+       LAYER_BELOW,
+       LAYER_NORMAL,
+       LAYER_ABOVE
+} stack_layer_t;
+
+typedef enum {
+       OPTION_NONE,
+       OPTION_TRUE,
+       OPTION_FALSE
+} option_bool_t;
+
+typedef enum {
+       ALTER_TOGGLE,
+       ALTER_SET
+} alter_state_t;
+
+typedef enum {
+       CYCLE_NEXT,
+       CYCLE_PREV
+} cycle_dir_t;
+
+typedef enum {
+       CIRCULATE_FORWARD,
+       CIRCULATE_BACKWARD
+} circulate_dir_t;
+
+typedef enum {
+       HISTORY_OLDER,
+       HISTORY_NEWER
+} history_dir_t;
+
+typedef enum {
+       DIR_NORTH,
+       DIR_WEST,
+       DIR_SOUTH,
+       DIR_EAST
+} direction_t;
+
+typedef enum {
+       HANDLE_LEFT = 1 << 0,
+       HANDLE_TOP = 1 << 1,
+       HANDLE_RIGHT = 1 << 2,
+       HANDLE_BOTTOM = 1 << 3,
+       HANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT,
+       HANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT,
+       HANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT,
+       HANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT
+} resize_handle_t;
+
+typedef enum {
+       ACTION_NONE,
+       ACTION_FOCUS,
+       ACTION_MOVE,
+       ACTION_RESIZE_SIDE,
+       ACTION_RESIZE_CORNER
+} pointer_action_t;
+
+typedef enum {
+       LAYOUT_TILED,
+       LAYOUT_MONOCLE
+} layout_t;
+
+typedef enum {
+       FLIP_HORIZONTAL,
+       FLIP_VERTICAL
+} flip_t;
+
+typedef enum {
+       FIRST_CHILD,
+       SECOND_CHILD
+} child_polarity_t;
+
+typedef struct {
+       option_bool_t automatic;
+       option_bool_t focused;
+       option_bool_t local;
+       option_bool_t active;
+       option_bool_t leaf;
+       option_bool_t window;
+       option_bool_t tiled;
+       option_bool_t pseudo_tiled;
+       option_bool_t floating;
+       option_bool_t fullscreen;
+       option_bool_t hidden;
+       option_bool_t sticky;
+       option_bool_t private;
+       option_bool_t locked;
+       option_bool_t urgent;
+       option_bool_t same_class;
+       option_bool_t descendant_of;
+       option_bool_t ancestor_of;
+       option_bool_t below;
+       option_bool_t normal;
+       option_bool_t above;
+} node_select_t;
+
+typedef struct {
+       option_bool_t occupied;
+       option_bool_t focused;
+       option_bool_t urgent;
+       option_bool_t local;
+} desktop_select_t;
+
+typedef struct {
+       option_bool_t occupied;
+       option_bool_t focused;
+} monitor_select_t;
+
+typedef struct icccm_props_t icccm_props_t;
+struct icccm_props_t {
+       bool take_focus;
+       bool input_hint;
+       bool delete_window;
+};
+
+typedef struct {
+       char class_name[3 * SMALEN / 2];
+       char instance_name[3 * SMALEN / 2];
+       unsigned int border_width;
+       bool urgent;
+       bool shown;
+       client_state_t state;
+       client_state_t last_state;
+       stack_layer_t layer;
+       stack_layer_t last_layer;
+       xcb_rectangle_t floating_rectangle;
+       xcb_rectangle_t tiled_rectangle;
+       xcb_size_hints_t size_hints;
+       icccm_props_t icccm_props;
+       wm_flags_t wm_flags;
+} client_t;
+
+typedef struct presel_t presel_t;
+struct presel_t {
+       double split_ratio;
+       direction_t split_dir;
+       xcb_window_t feedback;
+};
+
+typedef struct node_t node_t;
+struct node_t {
+       uint32_t id;
+       split_type_t split_type;
+       double split_ratio;
+       int birth_rotation;
+       presel_t *presel;
+       xcb_rectangle_t rectangle;
+       bool vacant;
+       bool hidden;
+       bool sticky;
+       bool private;
+       bool locked;
+       node_t *first_child;
+       node_t *second_child;
+       node_t *parent;
+       client_t *client;
+};
+
+typedef struct padding_t padding_t;
+struct padding_t {
+       int top;
+       int right;
+       int bottom;
+       int left;
+};
+
+typedef struct desktop_t desktop_t;
+struct desktop_t {
+       char name[SMALEN];
+       uint32_t id;
+       layout_t layout;
+       node_t *root;
+       node_t *focus;
+       desktop_t *prev;
+       desktop_t *next;
+       padding_t padding;
+       int window_gap;
+       unsigned int border_width;
+};
+
+typedef struct monitor_t monitor_t;
+struct monitor_t {
+       char name[SMALEN];
+       uint32_t id;
+       xcb_randr_output_t randr_id;
+       xcb_window_t root;
+       bool wired;
+       padding_t padding;
+       unsigned int sticky_count;
+       int window_gap;
+       unsigned int border_width;
+       xcb_rectangle_t rectangle;
+       desktop_t *desk;
+       desktop_t *desk_head;
+       desktop_t *desk_tail;
+       monitor_t *prev;
+       monitor_t *next;
+};
+
+typedef struct {
+       monitor_t *monitor;
+       desktop_t *desktop;
+       node_t *node;
+} coordinates_t;
+
+typedef struct history_t history_t;
+struct history_t {
+       coordinates_t loc;
+       bool latest;
+       history_t *prev;
+       history_t *next;
+};
+
+typedef struct stacking_list_t stacking_list_t;
+struct stacking_list_t {
+       node_t *node;
+       stacking_list_t *prev;
+       stacking_list_t *next;
+};
+
+typedef struct subscriber_list_t subscriber_list_t;
+struct subscriber_list_t {
+       int fd;
+       FILE *stream;
+       int field;
+       subscriber_list_t *prev;
+       subscriber_list_t *next;
+};
+
+typedef struct rule_t rule_t;
+struct rule_t {
+       char class_name[MAXLEN];
+       char instance_name[MAXLEN];
+       char effect[MAXLEN];
+       bool one_shot;
+       rule_t *prev;
+       rule_t *next;
+};
+
+typedef struct {
+       char class_name[3 * SMALEN / 2];
+       char instance_name[3 * SMALEN / 2];
+       char monitor_desc[MAXLEN];
+       char desktop_desc[MAXLEN];
+       char node_desc[MAXLEN];
+       char split_dir[SMALEN];
+       double split_ratio;
+       stack_layer_t *layer;
+       client_state_t *state;
+       bool hidden;
+       bool sticky;
+       bool private;
+       bool locked;
+       bool center;
+       bool follow;
+       bool manage;
+       bool focus;
+       bool border;
+} rule_consequence_t;
+
+typedef struct pending_rule_t pending_rule_t;
+struct pending_rule_t {
+       int fd;
+       xcb_window_t win;
+       rule_consequence_t *csq;
+       pending_rule_t *prev;
+       pending_rule_t *next;
+};
+
+typedef struct {
+    double x;
+    double y;
+} dpoint_t;
+
+#endif
diff --git a/src/window.c b/src/window.c
new file mode 100644 (file)
index 0000000..26d436e
--- /dev/null
@@ -0,0 +1,893 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "monitor.h"
+#include "query.h"
+#include "rule.h"
+#include "settings.h"
+#include "geometry.h"
+#include "pointer.h"
+#include "stack.h"
+#include "tree.h"
+#include "parse.h"
+#include "window.h"
+
+void schedule_window(xcb_window_t win)
+{
+       coordinates_t loc;
+       uint8_t override_redirect = 0;
+       xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
+
+       if (wa != NULL) {
+               override_redirect = wa->override_redirect;
+               free(wa);
+       }
+
+       if (override_redirect || locate_window(win, &loc)) {
+               return;
+       }
+
+       /* ignore pending windows */
+       for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+               if (pr->win == win) {
+                       return;
+               }
+       }
+
+       rule_consequence_t *csq = make_rule_conquence();
+       apply_rules(win, csq);
+       if (!schedule_rules(win, csq)) {
+               manage_window(win, csq, -1);
+               free(csq);
+       }
+}
+
+void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
+{
+       monitor_t *m = mon;
+       desktop_t *d = mon->desk;
+       node_t *f = mon->desk->focus;
+
+       parse_rule_consequence(fd, csq);
+
+       if (ewmh_handle_struts(win)) {
+               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+                       arrange(m, m->desk);
+               }
+       }
+
+       if (!csq->manage) {
+               free(csq->layer);
+               free(csq->state);
+               window_show(win);
+               return;
+       }
+
+       if (csq->node_desc[0] != '\0') {
+               coordinates_t ref = {m, d, f};
+               coordinates_t trg = {NULL, NULL, NULL};
+               if (node_from_desc(csq->node_desc, &ref, &trg) == SELECTOR_OK) {
+                       m = trg.monitor;
+                       d = trg.desktop;
+                       f = trg.node;
+               }
+       } else if (csq->desktop_desc[0] != '\0') {
+               coordinates_t ref = {m, d, NULL};
+               coordinates_t trg = {NULL, NULL, NULL};
+               if (desktop_from_desc(csq->desktop_desc, &ref, &trg) == SELECTOR_OK) {
+                       m = trg.monitor;
+                       d = trg.desktop;
+                       f = trg.desktop->focus;
+               }
+       } else if (csq->monitor_desc[0] != '\0') {
+               coordinates_t ref = {m, NULL, NULL};
+               coordinates_t trg = {NULL, NULL, NULL};
+               if (monitor_from_desc(csq->monitor_desc, &ref, &trg) == SELECTOR_OK) {
+                       m = trg.monitor;
+                       d = trg.monitor->desk;
+                       f = trg.monitor->desk->focus;
+               }
+       }
+
+       if (csq->sticky) {
+               m = mon;
+               d = mon->desk;
+               f = mon->desk->focus;
+       }
+
+       if (csq->split_dir[0] != '\0' && f != NULL) {
+               direction_t dir;
+               if (parse_direction(csq->split_dir, &dir)) {
+                       presel_dir(m, d, f, dir);
+               }
+       }
+
+       if (csq->split_ratio != 0 && f != NULL) {
+               presel_ratio(m, d, f, csq->split_ratio);
+       }
+
+       node_t *n = make_node(win);
+       client_t *c = make_client();
+       c->border_width = csq->border ? d->border_width : 0;
+       n->client = c;
+       initialize_client(n);
+       initialize_floating_rectangle(n);
+
+       if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
+               csq->center = true;
+       }
+
+       monitor_t *mm = monitor_from_client(c);
+       embrace_client(mm, c);
+       adapt_geometry(&mm->rectangle, &m->rectangle, n);
+
+       if (csq->center) {
+               window_center(m, c);
+       }
+
+       snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
+       snprintf(c->instance_name, sizeof(c->instance_name), "%s", csq->instance_name);
+
+       f = insert_node(m, d, n, f);
+       clients_count++;
+
+       put_status(SBSC_MASK_NODE_MANAGE, "node_manage 0x%08X 0x%08X 0x%08X 0x%08X\n", m->id, d->id, win, f!=NULL?f->id:0);
+
+       if (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {
+               c->layer = f->client->layer;
+       }
+
+       if (csq->layer != NULL) {
+               c->layer = *(csq->layer);
+       }
+
+       if (csq->state != NULL) {
+               set_state(m, d, n, *(csq->state));
+       }
+
+       set_hidden(m, d, n, csq->hidden);
+       set_sticky(m, d, n, csq->sticky);
+       set_private(m, d, n, csq->private);
+       set_locked(m, d, n, csq->locked);
+
+       arrange(m, d);
+
+       uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+       xcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);
+       set_window_state(win, XCB_ICCCM_WM_STATE_NORMAL);
+       window_grab_buttons(win);
+
+       if (d == m->desk) {
+               show_node(d, n);
+       } else {
+               hide_node(d, n);
+       }
+
+       if (!csq->hidden && csq->focus) {
+               if (d == mon->desk || csq->follow) {
+                       focus_node(m, d, n);
+               } else {
+                       activate_node(m, d, n);
+               }
+       } else {
+               stack(d, n, false);
+               draw_border(n, false, (m == mon));
+       }
+
+       ewmh_set_wm_desktop(n, d);
+       ewmh_update_client_list(false);
+       free(csq->layer);
+       free(csq->state);
+}
+
+void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state)
+{
+       long data[] = {state, XCB_NONE};
+       xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, WM_STATE, WM_STATE, 32, 2, data);
+}
+
+void unmanage_window(xcb_window_t win)
+{
+       coordinates_t loc;
+       if (locate_window(win, &loc)) {
+               put_status(SBSC_MASK_NODE_UNMANAGE, "node_unmanage 0x%08X 0x%08X 0x%08X\n", loc.monitor->id, loc.desktop->id, win);
+               remove_node(loc.monitor, loc.desktop, loc.node);
+               set_window_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
+               arrange(loc.monitor, loc.desktop);
+       } else {
+               for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+                       if (pr->win == win) {
+                               remove_pending_rule(pr);
+                               return;
+                       }
+               }
+       }
+}
+
+bool is_presel_window(xcb_window_t win)
+{
+       xcb_icccm_get_wm_class_reply_t reply;
+       bool ret = false;
+       if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
+               if (streq(BSPWM_CLASS_NAME, reply.class_name) && streq(PRESEL_FEEDBACK_I, reply.instance_name)) {
+                       ret = true;
+               }
+               xcb_icccm_get_wm_class_reply_wipe(&reply);
+       }
+       return ret;
+}
+
+void initialize_presel_feedback(node_t *n)
+{
+       if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
+               return;
+       }
+
+       xcb_window_t win = xcb_generate_id(dpy);
+       uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK;
+       uint32_t values[] = {get_color_pixel(presel_feedback_color), 1, focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0};
+       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                                 XCB_COPY_FROM_PARENT, mask, values);
+
+       xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
+       window_grab_buttons(win);
+       stacking_list_t *s = stack_tail;
+       while (s != NULL && !IS_TILED(s->node->client)) {
+               s = s->prev;
+       }
+       if (s != NULL) {
+               window_above(win, s->node->id);
+       }
+       n->presel->feedback = win;
+}
+
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL || n->presel == NULL || d->layout == LAYOUT_MONOCLE) {
+               return;
+       }
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, false);
+       }
+
+       bool exists = (n->presel->feedback != XCB_NONE);
+       if (!exists) {
+               initialize_presel_feedback(n);
+       }
+
+       int gap = gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap;
+       presel_t *p = n->presel;
+       xcb_rectangle_t rect = n->rectangle;
+       rect.x = rect.y = 0;
+       rect.width -= gap;
+       rect.height -= gap;
+       xcb_rectangle_t presel_rect = rect;
+
+       switch (p->split_dir) {
+               case DIR_NORTH:
+                       presel_rect.height = p->split_ratio * rect.height;
+                       break;
+               case DIR_EAST:
+                       presel_rect.width = (1 - p->split_ratio) * rect.width;
+                       presel_rect.x = rect.width - presel_rect.width;
+                       break;
+               case DIR_SOUTH:
+                       presel_rect.height = (1 - p->split_ratio) * rect.height;
+                       presel_rect.y = rect.height - presel_rect.height;
+                       break;
+               case DIR_WEST:
+                       presel_rect.width = p->split_ratio * rect.width;
+                       break;
+       }
+
+       window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
+                          presel_rect.width, presel_rect.height);
+
+       if (!exists && m->desk == d) {
+               window_show(p->feedback);
+       }
+
+       if (focus_follows_pointer) {
+               listen_enter_notify(d->root, true);
+       }
+}
+
+void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (n->presel != NULL) {
+                       draw_presel_feedback(m, d, n);
+               }
+               refresh_presel_feedbacks(m, d, n->first_child);
+               refresh_presel_feedbacks(m, d, n->second_child);
+       }
+}
+
+void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (n->presel != NULL) {
+                       window_show(n->presel->feedback);
+               }
+               show_presel_feedbacks(m, d, n->first_child);
+               show_presel_feedbacks(m, d, n->second_child);
+       }
+}
+
+void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (n->presel != NULL) {
+                       window_hide(n->presel->feedback);
+               }
+               hide_presel_feedbacks(m, d, n->first_child);
+               hide_presel_feedbacks(m, d, n->second_child);
+       }
+}
+
+void update_colors(void)
+{
+       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+                       update_colors_in(d->root, d, m);
+               }
+       }
+}
+
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
+{
+       if (n == NULL) {
+               return;
+       } else {
+               if (n->presel != NULL) {
+                       uint32_t pxl = get_color_pixel(presel_feedback_color);
+                       xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
+                       if (d == m->desk) {
+                               /* hack to induce back pixel refresh */
+                               window_hide(n->presel->feedback);
+                               window_show(n->presel->feedback);
+                       }
+               }
+               if (n == d->focus) {
+                       draw_border(n, true, (m == mon));
+               } else if (n->client != NULL) {
+                       draw_border(n, false, (m == mon));
+               } else {
+                       update_colors_in(n->first_child, d, m);
+                       update_colors_in(n->second_child, d, m);
+               }
+       }
+}
+
+void draw_border(node_t *n, bool focused_node, bool focused_monitor)
+{
+       if (n == NULL) {
+               return;
+       }
+
+       uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
+       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+               if (f->client != NULL && f->client->border_width > 0) {
+                       window_draw_border(f->id, border_color_pxl);
+               }
+       }
+}
+
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
+{
+       xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
+}
+
+void adopt_orphans(void)
+{
+       xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
+       if (qtr == NULL) {
+               return;
+       }
+
+       int len = xcb_query_tree_children_length(qtr);
+       xcb_window_t *wins = xcb_query_tree_children(qtr);
+
+       for (int i = 0; i < len; i++) {
+               uint32_t idx;
+               xcb_window_t win = wins[i];
+               if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
+                       schedule_window(win);
+               }
+       }
+
+       free(qtr);
+}
+
+uint32_t get_border_color(bool focused_node, bool focused_monitor)
+{
+       if (focused_monitor && focused_node) {
+               return get_color_pixel(focused_border_color);
+       } else if (focused_node) {
+               return get_color_pixel(active_border_color);
+       } else {
+               return get_color_pixel(normal_border_color);
+       }
+}
+
+void initialize_floating_rectangle(node_t *n)
+{
+       client_t *c = n->client;
+
+       xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
+
+       if (geo != NULL) {
+               c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
+       }
+
+       free(geo);
+}
+
+xcb_rectangle_t get_window_rectangle(node_t *n)
+{
+       client_t *c = n->client;
+       if (c != NULL) {
+               xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
+               if (g != NULL) {
+                       xcb_rectangle_t rect = (xcb_rectangle_t) {g->x, g->y, g->width, g->height};
+                       free(g);
+                       return rect;
+               }
+       }
+       return (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+}
+
+bool move_client(coordinates_t *loc, int dx, int dy)
+{
+       node_t *n = loc->node;
+
+       if (n == NULL || n->client == NULL) {
+               return false;
+       }
+
+       monitor_t *pm = NULL;
+
+       if (IS_TILED(n->client)) {
+               if (!grabbing) {
+                       return false;
+               }
+               xcb_window_t pwin = XCB_NONE;
+               query_pointer(&pwin, NULL);
+               if (pwin == n->id) {
+                       return false;
+               }
+               coordinates_t dst;
+               bool is_managed = (pwin != XCB_NONE && locate_window(pwin, &dst));
+               if (is_managed && dst.monitor == loc->monitor && IS_TILED(dst.node->client)) {
+                       swap_nodes(loc->monitor, loc->desktop, n, loc->monitor, loc->desktop, dst.node);
+                       return true;
+               } else {
+                       if (is_managed && dst.monitor == loc->monitor) {
+                               return false;
+                       } else {
+                               xcb_point_t pt = {0, 0};
+                               query_pointer(NULL, &pt);
+                               pm = monitor_from_point(pt);
+                       }
+               }
+       } else {
+               client_t *c = n->client;
+               xcb_rectangle_t rect = c->floating_rectangle;
+               int16_t x = rect.x + dx;
+               int16_t y = rect.y + dy;
+
+               if (focus_follows_pointer) {
+                       listen_enter_notify(loc->desktop->root, false);
+               }
+
+               window_move(n->id, x, y);
+
+               if (focus_follows_pointer) {
+                       listen_enter_notify(loc->desktop->root, true);
+               }
+
+               c->floating_rectangle.x = x;
+               c->floating_rectangle.y = y;
+               if (!grabbing) {
+                       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, rect.width, rect.height, x, y);
+               }
+               pm = monitor_from_client(c);
+       }
+
+       if (pm == NULL || pm == loc->monitor) {
+               return true;
+       }
+
+       bool focused = (n == mon->desk->focus);
+       transfer_node(loc->monitor, loc->desktop, n, pm, pm->desk, pm->desk->focus);
+       loc->monitor = pm;
+       loc->desktop = pm->desk;
+       if (focused) {
+               focus_node(pm, pm->desk, n);
+       }
+
+       return true;
+}
+
+bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy)
+{
+       node_t *n = loc->node;
+       if (n == NULL || n->client == NULL || n->client->state == STATE_FULLSCREEN) {
+               return false;
+       }
+       node_t *horizontal_fence = NULL, *vertical_fence = NULL;
+       xcb_rectangle_t rect = get_rectangle(NULL, n);
+       uint16_t width = rect.width, height = rect.height;
+       int16_t x = rect.x, y = rect.y;
+       if (n->client->state == STATE_TILED) {
+               if (rh & HANDLE_LEFT) {
+                       vertical_fence = find_fence(n, DIR_WEST);
+               } else if (rh & HANDLE_RIGHT) {
+                       vertical_fence = find_fence(n, DIR_EAST);
+               }
+               if (rh & HANDLE_TOP) {
+                       horizontal_fence = find_fence(n, DIR_NORTH);
+               } else if (rh & HANDLE_BOTTOM) {
+                       horizontal_fence = find_fence(n, DIR_SOUTH);
+               }
+               if (vertical_fence == NULL && horizontal_fence == NULL) {
+                       return false;
+               }
+               if (vertical_fence != NULL) {
+                       double sr = vertical_fence->split_ratio + (double) dx / vertical_fence->rectangle.width;
+                       sr = MAX(0, sr);
+                       sr = MIN(1, sr);
+                       vertical_fence->split_ratio = sr;
+               }
+               if (horizontal_fence != NULL) {
+                       double sr = horizontal_fence->split_ratio + (double) dy / horizontal_fence->rectangle.height;
+                       sr = MAX(0, sr);
+                       sr = MIN(1, sr);
+                       horizontal_fence->split_ratio = sr;
+               }
+               arrange(loc->monitor, loc->desktop);
+       } else {
+               int w = width + dx * (rh & HANDLE_LEFT ? -1 : (rh & HANDLE_RIGHT ? 1 : 0));
+               int h = height + dy * (rh & HANDLE_TOP ? -1 : (rh & HANDLE_BOTTOM ? 1 : 0));
+               width = MAX(1, w);
+               height = MAX(1, h);
+               apply_size_hints(n->client, &width, &height);
+               if (rh & HANDLE_LEFT) {
+                       x += rect.width - width;
+               }
+               if (rh & HANDLE_TOP) {
+                       y += rect.height - height;
+               }
+               n->client->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
+               if (n->client->state == STATE_FLOATING) {
+                       if (focus_follows_pointer) {
+                               listen_enter_notify(loc->desktop->root, false);
+                       }
+
+                       window_move_resize(n->id, x, y, width, height);
+
+                       if (focus_follows_pointer) {
+                               listen_enter_notify(loc->desktop->root, true);
+                       }
+
+                       if (!grabbing) {
+                               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, width, height, x, y);
+                       }
+               } else {
+                       arrange(loc->monitor, loc->desktop);
+               }
+       }
+       return true;
+}
+
+/* taken from awesomeWM */
+void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height)
+{
+       if (!honor_size_hints) {
+               return;
+       }
+
+       int32_t minw = 0, minh = 0;
+       int32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;
+
+       if (c->state == STATE_FULLSCREEN) {
+               return;
+       }
+
+       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
+               basew = c->size_hints.base_width;
+               baseh = c->size_hints.base_height;
+               real_basew = basew;
+               real_baseh = baseh;
+       } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
+               /* base size is substituted with min size if not specified */
+               basew = c->size_hints.min_width;
+               baseh = c->size_hints.min_height;
+       }
+
+       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
+               minw = c->size_hints.min_width;
+               minh = c->size_hints.min_height;
+       } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
+               /* min size is substituted with base size if not specified */
+               minw = c->size_hints.base_width;
+               minh = c->size_hints.base_height;
+       }
+
+       /* Handle the size aspect ratio */
+       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT &&
+           c->size_hints.min_aspect_den > 0 &&
+           c->size_hints.max_aspect_den > 0 &&
+           *height > real_baseh &&
+           *width > real_basew) {
+               /* ICCCM mandates:
+                * If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the
+                * window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing
+                * should be subtracted from the window size. (The minimum size is not to be used in place of the base size for
+                * this purpose.)
+                */
+               double dx = *width - real_basew;
+               double dy = *height - real_baseh;
+               double ratio = dx / dy;
+               double min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;
+               double max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;
+
+               if (max > 0 && min > 0 && ratio > 0) {
+                       if (ratio < min) {
+                               /* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */
+                               dy = dx / min + 0.5;
+                               *width  = dx + real_basew;
+                               *height = dy + real_baseh;
+                       } else if (ratio > max) {
+                               /* dx is too high, lower it (+0.5 for proper rounding) */
+                               dx = dy * max + 0.5;
+                               *width  = dx + real_basew;
+                               *height = dy + real_baseh;
+                       }
+               }
+       }
+
+       /* Handle the minimum size */
+       *width = MAX(*width, minw);
+       *height = MAX(*height, minh);
+
+       /* Handle the maximum size */
+       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
+       {
+               if (c->size_hints.max_width > 0) {
+                       *width = MIN(*width, c->size_hints.max_width);
+               }
+               if (c->size_hints.max_height > 0) {
+                       *height = MIN(*height, c->size_hints.max_height);
+               }
+       }
+
+       /* Handle the size increment */
+       if (c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE) &&
+           c->size_hints.width_inc > 0 && c->size_hints.height_inc > 0) {
+               uint16_t t1 = *width, t2 = *height;
+               unsigned_subtract(t1, basew);
+               unsigned_subtract(t2, baseh);
+               *width -= t1 % c->size_hints.width_inc;
+               *height -= t2 % c->size_hints.height_inc;
+       }
+}
+
+void query_pointer(xcb_window_t *win, xcb_point_t *pt)
+{
+       xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
+
+       if (qpr != NULL) {
+               if (win != NULL) {
+                       *win = qpr->child;
+                       xcb_point_t pt = {qpr->root_x, qpr->root_y};
+                       for (stacking_list_t *s = stack_tail; s != NULL; s = s->prev) {
+                               if (!s->node->client->shown || s->node->hidden) {
+                                       continue;
+                               }
+                               xcb_rectangle_t rect = get_rectangle(NULL, s->node);
+                               if (is_inside(pt, rect)) {
+                                       if (s->node->id == qpr->child || is_presel_window(qpr->child)) {
+                                               *win = s->node->id;
+                                       }
+                                       break;
+                               }
+                       }
+               }
+               if (pt != NULL) {
+                       *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
+               }
+       }
+
+       free(qpr);
+}
+
+void window_border_width(xcb_window_t win, uint32_t bw)
+{
+       uint32_t values[] = {bw};
+       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
+}
+
+void window_move(xcb_window_t win, int16_t x, int16_t y)
+{
+       uint32_t values[] = {x, y};
+       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
+}
+
+void window_resize(xcb_window_t win, uint16_t w, uint16_t h)
+{
+       uint32_t values[] = {w, h};
+       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);
+}
+
+void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
+{
+       uint32_t values[] = {x, y, w, h};
+       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
+}
+
+void window_center(monitor_t *m, client_t *c)
+{
+       xcb_rectangle_t *r = &c->floating_rectangle;
+       xcb_rectangle_t a = m->rectangle;
+       if (r->width >= a.width) {
+               r->x = a.x;
+       } else {
+               r->x = a.x + (a.width - r->width) / 2;
+       }
+       if (r->height >= a.height) {
+               r->y = a.y;
+       } else {
+               r->y = a.y + (a.height - r->height) / 2;
+       }
+       r->x -= c->border_width;
+       r->y -= c->border_width;
+}
+
+void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
+{
+       if (w2 == XCB_NONE) {
+               return;
+       }
+       uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
+       uint32_t values[] = {w2, mode};
+       xcb_configure_window(dpy, w1, mask, values);
+}
+
+void window_above(xcb_window_t w1, xcb_window_t w2)
+{
+       window_stack(w1, w2, XCB_STACK_MODE_ABOVE);
+}
+
+void window_below(xcb_window_t w1, xcb_window_t w2)
+{
+       window_stack(w1, w2, XCB_STACK_MODE_BELOW);
+}
+
+void window_lower(xcb_window_t win)
+{
+       uint32_t values[] = {XCB_STACK_MODE_BELOW};
+       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
+}
+
+void window_set_visibility(xcb_window_t win, bool visible)
+{
+       uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
+       uint32_t values_on[] = {ROOT_EVENT_MASK};
+       xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
+       if (visible) {
+               xcb_map_window(dpy, win);
+       } else {
+               xcb_unmap_window(dpy, win);
+       }
+       xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
+}
+
+void window_hide(xcb_window_t win)
+{
+       window_set_visibility(win, false);
+}
+
+void window_show(xcb_window_t win)
+{
+       window_set_visibility(win, true);
+}
+
+void update_input_focus(void)
+{
+       set_input_focus(mon->desk->focus);
+}
+
+void set_input_focus(node_t *n)
+{
+       if (n == NULL || n->client == NULL) {
+               clear_input_focus();
+       } else {
+               if (n->client->icccm_props.input_hint) {
+                       xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
+               } else if (n->client->icccm_props.take_focus) {
+                       send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
+               }
+       }
+}
+
+void clear_input_focus(void)
+{
+       xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+}
+
+void center_pointer(xcb_rectangle_t r)
+{
+       if (grabbing) {
+               return;
+       }
+       int16_t cx = r.x + r.width / 2;
+       int16_t cy = r.y + r.height / 2;
+       xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
+}
+
+void get_atom(char *name, xcb_atom_t *atom)
+{
+       xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
+       if (reply != NULL) {
+               *atom = reply->atom;
+       } else {
+               *atom = XCB_NONE;
+       }
+       free(reply);
+}
+
+void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
+{
+       xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
+}
+
+void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
+{
+       xcb_client_message_event_t *e = calloc(32, 1);
+
+       e->response_type = XCB_CLIENT_MESSAGE;
+       e->window = win;
+       e->type = property;
+       e->format = 32;
+       e->data.data32[0] = value;
+       e->data.data32[1] = XCB_CURRENT_TIME;
+
+       xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) e);
+       xcb_flush(dpy);
+       free(e);
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644 (file)
index 0000000..0d8d5f5
--- /dev/null
@@ -0,0 +1,76 @@
+/* Copyright (c) 2012, Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPWM_WINDOW_H
+#define BSPWM_WINDOW_H
+
+#include <stdarg.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_icccm.h>
+#include "types.h"
+
+void schedule_window(xcb_window_t win);
+void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);
+void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state);
+void unmanage_window(xcb_window_t win);
+bool is_presel_window(xcb_window_t win);
+void initialize_presel_feedback(node_t *n);
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n);
+void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
+void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
+void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
+void update_colors(void);
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m);
+void draw_border(node_t *n, bool focused_node, bool focused_monitor);
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl);
+void adopt_orphans(void);
+uint32_t get_border_color(bool focused_node, bool focused_monitor);
+void initialize_floating_rectangle(node_t *n);
+xcb_rectangle_t get_window_rectangle(node_t *n);
+bool move_client(coordinates_t *loc, int dx, int dy);
+bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy);
+void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height);
+void query_pointer(xcb_window_t *win, xcb_point_t *pt);
+void window_border_width(xcb_window_t win, uint32_t bw);
+void window_move(xcb_window_t win, int16_t x, int16_t y);
+void window_resize(xcb_window_t win, uint16_t w, uint16_t h);
+void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h);
+void window_center(monitor_t *m, client_t *c);
+void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode);
+void window_above(xcb_window_t w1, xcb_window_t w2);
+void window_below(xcb_window_t w1, xcb_window_t w2);
+void window_lower(xcb_window_t win);
+void window_set_visibility(xcb_window_t win, bool visible);
+void window_hide(xcb_window_t win);
+void window_show(xcb_window_t win);
+void update_input_focus(void);
+void set_input_focus(node_t *n);
+void clear_input_focus(void);
+void center_pointer(xcb_rectangle_t r);
+void get_atom(char *name, xcb_atom_t *atom);
+void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value);
+void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value);
+
+#endif
diff --git a/stack.c b/stack.c
deleted file mode 100644 (file)
index 4ec730d..0000000
--- a/stack.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include "bspwm.h"
-#include "window.h"
-#include "subscribe.h"
-#include "ewmh.h"
-#include "tree.h"
-#include "stack.h"
-
-stacking_list_t *make_stack(node_t *n)
-{
-       stacking_list_t *s = calloc(1, sizeof(stacking_list_t));
-       s->node = n;
-       s->prev = s->next = NULL;
-       return s;
-}
-
-void stack_insert_after(stacking_list_t *a, node_t *n)
-{
-       stacking_list_t *s = make_stack(n);
-       if (a == NULL) {
-               stack_head = stack_tail = s;
-       } else {
-               if (a->node == n) {
-                       free(s);
-                       return;
-               }
-               remove_stack_node(n);
-               stacking_list_t *b = a->next;
-               if (b != NULL) {
-                       b->prev = s;
-               }
-               s->next = b;
-               s->prev = a;
-               a->next = s;
-               if (stack_tail == a) {
-                       stack_tail = s;
-               }
-       }
-}
-
-void stack_insert_before(stacking_list_t *a, node_t *n)
-{
-       stacking_list_t *s = make_stack(n);
-       if (a == NULL) {
-               stack_head = stack_tail = s;
-       } else {
-               if (a->node == n) {
-                       free(s);
-                       return;
-               }
-               remove_stack_node(n);
-               stacking_list_t *b = a->prev;
-               if (b != NULL) {
-                       b->next = s;
-               }
-               s->prev = b;
-               s->next = a;
-               a->prev = s;
-               if (stack_head == a) {
-                       stack_head = s;
-               }
-       }
-}
-
-void remove_stack(stacking_list_t *s)
-{
-       if (s == NULL) {
-               return;
-       }
-       stacking_list_t *a = s->prev;
-       stacking_list_t *b = s->next;
-       if (a != NULL) {
-               a->next = b;
-       }
-       if (b != NULL) {
-               b->prev = a;
-       }
-       if (s == stack_head) {
-               stack_head = b;
-       }
-       if (s == stack_tail) {
-               stack_tail = a;
-       }
-       free(s);
-}
-
-void remove_stack_node(node_t *n)
-{
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
-                       if (s->node == f) {
-                               remove_stack(s);
-                               break;
-                       }
-               }
-       }
-}
-
-int stack_level(client_t *c)
-{
-       int layer_level = (c->layer == LAYER_NORMAL ? 1 : (c->layer == LAYER_BELOW ? 0 : 2));
-       int state_level = (IS_TILED(c) ? 0 : (IS_FLOATING(c) ? 2 : 1));
-       return 3 * layer_level + state_level;
-}
-
-int stack_cmp(client_t *c1, client_t *c2)
-{
-       return stack_level(c1) - stack_level(c2);
-}
-
-stacking_list_t *limit_above(node_t *n)
-{
-       stacking_list_t *s = stack_head;
-       while (s != NULL && stack_cmp(n->client, s->node->client) >= 0) {
-               s = s->next;
-       }
-       if (s == NULL) {
-               s = stack_tail;
-       }
-       if (s->node == n) {
-               s = s->prev;
-       }
-       return s;
-}
-
-stacking_list_t *limit_below(node_t *n)
-{
-       stacking_list_t *s = stack_tail;
-       while (s != NULL && stack_cmp(n->client, s->node->client) <= 0) {
-               s = s->prev;
-       }
-       if (s == NULL) {
-               s = stack_head;
-       }
-       if (s->node == n) {
-               s = s->next;
-       }
-       return s;
-}
-
-void stack(desktop_t *d, node_t *n, bool focused)
-{
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client == NULL || (IS_FLOATING(f->client) && !auto_raise)) {
-                       continue;
-               }
-
-               if (stack_head == NULL) {
-                       stack_insert_after(NULL, f);
-               } else {
-                       stacking_list_t *s = (focused ? limit_above(f) : limit_below(f));
-                       if (s == NULL) {
-                               continue;
-                       }
-                       int i = stack_cmp(f->client, s->node->client);
-                       if (i < 0 || (i == 0 && !focused)) {
-                               stack_insert_before(s, f);
-                               window_below(f->id, s->node->id);
-                               put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X below 0x%08X\n", f->id, s->node->id);
-                       } else {
-                               stack_insert_after(s, f);
-                               window_above(f->id, s->node->id);
-                               put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X above 0x%08X\n", f->id, s->node->id);
-                       }
-               }
-       }
-
-       ewmh_update_client_list(true);
-       restack_presel_feedbacks(d);
-}
-
-void restack_presel_feedbacks(desktop_t *d)
-{
-       stacking_list_t *s = stack_tail;
-       while (s != NULL && !IS_TILED(s->node->client)) {
-               s = s->prev;
-       }
-       if (s != NULL) {
-               restack_presel_feedbacks_in(d->root, s->node);
-       }
-}
-
-void restack_presel_feedbacks_in(node_t *r, node_t *n)
-{
-       if (r == NULL) {
-               return;
-       } else {
-               if (r->presel != NULL) {
-                       window_above(r->presel->feedback, n->id);
-               }
-               restack_presel_feedbacks_in(r->first_child, n);
-               restack_presel_feedbacks_in(r->second_child, n);
-       }
-}
diff --git a/stack.h b/stack.h
deleted file mode 100644 (file)
index 21bd048..0000000
--- a/stack.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_STACK_H
-#define BSPWM_STACK_H
-
-stacking_list_t *make_stack(node_t *n);
-void stack_insert_after(stacking_list_t *a, node_t *n);
-void stack_insert_before(stacking_list_t *a, node_t *n);
-void remove_stack(stacking_list_t *s);
-void remove_stack_node(node_t *n);
-int stack_level(client_t *c);
-int stack_cmp(client_t *c1, client_t *c2);
-stacking_list_t *limit_above(node_t *n);
-stacking_list_t *limit_below(node_t *n);
-void stack(desktop_t *d, node_t *n, bool focused);
-void restack_presel_feedbacks(desktop_t *d);
-void restack_presel_feedbacks_in(node_t *r, node_t *n);
-
-#endif
diff --git a/subscribe.c b/subscribe.c
deleted file mode 100644 (file)
index 8a0b2a1..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "settings.h"
-#include "subscribe.h"
-
-subscriber_list_t *make_subscriber_list(FILE *stream, int field)
-{
-       subscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t));
-       sb->prev = sb->next = NULL;
-       sb->stream = stream;
-       sb->field = field;
-       return sb;
-}
-
-void remove_subscriber(subscriber_list_t *sb)
-{
-       if (sb == NULL) {
-               return;
-       }
-       subscriber_list_t *a = sb->prev;
-       subscriber_list_t *b = sb->next;
-       if (a != NULL) {
-               a->next = b;
-       }
-       if (b != NULL) {
-               b->prev = a;
-       }
-       if (sb == subscribe_head) {
-               subscribe_head = b;
-       }
-       if (sb == subscribe_tail) {
-               subscribe_tail = a;
-       }
-       fclose(sb->stream);
-       free(sb);
-}
-
-void add_subscriber(FILE *stream, int field)
-{
-       subscriber_list_t *sb = make_subscriber_list(stream, field);
-       if (subscribe_head == NULL) {
-               subscribe_head = subscribe_tail = sb;
-       } else {
-               subscribe_tail->next = sb;
-               sb->prev = subscribe_tail;
-               subscribe_tail = sb;
-       }
-       if (sb->field & SBSC_MASK_REPORT) {
-               print_report(sb->stream);
-       }
-}
-
-int print_report(FILE *stream)
-{
-       fprintf(stream, "%s", status_prefix);
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name);
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       char c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o'));
-                       if (m->desk == d) {
-                               c = toupper(c);
-                       }
-                       fprintf(stream, ":%c%s", c, d->name);
-               }
-               if (m->desk != NULL) {
-                       fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout));
-                       if (m->desk->focus != NULL) {
-                               node_t *n = m->desk->focus;
-                               if (n->client != NULL) {
-                                       fprintf(stream, ":T%c", STATE_CHR(n->client->state));
-                               } else {
-                                       fprintf(stream, ":T@");
-                               }
-                               int i = 0;
-                               char flags[4];
-                               if (n->sticky) {
-                                       flags[i++] = 'S';
-                               }
-                               if (n->private) {
-                                       flags[i++] = 'P';
-                               }
-                               if (n->locked) {
-                                       flags[i++] = 'L';
-                               }
-                               flags[i] = '\0';
-                               fprintf(stream, ":G%s", flags);
-                       }
-               }
-               if (m != mon_tail) {
-                       fprintf(stream, "%s", ":");
-               }
-       }
-       fprintf(stream, "%s", "\n");
-       return fflush(stream);
-}
-
-void put_status(subscriber_mask_t mask, ...)
-{
-       subscriber_list_t *sb = subscribe_head;
-       int ret;
-       while (sb != NULL) {
-               subscriber_list_t *next = sb->next;
-               if (sb->field & mask) {
-                       if (mask == SBSC_MASK_REPORT) {
-                               ret = print_report(sb->stream);
-                       } else {
-                               char *fmt;
-                               va_list args;
-                               va_start(args, mask);
-                               fmt = va_arg(args, char *);
-                               vfprintf(sb->stream, fmt, args);
-                               va_end(args);
-                               ret = fflush(sb->stream);
-                       }
-                       if (ret != 0) {
-                               remove_subscriber(sb);
-                       }
-               }
-               sb = next;
-       }
-}
diff --git a/subscribe.h b/subscribe.h
deleted file mode 100644 (file)
index 496ce8d..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_SUBSCRIBE_H
-#define BSPWM_SUBSCRIBE_H
-
-typedef enum {
-       SBSC_MASK_REPORT = 1 << 0,
-       SBSC_MASK_MONITOR_ADD = 1 << 1,
-       SBSC_MASK_MONITOR_RENAME = 1 << 2,
-       SBSC_MASK_MONITOR_REMOVE = 1 << 3,
-       SBSC_MASK_MONITOR_SWAP = 1 << 4,
-       SBSC_MASK_MONITOR_FOCUS = 1 << 5,
-       SBSC_MASK_MONITOR_GEOMETRY = 1 << 6,
-       SBSC_MASK_DESKTOP_ADD = 1 << 7,
-       SBSC_MASK_DESKTOP_RENAME = 1 << 8,
-       SBSC_MASK_DESKTOP_REMOVE = 1 << 9,
-       SBSC_MASK_DESKTOP_SWAP = 1 << 10,
-       SBSC_MASK_DESKTOP_TRANSFER = 1 << 11,
-       SBSC_MASK_DESKTOP_FOCUS = 1 << 12,
-       SBSC_MASK_DESKTOP_ACTIVATE = 1 << 13,
-       SBSC_MASK_DESKTOP_LAYOUT = 1 << 14,
-       SBSC_MASK_NODE_MANAGE = 1 << 15,
-       SBSC_MASK_NODE_UNMANAGE = 1 << 16,
-       SBSC_MASK_NODE_SWAP = 1 << 17,
-       SBSC_MASK_NODE_TRANSFER = 1 << 18,
-       SBSC_MASK_NODE_FOCUS = 1 << 19,
-       SBSC_MASK_NODE_PRESEL = 1 << 20,
-       SBSC_MASK_NODE_STACK = 1 << 21,
-       SBSC_MASK_NODE_ACTIVATE = 1 << 22,
-       SBSC_MASK_NODE_GEOMETRY = 1 << 23,
-       SBSC_MASK_NODE_STATE = 1 << 24,
-       SBSC_MASK_NODE_FLAG = 1 << 25,
-       SBSC_MASK_NODE_LAYER = 1 << 26,
-       SBSC_MASK_POINTER_ACTION = 1 << 27,
-       SBSC_MASK_MONITOR = (1 << 7) - (1 << 1),
-       SBSC_MASK_DESKTOP = (1 << 15) - (1 << 7),
-       SBSC_MASK_NODE = (1 << 28) - (1 << 15),
-       SBSC_MASK_ALL = (1 << 28) - 1
-} subscriber_mask_t;
-
-subscriber_list_t *make_subscriber_list(FILE *stream, int field);
-void remove_subscriber(subscriber_list_t *sb);
-void add_subscriber(FILE *stream, int field);
-int print_report(FILE *stream);
-void put_status(subscriber_mask_t mask, ...);
-
-#endif
diff --git a/tree.c b/tree.c
deleted file mode 100644 (file)
index 4ffc9ef..0000000
--- a/tree.c
+++ /dev/null
@@ -1,1948 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "ewmh.h"
-#include "history.h"
-#include "monitor.h"
-#include "query.h"
-#include "geometry.h"
-#include "subscribe.h"
-#include "settings.h"
-#include "pointer.h"
-#include "stack.h"
-#include "window.h"
-#include "tree.h"
-
-void arrange(monitor_t *m, desktop_t *d)
-{
-       if (d->root == NULL) {
-               return;
-       }
-
-       layout_t l = d->layout;
-
-       if (single_monocle && tiled_count(d->root) <= 1) {
-               l = LAYOUT_MONOCLE;
-       }
-
-       xcb_rectangle_t rect = m->rectangle;
-
-       if (!paddingless_monocle || l != LAYOUT_MONOCLE) {
-               rect.x += m->padding.left + d->padding.left;
-               rect.y += m->padding.top + d->padding.top;
-               rect.width -= m->padding.left + d->padding.left + d->padding.right + m->padding.right;
-               rect.height -= m->padding.top + d->padding.top + d->padding.bottom + m->padding.bottom;
-       }
-
-       if (!gapless_monocle || l != LAYOUT_MONOCLE) {
-               rect.x += d->window_gap;
-               rect.y += d->window_gap;
-               rect.width -= d->window_gap;
-               rect.height -= d->window_gap;
-       }
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, false);
-       }
-
-       apply_layout(m, d, d->root, l, rect, rect);
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, true);
-       }
-}
-
-void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       n->rectangle = rect;
-
-       if (pointer_follows_focus && mon->desk->focus == n) {
-               xcb_rectangle_t r = rect;
-               r.width -= d->window_gap;
-               r.height -= d->window_gap;
-               center_pointer(r);
-       }
-
-       if (n->presel != NULL) {
-               draw_presel_feedback(m, d, n);
-       }
-
-       if (is_leaf(n)) {
-
-               if (n->client == NULL) {
-                       return;
-               }
-
-               unsigned int bw;
-               if ((borderless_monocle && n->client->state == STATE_TILED && l == LAYOUT_MONOCLE)
-                   || n->client->state == STATE_FULLSCREEN) {
-                       bw = 0;
-               } else {
-                       bw = n->client->border_width;
-               }
-
-               xcb_rectangle_t r;
-               xcb_rectangle_t cr = get_window_rectangle(n);
-               client_state_t s = n->client->state;
-               if (s == STATE_TILED || s == STATE_PSEUDO_TILED) {
-                       int wg = (gapless_monocle && l == LAYOUT_MONOCLE ? 0 : d->window_gap);
-                       /* tiled clients */
-                       if (s == STATE_TILED) {
-                               r = rect;
-                               int bleed = wg + 2 * bw;
-                               r.width = (bleed < r.width ? r.width - bleed : 1);
-                               r.height = (bleed < r.height ? r.height - bleed : 1);
-                       /* pseudo-tiled clients */
-                       } else {
-                               r = n->client->floating_rectangle;
-                               if (center_pseudo_tiled) {
-                                       r.x = rect.x - bw + (rect.width - wg - r.width) / 2;
-                                       r.y = rect.y - bw + (rect.height - wg - r.height) / 2;
-                               } else {
-                                       r.x = rect.x;
-                                       r.y = rect.y;
-                               }
-                       }
-                       n->client->tiled_rectangle = r;
-               /* floating clients */
-               } else if (s == STATE_FLOATING) {
-                       r = n->client->floating_rectangle;
-               /* fullscreen clients */
-               } else {
-                       r = m->rectangle;
-                       n->client->tiled_rectangle = r;
-               }
-
-               apply_size_hints(n->client, &r.width, &r.height);
-
-               if (!rect_eq(r, cr)) {
-                       window_move_resize(n->id, r.x, r.y, r.width, r.height);
-                       if (!grabbing) {
-                               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", m->id, d->id, n->id, r.width, r.height, r.x, r.y);
-                       }
-               }
-
-               window_border_width(n->id, bw);
-
-       } else {
-               xcb_rectangle_t first_rect;
-               xcb_rectangle_t second_rect;
-
-               if (l == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) {
-                       first_rect = second_rect = rect;
-               } else {
-                       unsigned int fence;
-                       if (n->split_type == TYPE_VERTICAL) {
-                               fence = rect.width * n->split_ratio;
-                               first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
-                               second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
-                       } else {
-                               fence = rect.height * n->split_ratio;
-                               first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
-                               second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
-                       }
-               }
-
-               apply_layout(m, d, n->first_child, l, first_rect, root_rect);
-               apply_layout(m, d, n->second_child, l, second_rect, root_rect);
-       }
-}
-
-presel_t *make_presel(void)
-{
-       presel_t *p = calloc(1, sizeof(presel_t));
-       p->split_dir = DIR_EAST;
-       p->split_ratio = split_ratio;
-       p->feedback = XCB_NONE;
-       return p;
-}
-
-void set_ratio(node_t *n, double rat)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       n->split_ratio = rat;
-}
-
-void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)
-{
-       if (n->presel == NULL) {
-               n->presel = make_presel();
-       }
-
-       n->presel->split_dir = dir;
-
-       put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X dir %s\n", m->id, d->id, n->id, SPLIT_DIR_STR(dir));
-}
-
-void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)
-{
-       if (n->presel == NULL) {
-               n->presel = make_presel();
-       }
-
-       n->presel->split_ratio = ratio;
-
-       put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X ratio %lf\n", m->id, d->id, n->id, ratio);
-}
-
-void cancel_presel(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n->presel == NULL) {
-               return;
-       }
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(n, false);
-       }
-
-       if (n->presel->feedback != XCB_NONE) {
-               xcb_destroy_window(dpy, n->presel->feedback);
-       }
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(n, true);
-       }
-
-       free(n->presel);
-       n->presel = NULL;
-
-       put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X cancel\n", m->id, d->id, n->id);
-}
-
-void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-       cancel_presel(m, d, n);
-       cancel_presel_in(m, d, n->first_child);
-       cancel_presel_in(m, d, n->second_child);
-}
-
-node_t *find_public(desktop_t *d)
-{
-       unsigned int b_manual_area = 0;
-       unsigned int b_automatic_area = 0;
-       node_t *b_manual = NULL;
-       node_t *b_automatic = NULL;
-       for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
-               if (n->vacant) {
-                       continue;
-               }
-               unsigned int n_area = node_area(d, n);
-               if (n_area > b_manual_area && (n->presel != NULL || !n->private)) {
-                       b_manual = n;
-                       b_manual_area = n_area;
-               }
-               if (n_area > b_automatic_area &&
-                   n->presel == NULL && !n->private && private_count(n->parent) == 0) {
-                       b_automatic = n;
-                       b_automatic_area = n_area;
-               }
-       }
-       if (b_automatic != NULL) {
-               return b_automatic;
-       } else {
-               return b_manual;
-       }
-}
-
-node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
-{
-       if (d == NULL || n == NULL) {
-               return NULL;
-       }
-
-       /* n: inserted node */
-       /* c: new internal node */
-       /* f: focus or insertion anchor */
-       /* p: parent of focus */
-       /* g: grand parent of focus */
-
-       if (f == NULL) {
-               f = d->root;
-       }
-
-       if (f == NULL) {
-               d->root = n;
-       } else if (IS_RECEPTACLE(f) && f->presel == NULL) {
-               node_t *p = f->parent;
-               if (p != NULL) {
-                       if (is_first_child(f)) {
-                               p->first_child = n;
-                       } else {
-                               p->second_child = n;
-                       }
-               } else {
-                       d->root = n;
-               }
-               n->parent = p;
-               free(f);
-               f = NULL;
-       } else {
-               node_t *c = make_node(XCB_NONE);
-               node_t *p = f->parent;
-               if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
-                       node_t *k = find_public(d);
-                       if (k != NULL) {
-                               f = k;
-                               p = f->parent;
-                       }
-                       if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
-                               xcb_rectangle_t rect = get_rectangle(d, f);
-                               presel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));
-                       }
-               }
-               if (f->presel == NULL && p != NULL && p->vacant) {
-                       f = p;
-                       p = f->parent;
-               }
-               n->parent = c;
-               c->birth_rotation = f->birth_rotation;
-               if (f->presel == NULL) {
-                       if (p == NULL) {
-                               if (initial_polarity == FIRST_CHILD) {
-                                       c->first_child = n;
-                                       c->second_child = f;
-                               } else {
-                                       c->first_child = f;
-                                       c->second_child = n;
-                               }
-                               if (m->rectangle.width > m->rectangle.height) {
-                                       c->split_type = TYPE_VERTICAL;
-                               } else {
-                                       c->split_type = TYPE_HORIZONTAL;
-                               }
-                               f->parent = c;
-                               d->root = c;
-                       } else {
-                               node_t *g = p->parent;
-                               c->parent = g;
-                               if (g != NULL) {
-                                       if (is_first_child(p)) {
-                                               g->first_child = c;
-                                       } else {
-                                               g->second_child = c;
-                                       }
-                               } else {
-                                       d->root = c;
-                               }
-                               c->split_type = p->split_type;
-                               c->split_ratio = p->split_ratio;
-                               p->parent = c;
-                               int rot;
-                               if (is_first_child(f)) {
-                                       c->first_child = n;
-                                       c->second_child = p;
-                                       rot = 90;
-                               } else {
-                                       c->first_child = p;
-                                       c->second_child = n;
-                                       rot = 270;
-                               }
-                               n->birth_rotation = rot;
-                               if (!n->vacant) {
-                                       rotate_tree(p, rot);
-                               }
-                       }
-               } else {
-                       if (p != NULL) {
-                               if (is_first_child(f)) {
-                                       p->first_child = c;
-                               } else {
-                                       p->second_child = c;
-                               }
-                       }
-                       c->split_ratio = f->presel->split_ratio;
-                       c->parent = p;
-                       f->parent = c;
-                       f->birth_rotation = 0;
-                       switch (f->presel->split_dir) {
-                               case DIR_WEST:
-                                       c->split_type = TYPE_VERTICAL;
-                                       c->first_child = n;
-                                       c->second_child = f;
-                                       break;
-                               case DIR_EAST:
-                                       c->split_type = TYPE_VERTICAL;
-                                       c->first_child = f;
-                                       c->second_child = n;
-                                       break;
-                               case DIR_NORTH:
-                                       c->split_type = TYPE_HORIZONTAL;
-                                       c->first_child = n;
-                                       c->second_child = f;
-                                       break;
-                               case DIR_SOUTH:
-                                       c->split_type = TYPE_HORIZONTAL;
-                                       c->first_child = f;
-                                       c->second_child = n;
-                                       break;
-                       }
-                       if (d->root == f) {
-                               d->root = c;
-                       }
-                       cancel_presel(m, d, f);
-               }
-       }
-
-       propagate_flags_upward(m, d, n);
-
-       if (d->focus == NULL && is_focusable(n)) {
-               d->focus = n;
-       }
-
-       return f;
-}
-
-void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n)
-{
-       node_t *r = make_node(XCB_NONE);
-       insert_node(m, d, r, n);
-}
-
-bool activate_node(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (d == NULL) {
-               d = history_last_desktop(m, NULL);
-               if (d == NULL) {
-                       d = m->desk_head;
-               }
-       }
-
-       if (d == NULL) {
-               return false;
-       }
-
-       if (n == NULL && d->root != NULL) {
-               n = history_last_node(d, NULL);
-               if (n == NULL) {
-                       n = first_focusable_leaf(d->root);
-               }
-       }
-
-       if (d == mon->desk || (n != NULL && !is_focusable(n))) {
-               return false;
-       }
-
-       if (n != NULL) {
-               if (d->focus != NULL && n != d->focus) {
-                       neutralize_occluding_windows(m, d, n);
-               }
-               stack(d, n, true);
-               if (d->focus != n) {
-                       for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
-                               if (f->client != NULL && !is_descendant(f, n)) {
-                                       window_draw_border(f->id, get_border_color(false, (m == mon)));
-                               }
-                       }
-               }
-               draw_border(n, true, (m == mon));
-       }
-
-       activate_desktop(m, d);
-
-       d->focus = n;
-       history_add(m, d, n);
-
-       put_status(SBSC_MASK_REPORT);
-
-       if (n == NULL) {
-               return true;
-       }
-
-       put_status(SBSC_MASK_NODE_ACTIVATE, "node_activate 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
-
-       return true;
-}
-
-void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else if (n->sticky) {
-               transfer_node(m, ds, n, m, dd, dd->focus);
-       } else {
-               /* we need references to the children because n might be freed after
-                * the first recursive call */
-               node_t *first_child = n->first_child;
-               node_t *second_child = n->second_child;
-               transfer_sticky_nodes(m, ds, dd, first_child);
-               transfer_sticky_nodes(m, ds, dd, second_child);
-       }
-}
-
-bool focus_node(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (m == NULL) {
-               m = history_last_monitor(NULL);
-               if (m == NULL) {
-                       m = mon_head;
-               }
-       }
-
-       if (m == NULL) {
-               return false;
-       }
-
-       if (d == NULL) {
-               d = history_last_desktop(m, NULL);
-               if (d == NULL) {
-                       d = m->desk_head;
-               }
-       }
-
-       if (d == NULL) {
-               return false;
-       }
-
-       if (n == NULL && d->root != NULL) {
-               n = history_last_node(d, NULL);
-               if (n == NULL) {
-                       n = first_focusable_leaf(d->root);
-               }
-       }
-
-       if (n != NULL && !is_focusable(n)) {
-               return false;
-       }
-
-       if ((mon != NULL && mon->desk != d) || n == NULL || n->client == NULL) {
-               clear_input_focus();
-       }
-
-       if (m->sticky_count > 0 && d != m->desk) {
-               sticky_still = false;
-               transfer_sticky_nodes(m, m->desk, d, m->desk->root);
-               sticky_still = true;
-               if (n == NULL && d->focus != NULL) {
-                       n = d->focus;
-               }
-       }
-
-       if (d->focus != NULL && n != d->focus) {
-               neutralize_occluding_windows(m, d, n);
-       }
-
-       if (n != NULL && n->client != NULL && n->client->urgent) {
-               set_urgent(m, d, n, false);
-       }
-
-       if (mon != m) {
-               if (mon != NULL) {
-                       for (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {
-                               draw_border(e->focus, true, false);
-                       }
-               }
-               for (desktop_t *e = m->desk_head; e != NULL; e = e->next) {
-                       if (e == d) {
-                               continue;
-                       }
-                       draw_border(e->focus, true, true);
-               }
-       }
-
-       if (d->focus != n) {
-               for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
-                       if (f->client != NULL && !is_descendant(f, n)) {
-                               window_draw_border(f->id, get_border_color(false, true));
-                       }
-               }
-       }
-
-       draw_border(n, true, true);
-
-       focus_desktop(m, d);
-
-       d->focus = n;
-       ewmh_update_active_window();
-       history_add(m, d, n);
-
-       put_status(SBSC_MASK_REPORT);
-
-       if (n == NULL) {
-               return true;
-       }
-
-       put_status(SBSC_MASK_NODE_FOCUS, "node_focus 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
-
-       stack(d, n, true);
-       set_input_focus(n);
-
-       if (pointer_follows_focus) {
-               center_pointer(get_rectangle(d, n));
-       }
-
-       return true;
-}
-
-void hide_node(desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else {
-               if (!n->hidden) {
-                       if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
-                               window_hide(n->presel->feedback);
-                       }
-                       if (n->client != NULL) {
-                               window_hide(n->id);
-                       }
-               }
-               if (n->client != NULL) {
-                       n->client->shown = false;
-               }
-               hide_node(d, n->first_child);
-               hide_node(d, n->second_child);
-       }
-}
-
-void show_node(desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else {
-               if (!n->hidden) {
-                       if (n->client != NULL) {
-                               window_show(n->id);
-                       }
-                       if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
-                               window_show(n->presel->feedback);
-                       }
-               }
-               if (n->client != NULL) {
-                       n->client->shown = true;
-               }
-               show_node(d, n->first_child);
-               show_node(d, n->second_child);
-       }
-}
-
-node_t *make_node(uint32_t id)
-{
-       if (id == XCB_NONE) {
-               id = xcb_generate_id(dpy);
-       }
-       node_t *n = calloc(1, sizeof(node_t));
-       n->id = id;
-       n->parent = n->first_child = n->second_child = NULL;
-       n->vacant = n->hidden = n->sticky = n->private = n->locked = false;
-       n->split_ratio = split_ratio;
-       n->split_type = TYPE_VERTICAL;
-       n->birth_rotation = 0;
-       n->presel = NULL;
-       n->client = NULL;
-       return n;
-}
-
-client_t *make_client(void)
-{
-       client_t *c = calloc(1, sizeof(client_t));
-       c->state = c->last_state = STATE_TILED;
-       c->layer = c->last_layer = LAYER_NORMAL;
-       snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
-       snprintf(c->instance_name, sizeof(c->instance_name), "%s", MISSING_VALUE);
-       c->border_width = border_width;
-       c->urgent = false;
-       c->shown = false;
-       c->wm_flags = 0;
-       c->icccm_props.input_hint = true;
-       c->icccm_props.take_focus = false;
-       c->icccm_props.delete_window = false;
-       c->size_hints.flags = 0;
-       return c;
-}
-
-void initialize_client(node_t *n)
-{
-       xcb_window_t win = n->id;
-       client_t *c = n->client;
-       xcb_icccm_get_wm_protocols_reply_t protos;
-       if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protos, NULL) == 1) {
-               for (uint32_t i = 0; i < protos.atoms_len; i++) {
-                       if (protos.atoms[i] == WM_TAKE_FOCUS) {
-                               c->icccm_props.take_focus = true;
-                       } else if (protos.atoms[i] == WM_DELETE_WINDOW) {
-                               c->icccm_props.delete_window = true;
-                       }
-               }
-               xcb_icccm_get_wm_protocols_reply_wipe(&protos);
-       }
-       xcb_ewmh_get_atoms_reply_t wm_state;
-       if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
-               for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_WM_STATES; i++) {
-#define HANDLE_WM_STATE(s) \
-                       if (wm_state.atoms[i] == ewmh->_NET_WM_STATE_##s) { \
-                               c->wm_flags |= WM_FLAG_##s; continue; \
-                       }
-                       HANDLE_WM_STATE(MODAL)
-                       HANDLE_WM_STATE(STICKY)
-                       HANDLE_WM_STATE(MAXIMIZED_VERT)
-                       HANDLE_WM_STATE(MAXIMIZED_HORZ)
-                       HANDLE_WM_STATE(SHADED)
-                       HANDLE_WM_STATE(SKIP_TASKBAR)
-                       HANDLE_WM_STATE(SKIP_PAGER)
-                       HANDLE_WM_STATE(HIDDEN)
-                       HANDLE_WM_STATE(FULLSCREEN)
-                       HANDLE_WM_STATE(ABOVE)
-                       HANDLE_WM_STATE(BELOW)
-                       HANDLE_WM_STATE(DEMANDS_ATTENTION)
-#undef HANDLE_WM_STATE
-               }
-               xcb_ewmh_get_atoms_reply_wipe(&wm_state);
-       }
-       xcb_icccm_wm_hints_t hints;
-       if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1
-               && (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {
-               c->icccm_props.input_hint = hints.input;
-       }
-       xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &c->size_hints, NULL);
-}
-
-bool is_focusable(node_t *n)
-{
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client != NULL && !f->hidden) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool is_leaf(node_t *n)
-{
-       return (n != NULL && n->first_child == NULL && n->second_child == NULL);
-}
-
-bool is_first_child(node_t *n)
-{
-       return (n != NULL && n->parent != NULL && n->parent->first_child == n);
-}
-
-bool is_second_child(node_t *n)
-{
-       return (n != NULL && n->parent != NULL && n->parent->second_child == n);
-}
-
-unsigned int clients_count_in(node_t *n)
-{
-       if (n == NULL) {
-               return 0;
-       } else {
-               return (n->client != NULL ? 1 : 0) +
-                       clients_count_in(n->first_child) +
-                       clients_count_in(n->second_child);
-       }
-}
-
-node_t *brother_tree(node_t *n)
-{
-       if (n == NULL || n->parent == NULL) {
-               return NULL;
-       }
-       if (is_first_child(n)) {
-               return n->parent->second_child;
-       } else {
-               return n->parent->first_child;
-       }
-}
-
-node_t *first_extrema(node_t *n)
-{
-       if (n == NULL) {
-               return NULL;
-       } else if (n->first_child == NULL) {
-               return n;
-       } else {
-               return first_extrema(n->first_child);
-       }
-}
-
-node_t *second_extrema(node_t *n)
-{
-       if (n == NULL) {
-               return NULL;
-       } else if (n->second_child == NULL) {
-               return n;
-       } else {
-               return second_extrema(n->second_child);
-       }
-}
-
-node_t *first_focusable_leaf(node_t *n)
-{
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client != NULL && !f->hidden) {
-                       return f;
-               }
-       }
-       return NULL;
-}
-
-node_t *next_leaf(node_t *n, node_t *r)
-{
-       if (n == NULL) {
-               return NULL;
-       }
-       node_t *p = n;
-       while (is_second_child(p) && p != r) {
-               p = p->parent;
-       }
-       if (p == r) {
-               return NULL;
-       }
-       return first_extrema(p->parent->second_child);
-}
-
-node_t *prev_leaf(node_t *n, node_t *r)
-{
-       if (n == NULL) {
-               return NULL;
-       }
-       node_t *p = n;
-       while (is_first_child(p) && p != r) {
-               p = p->parent;
-       }
-       if (p == r) {
-               return NULL;
-       }
-       return second_extrema(p->parent->first_child);
-}
-
-node_t *next_tiled_leaf(node_t *n, node_t *r)
-{
-       node_t *next = next_leaf(n, r);
-       if (next == NULL || (next->client != NULL && !next->vacant)) {
-               return next;
-       } else {
-               return next_tiled_leaf(next, r);
-       }
-}
-
-node_t *prev_tiled_leaf(node_t *n, node_t *r)
-{
-       node_t *prev = prev_leaf(n, r);
-       if (prev == NULL || (prev->client != NULL && !prev->vacant)) {
-               return prev;
-       } else {
-               return prev_tiled_leaf(prev, r);
-       }
-}
-
-/* Returns true if *b* is adjacent to *a* in the direction *dir* */
-bool is_adjacent(node_t *a, node_t *b, direction_t dir)
-{
-       switch (dir) {
-               case DIR_EAST:
-                       return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
-                       break;
-               case DIR_SOUTH:
-                       return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
-                       break;
-               case DIR_WEST:
-                       return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
-                       break;
-               case DIR_NORTH:
-                       return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
-                       break;
-       }
-       return false;
-}
-
-node_t *find_fence(node_t *n, direction_t dir)
-{
-       node_t *p;
-
-       if (n == NULL) {
-               return NULL;
-       }
-
-       p = n->parent;
-
-       while (p != NULL) {
-               if ((dir == DIR_NORTH && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
-                   (dir == DIR_WEST && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
-                   (dir == DIR_SOUTH && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
-                   (dir == DIR_EAST && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
-                       return p;
-               p = p->parent;
-       }
-
-       return NULL;
-}
-
-/* returns *true* if *a* is a child of *b* */
-bool is_child(node_t *a, node_t *b)
-{
-       if (a == NULL || b == NULL) {
-               return false;
-       }
-       return (a->parent != NULL && a->parent == b);
-}
-
-/* returns *true* if *a* is a descendant of *b* */
-bool is_descendant(node_t *a, node_t *b)
-{
-       if (a == NULL || b == NULL) {
-               return false;
-       }
-       while (a != b && a != NULL) {
-               a = a->parent;
-       }
-       return a == b;
-}
-
-bool find_by_id(uint32_t id, coordinates_t *loc)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       node_t *n = find_by_id_in(d->root, id);
-                       if (n != NULL) {
-                               loc->monitor = m;
-                               loc->desktop = d;
-                               loc->node = n;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-node_t *find_by_id_in(node_t *r, uint32_t id)
-{
-       if (r == NULL) {
-               return NULL;
-       } else if (r->id == id) {
-               return r;
-       } else {
-               node_t *f = find_by_id_in(r->first_child, id);
-               if (f != NULL) {
-                       return f;
-               } else {
-                       return find_by_id_in(r->second_child, id);
-               }
-       }
-}
-
-void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel)
-{
-       if (ref->node == NULL) {
-               return;
-       }
-
-       xcb_rectangle_t rect = get_rectangle(ref->desktop, ref->node);
-       double md = DBL_MAX, mr = UINT32_MAX;
-
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               desktop_t *d = m->desk;
-               for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
-                       coordinates_t loc = {m, d, f};
-                       xcb_rectangle_t r = get_rectangle(d, f);
-                       if (f == ref->node ||
-                           f->client == NULL ||
-                           f->hidden ||
-                           is_descendant(f, ref->node) ||
-                           !node_matches(&loc, ref, sel) ||
-                           !on_dir_side(rect, r, dir)) {
-                               continue;
-                       }
-                       double fd = distance_center(rect, r);
-                       uint32_t fr = history_rank(f);
-                       if (fd < md || (fd == md && fr < mr)) {
-                               md = fd;
-                               mr = fr;
-                               *dst = loc;
-                       }
-               }
-       }
-}
-
-unsigned int node_area(desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return 0;
-       }
-       return area(get_rectangle(d, n));
-}
-
-int tiled_count(node_t *n)
-{
-       if (n == NULL) {
-               return 0;
-       }
-       int cnt = 0;
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (!f->hidden && f->client != NULL && IS_TILED(f->client)) {
-                       cnt++;
-               }
-       }
-       return cnt;
-}
-
-void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel)
-{
-       unsigned int b_area = 0;
-
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
-                               coordinates_t loc = {m, d, f};
-                               if (f->client == NULL || f->vacant || !node_matches(&loc, ref, sel)) {
-                                       continue;
-                               }
-                               unsigned int f_area = node_area(d, f);
-                               if (f_area > b_area) {
-                                       *dst = loc;
-                                       b_area = f_area;
-                               }
-                       }
-               }
-       }
-}
-
-void rotate_tree(node_t *n, int deg)
-{
-       if (n == NULL || is_leaf(n) || deg == 0) {
-               return;
-       }
-
-       node_t *tmp;
-
-       if ((deg == 90 && n->split_type == TYPE_HORIZONTAL) ||
-           (deg == 270 && n->split_type == TYPE_VERTICAL) ||
-           deg == 180) {
-               tmp = n->first_child;
-               n->first_child = n->second_child;
-               n->second_child = tmp;
-               n->split_ratio = 1.0 - n->split_ratio;
-       }
-
-       if (deg != 180) {
-               if (n->split_type == TYPE_HORIZONTAL) {
-                       n->split_type = TYPE_VERTICAL;
-               } else if (n->split_type == TYPE_VERTICAL) {
-                       n->split_type = TYPE_HORIZONTAL;
-               }
-       }
-
-       rotate_tree(n->first_child, deg);
-       rotate_tree(n->second_child, deg);
-}
-
-void rotate_brother(node_t *n)
-{
-       rotate_tree(brother_tree(n), n->birth_rotation);
-}
-
-void unrotate_tree(node_t *n, int rot)
-{
-       if (rot == 0) {
-               return;
-       }
-       rotate_tree(n, 360 - rot);
-}
-
-void unrotate_brother(node_t *n)
-{
-       unrotate_tree(brother_tree(n), n->birth_rotation);
-}
-
-void flip_tree(node_t *n, flip_t flp)
-{
-       if (n == NULL || is_leaf(n)) {
-               return;
-       }
-
-       node_t *tmp;
-
-       if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL) ||
-           (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
-               tmp = n->first_child;
-               n->first_child = n->second_child;
-               n->second_child = tmp;
-               n->split_ratio = 1.0 - n->split_ratio;
-       }
-
-       flip_tree(n->first_child, flp);
-       flip_tree(n->second_child, flp);
-}
-
-void equalize_tree(node_t *n)
-{
-       if (n == NULL || n->vacant) {
-               return;
-       } else {
-               n->split_ratio = split_ratio;
-               equalize_tree(n->first_child);
-               equalize_tree(n->second_child);
-       }
-}
-
-int balance_tree(node_t *n)
-{
-       if (n == NULL || n->vacant) {
-               return 0;
-       } else if (is_leaf(n)) {
-               return 1;
-       } else {
-               int b1 = balance_tree(n->first_child);
-               int b2 = balance_tree(n->second_child);
-               int b = b1 + b2;
-               if (b1 > 0 && b2 > 0) {
-                       n->split_ratio = (double) b1 / b;
-               }
-               return b;
-       }
-}
-
-void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (d == NULL || n == NULL) {
-               return;
-       }
-
-       node_t *p = n->parent;
-
-       if (p == NULL) {
-               d->root = NULL;
-               d->focus = NULL;
-       } else {
-               if (d->focus == p || is_descendant(d->focus, n)) {
-                       d->focus = NULL;
-               }
-
-               history_remove(d, p, false);
-               cancel_presel(m, d, p);
-               if (p->sticky) {
-                       m->sticky_count--;
-               }
-
-               node_t *b = brother_tree(n);
-               node_t *g = p->parent;
-
-               if (!n->vacant) {
-                       unrotate_tree(b, n->birth_rotation);
-               }
-
-               b->parent = g;
-
-               if (g != NULL) {
-                       if (is_first_child(p)) {
-                               g->first_child = b;
-                       } else {
-                               g->second_child = b;
-                       }
-               } else {
-                       d->root = b;
-               }
-
-               b->birth_rotation = p->birth_rotation;
-
-               free(p);
-               n->parent = NULL;
-
-               propagate_flags_upward(m, d, b);
-       }
-}
-
-void close_node(node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else if (n->client != NULL) {
-               if (n->client->icccm_props.delete_window) {
-                       send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
-               } else {
-                       xcb_kill_client(dpy, n->id);
-               }
-       } else {
-               close_node(n->first_child);
-               close_node(n->second_child);
-       }
-}
-
-void kill_node(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client != NULL) {
-                       xcb_kill_client(dpy, f->id);
-               }
-       }
-
-       remove_node(m, d, n);
-}
-
-void remove_node(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       unlink_node(m, d, n);
-       history_remove(d, n, true);
-       remove_stack_node(n);
-       cancel_presel_in(m, d, n);
-       if (m->sticky_count > 0) {
-               m->sticky_count -= sticky_count(n);
-       }
-       clients_count -= clients_count_in(n);
-       if (is_descendant(grabbed_node, n)) {
-               grabbed_node = NULL;
-       }
-       free_node(n);
-
-       ewmh_update_client_list(false);
-       ewmh_update_client_list(true);
-
-       if (mon != NULL && d->focus == NULL) {
-               if (d == mon->desk) {
-                       focus_node(m, d, NULL);
-               } else {
-                       activate_node(m, d, NULL);
-               }
-       }
-}
-
-void free_node(node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-       node_t *first_child = n->first_child;
-       node_t *second_child = n->second_child;
-       free(n->client);
-       free(n);
-       free_node(first_child);
-       free_node(second_child);
-}
-
-bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
-{
-       if (n1 == NULL || n2 == NULL || n1 == n2 || is_descendant(n1, n2) || is_descendant(n2, n1) ||
-           (d1 != d2 && ((m1->sticky_count > 0 && sticky_count(n1) > 0) ||
-                         (m2->sticky_count > 0 && sticky_count(n2) > 0)))) {
-               return false;
-       }
-
-       put_status(SBSC_MASK_NODE_SWAP, "node_swap 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, n1->id, m2->id, d2->id, n2->id);
-
-       node_t *pn1 = n1->parent;
-       node_t *pn2 = n2->parent;
-       bool n1_first_child = is_first_child(n1);
-       bool n2_first_child = is_first_child(n2);
-       int br1 = n1->birth_rotation;
-       int br2 = n2->birth_rotation;
-       bool n1_held_focus = is_descendant(d1->focus, n1);
-       bool n2_held_focus = is_descendant(d2->focus, n2);
-       node_t *last_d1_focus = d1->focus;
-       node_t *last_d2_focus = d2->focus;
-
-       if (pn1 != NULL) {
-               if (n1_first_child) {
-                       pn1->first_child = n2;
-               } else {
-                       pn1->second_child = n2;
-               }
-       }
-
-       if (pn2 != NULL) {
-               if (n2_first_child) {
-                       pn2->first_child = n1;
-               } else {
-                       pn2->second_child = n1;
-               }
-       }
-
-       n1->parent = pn2;
-       n2->parent = pn1;
-       n1->birth_rotation = br2;
-       n2->birth_rotation = br1;
-
-       propagate_flags_upward(m2, d2, n1);
-       propagate_flags_upward(m1, d1, n2);
-
-       if (d1 != d2) {
-               if (d1->root == n1) {
-                       d1->root = n2;
-               }
-
-               if (d2->root == n2) {
-                       d2->root = n1;
-               }
-
-               if (n1_held_focus) {
-                       d1->focus = n2_held_focus ? last_d2_focus : n2;
-               }
-
-               if (n2_held_focus) {
-                       d2->focus = n1_held_focus ? last_d1_focus : n1;
-               }
-
-               if (m1 != m2) {
-                       adapt_geometry(&m2->rectangle, &m1->rectangle, n2);
-                       adapt_geometry(&m1->rectangle, &m2->rectangle, n1);
-               }
-
-               ewmh_set_wm_desktop(n1, d2);
-               ewmh_set_wm_desktop(n2, d1);
-
-               history_swap_nodes(m1, d1, n1, m2, d2, n2);
-
-               if (m1->desk != d1 && m2->desk == d2) {
-                       show_node(d2, n1);
-                       hide_node(d2, n2);
-               } else if (m1->desk == d1 && m2->desk != d2) {
-                       hide_node(d1, n1);
-                       show_node(d1, n2);
-               }
-
-               if (n1_held_focus) {
-                       if (d1 == mon->desk) {
-                               focus_node(m1, d1, d1->focus);
-                       } else {
-                               activate_node(m1, d1, d1->focus);
-                       }
-               } else {
-                       draw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));
-               }
-
-               if (n2_held_focus) {
-                       if (d2 == mon->desk) {
-                               focus_node(m2, d2, d2->focus);
-                       } else {
-                               activate_node(m2, d2, d2->focus);
-                       }
-               } else {
-                       draw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));
-               }
-       }
-
-       arrange(m1, d1);
-
-       if (d1 != d2) {
-               arrange(m2, d2);
-       }
-
-       return true;
-}
-
-bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
-{
-       if (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {
-               return false;
-       }
-
-       if (sticky_still && ds != dd && ms->sticky_count > 0 && sticky_count(ns) > 0) {
-               return false;
-       }
-
-       put_status(SBSC_MASK_NODE_TRANSFER, "node_transfer 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", ms->id, ds->id, ns->id, md->id, dd->id, nd!=NULL?nd->id:0);
-
-       bool held_focus = is_descendant(ds->focus, ns);
-       /* avoid ending up with a dangling pointer (because of unlink_node) */
-       node_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;
-
-       if (held_focus && ds == mon->desk) {
-               clear_input_focus();
-       }
-
-       unlink_node(ms, ds, ns);
-       insert_node(md, dd, ns, nd);
-
-       if (md != ms && (ns->client == NULL || monitor_from_client(ns->client) != md)) {
-               adapt_geometry(&ms->rectangle, &md->rectangle, ns);
-       }
-
-       if (ds != dd) {
-               ewmh_set_wm_desktop(ns, dd);
-               if (sticky_still) {
-                       if (ds == ms->desk && dd != md->desk) {
-                               hide_node(ds, ns);
-                       } else if (ds != ms->desk && dd == md->desk) {
-                               show_node(dd, ns);
-                       }
-               }
-       }
-
-       history_transfer_node(md, dd, ns);
-       stack(dd, ns, false);
-
-       if (ds == dd) {
-               if (held_focus) {
-                       if (ds == mon->desk) {
-                               focus_node(ms, ds, last_ds_focus);
-                       } else {
-                               activate_node(ms, ds, last_ds_focus);
-                       }
-               } else {
-                       draw_border(ns, is_descendant(ns, ds->focus), (ms == mon));
-               }
-       } else {
-               if (held_focus) {
-                       if (ds == mon->desk) {
-                               focus_node(ms, ds, ds->focus);
-                       } else {
-                               activate_node(ms, ds, ds->focus);
-                       }
-               }
-               if (dd->focus == ns) {
-                       if (dd == mon->desk) {
-                               focus_node(md, dd, held_focus ? last_ds_focus : dd->focus);
-                       } else {
-                               activate_node(md, dd, held_focus ? last_ds_focus : dd->focus);
-                       }
-               } else {
-                       draw_border(ns, is_descendant(ns, dd->focus), (md == mon));
-               }
-       }
-
-       arrange(ms, ds);
-
-       if (ds != dd) {
-               arrange(md, dd);
-       }
-
-       return true;
-}
-
-bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel)
-{
-       if (ref->node == NULL) {
-               return false;
-       }
-
-       monitor_t *m = ref->monitor;
-       desktop_t *d = ref->desktop;
-       node_t *n = ref->node;
-
-       node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
-
-#define HANDLE_BOUNDARIES(f)  \
-       while (f == NULL) { \
-               d = (dir == CYCLE_PREV ? d->prev : d->next); \
-               if (d == NULL) { \
-                       m = (dir == CYCLE_PREV ? m->prev : m->next); \
-                       if (m == NULL) { \
-                               m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
-                       } \
-                       d = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
-               } \
-               f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); \
-       }
-       HANDLE_BOUNDARIES(f);
-
-       while (f != n) {
-               coordinates_t loc = {m, d, f};
-               if (f->client != NULL && !f->hidden && node_matches(&loc, ref, sel)) {
-                       *dst = loc;
-                       return true;
-               }
-               f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
-               HANDLE_BOUNDARIES(f);
-       }
-#undef HANDLE_EXTREMUM
-       return false;
-}
-
-void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir)
-{
-       if (n == NULL || d->focus == NULL || is_leaf(n)) {
-               return;
-       }
-       node_t *p = d->focus->parent;
-       bool focus_first_child = is_first_child(d->focus);
-       if (dir == CIRCULATE_FORWARD) {
-               node_t *e = second_extrema(n);
-               while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
-                       e = prev_leaf(e, n);
-               }
-               for (node_t *s = e, *f = prev_tiled_leaf(s, n); f != NULL; s = prev_tiled_leaf(f, n), f = prev_tiled_leaf(s, n)) {
-                       swap_nodes(m, d, f, m, d, s);
-               }
-       } else {
-               node_t *e = first_extrema(n);
-               while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
-                       e = next_leaf(e, n);
-               }
-               for (node_t *f = e, *s = next_tiled_leaf(f, n); s != NULL; f = next_tiled_leaf(s, n), s = next_tiled_leaf(f, n)) {
-                       swap_nodes(m, d, f, m, d, s);
-               }
-       }
-       if (focus_first_child) {
-               focus_node(m, d, p->first_child);
-       } else {
-               focus_node(m, d, p->second_child);
-       }
-}
-
-void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n->vacant == value) {
-               return;
-       }
-
-       propagate_vacant_downward(m, d, n, value);
-       propagate_vacant_upward(m, d, n);
-}
-
-void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n->vacant == value) {
-               return;
-       }
-
-       n->vacant = value;
-
-       if (value) {
-               unrotate_brother(n);
-               cancel_presel(m, d, n);
-       } else {
-               rotate_brother(n);
-       }
-}
-
-void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       set_vacant_local(m, d, n, value);
-
-       propagate_vacant_downward(m, d, n->first_child, value);
-       propagate_vacant_downward(m, d, n->second_child, value);
-}
-
-void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       node_t *p = n->parent;
-
-       if (p != NULL) {
-               set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
-       }
-
-       propagate_vacant_upward(m, d, p);
-}
-
-bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
-{
-       if (n == NULL || n->client->layer == l) {
-               return false;
-       }
-
-       n->client->last_layer = n->client->layer;
-       n->client->layer = l;
-
-       if (l == LAYER_ABOVE) {
-               n->client->wm_flags |= WM_FLAG_ABOVE;
-               n->client->wm_flags &= ~WM_FLAG_BELOW;
-       } else if (l == LAYER_BELOW) {
-               n->client->wm_flags |= WM_FLAG_BELOW;
-               n->client->wm_flags &= ~WM_FLAG_ABOVE;
-       } else {
-               n->client->wm_flags &= ~(WM_FLAG_ABOVE | WM_FLAG_BELOW);
-       }
-
-       ewmh_wm_state_update(n);
-
-       put_status(SBSC_MASK_NODE_LAYER, "node_layer 0x%08X 0x%08X 0x%08X %s\n", m->id, d->id, n->id, LAYER_STR(l));
-
-       if (d->focus == n) {
-               neutralize_occluding_windows(m, d, n);
-       }
-
-       stack(d, n, (d->focus == n));
-
-       return true;
-}
-
-bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
-{
-       if (n == NULL || n->client == NULL || n->client->state == s) {
-               return false;
-       }
-
-       client_t *c = n->client;
-
-       c->last_state = c->state;
-       c->state = s;
-
-       switch (c->last_state) {
-               case STATE_TILED:
-               case STATE_PSEUDO_TILED:
-                       break;
-               case STATE_FLOATING:
-                       set_floating(m, d, n, false);
-                       break;
-               case STATE_FULLSCREEN:
-                       set_fullscreen(m, d, n, false);
-                       break;
-       }
-
-       put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s off\n", m->id, d->id, n->id, STATE_STR(c->last_state));
-
-       switch (c->state) {
-               case STATE_TILED:
-               case STATE_PSEUDO_TILED:
-                       break;
-               case STATE_FLOATING:
-                       set_floating(m, d, n, true);
-                       break;
-               case STATE_FULLSCREEN:
-                       set_fullscreen(m, d, n, true);
-                       break;
-       }
-
-       put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s on\n", m->id, d->id, n->id, STATE_STR(c->state));
-
-       if (n == m->desk->focus) {
-               put_status(SBSC_MASK_REPORT);
-       }
-
-       return true;
-}
-
-void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       cancel_presel(m, d, n);
-       set_vacant(m, d, n, value);
-
-       if (!value && d->focus == n) {
-               neutralize_occluding_windows(m, d, n);
-       }
-
-       stack(d, n, (d->focus == n));
-}
-
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       client_t *c = n->client;
-
-       cancel_presel(m, d, n);
-       set_vacant(m, d, n, value);
-
-       if (value) {
-               c->wm_flags |= WM_FLAG_FULLSCREEN;
-               c->last_layer = c->layer;
-               c->layer = LAYER_ABOVE;
-       } else {
-               c->wm_flags &= ~WM_FLAG_FULLSCREEN;
-               c->layer = c->last_layer;
-               if (d->focus == n) {
-                       neutralize_occluding_windows(m, d, n);
-               }
-       }
-
-       ewmh_wm_state_update(n);
-       stack(d, n, (d->focus == n));
-}
-
-void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n)
-{
-       bool changed = false;
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
-                       if (a != f && a->client != NULL && f->client != NULL &&
-                           IS_FULLSCREEN(a->client) && stack_cmp(f->client, a->client) < 0) {
-                               set_state(m, d, a, a->client->last_state);
-                               changed = true;
-                       }
-               }
-       }
-       if (changed) {
-               arrange(m, d);
-       }
-}
-
-void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       node_t *p = n->parent;
-
-       if (p != NULL) {
-               set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
-               set_hidden_local(m, d, p, (p->first_child->hidden && p->second_child->hidden));
-       }
-
-       propagate_flags_upward(m, d, p);
-}
-
-void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL || n->hidden == value) {
-               return;
-       }
-
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, false);
-       }
-
-       bool held_focus = is_descendant(d->focus, n);
-
-       propagate_hidden_downward(m, d, n, value);
-       propagate_hidden_upward(m, d, n);
-
-       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X hidden %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
-       if (held_focus || d->focus == NULL) {
-               if (d->focus != NULL) {
-                       d->focus = NULL;
-                       draw_border(n, false, (mon == m));
-               }
-               if (d == mon->desk) {
-                       focus_node(m, d, d->focus);
-               } else {
-                       activate_node(m, d, d->focus);
-               }
-       }
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, true);
-       }
-}
-
-void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n->hidden == value) {
-               return;
-       }
-
-       n->hidden = value;
-
-       if (n->client != NULL) {
-               if (n->client->shown) {
-                       window_set_visibility(n->id, !value);
-               }
-
-               if (IS_TILED(n->client)) {
-                       set_vacant(m, d, n, value);
-               }
-
-               if (value) {
-                       n->client->wm_flags |= WM_FLAG_HIDDEN;
-               } else {
-                       n->client->wm_flags &= ~WM_FLAG_HIDDEN;
-               }
-
-               ewmh_wm_state_update(n);
-       }
-}
-
-void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       set_hidden_local(m, d, n, value);
-
-       propagate_hidden_downward(m, d, n->first_child, value);
-       propagate_hidden_downward(m, d, n->second_child, value);
-}
-
-void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       node_t *p = n->parent;
-
-       if (p != NULL) {
-               set_hidden_local(m, d, p, p->first_child->hidden && p->second_child->hidden);
-       }
-
-       propagate_hidden_upward(m, d, p);
-}
-
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL || n->sticky == value) {
-               return;
-       }
-
-       if (d != m->desk) {
-               transfer_node(m, d, n, m, m->desk, m->desk->focus);
-       }
-
-       n->sticky = value;
-
-       if (value) {
-               m->sticky_count++;
-       } else {
-               m->sticky_count--;
-       }
-
-       if (n->client != NULL) {
-               if (value) {
-                       n->client->wm_flags |= WM_FLAG_STICKY;
-               } else {
-                       n->client->wm_flags &= ~WM_FLAG_STICKY;
-               }
-               ewmh_wm_state_update(n);
-       }
-
-       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X sticky %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
-       if (n == m->desk->focus) {
-               put_status(SBSC_MASK_REPORT);
-       }
-}
-
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL || n->private == value) {
-               return;
-       }
-
-       n->private = value;
-
-       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X private %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
-       if (n == m->desk->focus) {
-               put_status(SBSC_MASK_REPORT);
-       }
-}
-
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (n == NULL || n->locked == value) {
-               return;
-       }
-
-       n->locked = value;
-
-       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X locked %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
-       if (n == m->desk->focus) {
-               put_status(SBSC_MASK_REPORT);
-       }
-}
-
-void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
-       if (value && mon->desk->focus == n) {
-               return;
-       }
-
-       n->client->urgent = value;
-
-       if (value) {
-               n->client->wm_flags |= WM_FLAG_DEMANDS_ATTENTION;
-       } else {
-               n->client->wm_flags &= ~WM_FLAG_DEMANDS_ATTENTION;
-       }
-
-       ewmh_wm_state_update(n);
-
-       put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X urgent %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-       put_status(SBSC_MASK_REPORT);
-}
-
-/* Returns true if a contains b */
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
-{
-       return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
-               a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
-}
-
-xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n)
-{
-       client_t *c = n->client;
-       if (c != NULL) {
-               if (IS_FLOATING(c)) {
-                       return c->floating_rectangle;
-               } else {
-                       return c->tiled_rectangle;
-               }
-       } else {
-               int wg = (d == NULL ? 0 : (gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap));
-               xcb_rectangle_t rect = n->rectangle;
-               rect.width -= wg;
-               rect.height -= wg;
-               return rect;
-       }
-}
-
-void listen_enter_notify(node_t *n, bool enable)
-{
-       uint32_t mask = CLIENT_EVENT_MASK | (enable ? XCB_EVENT_MASK_ENTER_WINDOW : 0);
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client == NULL) {
-                       continue;
-               }
-               xcb_change_window_attributes(dpy, f->id, XCB_CW_EVENT_MASK, &mask);
-               if (f->presel != NULL) {
-                       xcb_change_window_attributes(dpy, f->presel->feedback, XCB_CW_EVENT_MASK, &mask);
-               }
-       }
-}
-
-#define DEF_FLAG_COUNT(flag) \
-       unsigned int flag##_count(node_t *n) \
-       { \
-               if (n == NULL) { \
-                       return 0; \
-               } else { \
-                       return ((n->flag ? 1 : 0) + \
-                               flag##_count(n->first_child) + \
-                               flag##_count(n->second_child)); \
-               } \
-       }
-       DEF_FLAG_COUNT(sticky)
-       DEF_FLAG_COUNT(private)
-       DEF_FLAG_COUNT(locked)
-#undef DEF_FLAG_COUNT
diff --git a/tree.h b/tree.h
deleted file mode 100644 (file)
index 1eb6764..0000000
--- a/tree.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_TREE_H
-#define BSPWM_TREE_H
-
-void arrange(monitor_t *m, desktop_t *d);
-void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect);
-presel_t *make_presel(void);
-void set_ratio(node_t *n, double rat);
-void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);
-void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);
-void cancel_presel(monitor_t *m, desktop_t *d, node_t *n);
-void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n);
-node_t *find_public(desktop_t *d);
-node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
-void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n);
-bool activate_node(monitor_t *m, desktop_t *d, node_t *n);
-void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n);
-bool focus_node(monitor_t *m, desktop_t *d, node_t *n);
-void hide_node(desktop_t *d, node_t *n);
-void show_node(desktop_t *d, node_t *n);
-node_t *make_node(uint32_t id);
-client_t *make_client(void);
-void initialize_client(node_t *n);
-bool is_focusable(node_t *n);
-bool is_leaf(node_t *n);
-bool is_first_child(node_t *n);
-bool is_second_child(node_t *n);
-unsigned int clients_count_in(node_t *n);
-node_t *brother_tree(node_t *n);
-node_t *first_extrema(node_t *n);
-node_t *second_extrema(node_t *n);
-node_t *first_focusable_leaf(node_t *n);
-node_t *next_leaf(node_t *n, node_t *r);
-node_t *prev_leaf(node_t *n, node_t *r);
-node_t *next_tiled_leaf(node_t *n, node_t *r);
-node_t *prev_tiled_leaf(node_t *n, node_t *r);
-bool is_adjacent(node_t *a, node_t *b, direction_t dir);
-node_t *find_fence(node_t *n, direction_t dir);
-bool is_child(node_t *a, node_t *b);
-bool is_descendant(node_t *a, node_t *b);
-bool find_by_id(uint32_t id, coordinates_t *loc);
-node_t *find_by_id_in(node_t *r, uint32_t id);
-void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel);
-unsigned int node_area(desktop_t *d, node_t *n);
-int tiled_count(node_t *n);
-void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel);
-void rotate_tree(node_t *n, int deg);
-void rotate_brother(node_t *n);
-void unrotate_tree(node_t *n, int rot);
-void unrotate_brother(node_t *n);
-void flip_tree(node_t *n, flip_t flp);
-void equalize_tree(node_t *n);
-int balance_tree(node_t *n);
-void unlink_node(monitor_t *m, desktop_t *d, node_t *n);
-void close_node(node_t *n);
-void kill_node(monitor_t *m, desktop_t *d, node_t *n);
-void remove_node(monitor_t *m, desktop_t *d, node_t *n);
-void free_node(node_t *n);
-bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
-bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd);
-bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel);
-void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);
-void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n);
-bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
-bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
-void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);
-void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n);
-void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n);
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
-xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n);
-void listen_enter_notify(node_t *n, bool enable);
-
-unsigned int sticky_count(node_t *n);
-unsigned int private_count(node_t *n);
-unsigned int locked_count(node_t *n);
-
-#endif
diff --git a/types.h b/types.h
deleted file mode 100644 (file)
index 39b5cf7..0000000
--- a/types.h
+++ /dev/null
@@ -1,344 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_TYPES_H
-#define BSPWM_TYPES_H
-#include <stdbool.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_icccm.h>
-#include <xcb/randr.h>
-#include <xcb/xcb_event.h>
-#include "helpers.h"
-
-#define MISSING_VALUE        "N/A"
-#define MAX_WM_STATES        4
-
-typedef enum {
-       TYPE_HORIZONTAL,
-       TYPE_VERTICAL
-} split_type_t;
-
-typedef enum {
-       MODE_AUTOMATIC,
-       MODE_MANUAL
-} split_mode_t;
-
-typedef enum {
-       STATE_TILED,
-       STATE_PSEUDO_TILED,
-       STATE_FLOATING,
-       STATE_FULLSCREEN
-} client_state_t;
-
-typedef enum {
-       WM_FLAG_MODAL = 1 << 0,
-       WM_FLAG_STICKY = 1 << 1,
-       WM_FLAG_MAXIMIZED_VERT = 1 << 2,
-       WM_FLAG_MAXIMIZED_HORZ = 1 << 3,
-       WM_FLAG_SHADED = 1 << 4,
-       WM_FLAG_SKIP_TASKBAR = 1 << 5,
-       WM_FLAG_SKIP_PAGER = 1 << 6,
-       WM_FLAG_HIDDEN = 1 << 7,
-       WM_FLAG_FULLSCREEN = 1 << 8,
-       WM_FLAG_ABOVE = 1 << 9,
-       WM_FLAG_BELOW = 1 << 10,
-       WM_FLAG_DEMANDS_ATTENTION = 1 << 11,
-} wm_flags_t;
-
-typedef enum {
-       LAYER_BELOW,
-       LAYER_NORMAL,
-       LAYER_ABOVE
-} stack_layer_t;
-
-typedef enum {
-       OPTION_NONE,
-       OPTION_TRUE,
-       OPTION_FALSE
-} option_bool_t;
-
-typedef enum {
-       ALTER_TOGGLE,
-       ALTER_SET
-} alter_state_t;
-
-typedef enum {
-       CYCLE_NEXT,
-       CYCLE_PREV
-} cycle_dir_t;
-
-typedef enum {
-       CIRCULATE_FORWARD,
-       CIRCULATE_BACKWARD
-} circulate_dir_t;
-
-typedef enum {
-       HISTORY_OLDER,
-       HISTORY_NEWER
-} history_dir_t;
-
-typedef enum {
-       DIR_NORTH,
-       DIR_WEST,
-       DIR_SOUTH,
-       DIR_EAST
-} direction_t;
-
-typedef enum {
-       HANDLE_LEFT = 1 << 0,
-       HANDLE_TOP = 1 << 1,
-       HANDLE_RIGHT = 1 << 2,
-       HANDLE_BOTTOM = 1 << 3,
-       HANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT,
-       HANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT,
-       HANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT,
-       HANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT
-} resize_handle_t;
-
-typedef enum {
-       ACTION_NONE,
-       ACTION_FOCUS,
-       ACTION_MOVE,
-       ACTION_RESIZE_SIDE,
-       ACTION_RESIZE_CORNER
-} pointer_action_t;
-
-typedef enum {
-       LAYOUT_TILED,
-       LAYOUT_MONOCLE
-} layout_t;
-
-typedef enum {
-       FLIP_HORIZONTAL,
-       FLIP_VERTICAL
-} flip_t;
-
-typedef enum {
-       FIRST_CHILD,
-       SECOND_CHILD
-} child_polarity_t;
-
-typedef struct {
-       option_bool_t automatic;
-       option_bool_t focused;
-       option_bool_t local;
-       option_bool_t active;
-       option_bool_t leaf;
-       option_bool_t window;
-       option_bool_t tiled;
-       option_bool_t pseudo_tiled;
-       option_bool_t floating;
-       option_bool_t fullscreen;
-       option_bool_t hidden;
-       option_bool_t sticky;
-       option_bool_t private;
-       option_bool_t locked;
-       option_bool_t urgent;
-       option_bool_t same_class;
-       option_bool_t descendant_of;
-       option_bool_t ancestor_of;
-       option_bool_t below;
-       option_bool_t normal;
-       option_bool_t above;
-} node_select_t;
-
-typedef struct {
-       option_bool_t occupied;
-       option_bool_t focused;
-       option_bool_t urgent;
-       option_bool_t local;
-} desktop_select_t;
-
-typedef struct {
-       option_bool_t occupied;
-       option_bool_t focused;
-} monitor_select_t;
-
-typedef struct icccm_props_t icccm_props_t;
-struct icccm_props_t {
-       bool take_focus;
-       bool input_hint;
-       bool delete_window;
-};
-
-typedef struct {
-       char class_name[3 * SMALEN / 2];
-       char instance_name[3 * SMALEN / 2];
-       unsigned int border_width;
-       bool urgent;
-       bool shown;
-       client_state_t state;
-       client_state_t last_state;
-       stack_layer_t layer;
-       stack_layer_t last_layer;
-       xcb_rectangle_t floating_rectangle;
-       xcb_rectangle_t tiled_rectangle;
-       xcb_size_hints_t size_hints;
-       icccm_props_t icccm_props;
-       wm_flags_t wm_flags;
-} client_t;
-
-typedef struct presel_t presel_t;
-struct presel_t {
-       double split_ratio;
-       direction_t split_dir;
-       xcb_window_t feedback;
-};
-
-typedef struct node_t node_t;
-struct node_t {
-       uint32_t id;
-       split_type_t split_type;
-       double split_ratio;
-       int birth_rotation;
-       presel_t *presel;
-       xcb_rectangle_t rectangle;
-       bool vacant;
-       bool hidden;
-       bool sticky;
-       bool private;
-       bool locked;
-       node_t *first_child;
-       node_t *second_child;
-       node_t *parent;
-       client_t *client;
-};
-
-typedef struct padding_t padding_t;
-struct padding_t {
-       int top;
-       int right;
-       int bottom;
-       int left;
-};
-
-typedef struct desktop_t desktop_t;
-struct desktop_t {
-       char name[SMALEN];
-       uint32_t id;
-       layout_t layout;
-       node_t *root;
-       node_t *focus;
-       desktop_t *prev;
-       desktop_t *next;
-       padding_t padding;
-       int window_gap;
-       unsigned int border_width;
-};
-
-typedef struct monitor_t monitor_t;
-struct monitor_t {
-       char name[SMALEN];
-       uint32_t id;
-       xcb_randr_output_t randr_id;
-       xcb_window_t root;
-       bool wired;
-       padding_t padding;
-       unsigned int sticky_count;
-       int window_gap;
-       unsigned int border_width;
-       xcb_rectangle_t rectangle;
-       desktop_t *desk;
-       desktop_t *desk_head;
-       desktop_t *desk_tail;
-       monitor_t *prev;
-       monitor_t *next;
-};
-
-typedef struct {
-       monitor_t *monitor;
-       desktop_t *desktop;
-       node_t *node;
-} coordinates_t;
-
-typedef struct history_t history_t;
-struct history_t {
-       coordinates_t loc;
-       bool latest;
-       history_t *prev;
-       history_t *next;
-};
-
-typedef struct stacking_list_t stacking_list_t;
-struct stacking_list_t {
-       node_t *node;
-       stacking_list_t *prev;
-       stacking_list_t *next;
-};
-
-typedef struct subscriber_list_t subscriber_list_t;
-struct subscriber_list_t {
-       int fd;
-       FILE *stream;
-       int field;
-       subscriber_list_t *prev;
-       subscriber_list_t *next;
-};
-
-typedef struct rule_t rule_t;
-struct rule_t {
-       char class_name[MAXLEN];
-       char instance_name[MAXLEN];
-       char effect[MAXLEN];
-       bool one_shot;
-       rule_t *prev;
-       rule_t *next;
-};
-
-typedef struct {
-       char class_name[3 * SMALEN / 2];
-       char instance_name[3 * SMALEN / 2];
-       char monitor_desc[MAXLEN];
-       char desktop_desc[MAXLEN];
-       char node_desc[MAXLEN];
-       char split_dir[SMALEN];
-       double split_ratio;
-       stack_layer_t *layer;
-       client_state_t *state;
-       bool hidden;
-       bool sticky;
-       bool private;
-       bool locked;
-       bool center;
-       bool follow;
-       bool manage;
-       bool focus;
-       bool border;
-} rule_consequence_t;
-
-typedef struct pending_rule_t pending_rule_t;
-struct pending_rule_t {
-       int fd;
-       xcb_window_t win;
-       rule_consequence_t *csq;
-       pending_rule_t *prev;
-       pending_rule_t *next;
-};
-
-typedef struct {
-    double x;
-    double y;
-} dpoint_t;
-
-#endif
diff --git a/window.c b/window.c
deleted file mode 100644 (file)
index 26d436e..0000000
--- a/window.c
+++ /dev/null
@@ -1,893 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "monitor.h"
-#include "query.h"
-#include "rule.h"
-#include "settings.h"
-#include "geometry.h"
-#include "pointer.h"
-#include "stack.h"
-#include "tree.h"
-#include "parse.h"
-#include "window.h"
-
-void schedule_window(xcb_window_t win)
-{
-       coordinates_t loc;
-       uint8_t override_redirect = 0;
-       xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
-
-       if (wa != NULL) {
-               override_redirect = wa->override_redirect;
-               free(wa);
-       }
-
-       if (override_redirect || locate_window(win, &loc)) {
-               return;
-       }
-
-       /* ignore pending windows */
-       for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
-               if (pr->win == win) {
-                       return;
-               }
-       }
-
-       rule_consequence_t *csq = make_rule_conquence();
-       apply_rules(win, csq);
-       if (!schedule_rules(win, csq)) {
-               manage_window(win, csq, -1);
-               free(csq);
-       }
-}
-
-void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
-{
-       monitor_t *m = mon;
-       desktop_t *d = mon->desk;
-       node_t *f = mon->desk->focus;
-
-       parse_rule_consequence(fd, csq);
-
-       if (ewmh_handle_struts(win)) {
-               for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-                       arrange(m, m->desk);
-               }
-       }
-
-       if (!csq->manage) {
-               free(csq->layer);
-               free(csq->state);
-               window_show(win);
-               return;
-       }
-
-       if (csq->node_desc[0] != '\0') {
-               coordinates_t ref = {m, d, f};
-               coordinates_t trg = {NULL, NULL, NULL};
-               if (node_from_desc(csq->node_desc, &ref, &trg) == SELECTOR_OK) {
-                       m = trg.monitor;
-                       d = trg.desktop;
-                       f = trg.node;
-               }
-       } else if (csq->desktop_desc[0] != '\0') {
-               coordinates_t ref = {m, d, NULL};
-               coordinates_t trg = {NULL, NULL, NULL};
-               if (desktop_from_desc(csq->desktop_desc, &ref, &trg) == SELECTOR_OK) {
-                       m = trg.monitor;
-                       d = trg.desktop;
-                       f = trg.desktop->focus;
-               }
-       } else if (csq->monitor_desc[0] != '\0') {
-               coordinates_t ref = {m, NULL, NULL};
-               coordinates_t trg = {NULL, NULL, NULL};
-               if (monitor_from_desc(csq->monitor_desc, &ref, &trg) == SELECTOR_OK) {
-                       m = trg.monitor;
-                       d = trg.monitor->desk;
-                       f = trg.monitor->desk->focus;
-               }
-       }
-
-       if (csq->sticky) {
-               m = mon;
-               d = mon->desk;
-               f = mon->desk->focus;
-       }
-
-       if (csq->split_dir[0] != '\0' && f != NULL) {
-               direction_t dir;
-               if (parse_direction(csq->split_dir, &dir)) {
-                       presel_dir(m, d, f, dir);
-               }
-       }
-
-       if (csq->split_ratio != 0 && f != NULL) {
-               presel_ratio(m, d, f, csq->split_ratio);
-       }
-
-       node_t *n = make_node(win);
-       client_t *c = make_client();
-       c->border_width = csq->border ? d->border_width : 0;
-       n->client = c;
-       initialize_client(n);
-       initialize_floating_rectangle(n);
-
-       if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
-               csq->center = true;
-       }
-
-       monitor_t *mm = monitor_from_client(c);
-       embrace_client(mm, c);
-       adapt_geometry(&mm->rectangle, &m->rectangle, n);
-
-       if (csq->center) {
-               window_center(m, c);
-       }
-
-       snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
-       snprintf(c->instance_name, sizeof(c->instance_name), "%s", csq->instance_name);
-
-       f = insert_node(m, d, n, f);
-       clients_count++;
-
-       put_status(SBSC_MASK_NODE_MANAGE, "node_manage 0x%08X 0x%08X 0x%08X 0x%08X\n", m->id, d->id, win, f!=NULL?f->id:0);
-
-       if (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {
-               c->layer = f->client->layer;
-       }
-
-       if (csq->layer != NULL) {
-               c->layer = *(csq->layer);
-       }
-
-       if (csq->state != NULL) {
-               set_state(m, d, n, *(csq->state));
-       }
-
-       set_hidden(m, d, n, csq->hidden);
-       set_sticky(m, d, n, csq->sticky);
-       set_private(m, d, n, csq->private);
-       set_locked(m, d, n, csq->locked);
-
-       arrange(m, d);
-
-       uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
-       xcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);
-       set_window_state(win, XCB_ICCCM_WM_STATE_NORMAL);
-       window_grab_buttons(win);
-
-       if (d == m->desk) {
-               show_node(d, n);
-       } else {
-               hide_node(d, n);
-       }
-
-       if (!csq->hidden && csq->focus) {
-               if (d == mon->desk || csq->follow) {
-                       focus_node(m, d, n);
-               } else {
-                       activate_node(m, d, n);
-               }
-       } else {
-               stack(d, n, false);
-               draw_border(n, false, (m == mon));
-       }
-
-       ewmh_set_wm_desktop(n, d);
-       ewmh_update_client_list(false);
-       free(csq->layer);
-       free(csq->state);
-}
-
-void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state)
-{
-       long data[] = {state, XCB_NONE};
-       xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, WM_STATE, WM_STATE, 32, 2, data);
-}
-
-void unmanage_window(xcb_window_t win)
-{
-       coordinates_t loc;
-       if (locate_window(win, &loc)) {
-               put_status(SBSC_MASK_NODE_UNMANAGE, "node_unmanage 0x%08X 0x%08X 0x%08X\n", loc.monitor->id, loc.desktop->id, win);
-               remove_node(loc.monitor, loc.desktop, loc.node);
-               set_window_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
-               arrange(loc.monitor, loc.desktop);
-       } else {
-               for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
-                       if (pr->win == win) {
-                               remove_pending_rule(pr);
-                               return;
-                       }
-               }
-       }
-}
-
-bool is_presel_window(xcb_window_t win)
-{
-       xcb_icccm_get_wm_class_reply_t reply;
-       bool ret = false;
-       if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
-               if (streq(BSPWM_CLASS_NAME, reply.class_name) && streq(PRESEL_FEEDBACK_I, reply.instance_name)) {
-                       ret = true;
-               }
-               xcb_icccm_get_wm_class_reply_wipe(&reply);
-       }
-       return ret;
-}
-
-void initialize_presel_feedback(node_t *n)
-{
-       if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
-               return;
-       }
-
-       xcb_window_t win = xcb_generate_id(dpy);
-       uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK;
-       uint32_t values[] = {get_color_pixel(presel_feedback_color), 1, focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0};
-       xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
-                                 XCB_COPY_FROM_PARENT, mask, values);
-
-       xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
-       window_grab_buttons(win);
-       stacking_list_t *s = stack_tail;
-       while (s != NULL && !IS_TILED(s->node->client)) {
-               s = s->prev;
-       }
-       if (s != NULL) {
-               window_above(win, s->node->id);
-       }
-       n->presel->feedback = win;
-}
-
-void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL || n->presel == NULL || d->layout == LAYOUT_MONOCLE) {
-               return;
-       }
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, false);
-       }
-
-       bool exists = (n->presel->feedback != XCB_NONE);
-       if (!exists) {
-               initialize_presel_feedback(n);
-       }
-
-       int gap = gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap;
-       presel_t *p = n->presel;
-       xcb_rectangle_t rect = n->rectangle;
-       rect.x = rect.y = 0;
-       rect.width -= gap;
-       rect.height -= gap;
-       xcb_rectangle_t presel_rect = rect;
-
-       switch (p->split_dir) {
-               case DIR_NORTH:
-                       presel_rect.height = p->split_ratio * rect.height;
-                       break;
-               case DIR_EAST:
-                       presel_rect.width = (1 - p->split_ratio) * rect.width;
-                       presel_rect.x = rect.width - presel_rect.width;
-                       break;
-               case DIR_SOUTH:
-                       presel_rect.height = (1 - p->split_ratio) * rect.height;
-                       presel_rect.y = rect.height - presel_rect.height;
-                       break;
-               case DIR_WEST:
-                       presel_rect.width = p->split_ratio * rect.width;
-                       break;
-       }
-
-       window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
-                          presel_rect.width, presel_rect.height);
-
-       if (!exists && m->desk == d) {
-               window_show(p->feedback);
-       }
-
-       if (focus_follows_pointer) {
-               listen_enter_notify(d->root, true);
-       }
-}
-
-void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else {
-               if (n->presel != NULL) {
-                       draw_presel_feedback(m, d, n);
-               }
-               refresh_presel_feedbacks(m, d, n->first_child);
-               refresh_presel_feedbacks(m, d, n->second_child);
-       }
-}
-
-void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else {
-               if (n->presel != NULL) {
-                       window_show(n->presel->feedback);
-               }
-               show_presel_feedbacks(m, d, n->first_child);
-               show_presel_feedbacks(m, d, n->second_child);
-       }
-}
-
-void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
-{
-       if (n == NULL) {
-               return;
-       } else {
-               if (n->presel != NULL) {
-                       window_hide(n->presel->feedback);
-               }
-               hide_presel_feedbacks(m, d, n->first_child);
-               hide_presel_feedbacks(m, d, n->second_child);
-       }
-}
-
-void update_colors(void)
-{
-       for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-               for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
-                       update_colors_in(d->root, d, m);
-               }
-       }
-}
-
-void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
-{
-       if (n == NULL) {
-               return;
-       } else {
-               if (n->presel != NULL) {
-                       uint32_t pxl = get_color_pixel(presel_feedback_color);
-                       xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
-                       if (d == m->desk) {
-                               /* hack to induce back pixel refresh */
-                               window_hide(n->presel->feedback);
-                               window_show(n->presel->feedback);
-                       }
-               }
-               if (n == d->focus) {
-                       draw_border(n, true, (m == mon));
-               } else if (n->client != NULL) {
-                       draw_border(n, false, (m == mon));
-               } else {
-                       update_colors_in(n->first_child, d, m);
-                       update_colors_in(n->second_child, d, m);
-               }
-       }
-}
-
-void draw_border(node_t *n, bool focused_node, bool focused_monitor)
-{
-       if (n == NULL) {
-               return;
-       }
-
-       uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
-       for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
-               if (f->client != NULL && f->client->border_width > 0) {
-                       window_draw_border(f->id, border_color_pxl);
-               }
-       }
-}
-
-void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
-{
-       xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
-}
-
-void adopt_orphans(void)
-{
-       xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
-       if (qtr == NULL) {
-               return;
-       }
-
-       int len = xcb_query_tree_children_length(qtr);
-       xcb_window_t *wins = xcb_query_tree_children(qtr);
-
-       for (int i = 0; i < len; i++) {
-               uint32_t idx;
-               xcb_window_t win = wins[i];
-               if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
-                       schedule_window(win);
-               }
-       }
-
-       free(qtr);
-}
-
-uint32_t get_border_color(bool focused_node, bool focused_monitor)
-{
-       if (focused_monitor && focused_node) {
-               return get_color_pixel(focused_border_color);
-       } else if (focused_node) {
-               return get_color_pixel(active_border_color);
-       } else {
-               return get_color_pixel(normal_border_color);
-       }
-}
-
-void initialize_floating_rectangle(node_t *n)
-{
-       client_t *c = n->client;
-
-       xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
-
-       if (geo != NULL) {
-               c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
-       }
-
-       free(geo);
-}
-
-xcb_rectangle_t get_window_rectangle(node_t *n)
-{
-       client_t *c = n->client;
-       if (c != NULL) {
-               xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
-               if (g != NULL) {
-                       xcb_rectangle_t rect = (xcb_rectangle_t) {g->x, g->y, g->width, g->height};
-                       free(g);
-                       return rect;
-               }
-       }
-       return (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-}
-
-bool move_client(coordinates_t *loc, int dx, int dy)
-{
-       node_t *n = loc->node;
-
-       if (n == NULL || n->client == NULL) {
-               return false;
-       }
-
-       monitor_t *pm = NULL;
-
-       if (IS_TILED(n->client)) {
-               if (!grabbing) {
-                       return false;
-               }
-               xcb_window_t pwin = XCB_NONE;
-               query_pointer(&pwin, NULL);
-               if (pwin == n->id) {
-                       return false;
-               }
-               coordinates_t dst;
-               bool is_managed = (pwin != XCB_NONE && locate_window(pwin, &dst));
-               if (is_managed && dst.monitor == loc->monitor && IS_TILED(dst.node->client)) {
-                       swap_nodes(loc->monitor, loc->desktop, n, loc->monitor, loc->desktop, dst.node);
-                       return true;
-               } else {
-                       if (is_managed && dst.monitor == loc->monitor) {
-                               return false;
-                       } else {
-                               xcb_point_t pt = {0, 0};
-                               query_pointer(NULL, &pt);
-                               pm = monitor_from_point(pt);
-                       }
-               }
-       } else {
-               client_t *c = n->client;
-               xcb_rectangle_t rect = c->floating_rectangle;
-               int16_t x = rect.x + dx;
-               int16_t y = rect.y + dy;
-
-               if (focus_follows_pointer) {
-                       listen_enter_notify(loc->desktop->root, false);
-               }
-
-               window_move(n->id, x, y);
-
-               if (focus_follows_pointer) {
-                       listen_enter_notify(loc->desktop->root, true);
-               }
-
-               c->floating_rectangle.x = x;
-               c->floating_rectangle.y = y;
-               if (!grabbing) {
-                       put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, rect.width, rect.height, x, y);
-               }
-               pm = monitor_from_client(c);
-       }
-
-       if (pm == NULL || pm == loc->monitor) {
-               return true;
-       }
-
-       bool focused = (n == mon->desk->focus);
-       transfer_node(loc->monitor, loc->desktop, n, pm, pm->desk, pm->desk->focus);
-       loc->monitor = pm;
-       loc->desktop = pm->desk;
-       if (focused) {
-               focus_node(pm, pm->desk, n);
-       }
-
-       return true;
-}
-
-bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy)
-{
-       node_t *n = loc->node;
-       if (n == NULL || n->client == NULL || n->client->state == STATE_FULLSCREEN) {
-               return false;
-       }
-       node_t *horizontal_fence = NULL, *vertical_fence = NULL;
-       xcb_rectangle_t rect = get_rectangle(NULL, n);
-       uint16_t width = rect.width, height = rect.height;
-       int16_t x = rect.x, y = rect.y;
-       if (n->client->state == STATE_TILED) {
-               if (rh & HANDLE_LEFT) {
-                       vertical_fence = find_fence(n, DIR_WEST);
-               } else if (rh & HANDLE_RIGHT) {
-                       vertical_fence = find_fence(n, DIR_EAST);
-               }
-               if (rh & HANDLE_TOP) {
-                       horizontal_fence = find_fence(n, DIR_NORTH);
-               } else if (rh & HANDLE_BOTTOM) {
-                       horizontal_fence = find_fence(n, DIR_SOUTH);
-               }
-               if (vertical_fence == NULL && horizontal_fence == NULL) {
-                       return false;
-               }
-               if (vertical_fence != NULL) {
-                       double sr = vertical_fence->split_ratio + (double) dx / vertical_fence->rectangle.width;
-                       sr = MAX(0, sr);
-                       sr = MIN(1, sr);
-                       vertical_fence->split_ratio = sr;
-               }
-               if (horizontal_fence != NULL) {
-                       double sr = horizontal_fence->split_ratio + (double) dy / horizontal_fence->rectangle.height;
-                       sr = MAX(0, sr);
-                       sr = MIN(1, sr);
-                       horizontal_fence->split_ratio = sr;
-               }
-               arrange(loc->monitor, loc->desktop);
-       } else {
-               int w = width + dx * (rh & HANDLE_LEFT ? -1 : (rh & HANDLE_RIGHT ? 1 : 0));
-               int h = height + dy * (rh & HANDLE_TOP ? -1 : (rh & HANDLE_BOTTOM ? 1 : 0));
-               width = MAX(1, w);
-               height = MAX(1, h);
-               apply_size_hints(n->client, &width, &height);
-               if (rh & HANDLE_LEFT) {
-                       x += rect.width - width;
-               }
-               if (rh & HANDLE_TOP) {
-                       y += rect.height - height;
-               }
-               n->client->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
-               if (n->client->state == STATE_FLOATING) {
-                       if (focus_follows_pointer) {
-                               listen_enter_notify(loc->desktop->root, false);
-                       }
-
-                       window_move_resize(n->id, x, y, width, height);
-
-                       if (focus_follows_pointer) {
-                               listen_enter_notify(loc->desktop->root, true);
-                       }
-
-                       if (!grabbing) {
-                               put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, width, height, x, y);
-                       }
-               } else {
-                       arrange(loc->monitor, loc->desktop);
-               }
-       }
-       return true;
-}
-
-/* taken from awesomeWM */
-void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height)
-{
-       if (!honor_size_hints) {
-               return;
-       }
-
-       int32_t minw = 0, minh = 0;
-       int32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;
-
-       if (c->state == STATE_FULLSCREEN) {
-               return;
-       }
-
-       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
-               basew = c->size_hints.base_width;
-               baseh = c->size_hints.base_height;
-               real_basew = basew;
-               real_baseh = baseh;
-       } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
-               /* base size is substituted with min size if not specified */
-               basew = c->size_hints.min_width;
-               baseh = c->size_hints.min_height;
-       }
-
-       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
-               minw = c->size_hints.min_width;
-               minh = c->size_hints.min_height;
-       } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
-               /* min size is substituted with base size if not specified */
-               minw = c->size_hints.base_width;
-               minh = c->size_hints.base_height;
-       }
-
-       /* Handle the size aspect ratio */
-       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT &&
-           c->size_hints.min_aspect_den > 0 &&
-           c->size_hints.max_aspect_den > 0 &&
-           *height > real_baseh &&
-           *width > real_basew) {
-               /* ICCCM mandates:
-                * If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the
-                * window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing
-                * should be subtracted from the window size. (The minimum size is not to be used in place of the base size for
-                * this purpose.)
-                */
-               double dx = *width - real_basew;
-               double dy = *height - real_baseh;
-               double ratio = dx / dy;
-               double min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;
-               double max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;
-
-               if (max > 0 && min > 0 && ratio > 0) {
-                       if (ratio < min) {
-                               /* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */
-                               dy = dx / min + 0.5;
-                               *width  = dx + real_basew;
-                               *height = dy + real_baseh;
-                       } else if (ratio > max) {
-                               /* dx is too high, lower it (+0.5 for proper rounding) */
-                               dx = dy * max + 0.5;
-                               *width  = dx + real_basew;
-                               *height = dy + real_baseh;
-                       }
-               }
-       }
-
-       /* Handle the minimum size */
-       *width = MAX(*width, minw);
-       *height = MAX(*height, minh);
-
-       /* Handle the maximum size */
-       if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
-       {
-               if (c->size_hints.max_width > 0) {
-                       *width = MIN(*width, c->size_hints.max_width);
-               }
-               if (c->size_hints.max_height > 0) {
-                       *height = MIN(*height, c->size_hints.max_height);
-               }
-       }
-
-       /* Handle the size increment */
-       if (c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE) &&
-           c->size_hints.width_inc > 0 && c->size_hints.height_inc > 0) {
-               uint16_t t1 = *width, t2 = *height;
-               unsigned_subtract(t1, basew);
-               unsigned_subtract(t2, baseh);
-               *width -= t1 % c->size_hints.width_inc;
-               *height -= t2 % c->size_hints.height_inc;
-       }
-}
-
-void query_pointer(xcb_window_t *win, xcb_point_t *pt)
-{
-       xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
-
-       if (qpr != NULL) {
-               if (win != NULL) {
-                       *win = qpr->child;
-                       xcb_point_t pt = {qpr->root_x, qpr->root_y};
-                       for (stacking_list_t *s = stack_tail; s != NULL; s = s->prev) {
-                               if (!s->node->client->shown || s->node->hidden) {
-                                       continue;
-                               }
-                               xcb_rectangle_t rect = get_rectangle(NULL, s->node);
-                               if (is_inside(pt, rect)) {
-                                       if (s->node->id == qpr->child || is_presel_window(qpr->child)) {
-                                               *win = s->node->id;
-                                       }
-                                       break;
-                               }
-                       }
-               }
-               if (pt != NULL) {
-                       *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
-               }
-       }
-
-       free(qpr);
-}
-
-void window_border_width(xcb_window_t win, uint32_t bw)
-{
-       uint32_t values[] = {bw};
-       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
-}
-
-void window_move(xcb_window_t win, int16_t x, int16_t y)
-{
-       uint32_t values[] = {x, y};
-       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
-}
-
-void window_resize(xcb_window_t win, uint16_t w, uint16_t h)
-{
-       uint32_t values[] = {w, h};
-       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);
-}
-
-void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
-{
-       uint32_t values[] = {x, y, w, h};
-       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
-}
-
-void window_center(monitor_t *m, client_t *c)
-{
-       xcb_rectangle_t *r = &c->floating_rectangle;
-       xcb_rectangle_t a = m->rectangle;
-       if (r->width >= a.width) {
-               r->x = a.x;
-       } else {
-               r->x = a.x + (a.width - r->width) / 2;
-       }
-       if (r->height >= a.height) {
-               r->y = a.y;
-       } else {
-               r->y = a.y + (a.height - r->height) / 2;
-       }
-       r->x -= c->border_width;
-       r->y -= c->border_width;
-}
-
-void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
-{
-       if (w2 == XCB_NONE) {
-               return;
-       }
-       uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
-       uint32_t values[] = {w2, mode};
-       xcb_configure_window(dpy, w1, mask, values);
-}
-
-void window_above(xcb_window_t w1, xcb_window_t w2)
-{
-       window_stack(w1, w2, XCB_STACK_MODE_ABOVE);
-}
-
-void window_below(xcb_window_t w1, xcb_window_t w2)
-{
-       window_stack(w1, w2, XCB_STACK_MODE_BELOW);
-}
-
-void window_lower(xcb_window_t win)
-{
-       uint32_t values[] = {XCB_STACK_MODE_BELOW};
-       xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
-}
-
-void window_set_visibility(xcb_window_t win, bool visible)
-{
-       uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
-       uint32_t values_on[] = {ROOT_EVENT_MASK};
-       xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
-       if (visible) {
-               xcb_map_window(dpy, win);
-       } else {
-               xcb_unmap_window(dpy, win);
-       }
-       xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
-}
-
-void window_hide(xcb_window_t win)
-{
-       window_set_visibility(win, false);
-}
-
-void window_show(xcb_window_t win)
-{
-       window_set_visibility(win, true);
-}
-
-void update_input_focus(void)
-{
-       set_input_focus(mon->desk->focus);
-}
-
-void set_input_focus(node_t *n)
-{
-       if (n == NULL || n->client == NULL) {
-               clear_input_focus();
-       } else {
-               if (n->client->icccm_props.input_hint) {
-                       xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
-               } else if (n->client->icccm_props.take_focus) {
-                       send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
-               }
-       }
-}
-
-void clear_input_focus(void)
-{
-       xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
-}
-
-void center_pointer(xcb_rectangle_t r)
-{
-       if (grabbing) {
-               return;
-       }
-       int16_t cx = r.x + r.width / 2;
-       int16_t cy = r.y + r.height / 2;
-       xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
-}
-
-void get_atom(char *name, xcb_atom_t *atom)
-{
-       xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
-       if (reply != NULL) {
-               *atom = reply->atom;
-       } else {
-               *atom = XCB_NONE;
-       }
-       free(reply);
-}
-
-void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
-{
-       xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
-}
-
-void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
-{
-       xcb_client_message_event_t *e = calloc(32, 1);
-
-       e->response_type = XCB_CLIENT_MESSAGE;
-       e->window = win;
-       e->type = property;
-       e->format = 32;
-       e->data.data32[0] = value;
-       e->data.data32[1] = XCB_CURRENT_TIME;
-
-       xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) e);
-       xcb_flush(dpy);
-       free(e);
-}
diff --git a/window.h b/window.h
deleted file mode 100644 (file)
index 0d8d5f5..0000000
--- a/window.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Copyright (c) 2012, Bastien Dejean
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BSPWM_WINDOW_H
-#define BSPWM_WINDOW_H
-
-#include <stdarg.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_event.h>
-#include <xcb/xcb_icccm.h>
-#include "types.h"
-
-void schedule_window(xcb_window_t win);
-void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);
-void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state);
-void unmanage_window(xcb_window_t win);
-bool is_presel_window(xcb_window_t win);
-void initialize_presel_feedback(node_t *n);
-void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n);
-void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
-void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
-void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
-void update_colors(void);
-void update_colors_in(node_t *n, desktop_t *d, monitor_t *m);
-void draw_border(node_t *n, bool focused_node, bool focused_monitor);
-void window_draw_border(xcb_window_t win, uint32_t border_color_pxl);
-void adopt_orphans(void);
-uint32_t get_border_color(bool focused_node, bool focused_monitor);
-void initialize_floating_rectangle(node_t *n);
-xcb_rectangle_t get_window_rectangle(node_t *n);
-bool move_client(coordinates_t *loc, int dx, int dy);
-bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy);
-void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height);
-void query_pointer(xcb_window_t *win, xcb_point_t *pt);
-void window_border_width(xcb_window_t win, uint32_t bw);
-void window_move(xcb_window_t win, int16_t x, int16_t y);
-void window_resize(xcb_window_t win, uint16_t w, uint16_t h);
-void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h);
-void window_center(monitor_t *m, client_t *c);
-void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode);
-void window_above(xcb_window_t w1, xcb_window_t w2);
-void window_below(xcb_window_t w1, xcb_window_t w2);
-void window_lower(xcb_window_t win);
-void window_set_visibility(xcb_window_t win, bool visible);
-void window_hide(xcb_window_t win);
-void window_show(xcb_window_t win);
-void update_input_focus(void);
-void set_input_focus(node_t *n);
-void clear_input_focus(void);
-void center_pointer(xcb_rectangle_t r);
-void get_atom(char *name, xcb_atom_t *atom);
-void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value);
-void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value);
-
-#endif