summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Vogel <jvogel4@stny.rr.com>2014-06-10 06:26:37 +0700
committerWilly Sudiarto Raharjo <willysr@slackbuilds.org>2014-06-12 06:51:48 +0700
commit15c6d7579d8357b36c7d56e7a44566544335fe69 (patch)
tree396786f2e275f163d82af7a8779bddc96023e9a0
parentbe7f8252feabef1eee69ca8218e94abad1a965b5 (diff)
downloadslackbuilds-15c6d7579d8357b36c7d56e7a44566544335fe69.tar.gz
desktop/bspwm: Pull upstream fixes.
Signed-off-by: Willy Sudiarto Raharjo <willysr@slackbuilds.org>
-rw-r--r--desktop/bspwm/bspwm.SlackBuild13
-rw-r--r--desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch4719
-rw-r--r--desktop/bspwm/patches/bspwm-0.8.8_0ff26c34.patch16039
3 files changed, 16048 insertions, 4723 deletions
diff --git a/desktop/bspwm/bspwm.SlackBuild b/desktop/bspwm/bspwm.SlackBuild
index f5ce09ca24..2aaeb1111f 100644
--- a/desktop/bspwm/bspwm.SlackBuild
+++ b/desktop/bspwm/bspwm.SlackBuild
@@ -24,7 +24,7 @@
PRGNAM=bspwm
VERSION=${VERSION:-0.8.8}
-BUILD=${BUILD:-1}
+BUILD=${BUILD:-2}
TAG=${TAG:-_SBo}
if [ -z "$ARCH" ]; then
@@ -60,7 +60,12 @@ rm -rf $PKG
mkdir -p $TMP $PKG $OUTPUT
cd $TMP
rm -rf $PRGNAM-$VERSION
-tar xvf $CWD/$VERSION.tar.gz || tar xvf $CWD/$PRGNAM-$VERSION.tar.gz
+if [ ! -f $CWD/$PRGNAM-$VERSION.tar.gz ]; then
+ if [ -f $CWD/$VERSION.tar.gz ]; then
+ mv $CWD/$VERSION.tar.gz $CWD/$PRGNAM-$VERSION.tar.gz
+ fi
+fi
+tar xvf $CWD/$PRGNAM-$VERSION.tar.gz
cd $PRGNAM-$VERSION
chown -R root:root .
find -L . \
@@ -69,8 +74,8 @@ find -L . \
\( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \
-o -perm 440 -o -perm 400 \) -exec chmod 644 {} \;
-# patch to git commit 0ca5bf31 (use -l due to major spaces to tabs conversion in Jan 2014)
-patch -l -p1 < $CWD/patches/bspwm-0.8.8_0ca5bf3.patch
+# patch to git commit 0ff26c34 (use -l due to major spaces to tabs conversion in Jan 2014)
+patch -l -p1 < $CWD/patches/bspwm-0.8.8_0ff26c34.patch
CFLAGS="$SLKCFLAGS" \
CXXFLAGS="$SLKCFLAGS" \
diff --git a/desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch b/desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch
deleted file mode 100644
index 3e7dec1736..0000000000
--- a/desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch
+++ /dev/null
@@ -1,4719 +0,0 @@
-diff --git a/LICENSE b/LICENSE
-index b6ffa88..1f93c08 100644
---- a/LICENSE
-+++ b/LICENSE
-@@ -1,4 +1,4 @@
--Copyright (c) 2012-2013, Bastien Dejean
-+Copyright (c) 2012-2014, Bastien Dejean
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
-diff --git a/Sourcedeps b/Sourcedeps
-index 9b36a69..f757d4e 100644
---- a/Sourcedeps
-+++ b/Sourcedeps
-@@ -11,7 +11,7 @@ helpers.o: helpers.c bspwm.h types.h helpers.h
- history.o: history.c bspwm.h types.h helpers.h query.h
- messages.o: messages.c bspwm.h types.h helpers.h desktop.h ewmh.h \
- history.h monitor.h pointer.h query.h rule.h restore.h settings.h tree.h \
-- window.h messages.h
-+ window.h common.h subscribe.h messages.h
- monitor.o: monitor.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
- query.h settings.h tree.h window.h monitor.h
- pointer.o: pointer.c bspwm.h types.h helpers.h query.h settings.h stack.h \
-@@ -29,4 +29,4 @@ subscribe.o: subscribe.c bspwm.h types.h helpers.h tree.h settings.h \
- tree.o: tree.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
- monitor.h query.h settings.h stack.h window.h tree.h
- window.o: window.c bspwm.h types.h helpers.h ewmh.h monitor.h query.h \
-- rule.h settings.h stack.h tree.h window.h
-+ rule.h settings.h stack.h tree.h messages.h window.h
-diff --git a/bspc.c b/bspc.c
-index 1617afc..3903eaf 100644
---- a/bspc.c
-+++ b/bspc.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-@@ -66,10 +70,15 @@ int main(int argc, char *argv[])
- if (send(fd, msg, msg_len, 0) == -1)
- err("Failed to send the data.\n");
-
-- int ret = EXIT_SUCCESS, nb;
-+ int ret = 0, nb;
- while ((nb = recv(fd, rsp, sizeof(rsp), 0)) > 0) {
-- if (nb == 1 && rsp[0] == MESSAGE_FAILURE) {
-- ret = EXIT_FAILURE;
-+ if (nb == 1 && rsp[0] < MSG_LENGTH) {
-+ ret = rsp[0];
-+ if (ret == MSG_UNKNOWN) {
-+ warn("Unknown command.\n");
-+ } else if (ret == MSG_SYNTAX) {
-+ warn("Invalid syntax.\n");
-+ }
- } else {
- int end = MIN(nb, (int) sizeof(rsp) - 1);
- rsp[end--] = '\0';
-diff --git a/bspwm.c b/bspwm.c
-index 95049ae..b3d91c2 100644
---- a/bspwm.c
-+++ b/bspwm.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdio.h>
-@@ -57,9 +61,7 @@ int main(int argc, char *argv[])
- config_path[0] = '\0';
- int sock_fd, cli_fd, dpy_fd, max_fd, n;
- struct sockaddr_un sock_address;
-- size_t rsp_len = 0;
- char msg[BUFSIZ] = {0};
-- char rsp[BUFSIZ] = {0};
- xcb_generic_event_t *event;
- char opt;
-
-@@ -155,19 +157,21 @@ int main(int argc, char *argv[])
- cli_fd = accept(sock_fd, NULL, 0);
- if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg), 0)) > 0) {
- msg[n] = '\0';
-- if (handle_message(msg, n, rsp)) {
-- rsp_len = strlen(rsp);
-+ FILE *rsp = fdopen(cli_fd, "w");
-+ if (rsp != NULL) {
-+ int ret = handle_message(msg, n, rsp);
-+ if (ret == MSG_SUBSCRIBE) {
-+ add_subscriber(rsp);
- } else {
-- rsp[0] = MESSAGE_FAILURE;
-- rsp_len = 1;
-+ if (ret != MSG_SUCCESS)
-+ fprintf(rsp, "%c", ret);
-+ fflush(rsp);
-+ fclose(rsp);
- }
-- if (rsp_len == 1 && rsp[0] == MESSAGE_SUBSCRIBE) {
-- add_subscriber(cli_fd);
- } else {
-- send(cli_fd, rsp, rsp_len, 0);
-+ warn("Can't open the client socket as file.\n");
- close(cli_fd);
- }
-- rsp[0] = '\0';
- }
- }
-
-@@ -339,7 +343,8 @@ void put_status(void)
- subscriber_list_t *sb = subscribe_head;
- while (sb != NULL) {
- subscriber_list_t *next = sb->next;
-- feed_subscriber(sb);
-+ if (print_status(sb->stream) != 0)
-+ remove_subscriber(sb);
- sb = next;
- }
- }
-diff --git a/bspwm.h b/bspwm.h
-index 6732933..274d6ea 100644
---- a/bspwm.h
-+++ b/bspwm.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_BSPWM_H
-diff --git a/common.h b/common.h
-index 2b29ba0..bb565dd 100644
---- a/common.h
-+++ b/common.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_COMMON_H
-@@ -27,6 +31,14 @@
-
- #define SOCKET_PATH_TPL "/tmp/bspwm%s-socket"
- #define SOCKET_ENV_VAR "BSPWM_SOCKET"
--#define MESSAGE_FAILURE '\x18'
-+
-+enum {
-+ MSG_SUCCESS,
-+ MSG_FAILURE,
-+ MSG_SYNTAX,
-+ MSG_UNKNOWN,
-+ MSG_SUBSCRIBE,
-+ MSG_LENGTH
-+};
-
- #endif
-diff --git a/desktop.c b/desktop.c
-index 5b6a4fd..fcc9770 100644
---- a/desktop.c
-+++ b/desktop.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-diff --git a/desktop.h b/desktop.h
-index 5931cd2..812dc1c 100644
---- a/desktop.h
-+++ b/desktop.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_DESKTOP_H
-diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
-index 8151d22..8f5534c 100644
---- a/doc/CONTRIBUTING.md
-+++ b/doc/CONTRIBUTING.md
-@@ -4,14 +4,10 @@ You must be comfortable with [C][1], [XCB][2] and [Git][3].
-
- ## Coding Style
-
--I follow the [Linux Coding Style][4] with the following exceptions:
--- One *Tab* equals 4 spaces.
--- Always use `typedef ...` for structures.
-+I follow the [Linux Coding Style][4].
-
- ## Browsing the Code
-
--The first files you might want to look at are `types.h`, `bspwm.c` and `events.c`.
--
- If you use `vim`:
- - Hitting *K* will lead you to the manual page of the function under the cursor (works with most `xcb_*` functions), sometimes you'll have to explicitly specify the section of the manual you're interested in with *3K* (e.g.: `open`).
- - Install `ctags` and run `ctags *.{c,h}` in the directory holding the source. Then, hitting *Ctrl-]* will lead you to the definition of the function/variable/structure under the cursor (to go back: *Ctrl-T*).
-@@ -24,11 +20,11 @@ To produce debug executables, issue:
- make debug
- ```
-
--If you use `systemd`, X might be started on the same virtual terminal as `bspwm` and you won't see its output, hence use something like `startx -- vt08` to start X (you can switch to the virtual terminal number *n* with *Ctrl-Alt-Fn*).
-+If you use `systemd`, X might be started on the same virtual terminal as `bspwm` and you won't see its output, hence use something like `startx -- vt08` to start X (you can switch to the virtual terminal number *<n>* with *Ctrl-Alt-F<n>*).
-
- The debug messages are generated by the `PRINTF` and `PUTS` macros: feel free to use them.
-
--If you want to use [`gdb`][5], switch to a free virtual terminal, e.g. *Ctrl-Alt-F2* and issue:
-+If you want to use [`gdb`][5], switch to a free virtual terminal and issue:
-
- ```
- gdb bspwm $(pgrep -x bspwm)
-diff --git a/doc/TODO.md b/doc/TODO.md
-index 4757461..3785005 100644
---- a/doc/TODO.md
-+++ b/doc/TODO.md
-@@ -1,5 +1,5 @@
--- Desktops as nodes?
-+- Set more attributes in `make_client` (instead of doing it in `apply_rules`) and don't pass `XCB_NONE` as argument.
-+- Internal nodes selectors/actions: labels?
- - Invisible state.
- - Restore built-in pointer grabbing?
--- `FILE *` instead of `char *` for writing the server response?
- - Use BSD `sys/{queue/tree}.h` for {list,tree} structures?
-diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf
-deleted file mode 100644
-index 68d4d6d..0000000
---- a/doc/asciidoc.conf
-+++ /dev/null
-@@ -1,39 +0,0 @@
--#
--# Borrowed from pacman
--#
--
--[macros]
--(?su)[\\]?(?P<name>linkman):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
--
--[attributes]
--asterisk=&#42;
--plus=&#43;
--caret=&#94;
--startsb=&#91;
--endsb=&#93;
--backslash=&#92;
--tilde=&#126;
--apostrophe=&#39;
--backtick=&#96;
--litdd=&#45;&#45;
--
--ifdef::backend-docbook[]
--[linkman-inlinemacro]
--{0%{target}}
--{0#<citerefentry>}
--{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
--{0#</citerefentry>}
--endif::backend-docbook[]
--
--ifdef::backend-docbook[]
--ifndef::docbook-xsl-172[]
--# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
--# v1.72 breaks with this because it replaces dots not in roff requests.
--[listingblock]
--<example><title>{title}</title>
--<literallayout>
--|
--</literallayout>
--{title#}</example>
--endif::docbook-xsl-172[]
--endif::backend-docbook[]
-diff --git a/doc/bspwm.1 b/doc/bspwm.1
-index 0248bd5..b6e42ec 100644
---- a/doc/bspwm.1
-+++ b/doc/bspwm.1
-@@ -2,12 +2,12 @@
- .\" Title: bspwm
- .\" Author: [see the "Author" section]
- .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
--.\" Date: 01/03/2014
-+.\" Date: 02/17/2014
- .\" Manual: Bspwm Manual
- .\" Source: Bspwm 0.8.8
- .\" Language: English
- .\"
--.TH "BSPWM" "1" "01/03/2014" "Bspwm 0\&.8\&.8" "Bspwm Manual"
-+.TH "BSPWM" "1" "02/17/2014" "Bspwm 0\&.8\&.8" "Bspwm Manual"
- .\" -----------------------------------------------------------------
- .\" * Define some portability stuff
- .\" -----------------------------------------------------------------
-@@ -167,7 +167,7 @@ Select a window\&.
- .\}
- .nf
- WINDOW_SEL := <window_id>
-- | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.floating|\&.tiled][\&.like|\&.unlike][\&.manual][\&.urgent][\&.local]
-+ | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.floating|\&.tiled][\&.like|\&.unlike][\&.manual|\&.automatic][\&.urgent][\&.local]
- .fi
- .if n \{\
- .RE
-@@ -247,8 +247,12 @@ Only consider windows that have a different class than the current window\&.
- .PP
- manual
- .RS 4
--Only consider windows in manual splitting mode (see
--\fB\-\-presel\fR)\&.
-+Only consider windows in manual splitting mode\&.
-+.RE
-+.PP
-+automatic
-+.RS 4
-+Only consider windows in automatic splitting mode\&.
- .RE
- .PP
- local
-@@ -270,8 +274,8 @@ Select a desktop\&.
- .\}
- .nf
- DESKTOP_SEL := <desktop_name>
-- | ^<n>
-- | (CYCLE_DIR|last|focused[:MONITOR_SEL]|older|newer)[\&.occupied|\&.free][\&.urgent][\&.local]
-+ | [MONITOR_SEL:]^<n>
-+ | (CYCLE_DIR|last|[MONITOR_SEL:]focused|older|newer)[\&.occupied|\&.free][\&.urgent][\&.local]
- .fi
- .if n \{\
- .RE
-@@ -620,6 +624,11 @@ Flip the tree of the selected desktop\&.
- Rotate the tree of the selected desktop\&.
- .RE
- .PP
-+\fB\-E\fR, \fB\-\-equalize\fR
-+.RS 4
-+Reset the split ratios of the tree of the selected desktop\&.
-+.RE
-+.PP
- \fB\-B\fR, \fB\-\-balance\fR
- .RS 4
- Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area\&.
-@@ -821,7 +830,12 @@ Enable or disable the recording of window focus history\&.
- .PP
- \fB\-\-subscribe\fR
- .RS 4
--Continuously print status informations on standard output\&.
-+Continuously print status informations\&.
-+.RE
-+.PP
-+\fB\-\-get\-status\fR
-+.RS 4
-+Print the current status informations\&.
- .RE
- .RE
- .SS "Pointer"
-@@ -881,7 +895,7 @@ rule \fIOPTIONS\fR
- \fBOptions\fR
- .RS 4
- .PP
--\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name>|* [\fB\-o\fR|\fB\-\-one\-shot\fR] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)]
-+\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name>|* [\fB\-o\fR|\fB\-\-one\-shot\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|follow|manage|focus)=(true|false)] [split_dir=DIR]
- .RS 4
- Create a new rule\&.
- .RE
-@@ -906,7 +920,7 @@ List the rules\&.
- \fBGeneral Syntax\fR
- .RS 4
- .PP
--config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR] <key> [<value>]
-+config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-w \fIWINDOW_SEL\fR] <key> [<value>]
- .RS 4
- Get or set the value of <key>\&.
- .RE
-@@ -926,6 +940,24 @@ quit [<status>]
- Quit with an optional exit status\&.
- .RE
- .RE
-+.SH "EXIT CODES"
-+.sp
-+If the server can\(cqt handle a message, \fBbspc\fR will return with one of the following exit codes:
-+.PP
-+1
-+.RS 4
-+Failure\&.
-+.RE
-+.PP
-+2
-+.RS 4
-+Syntax error\&.
-+.RE
-+.PP
-+3
-+.RS 4
-+Unknown command\&.
-+.RE
- .SH "SETTINGS"
- .sp
- Colors are either X color names or \fI#RRGGBB\fR, booleans are \fItrue\fR or \fIfalse\fR\&.
-@@ -1076,7 +1108,7 @@ atom of each window according to its floating state\&.
- .PP
- \fIignore_ewmh_focus\fR
- .RS 4
--Ignore EWMH requests to focus a window\&.
-+Ignore EWMH focus requests coming from applications\&.
- .RE
- .PP
- \fIremove_disabled_monitor\fR
-@@ -1089,16 +1121,17 @@ Consider disabled monitors as disconnected\&.
- .RS 4
- Padding space added at the sides of the monitor or desktop\&.
- .RE
--.SS "Desktop Settings"
-+.SS "Desktop and Window Settings"
- .PP
--\fIwindow_gap\fR
-+\fIborder_width\fR
- .RS 4
--Size of the gap that separates windows\&.
-+Window border width\&.
- .RE
-+.SS "Desktop Settings"
- .PP
--\fIborder_width\fR
-+\fIwindow_gap\fR
- .RS 4
--Window border width\&.
-+Size of the gap that separates windows\&.
- .RE
- .SH "STATUS FORMAT"
- .sp
-diff --git a/doc/bspwm.1.txt b/doc/bspwm.1.txt
-index 278d089..3cad790 100644
---- a/doc/bspwm.1.txt
-+++ b/doc/bspwm.1.txt
-@@ -134,15 +134,14 @@ Select a window.
-
- ----
- WINDOW_SEL := <window_id>
-- | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[.floating|.tiled][.like|.unlike][.manual][.urgent][.local]
-+ | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[.floating|.tiled][.like|.unlike][.manual|.automatic][.urgent][.local]
- ----
-
- Primary Selectors
- ^^^^^^^^^^^^^^^^^
-
- 'DIR'::
-- Selects the window in the given (spacial) direction relative to the active
-- window.
-+ Selects the window in the given (spacial) direction relative to the active window.
-
- 'CYCLE_DIR'::
- Selects the window in the given (cyclic) direction.
-@@ -178,7 +177,10 @@ unlike::
- Only consider windows that have a different class than the current window.
-
- manual::
-- Only consider windows in manual splitting mode (see *--presel*).
-+ Only consider windows in manual splitting mode.
-+
-+automatic::
-+ Only consider windows in automatic splitting mode.
-
- local::
- Only consider windows of the current desktop.
-@@ -193,8 +195,8 @@ Select a desktop.
-
- ----
- DESKTOP_SEL := <desktop_name>
-- | ^<n>
-- | (CYCLE_DIR|last|focused[:MONITOR_SEL]|older|newer)[.occupied|.free][.urgent][.local]
-+ | [MONITOR_SEL:]^<n>
-+ | (CYCLE_DIR|last|[MONITOR_SEL:]focused|older|newer)[.occupied|.free][.urgent][.local]
- ----
-
- Primary Selectors
-@@ -395,6 +397,9 @@ Options
- *-R*, *--rotate* '90|270|180'::
- Rotate the tree of the selected desktop.
-
-+*-E*, *--equalize*::
-+ Reset the split ratios of the tree of the selected desktop.
-+
- *-B*, *--balance*::
- Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area.
-
-@@ -508,7 +513,10 @@ Options
- Enable or disable the recording of window focus history.
-
- *--subscribe*::
-- Continuously print status informations on standard output.
-+ Continuously print status informations.
-+
-+*--get-status*::
-+ Print the current status informations.
-
- Pointer
- ~~~~~~~
-@@ -541,7 +549,7 @@ rule 'OPTIONS'
- Options
- ^^^^^^^
-
--*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)]::
-+*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|follow|manage|focus)=(true|false)] [split_dir=DIR]::
- Create a new rule.
-
- *-r*, *--remove* ^<n>|head|tail|<class_name>|<instance_name>|*...::
-@@ -556,7 +564,7 @@ Config
- General Syntax
- ^^^^^^^^^^^^^^
-
--config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'] <key> [<value>]::
-+config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-w 'WINDOW_SEL'] <key> [<value>]::
- Get or set the value of <key>.
-
- Quit
-@@ -568,6 +576,19 @@ General Syntax
- quit [<status>]::
- Quit with an optional exit status.
-
-+Exit Codes
-+----------
-+
-+If the server can't handle a message, *bspc* will return with one of the following exit codes:
-+
-+1::
-+ Failure.
-+2::
-+ Syntax error.
-+3::
-+ Unknown command.
-+
-+
- Settings
- --------
- Colors are either http://en.wikipedia.org/wiki/X11_color_names[X color names] or '#RRGGBB', booleans are 'true' or 'false'.
-@@ -653,7 +674,7 @@ Global Settings
- Set the value of the '_BSPWM_FLOATING_WINDOW' atom of each window according to its floating state.
-
- 'ignore_ewmh_focus'::
-- Ignore EWMH requests to focus a window.
-+ Ignore EWMH focus requests coming from applications.
-
- 'remove_disabled_monitor'::
- Consider disabled monitors as disconnected.
-@@ -667,15 +688,18 @@ Monitor and Desktop Settings
- 'left_padding'::
- Padding space added at the sides of the monitor or desktop.
-
-+Desktop and Window Settings
-+~~~~~~~~~~~~~~~~~~~~~~~~~~~
-+
-+'border_width'::
-+ Window border width.
-+
- Desktop Settings
- ~~~~~~~~~~~~~~~~
-
- 'window_gap'::
- Size of the gap that separates windows.
-
--'border_width'::
-- Window border width.
--
-
- Status Format
- -------------
-diff --git a/events.c b/events.c
-index b41a9a7..559030d 100644
---- a/events.c
-+++ b/events.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-@@ -87,26 +91,38 @@ void configure_request(xcb_generic_event_t *evt)
-
- coordinates_t loc;
- bool is_managed = locate_window(e->window, &loc);
-+ client_t *c = (is_managed ? loc.node->client : NULL);
-+ int w = 0, h = 0;
-
-- if (is_managed && !is_floating(loc.node->client)) {
-+ if (is_managed && !is_floating(c)) {
- if (e->value_mask & XCB_CONFIG_WINDOW_X)
-- loc.node->client->floating_rectangle.x = e->x;
-+ c->floating_rectangle.x = e->x;
- if (e->value_mask & XCB_CONFIG_WINDOW_Y)
-- loc.node->client->floating_rectangle.y = e->y;
-+ c->floating_rectangle.y = e->y;
- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
-- loc.node->client->floating_rectangle.width = e->width;
-+ w = e->width;
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
-- loc.node->client->floating_rectangle.height = e->height;
-+ h = e->height;
-+
-+ if (w != 0) {
-+ restrain_floating_width(c, &w);
-+ c->floating_rectangle.width = w;
-+ }
-+
-+ if (h != 0) {
-+ restrain_floating_height(c, &h);
-+ c->floating_rectangle.height = h;
-+ }
-
- xcb_configure_notify_event_t evt;
- xcb_rectangle_t rect;
-- xcb_window_t win = loc.node->client->window;
-- unsigned int bw = loc.node->client->border_width;
-+ xcb_window_t win = c->window;
-+ unsigned int bw = c->border_width;
-
-- if (loc.node->client->fullscreen)
-+ if (c->fullscreen)
- rect = loc.monitor->rectangle;
- else
-- rect = loc.node->client->tiled_rectangle;
-+ rect = c->tiled_rectangle;
-
- evt.response_type = XCB_CONFIGURE_NOTIFY;
- evt.event = win;
-@@ -121,7 +137,7 @@ void configure_request(xcb_generic_event_t *evt)
-
- xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
-
-- if (loc.node->client->pseudo_tiled)
-+ if (c->pseudo_tiled)
- arrange(loc.monitor, loc.desktop);
- } else {
- uint16_t mask = 0;
-@@ -132,28 +148,34 @@ void configure_request(xcb_generic_event_t *evt)
- mask |= XCB_CONFIG_WINDOW_X;
- values[i++] = e->x;
- if (is_managed)
-- loc.node->client->floating_rectangle.x = e->x;
-+ c->floating_rectangle.x = e->x;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
- mask |= XCB_CONFIG_WINDOW_Y;
- values[i++] = e->y;
- if (is_managed)
-- loc.node->client->floating_rectangle.y = e->y;
-+ c->floating_rectangle.y = e->y;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
- mask |= XCB_CONFIG_WINDOW_WIDTH;
-- values[i++] = e->width;
-- if (is_managed)
-- loc.node->client->floating_rectangle.width = e->width;
-+ w = e->width;
-+ if (is_managed) {
-+ restrain_floating_width(c, &w);
-+ c->floating_rectangle.width = w;
-+ }
-+ values[i++] = w;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
- mask |= XCB_CONFIG_WINDOW_HEIGHT;
-- values[i++] = e->height;
-- if (is_managed)
-- loc.node->client->floating_rectangle.height = e->height;
-+ h = e->height;
-+ if (is_managed) {
-+ restrain_floating_height(c, &h);
-+ c->floating_rectangle.height = h;
-+ }
-+ values[i++] = h;
- }
-
- if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
-@@ -175,7 +197,7 @@ void configure_request(xcb_generic_event_t *evt)
- }
-
- if (is_managed)
-- translate_client(monitor_from_client(loc.node->client), loc.monitor, loc.node->client);
-+ translate_client(monitor_from_client(c), loc.monitor, c);
- }
-
- void destroy_notify(xcb_generic_event_t *evt)
-@@ -199,17 +221,38 @@ void unmap_notify(xcb_generic_event_t *evt)
- void property_notify(xcb_generic_event_t *evt)
- {
- xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
-- xcb_icccm_wm_hints_t hints;
-
- /* PRINTF("property notify %X\n", e->window); */
-
-- if (e->atom != XCB_ATOM_WM_HINTS)
-+ if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS)
- return;
-
- coordinates_t loc;
-- if (locate_window(e->window, &loc)
-- && xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1)
-+ 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_urgency(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;
-+ xcb_size_hints_t size_hints;
-+ if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &size_hints, NULL) == 1 &&
-+ (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE))) {
-+ c->min_width = size_hints.min_width;
-+ c->max_width = size_hints.max_width;
-+ c->min_height = size_hints.min_height;
-+ c->max_height = size_hints.max_height;
-+ int w = c->floating_rectangle.width;
-+ int h = c->floating_rectangle.height;
-+ restrain_floating_size(c, &w, &h);
-+ c->floating_rectangle.width = w;
-+ c->floating_rectangle.height = h;
-+ arrange(loc.monitor, loc.desktop);
-+ }
-+ }
- }
-
- void client_message(xcb_generic_event_t *evt)
-@@ -233,7 +276,8 @@ void client_message(xcb_generic_event_t *evt)
- 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 || loc.node == mon->desk->focus)
-+ if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
-+ loc.node == mon->desk->focus)
- return;
- if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node) {
- set_fullscreen(loc.desktop->focus, false);
-@@ -255,16 +299,16 @@ void focus_in(xcb_generic_event_t *evt)
-
- /* PRINTF("focus in %X %d %d\n", e->event, e->mode, e->detail); */
-
-- if (e->mode == XCB_NOTIFY_MODE_GRAB
-- || e->mode == XCB_NOTIFY_MODE_UNGRAB)
-+ if (e->mode == XCB_NOTIFY_MODE_GRAB ||
-+ e->mode == XCB_NOTIFY_MODE_UNGRAB)
- return;
- /* prevent focus stealing */
- if ((e->detail == XCB_NOTIFY_DETAIL_ANCESTOR ||
- e->detail == XCB_NOTIFY_DETAIL_INFERIOR ||
- e->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL ||
- e->detail == XCB_NOTIFY_DETAIL_NONLINEAR) &&
-- (mon->desk->focus == NULL
-- || mon->desk->focus->client->window != e->event))
-+ (mon->desk->focus == NULL ||
-+ mon->desk->focus->client->window != e->event))
- update_input_focus();
- }
-
-@@ -275,8 +319,9 @@ void enter_notify(xcb_generic_event_t *evt)
-
- PRINTF("enter notify %X %d %d\n", win, e->mode, e->detail);
-
-- if (e->mode != XCB_NOTIFY_MODE_NORMAL
-- || (mon->desk->focus != NULL && mon->desk->focus->client->window == win))
-+ if (e->mode != XCB_NOTIFY_MODE_NORMAL ||
-+ (mon->desk->focus != NULL &&
-+ mon->desk->focus->client->window == win))
- return;
-
- enable_motion_recorder();
-diff --git a/events.h b/events.h
-index 7812dad..1d718da 100644
---- a/events.h
-+++ b/events.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_EVENTS_H
-diff --git a/ewmh.c b/ewmh.c
-index 25d86d3..f7d1466 100644
---- a/ewmh.c
-+++ b/ewmh.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <string.h>
-diff --git a/ewmh.h b/ewmh.h
-index b41c4ee..9a757e6 100644
---- a/ewmh.h
-+++ b/ewmh.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_EWMH_H
-diff --git a/examples/panel/panel_bar b/examples/panel/panel_bar
-index 7cbd2fa..bee3ec7 100755
---- a/examples/panel/panel_bar
-+++ b/examples/panel/panel_bar
-@@ -6,11 +6,11 @@ while read -r line ; do
- case $line in
- S*)
- # clock output
-- sys_infos="\\br\\f6${line#?}"
-+ sys_infos="\\br\\f7${line#?}"
- ;;
- T*)
- # xtitle output
-- title="\\br\\f7${line#?}"
-+ title="\\br\\f4${line#?}"
- ;;
- W*)
- # bspwm internal state
-@@ -48,7 +48,7 @@ while read -r line ; do
- L*)
- # layout
- layout=$(printf "%s" "${name}" | sed 's/\(.\).*/\U\1/')
-- wm_infos="$wm_infos \\br\\f6$layout"
-+ wm_infos="$wm_infos \\br\\f2$layout"
- ;;
- esac
- shift
-diff --git a/examples/panel/panel_dzen2 b/examples/panel/panel_dzen2
-index ac5cf94..adf1df1 100755
---- a/examples/panel/panel_dzen2
-+++ b/examples/panel/panel_dzen2
-@@ -7,17 +7,13 @@ font_size=11
-
- . panel_colors
-
--adaptive_centering=0
- screen_width=$(sres -W)
- NORMIFS=$IFS
- FIELDIFS=':'
- PADDING=' '
-
--while getopts 'af:s:' opt ; do
-+while getopts 'f:s:' opt ; do
- case "$opt" in
-- a)
-- adaptive_centering=1
-- ;;
- f)
- font_family=$OPTARG
- ;;
-@@ -68,8 +64,8 @@ while read -r line ; do
- ;;
- o*)
- # occupied desktop
-- FG=$COLOR_OCUPPIED_FG
-- BG=$COLOR_OCUPPIED_BG
-+ FG=$COLOR_OCCUPIED_FG
-+ BG=$COLOR_OCCUPIED_BG
- ;;
- f*)
- # free desktop
-@@ -103,12 +99,14 @@ while read -r line ; do
- right_indent=$((screen_width - right_width))
- available_center=$((screen_width - (left_width + right_width)))
- if [ $available_center -lt $center_width ] ; then
-- center_indent=$((left_indent + left_width))
-+ center_indent=$left_width
- else
-- if [ $adaptive_centering -eq 1 ] ; then
-+ max_left_right_width=$left_width
-+ [ $left_width -lt $right_width ] && max_left_right_width=$right_width
-+ if [ $((2 * max_left_right_width + center_width)) -gt $screen_width ] ; then
- center_indent=$((left_width + (available_center - center_width) / 2))
- else
-- center_indent=$(( (screen_width - center_width) / 2 ))
-+ center_indent=$(((screen_width - center_width) / 2))
- fi
- fi
- printf "%s\n" "^pa($center_indent)$title^pa($left_indent)$wm_infos^pa($right_indent)$sys_infos"
-diff --git a/helpers.c b/helpers.c
-index ccc197a..2e2f23f 100644
---- a/helpers.c
-+++ b/helpers.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-@@ -83,9 +87,3 @@ double distance(xcb_point_t a, xcb_point_t b)
- {
- return hypot(a.x - b.x, a.y - b.y);
- }
--
--void center_rectangle(xcb_rectangle_t *src, xcb_rectangle_t dst)
--{
-- src->x = dst.x + (dst.width - src->width) / 2;
-- src->y = dst.y + (dst.height - src->height) / 2;
--}
-diff --git a/helpers.h b/helpers.h
-index 81821ad..9a88676 100644
---- a/helpers.h
-+++ b/helpers.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_HELPERS_H
-@@ -44,7 +48,6 @@
- #define SMALEN 32
- #define INIT_CAP 8
-
--#define REMLEN(x) (BUFSIZ - strlen(x) - 1)
- #define streq(s1, s2) (strcmp((s1), (s2)) == 0)
-
- #ifdef DEBUG
-@@ -59,6 +62,5 @@ void warn(char *fmt, ...);
- void err(char *fmt, ...);
- bool get_color(char *col, xcb_window_t win, uint32_t *pxl);
- double distance(xcb_point_t a, xcb_point_t b);
--void center_rectangle(xcb_rectangle_t *src, xcb_rectangle_t dst);
-
- #endif
-diff --git a/history.c b/history.c
-index 4f6882c..7440a08 100644
---- a/history.c
-+++ b/history.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-@@ -103,8 +107,8 @@ void history_remove(desktop_t *d, node_t *n)
- 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))) {
-+ 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 *d = c->prev;
- if (history_head == c)
- history_head = history_tail;
-@@ -173,10 +177,10 @@ bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst
-
- 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
-- || !node_matches(&h->loc, ref, sel))
-+ if (!h->latest ||
-+ h->loc.node == NULL ||
-+ h->loc.node == ref->node ||
-+ !node_matches(&h->loc, ref, sel))
- continue;
- if (!record_history)
- history_needle = h;
-@@ -193,9 +197,9 @@ bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *
-
- 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))
-+ if (!h->latest ||
-+ h->loc.desktop == ref->desktop ||
-+ !desktop_matches(&h->loc, ref, sel))
- continue;
- if (!record_history)
- history_needle = h;
-@@ -212,9 +216,9 @@ bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *
-
- 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
-- || !desktop_matches(&h->loc, ref, sel))
-+ if (!h->latest ||
-+ h->loc.monitor == ref->monitor ||
-+ !desktop_matches(&h->loc, ref, sel))
- continue;
- if (!record_history)
- history_needle = h;
-diff --git a/history.h b/history.h
-index b57b94f..7b55516 100644
---- a/history.h
-+++ b/history.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_HISTORY_H
-diff --git a/messages.c b/messages.c
-index 2eb5cd8..af98cc0 100644
---- a/messages.c
-+++ b/messages.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <errno.h>
-@@ -38,15 +42,17 @@
- #include "settings.h"
- #include "tree.h"
- #include "window.h"
-+#include "common.h"
-+#include "subscribe.h"
- #include "messages.h"
-
--bool handle_message(char *msg, int msg_len, char *rsp)
-+int handle_message(char *msg, int msg_len, FILE *rsp)
- {
- int cap = INIT_CAP;
- int num = 0;
- char **args = malloc(cap * sizeof(char *));
- if (args == NULL)
-- return false;
-+ return MSG_FAILURE;
-
- for (int i = 0, j = 0; i < msg_len; i++) {
- if (msg[i] == 0) {
-@@ -58,7 +64,7 @@ bool handle_message(char *msg, int msg_len, char *rsp)
- char **new = realloc(args, cap * sizeof(char *));
- if (new == NULL) {
- free(args);
-- return false;
-+ return MSG_FAILURE;
- } else {
- args = new;
- }
-@@ -67,16 +73,16 @@ bool handle_message(char *msg, int msg_len, char *rsp)
-
- if (num < 1) {
- free(args);
-- return false;
-+ return MSG_SYNTAX;
- }
-
- char **args_orig = args;
-- bool ret = process_message(args, num, rsp);
-+ int ret = process_message(args, num, rsp);
- free(args_orig);
- return ret;
- }
-
--bool process_message(char **args, int num, char *rsp)
-+int process_message(char **args, int num, FILE *rsp)
- {
- if (streq("window", *args)) {
- return cmd_window(++args, --num);
-@@ -100,13 +106,13 @@ bool process_message(char **args, int num, char *rsp)
- return cmd_quit(++args, --num);
- }
-
-- return false;
-+ return MSG_UNKNOWN;
- }
-
--bool cmd_window(char **args, int num)
-+int cmd_window(char **args, int num)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
-
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = ref;
-@@ -115,11 +121,11 @@ bool cmd_window(char **args, int num)
- if (node_from_desc(*args, &ref, &trg))
- num--, args++;
- else
-- return false;
-+ return MSG_FAILURE;
- }
-
- if (trg.node == NULL)
-- return false;
-+ return MSG_FAILURE;
-
- bool dirty = false;
-
-@@ -129,7 +135,7 @@ bool cmd_window(char **args, int num)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (!node_from_desc(*args, &trg, &dst))
-- return false;
-+ return MSG_FAILURE;
- }
- focus_node(dst.monitor, dst.desktop, dst.node);
- } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
-@@ -141,12 +147,12 @@ bool cmd_window(char **args, int num)
- trg.desktop = dst.desktop;
- }
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- coordinates_t dst;
- if (monitor_from_desc(*args, &trg, &dst)) {
- if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
-@@ -154,12 +160,12 @@ bool cmd_window(char **args, int num)
- trg.desktop = dst.monitor->desk;
- }
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-w", *args) || streq("--to-window", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- coordinates_t dst;
- if (node_from_desc(*args, &trg, &dst)) {
- if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
-@@ -167,12 +173,12 @@ bool cmd_window(char **args, int num)
- trg.desktop = dst.desktop;
- }
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-s", *args) || streq("--swap", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- coordinates_t dst;
- if (node_from_desc(*args, &trg, &dst)) {
- if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
-@@ -183,12 +189,12 @@ bool cmd_window(char **args, int num)
- dirty = true;
- }
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-t", *args) || streq("--toggle", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- char *key = strtok(*args, EQL_TOK);
- char *val = strtok(NULL, EQL_TOK);
- alter_state_t a;
-@@ -199,7 +205,7 @@ bool cmd_window(char **args, int num)
- if (parse_bool(val, &b))
- a = ALTER_SET;
- else
-- return false;
-+ return MSG_FAILURE;
- }
- if (streq("fullscreen", key)) {
- set_fullscreen(trg.node, (a == ALTER_SET ? b : !trg.node->client->fullscreen));
-@@ -217,13 +223,15 @@ bool cmd_window(char **args, int num)
- } else if (streq("private", key)) {
- set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->private));
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-p", *args) || streq("--presel", *args)) {
- num--, args++;
-- if (num < 1 || !is_tiled(trg.node->client)
-- || trg.desktop->layout != LAYOUT_TILED)
-- return false;
-+ if (num < 1)
-+ return MSG_SYNTAX;
-+ if (!is_tiled(trg.node->client) ||
-+ trg.desktop->layout != LAYOUT_TILED)
-+ return MSG_FAILURE;
- if (streq("cancel", *args)) {
- reset_mode(&trg);
- } else {
-@@ -233,11 +241,11 @@ bool cmd_window(char **args, int num)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1)
-- return false;
-+ return MSG_FAILURE;
- }
-- if (auto_cancel && trg.node->split_mode == MODE_MANUAL
-- && dir == trg.node->split_dir
-- && rat == trg.node->split_ratio) {
-+ if (auto_cancel && trg.node->split_mode == MODE_MANUAL &&
-+ dir == trg.node->split_dir &&
-+ rat == trg.node->split_ratio) {
- reset_mode(&trg);
- } else {
- trg.node->split_mode = MODE_MANUAL;
-@@ -246,19 +254,19 @@ bool cmd_window(char **args, int num)
- }
- window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- }
- } else if (streq("-e", *args) || streq("--edge", *args)) {
- num--, args++;
- if (num < 2)
-- return false;
-+ return MSG_SYNTAX;
- direction_t dir;
- if (!parse_direction(*args, &dir))
-- return false;
-+ return MSG_FAILURE;
- node_t *n = find_fence(trg.node, dir);
- if (n == NULL)
-- return false;
-+ return MSG_FAILURE;
- num--, args++;
- if ((*args)[0] == '+' || (*args)[0] == '-') {
- int pix;
-@@ -268,58 +276,58 @@ bool cmd_window(char **args, int num)
- if (rat > 0 && rat < 1)
- n->split_ratio = rat;
- else
-- return false;
-+ return MSG_FAILURE;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else {
- double rat;
- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1)
- n->split_ratio = rat;
- else
-- return false;
-+ return MSG_FAILURE;
- }
- dirty = true;
- } else if (streq("-r", *args) || streq("--ratio", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- double rat;
- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
- trg.node->split_ratio = rat;
- window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-R", *args) || streq("--rotate", *args)) {
- num--, args++;
- if (num < 2)
-- return false;
-+ return MSG_SYNTAX;
- direction_t dir;
- if (!parse_direction(*args, &dir))
-- return false;
-+ return MSG_FAILURE;
- node_t *n = find_fence(trg.node, dir);
- if (n == NULL)
-- return false;
-+ return MSG_FAILURE;
- num--, args++;
- int deg;
- if (parse_degree(*args, &deg)) {
- rotate_tree(n, deg);
- dirty = true;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-c", *args) || streq("--close", *args)) {
- if (num > 1)
-- return false;
-+ return MSG_SYNTAX;
- window_close(trg.node);
- } else if (streq("-k", *args) || streq("--kill", *args)) {
- if (num > 1)
-- return false;
-+ return MSG_SYNTAX;
- window_kill(trg.monitor, trg.desktop, trg.node);
- dirty = true;
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
-
- num--, args++;
-@@ -328,13 +336,13 @@ bool cmd_window(char **args, int num)
- if (dirty)
- arrange(trg.monitor, trg.desktop);
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_desktop(char **args, int num)
-+int cmd_desktop(char **args, int num)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
-
- coordinates_t ref = {mon, mon->desk, NULL};
- coordinates_t trg = ref;
-@@ -343,7 +351,7 @@ bool cmd_desktop(char **args, int num)
- if (desktop_from_desc(*args, &ref, &trg))
- num--, args++;
- else
-- return false;
-+ return MSG_FAILURE;
- }
-
- bool dirty = false;
-@@ -354,7 +362,7 @@ bool cmd_desktop(char **args, int num)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (!desktop_from_desc(*args, &trg, &dst))
-- return false;
-+ return MSG_FAILURE;
- }
- if (auto_alternate && dst.desktop == mon->desk) {
- desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
-@@ -363,29 +371,31 @@ bool cmd_desktop(char **args, int num)
- focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
- } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
- num--, args++;
-- if (num < 1 || trg.monitor->desk_head == trg.monitor->desk_tail)
-- return false;
-+ if (num < 1)
-+ return MSG_SYNTAX;
-+ if (trg.monitor->desk_head == trg.monitor->desk_tail)
-+ return MSG_FAILURE;
- coordinates_t dst;
- if (monitor_from_desc(*args, &trg, &dst)) {
- transfer_desktop(trg.monitor, dst.monitor, trg.desktop);
- trg.monitor = dst.monitor;
- update_current();
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-s", *args) || streq("--swap", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- coordinates_t dst;
- if (desktop_from_desc(*args, &trg, &dst))
- swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop);
- else
-- return false;
-+ return MSG_FAILURE;
- } else if (streq("-l", *args) || streq("--layout", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- layout_t lyt;
- cycle_dir_t cyc;
- if (parse_cycle_direction(*args, &cyc))
-@@ -393,66 +403,69 @@ bool cmd_desktop(char **args, int num)
- else if (parse_layout(*args, &lyt))
- change_layout(trg.monitor, trg.desktop, lyt);
- else
-- return false;
-+ return MSG_FAILURE;
- } else if (streq("-n", *args) || streq("--rename", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- snprintf(trg.desktop->name, sizeof(trg.desktop->name), "%s", *args);
- ewmh_update_desktop_names();
- put_status();
- } else if (streq("-r", *args) || streq("--remove", *args)) {
-- if (trg.desktop->root == NULL
-- && trg.monitor->desk_head != trg.monitor->desk_tail) {
-+ if (trg.desktop->root == NULL &&
-+ trg.monitor->desk_head != trg.monitor->desk_tail) {
- remove_desktop(trg.monitor, trg.desktop);
- show_desktop(trg.monitor->desk);
- update_current();
-- return true;
-+ return MSG_SUCCESS;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-c", *args) || streq("--cancel-presel", *args)) {
- reset_mode(&trg);
- } else if (streq("-F", *args) || streq("--flip", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- flip_t flp;
- if (parse_flip(*args, &flp)) {
- flip_tree(trg.desktop->root, flp);
- dirty = true;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-R", *args) || streq("--rotate", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- int deg;
- if (parse_degree(*args, &deg)) {
- rotate_tree(trg.desktop->root, deg);
- dirty = true;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
-+ } else if (streq("-E", *args) || streq("--equalize", *args)) {
-+ equalize_tree(trg.desktop->root);
-+ dirty = true;
- } else if (streq("-B", *args) || streq("--balance", *args)) {
- balance_tree(trg.desktop->root);
- dirty = true;
- } else if (streq("-C", *args) || streq("--circulate", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- circulate_dir_t cir;
- if (parse_circulate_direction(*args, &cir)) {
- circulate_leaves(trg.monitor, trg.desktop, cir);
- dirty = true;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- } else if (streq("-t", *args) || streq("--toggle", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- char *key = strtok(*args, EQL_TOK);
- char *val = strtok(NULL, EQL_TOK);
- alter_state_t a;
-@@ -463,14 +476,14 @@ bool cmd_desktop(char **args, int num)
- if (parse_bool(val, &b))
- a = ALTER_SET;
- else
-- return false;
-+ return MSG_FAILURE;
- }
- if (streq("floating", key))
- trg.desktop->floating = (a == ALTER_SET ? b : !trg.desktop->floating);
- else
-- return false;
-+ return MSG_FAILURE;
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-@@ -478,13 +491,13 @@ bool cmd_desktop(char **args, int num)
- if (dirty)
- arrange(trg.monitor, trg.desktop);
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_monitor(char **args, int num)
-+int cmd_monitor(char **args, int num)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
-
- coordinates_t ref = {mon, NULL, NULL};
- coordinates_t trg = ref;
-@@ -493,7 +506,7 @@ bool cmd_monitor(char **args, int num)
- if (monitor_from_desc(*args, &ref, &trg))
- num--, args++;
- else
-- return false;
-+ return MSG_FAILURE;
- }
-
- while (num > 0) {
-@@ -502,7 +515,7 @@ bool cmd_monitor(char **args, int num)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (!monitor_from_desc(*args, &trg, &dst))
-- return false;
-+ return MSG_FAILURE;
- }
- if (auto_alternate && dst.monitor == mon) {
- desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
-@@ -512,7 +525,7 @@ bool cmd_monitor(char **args, int num)
- } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- desktop_t *d = trg.monitor->desk_head;
- while (num > 0 && d != NULL) {
- snprintf(d->name, sizeof(d->name), "%s", *args);
-@@ -535,7 +548,7 @@ bool cmd_monitor(char **args, int num)
- } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- while (num > 0) {
- add_desktop(trg.monitor, make_desktop(*args));
- num--, args++;
-@@ -543,7 +556,7 @@ bool cmd_monitor(char **args, int num)
- } else if (streq("-r", *args) || streq("--remove-desktops", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- while (num > 0) {
- coordinates_t dst;
- if (locate_desktop(*args, &dst) && dst.monitor->desk_head != dst.monitor->desk_tail && dst.desktop->root == NULL) {
-@@ -555,7 +568,7 @@ bool cmd_monitor(char **args, int num)
- } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- desktop_t *d = trg.monitor->desk_head;
- while (d != NULL && num > 0) {
- desktop_t *next = d->next;
-@@ -571,28 +584,28 @@ bool cmd_monitor(char **args, int num)
- } else if (streq("-n", *args) || streq("--rename", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- snprintf(trg.monitor->name, sizeof(trg.monitor->name), "%s", *args);
- put_status();
- } else if (streq("-s", *args) || streq("--swap", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- coordinates_t dst;
- if (monitor_from_desc(*args, &trg, &dst))
- swap_monitors(trg.monitor, dst.monitor);
- else
-- return false;
-+ return MSG_FAILURE;
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_query(char **args, int num, char *rsp)
-+int cmd_query(char **args, int num, FILE *rsp)
- {
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = {NULL, NULL, NULL};
-@@ -617,7 +630,7 @@ bool cmd_query(char **args, int num, char *rsp)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (!monitor_from_desc(*args, &ref, &trg))
-- return false;
-+ return MSG_FAILURE;
- }
- t++;
- } else if (streq("-d", *args) || streq("--desktop", *args)) {
-@@ -626,7 +639,7 @@ bool cmd_query(char **args, int num, char *rsp)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (!desktop_from_desc(*args, &ref, &trg))
-- return false;
-+ return MSG_FAILURE;
- }
- t++;
- } else if (streq("-w", *args) || streq("--window", *args)) {
-@@ -634,17 +647,17 @@ bool cmd_query(char **args, int num, char *rsp)
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- if (!node_from_desc(*args, &ref, &trg))
-- return false;
-+ return MSG_FAILURE;
- }
- t++;
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-
- if (d != 1 || t > 1)
-- return false;
-+ return MSG_SYNTAX;
-
- if (dom == DOMAIN_HISTORY)
- query_history(trg, rsp);
-@@ -655,18 +668,18 @@ bool cmd_query(char **args, int num, char *rsp)
- else
- query_monitors(trg, dom, rsp);
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_rule(char **args, int num, char *rsp)
-+int cmd_rule(char **args, int num, FILE *rsp)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- while (num > 0) {
- if (streq("-a", *args) || streq("--add", *args)) {
- num--, args++;
- if (num < 2)
-- return false;
-+ return MSG_SYNTAX;
- rule_t *rule = make_rule();
- snprintf(rule->cause, sizeof(rule->cause), "%s", *args);
- num--, args++;
-@@ -687,7 +700,7 @@ bool cmd_rule(char **args, int num, char *rsp)
- } else if (streq("-r", *args) || streq("--remove", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- int idx;
- while (num > 0) {
- if (parse_index(*args, &idx))
-@@ -704,129 +717,135 @@ bool cmd_rule(char **args, int num, char *rsp)
- num--, args++;
- list_rules(num > 0 ? *args : NULL, rsp);
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_pointer(char **args, int num)
-+int cmd_pointer(char **args, int num)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- while (num > 0) {
- if (streq("-t", *args) || streq("--track", *args)) {
- num--, args++;
- if (num < 2)
-- return false;
-+ return MSG_SYNTAX;
- int x, y;
- if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1)
- track_pointer(x, y);
- else
-- return false;
-+ return MSG_FAILURE;
- } else if (streq("-g", *args) || streq("--grab", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- pointer_action_t pac;
- if (parse_pointer_action(*args, &pac))
- grab_pointer(pac);
- else
-- return false;
-+ return MSG_FAILURE;
- } else if (streq("-u", *args) || streq("--ungrab", *args)) {
- ungrab_pointer();
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_restore(char **args, int num)
-+int cmd_restore(char **args, int num)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- while (num > 0) {
- if (streq("-T", *args) || streq("--tree", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- restore_tree(*args);
- } else if (streq("-H", *args) || streq("--history", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- restore_history(*args);
- } else if (streq("-S", *args) || streq("--stack", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- restore_stack(*args);
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_control(char **args, int num, char *rsp)
-+int cmd_control(char **args, int num, FILE *rsp)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- while (num > 0) {
- if (streq("--adopt-orphans", *args)) {
- adopt_orphans();
-- } else if (streq("--put-status", *args)) {
-- put_status();
- } else if (streq("--toggle-visibility", *args)) {
- toggle_visibility();
- } else if (streq("--subscribe", *args)) {
-- snprintf(rsp, BUFSIZ, "%c", MESSAGE_SUBSCRIBE);
-+ return MSG_SUBSCRIBE;
-+ } else if (streq("--get-status", *args)) {
-+ print_status(rsp);
- } else if (streq("--record-history", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- bool b;
- if (parse_bool(*args, &b))
- record_history = b;
- else
-- return false;
-+ return MSG_SYNTAX;
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool cmd_config(char **args, int num, char *rsp)
-+int cmd_config(char **args, int num, FILE *rsp)
- {
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = {NULL, NULL, NULL};
- if ((*args)[0] == OPT_CHR) {
-- if (streq("-d", *args) || streq("--desktop", *args)) {
-+ if (streq("-m", *args) || streq("--monitor", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-+ return MSG_SYNTAX;
-+ if (!monitor_from_desc(*args, &ref, &trg))
-+ return MSG_FAILURE;
-+ } else if (streq("-d", *args) || streq("--desktop", *args)) {
-+ num--, args++;
-+ if (num < 1)
-+ return MSG_SYNTAX;
- if (!desktop_from_desc(*args, &ref, &trg))
-- return false;
-- } else if (streq("-m", *args) || streq("--monitor", *args)) {
-+ return MSG_FAILURE;
-+ } else if (streq("-w", *args) || streq("--window", *args)) {
- num--, args++;
- if (num < 1)
-- return false;
-- if (!monitor_from_desc(*args, &ref, &trg))
-- return false;
-+ return MSG_SYNTAX;
-+ if (!node_from_desc(*args, &ref, &trg))
-+ return MSG_FAILURE;
- } else {
-- return false;
-+ return MSG_SYNTAX;
- }
- num--, args++;
- }
-@@ -835,21 +854,23 @@ bool cmd_config(char **args, int num, char *rsp)
- else if (num == 1)
- return get_setting(trg, *args, rsp);
- else
-- return false;
-+ return MSG_SYNTAX;
- }
-
--bool cmd_quit(char **args, int num)
-+int cmd_quit(char **args, int num)
- {
- if (num > 0 && sscanf(*args, "%i", &exit_status) != 1)
-- return false;
-+ return MSG_FAILURE;
- running = false;
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool set_setting(coordinates_t loc, char *name, char *value)
-+int set_setting(coordinates_t loc, char *name, char *value)
- {
--#define DESKSET(k, v) \
-- if (loc.desktop != NULL) \
-+#define DESKWINSET(k, v) \
-+ if (loc.node != NULL) \
-+ loc.node->client->k = v; \
-+ else if (loc.desktop != NULL) \
- loc.desktop->k = v; \
- else if (loc.monitor != NULL) \
- for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) \
-@@ -861,12 +882,23 @@ bool set_setting(coordinates_t loc, char *name, char *value)
- if (streq("border_width", name)) {
- unsigned int bw;
- if (sscanf(value, "%u", &bw) != 1)
-- return false;
-- DESKSET(border_width, bw)
-+ return MSG_FAILURE;
-+ DESKWINSET(border_width, bw)
-+#undef DESKWINSET
-+#define DESKSET(k, v) \
-+ if (loc.desktop != NULL) \
-+ loc.desktop->k = v; \
-+ else if (loc.monitor != NULL) \
-+ for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) \
-+ d->k = v; \
-+ else \
-+ for (monitor_t *m = mon_head; m != NULL; m = m->next) \
-+ 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)
-- return false;
-+ return MSG_FAILURE;
- DESKSET(window_gap, wg)
- #undef DESKSET
- #define MONDESKSET(k, v) \
-@@ -880,22 +912,22 @@ bool set_setting(coordinates_t loc, char *name, char *value)
- } else if (streq("top_padding", name)) {
- int tp;
- if (sscanf(value, "%i", &tp) != 1)
-- return false;
-+ return MSG_FAILURE;
- MONDESKSET(top_padding, tp)
- } else if (streq("right_padding", name)) {
- int rp;
- if (sscanf(value, "%i", &rp) != 1)
-- return false;
-+ return MSG_FAILURE;
- MONDESKSET(right_padding, rp)
- } else if (streq("bottom_padding", name)) {
- int bp;
- if (sscanf(value, "%i", &bp) != 1)
-- return false;
-+ return MSG_FAILURE;
- MONDESKSET(bottom_padding, bp)
- } else if (streq("left_padding", name)) {
- int lp;
- if (sscanf(value, "%i", &lp) != 1)
-- return false;
-+ return MSG_FAILURE;
- MONDESKSET(left_padding, lp)
- #undef MONDESKSET
- #define SETSTR(s) \
-@@ -909,8 +941,8 @@ bool set_setting(coordinates_t loc, char *name, char *value)
- if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
- split_ratio = r;
- else
-- return false;
-- return true;
-+ return MSG_FAILURE;
-+ return MSG_SUCCESS;
- #define SETCOLOR(s) \
- } else if (streq(#s, name)) { \
- snprintf(s, sizeof(s), "%s", value);
-@@ -948,14 +980,14 @@ bool set_setting(coordinates_t loc, char *name, char *value)
- window_hide(m->root);
- disable_motion_recorder();
- }
-- return true;
-+ return MSG_SUCCESS;
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
- #define SETBOOL(s) \
- } else if (streq(#s, name)) { \
- if (!parse_bool(value, &s)) \
-- return false;
-+ return MSG_FAILURE;
- SETBOOL(borderless_monocle)
- SETBOOL(gapless_monocle)
- SETBOOL(pointer_follows_monitor)
-@@ -967,42 +999,44 @@ bool set_setting(coordinates_t loc, char *name, char *value)
- SETBOOL(remove_disabled_monitor)
- #undef SETBOOL
- } else {
-- return false;
-+ return MSG_FAILURE;
- }
-
- 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);
-
-- return true;
-+ return MSG_SUCCESS;
- }
-
--bool get_setting(coordinates_t loc, char *name, char* rsp)
-+int get_setting(coordinates_t loc, char *name, FILE* rsp)
- {
- if (streq("split_ratio", name))
-- snprintf(rsp, BUFSIZ, "%lf", split_ratio);
-+ fprintf(rsp, "%lf", split_ratio);
- else if (streq("window_gap", name))
- if (loc.desktop == NULL)
-- return false;
-+ return MSG_FAILURE;
- else
-- snprintf(rsp, BUFSIZ, "%i", loc.desktop->window_gap);
-+ fprintf(rsp, "%i", loc.desktop->window_gap);
- else if (streq("border_width", name))
-- if (loc.desktop == NULL)
-- return false;
-+ if (loc.node != NULL)
-+ fprintf(rsp, "%u", loc.node->client->border_width);
-+ else if (loc.desktop != NULL)
-+ fprintf(rsp, "%u", loc.desktop->border_width);
- else
-- snprintf(rsp, BUFSIZ, "%u", loc.desktop->border_width);
-+ return MSG_FAILURE;
- else if (streq("external_rules_command", name))
-- snprintf(rsp, BUFSIZ, "%s", external_rules_command);
-+ fprintf(rsp, "%s", external_rules_command);
- else if (streq("status_prefix", name))
-- snprintf(rsp, BUFSIZ, "%s", status_prefix);
-+ fprintf(rsp, "%s", status_prefix);
- #define MONDESKGET(k) \
- else if (streq(#k, name)) \
- if (loc.desktop != NULL) \
-- snprintf(rsp, BUFSIZ, "%i", loc.desktop->k); \
-+ fprintf(rsp, "%i", loc.desktop->k); \
- else if (loc.monitor != NULL) \
-- snprintf(rsp, BUFSIZ, "%i", loc.monitor->k); \
-+ fprintf(rsp, "%i", loc.monitor->k); \
- else \
-- return false;
-+ return MSG_FAILURE;
- MONDESKGET(top_padding)
- MONDESKGET(right_padding)
- MONDESKGET(bottom_padding)
-@@ -1010,7 +1044,7 @@ bool get_setting(coordinates_t loc, char *name, char* rsp)
- #undef DESKGET
- #define GETCOLOR(s) \
- else if (streq(#s, name)) \
-- snprintf(rsp, BUFSIZ, "%s", s);
-+ fprintf(rsp, "%s", s);
- GETCOLOR(focused_border_color)
- GETCOLOR(active_border_color)
- GETCOLOR(normal_border_color)
-@@ -1025,7 +1059,7 @@ bool get_setting(coordinates_t loc, char *name, char* rsp)
- #undef GETCOLOR
- #define GETBOOL(s) \
- else if (streq(#s, name)) \
-- snprintf(rsp, BUFSIZ, "%s", BOOLSTR(s));
-+ fprintf(rsp, "%s", BOOLSTR(s));
- GETBOOL(borderless_monocle)
- GETBOOL(gapless_monocle)
- GETBOOL(focus_follows_pointer)
-@@ -1038,8 +1072,8 @@ bool get_setting(coordinates_t loc, char *name, char* rsp)
- GETBOOL(remove_disabled_monitor)
- #undef GETBOOL
- else
-- return false;
-- return true;
-+ return MSG_FAILURE;
-+ return MSG_SUCCESS;
- }
-
- bool parse_bool(char *value, bool *b)
-diff --git a/messages.h b/messages.h
-index 46a27f4..aa0e447 100644
---- a/messages.h
-+++ b/messages.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_MESSAGES_H
-@@ -31,22 +35,20 @@
- #define CAT_CHR '.'
- #define EQL_TOK "="
-
--#define MESSAGE_SUBSCRIBE '\x01'
--
--bool handle_message(char *msg, int msg_len, char *rsp);
--bool process_message(char **args, int num, char *rsp);
--bool cmd_window(char **args, int num);
--bool cmd_desktop(char **args, int num);
--bool cmd_monitor(char **args, int num);
--bool cmd_query(char **args, int num, char *rsp);
--bool cmd_rule(char **args, int num, char *rsp);
--bool cmd_pointer(char **args, int num);
--bool cmd_restore(char **args, int num);
--bool cmd_control(char **args, int num, char *rsp);
--bool cmd_config(char **args, int num, char *rsp);
--bool cmd_quit(char **args, int num);
--bool set_setting(coordinates_t loc, char *name, char *value);
--bool get_setting(coordinates_t loc, char *name, char* rsp);
-+int handle_message(char *msg, int msg_len, FILE *rsp);
-+int process_message(char **args, int num, FILE *rsp);
-+int cmd_window(char **args, int num);
-+int cmd_desktop(char **args, int num);
-+int cmd_monitor(char **args, int num);
-+int cmd_query(char **args, int num, FILE *rsp);
-+int cmd_rule(char **args, int num, FILE *rsp);
-+int cmd_pointer(char **args, int num);
-+int cmd_restore(char **args, int num);
-+int cmd_control(char **args, int num, FILE *rsp);
-+int cmd_config(char **args, int num, FILE *rsp);
-+int cmd_quit(char **args, int num);
-+int set_setting(coordinates_t loc, char *name, char *value);
-+int get_setting(coordinates_t loc, char *name, FILE* rsp);
- bool parse_bool(char *value, bool *b);
- bool parse_layout(char *s, layout_t *l);
- bool parse_direction(char *s, direction_t *d);
-diff --git a/monitor.c b/monitor.c
-index ea8e8c1..10c4dac 100644
---- a/monitor.c
-+++ b/monitor.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <limits.h>
-@@ -393,8 +397,9 @@ bool import_monitors(void)
- monitor_t *next = m->next;
- if (m->wired) {
- for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
-- if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
-- && contains(mb->rectangle, m->rectangle)) {
-+ if (mb != m && mb->wired &&
-+ (m->desk == NULL || mb->desk == NULL) &&
-+ contains(mb->rectangle, m->rectangle)) {
- if (mm == m)
- mm = mb;
- merge_monitors(m, mb);
-@@ -421,6 +426,9 @@ bool import_monitors(void)
- if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
- add_desktop(m, make_desktop(NULL));
-
-+ if (!running && pri_mon != NULL && mon_head != pri_mon)
-+ swap_monitors(mon_head, pri_mon);
-+
- free(sres);
- update_motion_recorder();
- return (num_monitors > 0);
-diff --git a/monitor.h b/monitor.h
-index 3c5bc9f..1d290c0 100644
---- a/monitor.h
-+++ b/monitor.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_MONITOR_H
-diff --git a/pointer.c b/pointer.c
-index c06922e..4527c30 100644
---- a/pointer.c
-+++ b/pointer.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include "bspwm.h"
-@@ -173,8 +177,7 @@ void track_pointer(int root_x, int root_y)
- if (frozen_pointer->action == ACTION_NONE)
- return;
-
-- int16_t delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
-- uint16_t width, height;
-+ int delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
-
- pointer_action_t pac = frozen_pointer->action;
- monitor_t *m = frozen_pointer->monitor;
-@@ -311,15 +314,27 @@ void track_pointer(int root_x, int root_y)
- break;
- }
- }
-- width = MAX(1, w);
-- height = MAX(1, h);
-+
-+ int oldw = w, oldh = h;
-+ restrain_floating_size(c, &w, &h);
-+
- if (c->pseudo_tiled) {
-- c->floating_rectangle.width = width;
-- c->floating_rectangle.height = height;
-+ c->floating_rectangle.width = w;
-+ c->floating_rectangle.height = h;
- arrange(m, d);
- } else {
-- c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
-- window_move_resize(win, x, y, width, height);
-+ if (oldw == w) {
-+ c->floating_rectangle.x = x;
-+ c->floating_rectangle.width = w;
-+ }
-+ if (oldh == h) {
-+ c->floating_rectangle.y = y;
-+ c->floating_rectangle.height = h;
-+ }
-+ window_move_resize(win, c->floating_rectangle.x,
-+ c->floating_rectangle.y,
-+ c->floating_rectangle.width,
-+ c->floating_rectangle.height);
- }
- }
- break;
-diff --git a/pointer.h b/pointer.h
-index 534c66e..e156dfa 100644
---- a/pointer.h
-+++ b/pointer.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_POINTER_H
-diff --git a/query.c b/query.c
-index 13c3910..2f61882 100644
---- a/query.c
-+++ b/query.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdio.h>
-@@ -33,82 +37,78 @@
- #include "tree.h"
- #include "query.h"
-
--void query_monitors(coordinates_t loc, domain_t dom, char *rsp)
-+void query_monitors(coordinates_t loc, domain_t dom, FILE *rsp)
- {
-- char line[MAXLEN];
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (loc.monitor != NULL && m != loc.monitor)
- continue;
- if (dom != DOMAIN_DESKTOP) {
- if (dom == DOMAIN_MONITOR) {
-- snprintf(line, sizeof(line), "%s\n", m->name);
-- strncat(rsp, line, REMLEN(rsp));
-+ fprintf(rsp, "%s\n", m->name);
- continue;
- } else {
-- snprintf(line, sizeof(line), "%s %ux%u%+i%+i %i,%i,%i,%i", m->name, m->rectangle.width, m->rectangle.height, m->rectangle.x, m->rectangle.y, m->top_padding, m->right_padding, m->bottom_padding, m->left_padding);
-- strncat(rsp, line, REMLEN(rsp));
-- if (m == mon)
-- strncat(rsp, " *", REMLEN(rsp));
-- strncat(rsp, "\n", REMLEN(rsp));
-+ fprintf(rsp, "%s %ux%u%+i%+i %i,%i,%i,%i%s\n", m->name,
-+ m->rectangle.width,m->rectangle.height, m->rectangle.x, m->rectangle.y,
-+ m->top_padding, m->right_padding, m->bottom_padding, m->left_padding,
-+ (m == mon ? " *" : ""));
- }
- }
- query_desktops(m, dom, loc, (dom == DOMAIN_DESKTOP ? 0 : 1), rsp);
- }
- }
-
--void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, char *rsp)
-+void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, FILE *rsp)
- {
-- char line[MAXLEN];
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (loc.desktop != NULL && d != loc.desktop)
- continue;
- for (unsigned int i = 0; i < depth; i++)
-- strncat(rsp, " ", REMLEN(rsp));
-+ fprintf(rsp, "\t");
- if (dom == DOMAIN_DESKTOP) {
-- snprintf(line, sizeof(line), "%s\n", d->name);
-- strncat(rsp, line, REMLEN(rsp));
-+ fprintf(rsp, "%s\n", d->name);
- continue;
- } else {
-- snprintf(line, sizeof(line), "%s %u %i %i,%i,%i,%i %c %c", d->name, d->border_width, d->window_gap, d->top_padding, d->right_padding, d->bottom_padding, d->left_padding, (d->layout == LAYOUT_TILED ? 'T' : 'M'), (d->floating ? 'f' : '-'));
-- strncat(rsp, line, REMLEN(rsp));
-- if (d == m->desk)
-- strncat(rsp, " *", REMLEN(rsp));
-- strncat(rsp, "\n", REMLEN(rsp));
-+ fprintf(rsp, "%s %u %i %i,%i,%i,%i %c %c%s\n", d->name, d->border_width,
-+ d->window_gap,
-+ d->top_padding, d->right_padding, d->bottom_padding, d->left_padding,
-+ (d->layout == LAYOUT_TILED ? 'T' : 'M'), (d->floating ? 'f' : '-'),
-+ (d == m->desk ? " *" : ""));
- }
- query_tree(d, d->root, rsp, depth + 1);
- }
- }
-
--void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
-+void query_tree(desktop_t *d, node_t *n, FILE *rsp, unsigned int depth)
- {
- if (n == NULL)
- return;
-
-- char line[MAXLEN];
--
- for (unsigned int i = 0; i < depth; i++)
-- strncat(rsp, " ", REMLEN(rsp));
-+ fprintf(rsp, "\t");
-
- if (is_leaf(n)) {
- client_t *c = n->client;
-- snprintf(line, sizeof(line), "%c %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))), (c->floating ? 'f' : '-'), (c->pseudo_tiled ? 'd' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-'));
-+ fprintf(rsp, "%c %s %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c%c%s\n",
-+ (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')),
-+ c->class_name, c->instance_name, c->window, c->border_width,
-+ c->floating_rectangle.width, c->floating_rectangle.height,
-+ c->floating_rectangle.x, c->floating_rectangle.y,
-+ (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))),
-+ (c->floating ? 'f' : '-'), (c->pseudo_tiled ? 'd' : '-'), (c->fullscreen ? 'F' : '-'),
-+ (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'),
-+ (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-'),
-+ (n == d->focus ? " *" : ""));
- } else {
-- snprintf(line, sizeof(line), "%c %c %lf", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
-+ fprintf(rsp, "%c %c %lf\n", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'),
-+ (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
- }
-
-- strncat(rsp, line, REMLEN(rsp));
--
-- if (n == d->focus)
-- strncat(rsp, " *", REMLEN(rsp));
-- strncat(rsp, "\n", REMLEN(rsp));
--
- query_tree(d, n->first_child, rsp, depth + 1);
- query_tree(d, n->second_child, rsp, depth + 1);
- }
-
--void query_history(coordinates_t loc, char *rsp)
-+void query_history(coordinates_t loc, FILE *rsp)
- {
-- char line[MAXLEN];
- for (history_t *h = history_head; h != NULL; h = h->next) {
- if ((loc.monitor != NULL && h->loc.monitor != loc.monitor)
- || (loc.desktop != NULL && h->loc.desktop != loc.desktop))
-@@ -116,26 +116,18 @@ void query_history(coordinates_t loc, char *rsp)
- xcb_window_t win = XCB_NONE;
- if (h->loc.node != NULL)
- win = h->loc.node->client->window;
-- snprintf(line, sizeof(line), "%s %s 0x%X", h->loc.monitor->name, h->loc.desktop->name, win);
-- strncat(rsp, line, REMLEN(rsp));
-- strncat(rsp, "\n", REMLEN(rsp));
-+ fprintf(rsp, "%s %s 0x%X\n", h->loc.monitor->name, h->loc.desktop->name, win);
- }
- }
-
--void query_stack(char *rsp)
-+void query_stack(FILE *rsp)
- {
-- char line[MAXLEN];
-- for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
-- snprintf(line, sizeof(line), "0x%X", s->node->client->window);
-- strncat(rsp, line, REMLEN(rsp));
-- strncat(rsp, "\n", REMLEN(rsp));
-- }
-+ for (stacking_list_t *s = stack_head; s != NULL; s = s->next)
-+ fprintf(rsp, "0x%X\n", s->node->client->window);
- }
-
--void query_windows(coordinates_t loc, char *rsp)
-+void query_windows(coordinates_t loc, FILE *rsp)
- {
-- char line[MAXLEN];
--
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (loc.monitor != NULL && m != loc.monitor)
- continue;
-@@ -145,8 +137,7 @@ void query_windows(coordinates_t loc, char *rsp)
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (loc.node != NULL && n != loc.node)
- continue;
-- snprintf(line, sizeof(line), "0x%X\n", n->client->window);
-- strncat(rsp, line, REMLEN(rsp));
-+ fprintf(rsp, "0x%X\n", n->client->window);
- }
- }
- }
-@@ -154,7 +145,7 @@ void query_windows(coordinates_t loc, char *rsp)
-
- bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
- {
-- client_select_t sel = {CLIENT_TYPE_ALL, CLIENT_CLASS_ALL, false, false, false};
-+ client_select_t sel = {CLIENT_TYPE_ALL, CLIENT_CLASS_ALL, CLIENT_MODE_ALL, false, false};
- char *tok;
- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
- tok[0] = '\0';
-@@ -167,10 +158,12 @@ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
- sel.class = CLIENT_CLASS_EQUAL;
- } else if (streq("unlike", tok)) {
- sel.class = CLIENT_CLASS_DIFFER;
-+ } else if (streq("manual", tok)) {
-+ sel.mode = CLIENT_MODE_MANUAL;
-+ } else if (streq("automatic", tok)) {
-+ sel.mode = CLIENT_MODE_AUTOMATIC;
- } else if (streq("urgent", tok)) {
- sel.urgent = true;
-- } else if (streq("manual", tok)) {
-- sel.manual = true;
- } else if (streq("local", tok)) {
- sel.local = true;
- }
-@@ -185,6 +178,14 @@ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
- history_dir_t hdi;
- if (parse_direction(desc, &dir)) {
- dst->node = nearest_neighbor(ref->monitor, ref->desktop, ref->node, dir, sel);
-+ if (dst->node == NULL && num_monitors > 1) {
-+ monitor_t *m = nearest_monitor(ref->monitor, dir, (desktop_select_t) {DESKTOP_STATUS_ALL, false, false});
-+ if (m != NULL) {
-+ dst->monitor = m;
-+ dst->desktop = m->desk;
-+ dst->node = m->desk->focus;
-+ }
-+ }
- } else if (parse_cycle_direction(desc, &cyc)) {
- dst->node = closest_node(ref->monitor, ref->desktop, ref->node, cyc, sel);
- } else if (parse_history_direction(desc, &hdi)) {
-@@ -246,13 +247,17 @@ bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
- dst->monitor = mon;
- dst->desktop = mon->desk;
- }
-- } else if ((colon = index(desc, ':')) != NULL) {
-+ } else if ((colon = strchr(desc, ':')) != NULL) {
- *colon = '\0';
-- if (streq("focused", desc))
-- if (monitor_from_desc(colon + 1, ref, dst))
-+ if (monitor_from_desc(desc, ref, dst)) {
-+ if (streq("focused", colon + 1)) {
- dst->desktop = dst->monitor->desk;
-+ } else if (parse_index(colon + 1, &idx)) {
-+ desktop_from_index(idx, dst, dst->monitor);
-+ }
-+ }
- } else if (parse_index(desc, &idx)) {
-- desktop_from_index(idx, dst);
-+ desktop_from_index(idx, dst, NULL);
- } else {
- locate_desktop(desc, dst);
- }
-@@ -343,9 +348,11 @@ bool locate_monitor(char *name, coordinates_t *loc)
- return false;
- }
-
--bool desktop_from_index(int i, coordinates_t *loc)
-+bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm)
- {
-- for (monitor_t *m = mon_head; m != NULL; m = m->next)
-+ 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, i--)
- if (i == 1) {
- loc->monitor = m;
-@@ -353,6 +360,7 @@ bool desktop_from_index(int i, coordinates_t *loc)
- loc->node = NULL;
- return true;
- }
-+ }
- return false;
- }
-
-@@ -370,6 +378,9 @@ bool monitor_from_index(int i, coordinates_t *loc)
-
- bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel)
- {
-+ if (ref->node == NULL || loc->node == NULL)
-+ return false;
-+
- if (sel.type != CLIENT_TYPE_ALL &&
- is_tiled(loc->node->client)
- ? sel.type == CLIENT_TYPE_FLOATING
-@@ -382,7 +393,10 @@ bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel)
- : sel.class == CLIENT_CLASS_EQUAL)
- return false;
-
-- if (sel.manual && loc->node->split_mode != MODE_MANUAL)
-+ if (sel.mode != CLIENT_MODE_ALL &&
-+ loc->node->split_mode == MODE_MANUAL
-+ ? sel.mode == CLIENT_MODE_AUTOMATIC
-+ : sel.mode == CLIENT_MODE_MANUAL)
- return false;
-
- if (sel.local && loc->desktop != ref->desktop)
-diff --git a/query.h b/query.h
-index bb2db30..8cd2ee7 100644
---- a/query.h
-+++ b/query.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_QUERY_H
-@@ -34,19 +38,19 @@ typedef enum {
- DOMAIN_STACK
- } domain_t;
-
--void query_monitors(coordinates_t loc, domain_t dom, char *rsp);
--void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, char *rsp);
--void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth);
--void query_history(coordinates_t loc, char *rsp);
--void query_stack(char *rsp);
--void query_windows(coordinates_t loc, char *rsp);
-+void query_monitors(coordinates_t loc, domain_t dom, FILE *rsp);
-+void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, FILE *rsp);
-+void query_tree(desktop_t *d, node_t *n, FILE *rsp, unsigned int depth);
-+void query_history(coordinates_t loc, FILE *rsp);
-+void query_stack(FILE *rsp);
-+void query_windows(coordinates_t loc, FILE *rsp);
- bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
- bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
- bool 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_index(int i, coordinates_t *loc);
-+bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm);
- bool monitor_from_index(int i, coordinates_t *loc);
- bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel);
- bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
-diff --git a/restore.c b/restore.c
-index f136156..24ae84d 100644
---- a/restore.c
-+++ b/restore.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <ctype.h>
-@@ -68,7 +72,8 @@ void restore_tree(char *file_path)
- unsigned int w, h;
- char end = 0;
- name[0] = '\0';
-- sscanf(line + level, "%s %ux%u%i%i %i,%i,%i,%i %c", name, &w, &h, &x, &y, &top, &right, &bottom, &left, &end);
-+ sscanf(line + level, "%s %ux%u%i%i %i,%i,%i,%i %c", name, &w, &h, &x, &y,
-+ &top, &right, &bottom, &left, &end);
- m = find_monitor(name);
- if (m == NULL)
- continue;
-@@ -79,7 +84,7 @@ void restore_tree(char *file_path)
- m->left_padding = left;
- if (end != 0)
- mon = m;
-- } else if (level == 2) {
-+ } else if (level == 1) {
- if (m == NULL)
- continue;
- int wg, top, right, bottom, left;
-@@ -87,7 +92,8 @@ void restore_tree(char *file_path)
- char floating, layout = 0, end = 0;
- name[0] = '\0';
- loc.desktop = NULL;
-- sscanf(line + level, "%s %u %i %i,%i,%i,%i %c %c %c", name, &bw, &wg, &top, &right, &bottom, &left, &layout, &floating, &end);
-+ sscanf(line + level, "%s %u %i %i,%i,%i,%i %c %c %c", name,
-+ &bw, &wg, &top, &right, &bottom, &left, &layout, &floating, &end);
- locate_desktop(name, &loc);
- d = loc.desktop;
- if (d == NULL)
-@@ -109,7 +115,7 @@ void restore_tree(char *file_path)
- if (m == NULL || d == NULL)
- continue;
- node_t *birth = make_node();
-- if (level == 4) {
-+ if (level == 2) {
- empty_desktop(d);
- d->root = birth;
- } else if (n != NULL) {
-@@ -135,10 +141,15 @@ void restore_tree(char *file_path)
- else if (st == 'V')
- n->split_type = TYPE_VERTICAL;
- } else {
-- client_t *c = make_client(XCB_NONE);
-+ client_t *c = make_client(XCB_NONE, d->border_width);
- num_clients++;
- char floating, pseudo_tiled, fullscreen, urgent, locked, sticky, private, sd, sm, end = 0;
-- sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &pseudo_tiled, &fullscreen, &urgent, &locked, &sticky, &private, &sm, &end);
-+ sscanf(line + level, "%c %s %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c %c", &br,
-+ c->class_name, c->instance_name, &c->window, &c->border_width,
-+ &c->floating_rectangle.width, &c->floating_rectangle.height,
-+ &c->floating_rectangle.x, &c->floating_rectangle.y,
-+ &sd, &floating, &pseudo_tiled, &fullscreen, &urgent,
-+ &locked, &sticky, &private, &sm, &end);
- c->floating = (floating == '-' ? false : true);
- c->pseudo_tiled = (pseudo_tiled == '-' ? false : true);
- c->fullscreen = (fullscreen == '-' ? false : true);
-diff --git a/restore.h b/restore.h
-index 7f0ae21..7debd57 100644
---- a/restore.h
-+++ b/restore.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_RESTORE_H
-diff --git a/rule.c b/rule.c
-index e886343..b669cb6 100644
---- a/rule.c
-+++ b/rule.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdio.h>
-@@ -148,12 +152,15 @@ void apply_rules(xcb_window_t win, rule_consequence_t *csq)
- 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) {
-+ 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) {
- csq->floating = 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->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);
-@@ -177,10 +184,14 @@ void apply_rules(xcb_window_t win, rule_consequence_t *csq)
-
- 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.min_width > 0 && size_hints.min_height > 0
-- && size_hints.min_width == size_hints.max_width
-- && size_hints.min_height == size_hints.max_height)
-+ if (size_hints.min_width > 0 && size_hints.min_height > 0 &&
-+ size_hints.min_width == size_hints.max_width &&
-+ size_hints.min_height == size_hints.max_height)
- csq->floating = true;
-+ csq->min_width = size_hints.min_width;
-+ csq->max_width = size_hints.max_width;
-+ csq->min_height = size_hints.min_height;
-+ csq->max_height = size_hints.max_height;
- }
-
- xcb_window_t transient_for = XCB_NONE;
-@@ -198,9 +209,9 @@ void apply_rules(xcb_window_t win, rule_consequence_t *csq)
- rule_t *rule = rule_head;
- while (rule != NULL) {
- rule_t *next = rule->next;
-- if (streq(rule->cause, MATCH_ANY)
-- || streq(rule->cause, csq->class_name)
-- || streq(rule->cause, csq->instance_name)) {
-+ if (streq(rule->cause, MATCH_ANY) ||
-+ streq(rule->cause, csq->class_name) ||
-+ streq(rule->cause, csq->instance_name)) {
- char effect[MAXLEN];
- snprintf(effect, sizeof(effect), "%s", rule->effect);
- char *key = strtok(effect, CSQ_BLK);
-@@ -265,10 +276,14 @@ void parse_rule_consequence(int fd, rule_consequence_t *csq)
- void parse_key_value(char *key, char *value, rule_consequence_t *csq)
- {
- bool v;
-- if (streq("desktop", key)) {
-- snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
-- } else if (streq("monitor", key)) {
-+ 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("window", 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 (parse_bool(value, &v)) {
- if (streq("floating", key))
- csq->floating = v;
-@@ -281,7 +296,6 @@ void parse_key_value(char *key, char *value, rule_consequence_t *csq)
- SETCSQ(sticky)
- SETCSQ(private)
- SETCSQ(center)
-- SETCSQ(lower)
- SETCSQ(follow)
- SETCSQ(manage)
- SETCSQ(focus)
-@@ -289,13 +303,11 @@ void parse_key_value(char *key, char *value, rule_consequence_t *csq)
- }
- }
-
--void list_rules(char *pattern, char *rsp)
-+void list_rules(char *pattern, FILE *rsp)
- {
-- char line[MAXLEN];
- for (rule_t *r = rule_head; r != NULL; r = r->next) {
- if (pattern != NULL && !streq(pattern, r->cause))
- continue;
-- snprintf(line, sizeof(line), "%s => %s\n", r->cause, r->effect);
-- strncat(rsp, line, REMLEN(rsp));
-+ fprintf(rsp, "%s => %s\n", r->cause, r->effect);
- }
- }
-diff --git a/rule.h b/rule.h
-index 6467aaf..f57301d 100644
---- a/rule.h
-+++ b/rule.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_RULE_H
-@@ -41,6 +45,6 @@ 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(char *pattern, char *rsp);
-+void list_rules(char *pattern, FILE *rsp);
-
- #endif
-diff --git a/settings.c b/settings.c
-index 997ef93..4b0ed4e 100644
---- a/settings.c
-+++ b/settings.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <unistd.h>
-diff --git a/settings.h b/settings.h
-index 372347e..06195f9 100644
---- a/settings.h
-+++ b/settings.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_SETTINGS_H
-diff --git a/stack.c b/stack.c
-index 3c4f6be..7351bd1 100644
---- a/stack.c
-+++ b/stack.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-@@ -41,6 +45,8 @@ void stack_insert_after(stacking_list_t *a, node_t *n)
- if (a == NULL) {
- stack_head = stack_tail = s;
- } else {
-+ if (a->node == n)
-+ return;
- remove_stack_node(n);
- stacking_list_t *b = a->next;
- if (b != NULL)
-@@ -59,6 +65,8 @@ void stack_insert_before(stacking_list_t *a, node_t *n)
- if (a == NULL) {
- stack_head = stack_tail = s;
- } else {
-+ if (a->node == n)
-+ return;
- remove_stack_node(n);
- stacking_list_t *b = a->prev;
- if (b != NULL)
-@@ -113,8 +121,14 @@ void stack(node_t *n, stack_flavor_t f)
- return;
- stacking_list_t *latest_tiled = NULL;
- stacking_list_t *oldest_floating = NULL;
-+ stacking_list_t *oldest_fullscreen = NULL;
- for (stacking_list_t *s = (f == STACK_ABOVE ? stack_tail : stack_head); s != NULL; s = (f == STACK_ABOVE ? s->prev : s->next)) {
- if (s->node != n) {
-+ if (s->node->client->fullscreen) {
-+ if (oldest_fullscreen == NULL)
-+ oldest_fullscreen = s;
-+ continue;
-+ }
- if (s->node->client->floating == n->client->floating) {
- if (f == STACK_ABOVE) {
- stack_insert_after(s, n);
-@@ -131,18 +145,24 @@ void stack(node_t *n, stack_flavor_t f)
- }
- }
- }
-- if (latest_tiled == NULL && oldest_floating == NULL)
-+ if (latest_tiled == NULL && oldest_floating == NULL && oldest_fullscreen == NULL)
- return;
- if (n->client->floating) {
-- if (latest_tiled == NULL)
-- return;
-+ if (latest_tiled != NULL) {
- window_above(n->client->window, latest_tiled->node->client->window);
- stack_insert_after(latest_tiled, n);
-+ } else if (oldest_fullscreen != NULL) {
-+ window_below(n->client->window, oldest_fullscreen->node->client->window);
-+ stack_insert_before(oldest_fullscreen, n);
-+ }
- } else {
-- if (oldest_floating == NULL)
-- return;
-+ if (oldest_floating != NULL) {
- window_below(n->client->window, oldest_floating->node->client->window);
- stack_insert_before(oldest_floating, n);
-+ } else if (oldest_fullscreen != NULL) {
-+ window_below(n->client->window, oldest_fullscreen->node->client->window);
-+ stack_insert_before(oldest_fullscreen, n);
-+ }
- }
- }
- }
-diff --git a/stack.h b/stack.h
-index 259a201..83f767f 100644
---- a/stack.h
-+++ b/stack.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_STACK_H
-diff --git a/subscribe.c b/subscribe.c
-index d876052..104c873 100644
---- a/subscribe.c
-+++ b/subscribe.c
-@@ -1,3 +1,31 @@
-+/* Copyright (c) 2012-2014, 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
-+ */
-+
- #include <stdlib.h>
- #include <unistd.h>
- #include <ctype.h>
-@@ -6,18 +34,11 @@
- #include "settings.h"
- #include "subscribe.h"
-
--subscriber_list_t *make_subscriber_list(int fd)
-+subscriber_list_t *make_subscriber_list(FILE *stream)
- {
- subscriber_list_t *sb = malloc(sizeof(subscriber_list_t));
- sb->prev = sb->next = NULL;
-- sb->fd = fd;
-- sb->stream = fdopen(fd, "w");
-- if (sb->stream == NULL) {
-- warn("Can't open subscriber %i\n", fd);
-- close(fd);
-- free(sb);
-- return NULL;
-- }
-+ sb->stream = stream;
- return sb;
- }
-
-@@ -39,11 +60,9 @@ void remove_subscriber(subscriber_list_t *sb)
- free(sb);
- }
-
--void add_subscriber(int fd)
-+void add_subscriber(FILE *stream)
- {
-- subscriber_list_t *sb = make_subscriber_list(fd);
-- if (sb == NULL)
-- return;
-+ subscriber_list_t *sb = make_subscriber_list(stream);
- if (subscribe_head == NULL) {
- subscribe_head = subscribe_tail = sb;
- } else {
-@@ -51,28 +70,26 @@ void add_subscriber(int fd)
- sb->prev = subscribe_tail;
- subscribe_tail = sb;
- }
-- feed_subscriber(sb);
-+ print_status(sb->stream);
- }
-
--void feed_subscriber(subscriber_list_t *sb)
-+int print_status(FILE *stream)
- {
-- fprintf(sb->stream, "%s", status_prefix);
-+ fprintf(stream, "%s", status_prefix);
- bool urgent = false;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
-- fprintf(sb->stream, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
-+ fprintf(stream, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
- for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
- urgent |= n->client->urgent;
- char c = (urgent ? 'u' : (d->root == NULL ? 'f' : 'o'));
- if (m->desk == d)
- c = toupper(c);
-- fprintf(sb->stream, "%c%s:", c, d->name);
-+ fprintf(stream, "%c%s:", c, d->name);
- }
- }
- if (mon != NULL && mon->desk != NULL)
-- fprintf(sb->stream, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
-- fprintf(sb->stream, "%s", "\n");
-- int ret = fflush(sb->stream);
-- if (ret != 0)
-- remove_subscriber(sb);
-+ fprintf(stream, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
-+ fprintf(stream, "%s", "\n");
-+ return fflush(stream);
- }
-diff --git a/subscribe.h b/subscribe.h
-index e5ce7de..31bb8c4 100644
---- a/subscribe.h
-+++ b/subscribe.h
-@@ -1,9 +1,37 @@
-+/* Copyright (c) 2012-2014, 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
-+ */
-+
- #ifndef BSPWM_SUBSCRIBE_H
- #define BSPWM_SUBSCRIBE_H
-
--subscriber_list_t *make_subscriber_list(int fd);
-+subscriber_list_t *make_subscriber_list(FILE *stream);
- void remove_subscriber(subscriber_list_t *sb);
--void add_subscriber(int fd);
--void feed_subscriber(subscriber_list_t *sb);
-+void add_subscriber(FILE *stream);
-+int print_status(FILE *stream);
-
- #endif
-diff --git a/tree.c b/tree.c
-index b9c7950..0756547 100644
---- a/tree.c
-+++ b/tree.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <float.h>
-@@ -60,24 +64,28 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
-
- if (is_leaf(n)) {
-
-- if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE)
-- || n->client->fullscreen)
-- n->client->border_width = 0;
-+ unsigned int bw;
-+ if ((borderless_monocle && is_tiled(n->client) &&
-+ !n->client->pseudo_tiled &&
-+ d->layout == LAYOUT_MONOCLE) ||
-+ n->client->fullscreen)
-+ bw = 0;
- else
-- n->client->border_width = d->border_width;
-+ bw = n->client->border_width;
-
- xcb_rectangle_t r;
- if (!n->client->fullscreen) {
- if (!n->client->floating) {
-+ int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
- if (n->client->pseudo_tiled) {
- /* pseudo-tiled clients */
- r = n->client->floating_rectangle;
-- center_rectangle(&r, rect);
-+ r.x = rect.x - bw + (rect.width - wg - r.width) / 2;
-+ r.y = rect.y - bw + (rect.height - wg - r.height) / 2;
- } else {
- /* tiled clients */
- r = rect;
-- int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
-- int bleed = wg + 2 * n->client->border_width;
-+ int bleed = wg + 2 * bw;
- r.width = (bleed < r.width ? r.width - bleed : 1);
- r.height = (bleed < r.height ? r.height - bleed : 1);
- }
-@@ -92,7 +100,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
- }
-
- window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
-- window_border_width(n->client->window, n->client->border_width);
-+ window_border_width(n->client->window, bw);
- window_draw_border(n, d->focus == n, m == mon);
-
- } else {
-@@ -107,7 +115,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x
- 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 if (n->split_type == TYPE_HORIZONTAL) {
-+ } 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};
-@@ -140,13 +148,14 @@ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
- } else {
- node_t *c = make_node();
- node_t *p = f->parent;
-- if (p != NULL && f->split_mode == MODE_AUTOMATIC
-- && (p->first_child->vacant || p->second_child->vacant)) {
-+ if (p != NULL && f->split_mode == MODE_AUTOMATIC &&
-+ (p->first_child->vacant || p->second_child->vacant)) {
- f = p;
- p = f->parent;
- }
-- if (((f->client != NULL && f->client->private) || (p != NULL && p->privacy_level > 0))
-- && f->split_mode == MODE_AUTOMATIC) {
-+ if (((f->client != NULL && f->client->private) ||
-+ (p != NULL && p->privacy_level > 0)) &&
-+ f->split_mode == MODE_AUTOMATIC) {
- node_t *closest = NULL;
- node_t *public = NULL;
- closest_public(d, f, &closest, &public);
-@@ -292,10 +301,16 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
- n = d->focus;
- }
-
-- if (n != NULL && d->focus != NULL && n != d->focus && d->focus->client->fullscreen) {
-+ if (n != NULL) {
-+ if (d->focus != NULL && n != d->focus && d->focus->client->fullscreen) {
- set_fullscreen(d->focus, false);
- arrange(m, d);
- }
-+ if (n->client->urgent) {
-+ n->client->urgent = false;
-+ put_status();
-+ }
-+ }
-
- if (mon != m) {
- for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
-@@ -326,8 +341,6 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
-
- PRINTF("focus node %X\n", n->client->window);
-
-- n->client->urgent = false;
--
- history_add(m, d, n);
- set_input_focus(n);
-
-@@ -362,12 +375,13 @@ node_t *make_node(void)
- return n;
- }
-
--client_t *make_client(xcb_window_t win)
-+client_t *make_client(xcb_window_t win, unsigned int border_width)
- {
- client_t *c = malloc(sizeof(client_t));
-- snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
-- c->border_width = BORDER_WIDTH;
- c->window = win;
-+ 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->pseudo_tiled = c->floating = c->fullscreen = false;
- c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false;
- xcb_icccm_get_wm_protocols_reply_t protocols;
-@@ -570,10 +584,10 @@ node_t *find_fence(node_t *n, direction_t dir)
- p = n->parent;
-
- while (p != NULL) {
-- if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y)
-- || (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x)
-- || (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height))
-- || (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
-+ if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
-+ (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
-+ (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
-+ (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
- return p;
- p = p->parent;
- }
-@@ -583,8 +597,8 @@ node_t *find_fence(node_t *n, direction_t dir)
-
- node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
- {
-- if (n == NULL || n->client->fullscreen
-- || (d->layout == LAYOUT_MONOCLE && is_tiled(n->client)))
-+ if (n == NULL || n->client->fullscreen ||
-+ (d->layout == LAYOUT_MONOCLE && is_tiled(n->client)))
- return NULL;
-
- node_t *nearest = NULL;
-@@ -735,9 +749,9 @@ void rotate_tree(node_t *n, int deg)
-
- node_t *tmp;
-
-- if ((deg == 90 && n->split_type == TYPE_HORIZONTAL)
-- || (deg == 270 && n->split_type == TYPE_VERTICAL)
-- || deg == 180) {
-+ 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;
-@@ -779,8 +793,8 @@ void flip_tree(node_t *n, flip_t flp)
-
- node_t *tmp;
-
-- if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL)
-- || (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
-+ 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;
-@@ -791,6 +805,17 @@ void flip_tree(node_t *n, flip_t 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) {
-@@ -902,7 +927,8 @@ void destroy_tree(node_t *n)
-
- 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 || (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
-+ if (n1 == NULL || n2 == NULL ||n1 == n2 ||
-+ (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
- return false;
-
- PRINTF("swap nodes %X %X\n", n1->client->window, n2->client->window);
-diff --git a/tree.h b/tree.h
-index 3a82a2e..3f0ee5d 100644
---- a/tree.h
-+++ b/tree.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_TREE_H
-@@ -32,7 +36,7 @@ void pseudo_focus(monitor_t *m, desktop_t *d, node_t *n);
- void focus_node(monitor_t *m, desktop_t *d, node_t *n);
- void update_current(void);
- node_t *make_node(void);
--client_t *make_client(xcb_window_t win);
-+client_t *make_client(xcb_window_t win, unsigned int border_width);
- bool is_leaf(node_t *n);
- bool is_tiled(client_t *c);
- bool is_floating(client_t *c);
-@@ -60,6 +64,7 @@ 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 remove_node(monitor_t *m, desktop_t *d, node_t *n);
-diff --git a/types.h b/types.h
-index 6495f0a..6c57713 100644
---- a/types.h
-+++ b/types.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_TYPES_H
-@@ -56,11 +60,17 @@ typedef enum {
- CLIENT_CLASS_DIFFER
- } client_class_t;
-
-+typedef enum {
-+ CLIENT_MODE_ALL,
-+ CLIENT_MODE_AUTOMATIC,
-+ CLIENT_MODE_MANUAL
-+} client_mode_t;
-+
- typedef struct {
- client_type_t type;
- client_class_t class;
-+ client_mode_t mode;
- bool urgent;
-- bool manual;
- bool local;
- } client_select_t;
-
-@@ -143,7 +153,8 @@ typedef struct {
-
- typedef struct {
- xcb_window_t window;
-- char class_name[SMALEN];
-+ char class_name[3 * SMALEN / 2];
-+ char instance_name[3 * SMALEN / 2];
- unsigned int border_width;
- bool pseudo_tiled;
- bool floating;
-@@ -155,6 +166,10 @@ typedef struct {
- bool icccm_focus;
- xcb_rectangle_t floating_rectangle;
- xcb_rectangle_t tiled_rectangle;
-+ uint16_t min_width;
-+ uint16_t max_width;
-+ uint16_t min_height;
-+ uint16_t max_height;
- xcb_atom_t wm_state[MAX_STATE];
- int num_states;
- } client_t;
-@@ -250,10 +265,16 @@ struct rule_t {
- };
-
- typedef struct {
-- char class_name[SMALEN];
-- char instance_name[SMALEN];
-- char desktop_desc[MAXLEN];
-+ 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];
-+ uint16_t min_width;
-+ uint16_t max_width;
-+ uint16_t min_height;
-+ uint16_t max_height;
- bool pseudo_tiled;
- bool floating;
- bool fullscreen;
-@@ -261,7 +282,6 @@ typedef struct {
- bool sticky;
- bool private;
- bool center;
-- bool lower;
- bool follow;
- bool manage;
- bool focus;
-diff --git a/window.c b/window.c
-index 4b1ed3f..ce69617 100644
---- a/window.c
-+++ b/window.c
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #include <stdlib.h>
-@@ -32,6 +36,7 @@
- #include "settings.h"
- #include "stack.h"
- #include "tree.h"
-+#include "messages.h"
- #include "window.h"
-
- void schedule_window(xcb_window_t win)
-@@ -65,12 +70,10 @@ 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 (csq->lower)
-- window_lower(win);
--
- if (!csq->manage) {
- disable_floating_atom(win);
- window_show(win);
-@@ -79,12 +82,21 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
-
- PRINTF("manage %X\n", win);
-
-- if (csq->desktop_desc[0] != '\0') {
-+ 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)) {
-+ 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)) {
- m = trg.monitor;
- d = trg.desktop;
-+ f = trg.desktop->focus;
- }
- } else if (csq->monitor_desc[0] != '\0') {
- coordinates_t ref = {m, NULL, NULL};
-@@ -92,16 +104,32 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
- if (monitor_from_desc(csq->monitor_desc, &ref, &trg)) {
- 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)) {
-+ f->split_mode = MODE_MANUAL;
-+ f->split_dir = dir;
-+ }
- }
-
-- client_t *c = make_client(win);
-+ client_t *c = make_client(win, d->border_width);
- update_floating_rectangle(c);
-+ if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0)
-+ csq->center = true;
-+ c->min_width = csq->min_width;
-+ c->max_width = csq->max_width;
-+ c->min_height = csq->min_height;
-+ c->max_height = csq->max_height;
- monitor_t *mm = monitor_from_client(c);
- embrace_client(mm, c);
- translate_client(mm, m, c);
-@@ -109,13 +137,14 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
- 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);
-
- csq->floating = csq->floating || d->floating;
-
- node_t *n = make_node();
- n->client = c;
-
-- insert_node(m, d, n, d->focus);
-+ insert_node(m, d, n, f);
-
- disable_floating_atom(c->window);
- set_pseudo_tiled(n, csq->pseudo_tiled);
-@@ -165,6 +194,8 @@ void unmanage_window(xcb_window_t win)
- if (locate_window(win, &loc)) {
- PRINTF("unmanage %X\n", win);
- remove_node(loc.monitor, loc.desktop, loc.node);
-+ if (frozen_pointer->window == win)
-+ frozen_pointer->action = ACTION_NONE;
- arrange(loc.monitor, loc.desktop);
- } else {
- for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
-@@ -266,8 +297,8 @@ pointer_state_t *make_pointer_state(void)
-
- 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));
-+ 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(client_t *c)
-@@ -519,12 +550,35 @@ void update_floating_rectangle(client_t *c)
-
- if (geo != NULL)
- c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
-- else
-- c->floating_rectangle = (xcb_rectangle_t) {0, 0, 32, 24};
-
- free(geo);
- }
-
-+void restrain_floating_width(client_t *c, int *width)
-+{
-+ if (*width < 1)
-+ *width = 1;
-+ if (c->min_width > 0 && *width < c->min_width)
-+ *width = c->min_width;
-+ else if (c->max_width > 0 && *width > c->max_width)
-+ *width = c->max_width;
-+}
-+
-+void restrain_floating_height(client_t *c, int *height)
-+{
-+ if (*height < 1)
-+ *height = 1;
-+ if (c->min_height > 0 && *height < c->min_height)
-+ *height = c->min_height;
-+ else if (c->max_height > 0 && *height > c->max_height)
-+ *height = c->max_height;
-+}
-+
-+void restrain_floating_size(client_t *c, int *width, int *height)
-+{
-+ restrain_floating_width(c, width);
-+ restrain_floating_height(c, height);
-+}
-
- void query_pointer(xcb_window_t *win, xcb_point_t *pt)
- {
-@@ -596,6 +650,8 @@ void window_center(monitor_t *m, client_t *c)
- 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)
-diff --git a/window.h b/window.h
-index 12bc117..9688ef3 100644
---- a/window.h
-+++ b/window.h
-@@ -1,25 +1,29 @@
--/* * Copyright (c) 2012-2013 Bastien Dejean
-+/* Copyright (c) 2012-2014, 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:
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
- *
-- * * Redistributions of source code must retain the above copyright notice, this
-+ * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-- * * 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.
-+ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
-+ * 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
-+ * 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.
-+ *
-+ * The views and conclusions contained in the software and documentation are those
-+ * of the authors and should not be interpreted as representing official policies,
-+ * either expressed or implied, of the FreeBSD Project.
- */
-
- #ifndef BSPWM_WINDOW_H
-@@ -54,6 +58,9 @@ void enable_floating_atom(xcb_window_t win);
- void disable_floating_atom(xcb_window_t win);
- uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor);
- void update_floating_rectangle(client_t *c);
-+void restrain_floating_width(client_t *c, int *width);
-+void restrain_floating_height(client_t *c, int *height);
-+void restrain_floating_size(client_t *c, int *width, int *height);
- void query_pointer(xcb_window_t *win, xcb_point_t *pt);
- bool window_focus(xcb_window_t win);
- void window_border_width(xcb_window_t win, uint32_t bw);
diff --git a/desktop/bspwm/patches/bspwm-0.8.8_0ff26c34.patch b/desktop/bspwm/patches/bspwm-0.8.8_0ff26c34.patch
new file mode 100644
index 0000000000..f5afedb119
--- /dev/null
+++ b/desktop/bspwm/patches/bspwm-0.8.8_0ff26c34.patch
@@ -0,0 +1,16039 @@
+diff --git a/LICENSE b/LICENSE
+index b6ffa88..1f93c08 100644
+--- a/LICENSE
++++ b/LICENSE
+@@ -1,14 +1,14 @@
+-Copyright (c) 2012-2013, Bastien Dejean
++Copyright (c) 2012-2014, 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:
++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.
++ 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.
++ 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
+@@ -22,5 +22,5 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those
+-of the authors and should not be interpreted as representing official policies,
++of the authors and should not be interpreted as representing official policies,
+ either expressed or implied, of the FreeBSD Project.
+diff --git a/Makefile b/Makefile
+index 2572636..3d601eb 100644
+--- a/Makefile
++++ b/Makefile
+@@ -58,9 +58,6 @@ uninstall:
+ rm -f "$(DESTDIR)$(BASHCPL)"/bspc
+ rm -f "$(DESTDIR)$(ZSHCPL)"/_bspc
+
+-deps:
+- $(CC) -MM *.c > Sourcedeps
+-
+ doc:
+ a2x -v -d manpage -f manpage -a revnumber=$(VERSION) doc/bspwm.1.txt
+
+diff --git a/Sourcedeps b/Sourcedeps
+index 9b36a69..5c93207 100644
+--- a/Sourcedeps
++++ b/Sourcedeps
+@@ -1,32 +1,18 @@
+-bspc.o: bspc.c helpers.h common.h
+-bspwm.o: bspwm.c types.h helpers.h desktop.h monitor.h settings.h \
+- messages.h subscribe.h events.h common.h window.h history.h stack.h \
+- ewmh.h rule.h bspwm.h
+-desktop.o: desktop.c bspwm.h types.h helpers.h ewmh.h history.h monitor.h \
+- query.h tree.h window.h desktop.h
+-events.o: events.c bspwm.h types.h helpers.h ewmh.h monitor.h query.h \
+- settings.h tree.h window.h events.h
+-ewmh.o: ewmh.c bspwm.h types.h helpers.h settings.h tree.h ewmh.h
+-helpers.o: helpers.c bspwm.h types.h helpers.h
+-history.o: history.c bspwm.h types.h helpers.h query.h
+-messages.o: messages.c bspwm.h types.h helpers.h desktop.h ewmh.h \
+- history.h monitor.h pointer.h query.h rule.h restore.h settings.h tree.h \
+- window.h messages.h
+-monitor.o: monitor.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
+- query.h settings.h tree.h window.h monitor.h
+-pointer.o: pointer.c bspwm.h types.h helpers.h query.h settings.h stack.h \
+- tree.h monitor.h window.h pointer.h
+-query.o: query.c bspwm.h types.h helpers.h desktop.h history.h messages.h \
+- monitor.h tree.h query.h
+-restore.o: restore.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
+- monitor.h query.h stack.h tree.h settings.h restore.h
+-rule.o: rule.c bspwm.h types.h helpers.h ewmh.h window.h messages.h \
+- settings.h rule.h
+-settings.o: settings.c bspwm.h types.h helpers.h settings.h
+-stack.o: stack.c bspwm.h types.h helpers.h window.h stack.h
+-subscribe.o: subscribe.c bspwm.h types.h helpers.h tree.h settings.h \
+- subscribe.h
+-tree.o: tree.c bspwm.h types.h helpers.h desktop.h ewmh.h history.h \
+- monitor.h query.h settings.h stack.h window.h tree.h
+-window.o: window.c bspwm.h types.h helpers.h ewmh.h monitor.h query.h \
+- rule.h settings.h stack.h tree.h window.h
++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 stack.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 tree.h types.h window.h
++events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h query.h settings.h tree.h types.h window.h
++ewmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h
++helpers.o: helpers.c bspwm.h helpers.h types.h
++history.o: history.c bspwm.h helpers.h query.h types.h
++messages.o: messages.c bspwm.h common.h desktop.h ewmh.h helpers.h history.h messages.h monitor.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 tree.h types.h window.h
++pointer.o: pointer.c bspwm.h helpers.h monitor.h pointer.h query.h settings.h stack.h tree.h types.h window.h
++query.o: query.c bspwm.h desktop.h helpers.h history.h messages.h monitor.h query.h tree.h types.h
++restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h restore.h settings.h stack.h tree.h types.h
++rule.o: rule.c bspwm.h ewmh.h helpers.h messages.h rule.h settings.h types.h window.h
++settings.o: settings.c bspwm.h helpers.h settings.h types.h
++stack.o: stack.c bspwm.h helpers.h stack.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 tree.h types.h window.h
++window.o: window.c bspwm.h ewmh.h helpers.h messages.h monitor.h query.h rule.h settings.h stack.h tree.h types.h window.h
+diff --git a/bspc.c b/bspc.c
+index 1617afc..3903eaf 100644
+--- a/bspc.c
++++ b/bspc.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -35,51 +39,56 @@
+
+ int main(int argc, char *argv[])
+ {
+- int fd;
+- struct sockaddr_un sock_address;
+- char msg[BUFSIZ], rsp[BUFSIZ];
++ int fd;
++ struct sockaddr_un sock_address;
++ char msg[BUFSIZ], rsp[BUFSIZ];
+
+- if (argc < 2)
+- err("No arguments given.\n");
++ if (argc < 2)
++ err("No arguments given.\n");
+
+- sock_address.sun_family = AF_UNIX;
+- char *sp = getenv(SOCKET_ENV_VAR);
+- if (sp != NULL)
+- snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
+- else
+- snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, getenv("DISPLAY"));
++ sock_address.sun_family = AF_UNIX;
++ char *sp = getenv(SOCKET_ENV_VAR);
++ if (sp != NULL)
++ snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
++ else
++ snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, getenv("DISPLAY"));
+
+- argc--, argv++;
+- int msg_len = 0;
++ 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;
+- }
++ 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 ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+- err("Failed to create the socket.\n");
++ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
++ err("Failed to create the socket.\n");
+
+- if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
+- err("Failed to connect to the socket.\n");
++ if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1)
++ err("Failed to connect to the socket.\n");
+
+- if (send(fd, msg, msg_len, 0) == -1)
+- err("Failed to send the data.\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), 0)) > 0) {
+- if (nb == 1 && rsp[0] == MESSAGE_FAILURE) {
+- ret = EXIT_FAILURE;
+- } else {
+- int end = MIN(nb, (int) sizeof(rsp) - 1);
+- rsp[end--] = '\0';
+- while (end >= 0 && isspace(rsp[end]))
+- rsp[end--] = '\0';
+- printf("%s\n", rsp);
+- fflush(stdout);
+- }
+- }
++ int ret = 0, nb;
++ while ((nb = recv(fd, rsp, sizeof(rsp), 0)) > 0) {
++ if (nb == 1 && rsp[0] < MSG_LENGTH) {
++ ret = rsp[0];
++ if (ret == MSG_UNKNOWN) {
++ warn("Unknown command.\n");
++ } else if (ret == MSG_SYNTAX) {
++ warn("Invalid syntax.\n");
++ }
++ } else {
++ int end = MIN(nb, (int) sizeof(rsp) - 1);
++ rsp[end--] = '\0';
++ while (end >= 0 && isspace(rsp[end]))
++ rsp[end--] = '\0';
++ printf("%s\n", rsp);
++ fflush(stdout);
++ }
++ }
+
+- close(fd);
+- return ret;
++ close(fd);
++ return ret;
+ }
+diff --git a/bspwm.c b/bspwm.c
+index 95049ae..d87f1fa 100644
+--- a/bspwm.c
++++ b/bspwm.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdio.h>
+@@ -52,305 +56,306 @@
+
+ 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;
+- size_t rsp_len = 0;
+- char msg[BUFSIZ] = {0};
+- char rsp[BUFSIZ] = {0};
+- xcb_generic_event_t *event;
+- char opt;
+-
+- while ((opt = getopt(argc, argv, "hvc:")) != (char)-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 (xcb_connection_has_error(dpy))
+- err("Can't open the default display.\n");
+-
+- 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
+- snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, getenv("DISPLAY"));
+-
+- 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");
+-
+- 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);
+- load_settings();
+- 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';
+- if (handle_message(msg, n, rsp)) {
+- rsp_len = strlen(rsp);
+- } else {
+- rsp[0] = MESSAGE_FAILURE;
+- rsp_len = 1;
+- }
+- if (rsp_len == 1 && rsp[0] == MESSAGE_SUBSCRIBE) {
+- add_subscriber(cli_fd);
+- } else {
+- send(cli_fd, rsp, rsp_len, 0);
+- close(cli_fd);
+- }
+- rsp[0] = '\0';
+- }
+- }
+-
+- if (FD_ISSET(dpy_fd, &descriptors)) {
+- while ((event = xcb_poll_for_event(dpy)) != NULL) {
+- handle_event(event);
+- free(event);
+- }
+- }
+- }
+-
+- if (xcb_connection_has_error(dpy)) {
+- warn("The server closed the connection.\n");
+- running = false;
+- }
+- }
+-
+- cleanup();
+- close(sock_fd);
+- unlink(socket_path);
+- xcb_ewmh_connection_wipe(ewmh);
+- xcb_destroy_window(dpy, motion_recorder);
+- free(ewmh);
+- xcb_flush(dpy);
+- xcb_disconnect(dpy);
+- return exit_status;
++ 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;
++ char opt;
++
++ while ((opt = getopt(argc, argv, "hvc:")) != (char)-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 (xcb_connection_has_error(dpy))
++ err("Can't open the default display.\n");
++
++ 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
++ snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, getenv("DISPLAY"));
++
++ 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");
++
++ 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) {
++ int ret = handle_message(msg, n, rsp);
++ if (ret == MSG_SUBSCRIBE) {
++ add_subscriber(rsp);
++ } else {
++ if (ret != MSG_SUCCESS)
++ fprintf(rsp, "%c", ret);
++ fflush(rsp);
++ fclose(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 (xcb_connection_has_error(dpy)) {
++ warn("The server closed the connection.\n");
++ running = false;
++ }
++ }
++
++ cleanup();
++ close(sock_fd);
++ unlink(socket_path);
++ xcb_ewmh_connection_wipe(ewmh);
++ xcb_destroy_window(dpy, motion_recorder);
++ free(ewmh);
++ xcb_flush(dpy);
++ xcb_disconnect(dpy);
++ return exit_status;
+ }
+
+ void init(void)
+ {
+- num_monitors = num_desktops = num_clients = 0;
+- monitor_uid = desktop_uid = 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;
+- last_motion_time = last_motion_x = last_motion_y = 0;
+- visible = auto_raise = sticky_still = record_history = true;
+- randr_base = 0;
+- exit_status = 0;
++ num_monitors = num_desktops = num_clients = 0;
++ monitor_uid = desktop_uid = 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;
++ last_motion_time = last_motion_x = last_motion_y = 0;
++ visible = auto_raise = sticky_still = record_history = true;
++ randr_base = 0;
++ exit_status = 0;
+ }
+
+ void setup(void)
+ {
+- init();
+- ewmh_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;
+- root_depth = screen->root_depth;
+-
+- uint32_t mask = XCB_CW_EVENT_MASK;
+- uint32_t values[] = {XCB_EVENT_MASK_POINTER_MOTION};
+- motion_recorder = xcb_generate_id(dpy);
+- xcb_create_window(dpy, XCB_COPY_FROM_PARENT, motion_recorder, root, 0, 0, screen_width, screen_height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values);
+-
+- xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
+- ewmh->_NET_SUPPORTING_WM_CHECK,
+- ewmh->_NET_DESKTOP_NAMES,
+- ewmh->_NET_NUMBER_OF_DESKTOPS,
+- ewmh->_NET_CURRENT_DESKTOP,
+- ewmh->_NET_CLIENT_LIST,
+- ewmh->_NET_ACTIVE_WINDOW,
+- ewmh->_NET_CLOSE_WINDOW,
+- ewmh->_NET_WM_DESKTOP,
+- ewmh->_NET_WM_STATE,
+- ewmh->_NET_WM_STATE_FULLSCREEN,
+- 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(motion_recorder);
++ init();
++ ewmh_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;
++ root_depth = screen->root_depth;
++
++ uint32_t mask = XCB_CW_EVENT_MASK;
++ uint32_t values[] = {XCB_EVENT_MASK_POINTER_MOTION};
++ motion_recorder = xcb_generate_id(dpy);
++ xcb_create_window(dpy, XCB_COPY_FROM_PARENT, motion_recorder, root, 0, 0, screen_width, screen_height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values);
++
++ xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
++ ewmh->_NET_SUPPORTING_WM_CHECK,
++ ewmh->_NET_DESKTOP_NAMES,
++ ewmh->_NET_NUMBER_OF_DESKTOPS,
++ ewmh->_NET_CURRENT_DESKTOP,
++ ewmh->_NET_CLIENT_LIST,
++ ewmh->_NET_ACTIVE_WINDOW,
++ ewmh->_NET_CLOSE_WINDOW,
++ ewmh->_NET_WM_DESKTOP,
++ ewmh->_NET_WM_STATE,
++ ewmh->_NET_WM_STATE_FULLSCREEN,
++ 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(motion_recorder);
+
+ #define GETATOM(a) \
+- get_atom(#a, &a);
+- GETATOM(WM_DELETE_WINDOW)
+- GETATOM(WM_TAKE_FOCUS)
+- GETATOM(_BSPWM_FLOATING_WINDOW)
++ get_atom(#a, &a);
++ GETATOM(WM_DELETE_WINDOW)
++ GETATOM(WM_TAKE_FOCUS)
++ GETATOM(_BSPWM_FLOATING_WINDOW)
+ #undef GETATOM
+
+- const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
+- if (qep->present && import_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 = add_monitor(rect);
+- add_desktop(m, make_desktop(NULL));
+- }
+- free(xsq);
+- } else {
+- warn("Xinerama is inactive.\n");
+- xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+- monitor_t *m = add_monitor(rect);
+- add_desktop(m, make_desktop(NULL));
+- }
+- }
+-
+- ewmh_update_number_of_desktops();
+- ewmh_update_desktop_names();
+- ewmh_update_current_desktop();
+- frozen_pointer = make_pointer_state();
+- 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);
++ 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 = add_monitor(rect);
++ add_desktop(m, make_desktop(NULL));
++ }
++ free(xsq);
++ } else {
++ warn("Xinerama is inactive.\n");
++ xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
++ monitor_t *m = add_monitor(rect);
++ add_desktop(m, make_desktop(NULL));
++ }
++ }
++
++ ewmh_update_number_of_desktops();
++ ewmh_update_desktop_names();
++ ewmh_update_current_desktop();
++ frozen_pointer = make_pointer_state();
++ 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");
+- }
++ 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)
+ {
+- while (mon_head != NULL)
+- remove_monitor(mon_head);
+- while (rule_head != NULL)
+- remove_rule(rule_head);
+- while (stack_head != NULL)
+- remove_stack(stack_head);
+- while (subscribe_head != NULL)
+- remove_subscriber(subscribe_head);
+- while (pending_rule_head != NULL)
+- remove_pending_rule(pending_rule_head);
+- empty_history();
+- free(frozen_pointer);
++ while (mon_head != NULL)
++ remove_monitor(mon_head);
++ while (rule_head != NULL)
++ remove_rule(rule_head);
++ while (stack_head != NULL)
++ remove_stack(stack_head);
++ while (subscribe_head != NULL)
++ remove_subscriber(subscribe_head);
++ while (pending_rule_head != NULL)
++ remove_pending_rule(pending_rule_head);
++ empty_history();
++ free(frozen_pointer);
+ }
+
+ void put_status(void)
+ {
+- subscriber_list_t *sb = subscribe_head;
+- while (sb != NULL) {
+- subscriber_list_t *next = sb->next;
+- feed_subscriber(sb);
+- sb = next;
+- }
++ subscriber_list_t *sb = subscribe_head;
++ while (sb != NULL) {
++ subscriber_list_t *next = sb->next;
++ if (print_status(sb->stream) != 0)
++ remove_subscriber(sb);
++ sb = next;
++ }
+ }
+
+ 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;
+- }
++ 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
+index 6732933..274d6ea 100644
+--- a/bspwm.h
++++ b/bspwm.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_BSPWM_H
+@@ -27,8 +31,8 @@
+
+ #include "types.h"
+
+-#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY)
+-#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
++#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY)
++#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
+
+ xcb_connection_t *dpy;
+ int default_screen, screen_width, screen_height;
+diff --git a/common.h b/common.h
+index 2b29ba0..bb565dd 100644
+--- a/common.h
++++ b/common.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_COMMON_H
+@@ -27,6 +31,14 @@
+
+ #define SOCKET_PATH_TPL "/tmp/bspwm%s-socket"
+ #define SOCKET_ENV_VAR "BSPWM_SOCKET"
+-#define MESSAGE_FAILURE '\x18'
++
++enum {
++ MSG_SUCCESS,
++ MSG_FAILURE,
++ MSG_SYNTAX,
++ MSG_UNKNOWN,
++ MSG_SUBSCRIBE,
++ MSG_LENGTH
++};
+
+ #endif
+diff --git a/contrib/bash_completion b/contrib/bash_completion
+index bd170ca..2b87851 100644
+--- a/contrib/bash_completion
++++ b/contrib/bash_completion
+@@ -1,27 +1,27 @@
+ _bspc() {
+- local commands='window desktop monitor query pointer rule restore control config quit'
++ local commands='window desktop monitor query pointer rule restore control config quit'
+
+- local settings='external_rules_command status_prefix focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color focused_sticky_border_color normal_sticky_border_color focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor apply_floating_atom auto_alternate auto_cancel history_aware_focus ignore_ewmh_focus remove_disabled_monitor'
++ local settings='external_rules_command status_prefix focused_border_color active_border_color normal_border_color presel_border_color focused_locked_border_color active_locked_border_color normal_locked_border_color focused_sticky_border_color normal_sticky_border_color focused_private_border_color active_private_border_color normal_private_border_color urgent_border_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio borderless_monocle gapless_monocle focus_follows_pointer pointer_follows_monitor apply_floating_atom auto_alternate auto_cancel history_aware_focus ignore_ewmh_focus remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'
+
+- COMPREPLY=()
++ COMPREPLY=()
+
+- if [[ $COMP_CWORD -ge 1 ]] ; then
+- local current_word="${COMP_WORDS[COMP_CWORD]}"
+- if [[ $COMP_CWORD -eq 1 ]] ; then
+- COMPREPLY=( $(compgen -W "$commands" -- "$current_word") )
+- return 0
+- else
+- local second_word=${COMP_WORDS[1]}
+- case $second_word in
+- config)
+- if [[ $COMP_CWORD -eq 2 ]] ; then
+- COMPREPLY=( $(compgen -W "$settings" -- "$current_word") )
+- return 0
+- fi
+- ;;
+- esac
+- fi
+- fi
++ if [[ $COMP_CWORD -ge 1 ]] ; then
++ local current_word="${COMP_WORDS[COMP_CWORD]}"
++ if [[ $COMP_CWORD -eq 1 ]] ; then
++ COMPREPLY=( $(compgen -W "$commands" -- "$current_word") )
++ return 0
++ else
++ local second_word=${COMP_WORDS[1]}
++ case $second_word in
++ config)
++ if [[ $COMP_CWORD -eq 2 ]] ; then
++ COMPREPLY=( $(compgen -W "$settings" -- "$current_word") )
++ return 0
++ fi
++ ;;
++ esac
++ fi
++ fi
+ }
+
+ complete -F _bspc bspc
+diff --git a/contrib/lightdm/bspwm-session b/contrib/lightdm/bspwm-session
+index f3a31e8..7167d85 100755
+--- a/contrib/lightdm/bspwm-session
++++ b/contrib/lightdm/bspwm-session
+@@ -6,9 +6,9 @@
+ # It is based on similar scripts included with Openbox.
+
+ if [ -n "$1" ]; then
+- echo "Usage: bspwm-session"
+- echo
+- exit 1
++ echo "Usage: bspwm-session"
++ echo
++ exit 1
+ fi
+
+ # Multi-user support:
+@@ -16,21 +16,21 @@ state_prefix=${XDG_CACHE_HOME:-"$HOME/.cache"}
+ mkdir -p "${state_prefix}"
+
+ if [ ! -d "${state_prefix}" ]; then
+- echo "bspwm-session: cache directory ‘${state_prefix}‘ is missing."
+- echo
+- exit 1
++ echo "bspwm-session: cache directory ‘${state_prefix}‘ is missing."
++ echo
++ exit 1
+ elif [ ! -w "${state_prefix}" ]; then
+- echo "bspwm-session: cache directory ‘${state_prefix}‘ is not writable."
+- echo
+- exit 1
++ echo "bspwm-session: cache directory ‘${state_prefix}‘ is not writable."
++ echo
++ exit 1
+ fi
+
+ state_path=$(mktemp -d "${state_prefix}/bspwm-session.XXXXXX")
+
+ if [ $? -ne 0 ]; then
+- echo "bspwm-session: failed to create state directory ‘${state_path}‘."
+- echo
+- exit 1
++ echo "bspwm-session: failed to create state directory ‘${state_path}‘."
++ echo
++ exit 1
+ fi
+
+ export BSPWM_SOCKET=${state_path}/bspwm-socket
+@@ -38,24 +38,24 @@ export BSPWM_SOCKET=${state_path}/bspwm-socket
+ # Trap: make sure everything started in ~/.config/bspwm/autostart is
+ # signalled when this script exits or dies. Also clean up $state_path.
+ function on_exit {
+- for child in $(jobs -p); do
+- jobs -p | grep -q $child && kill $child
+- done
+- # Extra paranoia
+- [[ -d "${state_path}" && -w "${state_path}" ]] && rm -rf -- "${state_path}"
++ for child in $(jobs -p); do
++ jobs -p | grep -q $child && kill $child
++ done
++ # Extra paranoia
++ [[ -d "${state_path}" && -w "${state_path}" ]] && rm -rf -- "${state_path}"
+ }
+
+ trap on_exit EXIT SIGHUP SIGINT SIGTERM
+
+ # Environment and autostart:
+ source_these=(
+- "/etc/profile",
+- "${HOME}/.profile",
+- "${XDG_CONFIG_HOME:-"$HOME/.config"}/bspwm/autostart"
++ "/etc/profile"
++ "${HOME}/.profile"
++ "${XDG_CONFIG_HOME:-"$HOME/.config"}/bspwm/autostart"
+ )
+
+ for file in "${source_these[@]}"; do
+- [ -r "${file}" ] && . "${file}"
++ [ -r "${file}" ] && . "${file}"
+ done
+
+ # Launch sxhkd:
+diff --git a/contrib/zsh_completion b/contrib/zsh_completion
+index 9c98b91..53881b3 100644
+--- a/contrib/zsh_completion
++++ b/contrib/zsh_completion
+@@ -1,23 +1,23 @@
+ #compdef bspc
+
+ _bspc() {
+- local -a commands settings
+- commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'rule' 'restore' 'control' 'config' 'quit')
+- settings=('external_rules_command' 'status_prefix' 'focused_border_color' 'active_border_color' 'normal_border_color' 'presel_border_color' 'focused_locked_border_color' 'active_locked_border_color' 'normal_locked_border_color' 'focused_sticky_border_color' 'normal_sticky_border_color' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'borderless_monocle' 'gapless_monocle' 'focus_follows_pointer' 'pointer_follows_monitor' 'apply_floating_atom' 'auto_alternate' 'auto_cancel' 'history_aware_focus' 'ignore_ewmh_focus' 'remove_disabled_monitor')
+- if (( CURRENT == 2 )) ; then
+- _values 'command' "$commands[@]"
+- elif (( CURRENT == 3 )) ; then
+- case $words[2] in
+- config)
+- _values 'setting' "$settings[@]"
+- ;;
+- *)
+- return 1
+- ;;
+- esac
+- else
+- return 1
+- fi
++ local -a commands settings
++ commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'rule' 'restore' 'control' 'config' 'quit')
++ settings=('external_rules_command' 'status_prefix' 'focused_border_color' 'active_border_color' 'normal_border_color' 'presel_border_color' 'focused_locked_border_color' 'active_locked_border_color' 'normal_locked_border_color' 'focused_sticky_border_color' 'normal_sticky_border_color' 'focused_private_border_color' 'active_private_border_color' 'normal_private_border_color' 'urgent_border_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'borderless_monocle' 'gapless_monocle' 'focus_follows_pointer' 'pointer_follows_monitor' 'apply_floating_atom' 'auto_alternate' 'auto_cancel' 'history_aware_focus' 'ignore_ewmh_focus' 'remove_disabled_monitors' 'remove_unplugged_monitors' 'merge_overlapping_monitors')
++ if (( CURRENT == 2 )) ; then
++ _values 'command' "$commands[@]"
++ elif (( CURRENT == 3 )) ; then
++ case $words[2] in
++ config)
++ _values 'setting' "$settings[@]"
++ ;;
++ *)
++ return 1
++ ;;
++ esac
++ else
++ return 1
++ fi
+ }
+
+ _bspc "$@"
+diff --git a/desktop.c b/desktop.c
+index 5b6a4fd..1bf8c01 100644
+--- a/desktop.c
++++ b/desktop.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -31,271 +35,277 @@
+ #include "tree.h"
+ #include "window.h"
+ #include "desktop.h"
++#include "settings.h"
+
+ void focus_desktop(monitor_t *m, desktop_t *d)
+ {
+- focus_monitor(m);
++ focus_monitor(m);
+
+- if (d == mon->desk)
+- return;
++ if (d == mon->desk)
++ return;
+
+- PRINTF("focus desktop %s\n", d->name);
++ PRINTF("focus desktop %s\n", d->name);
+
+- show_desktop(d);
+- hide_desktop(mon->desk);
++ show_desktop(d);
++ hide_desktop(mon->desk);
+
+- mon->desk = d;
++ mon->desk = d;
+
+- ewmh_update_current_desktop();
+- put_status();
++ ewmh_update_current_desktop();
++ put_status();
+ }
+
+ desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel)
+ {
+- desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
+- if (f == NULL)
+- f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
+-
+- while (f != d) {
+- coordinates_t loc = {m, f, NULL};
+- if (desktop_matches(&loc, &loc, sel))
+- return f;
+- f = (dir == CYCLE_PREV ? f->prev : f->next);
+- if (f == NULL)
+- f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
+- }
+-
+- return NULL;
++ desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
++ if (f == NULL)
++ f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
++
++ while (f != d) {
++ coordinates_t loc = {m, f, NULL};
++ if (desktop_matches(&loc, &loc, sel))
++ return f;
++ f = (dir == CYCLE_PREV ? f->prev : f->next);
++ if (f == NULL)
++ f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
++ }
++
++ return NULL;
+ }
+
+ void change_layout(monitor_t *m, desktop_t *d, layout_t l)
+ {
+- d->layout = l;
+- arrange(m, d);
+- if (d == mon->desk)
+- put_status();
++ d->layout = l;
++ arrange(m, d);
++ if (d == mon->desk)
++ put_status();
+ }
+
+ void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
+ {
+- if (ms == md)
+- return;
++ if (ms == md)
++ return;
+
+- desktop_t *dd = ms->desk;
+- unlink_desktop(ms, d);
+- insert_desktop(md, d);
++ desktop_t *dd = ms->desk;
++ unlink_desktop(ms, d);
++ insert_desktop(md, d);
+
+- if (d == dd) {
+- if (ms->desk != NULL)
+- show_desktop(ms->desk);
+- if (md->desk != d)
+- hide_desktop(d);
+- }
++ if (d == dd) {
++ if (ms->desk != NULL)
++ show_desktop(ms->desk);
++ if (md->desk != d)
++ hide_desktop(d);
++ }
+
+- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+- translate_client(ms, md, n->client);
++ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
++ translate_client(ms, md, n->client);
+
+- arrange(md, d);
++ arrange(md, d);
+
+- if (d != dd && md->desk == d)
+- show_desktop(d);
++ if (d != dd && md->desk == d)
++ show_desktop(d);
+
+- history_transfer_desktop(md, d);
++ history_transfer_desktop(md, d);
+
+- ewmh_update_wm_desktops();
+- ewmh_update_desktop_names();
+- ewmh_update_current_desktop();
+- put_status();
++ ewmh_update_wm_desktops();
++ ewmh_update_desktop_names();
++ ewmh_update_current_desktop();
++ put_status();
+ }
+
+ desktop_t *make_desktop(const char *name)
+ {
+- desktop_t *d = malloc(sizeof(desktop_t));
+- if (name == NULL)
+- snprintf(d->name, sizeof(d->name), "%s%d", DEFAULT_DESK_NAME, ++desktop_uid);
+- else
+- snprintf(d->name, sizeof(d->name), "%s", name);
+- d->layout = LAYOUT_TILED;
+- d->prev = d->next = NULL;
+- d->root = d->focus = NULL;
+- d->top_padding = d->right_padding = d->bottom_padding = d->left_padding = 0;
+- d->window_gap = WINDOW_GAP;
+- d->border_width = BORDER_WIDTH;
+- d->floating = false;
+- return d;
++ desktop_t *d = malloc(sizeof(desktop_t));
++ if (name == NULL)
++ snprintf(d->name, sizeof(d->name), "%s%d", DEFAULT_DESK_NAME, ++desktop_uid);
++ else
++ snprintf(d->name, sizeof(d->name), "%s", name);
++ d->prev = d->next = NULL;
++ d->root = d->focus = NULL;
++ initialize_desktop(d);
++ return d;
++}
++
++void initialize_desktop(desktop_t *d)
++{
++ d->layout = LAYOUT_TILED;
++ d->top_padding = d->right_padding = d->bottom_padding = d->left_padding = 0;
++ d->window_gap = window_gap;
++ d->border_width = border_width;
++ d->floating = false;
+ }
+
+ 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;
+- }
++ 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)
+ {
+- PRINTF("add desktop %s\n", d->name);
++ PRINTF("add desktop %s\n", d->name);
+
+- insert_desktop(m, d);
+- num_desktops++;
+- ewmh_update_number_of_desktops();
+- ewmh_update_desktop_names();
+- put_status();
++ insert_desktop(m, d);
++ num_desktops++;
++ ewmh_update_number_of_desktops();
++ ewmh_update_desktop_names();
++ put_status();
+ }
+
+ void empty_desktop(desktop_t *d)
+ {
+- destroy_tree(d->root);
+- d->root = d->focus = NULL;
++ destroy_tree(d->root);
++ d->root = d->focus = NULL;
+ }
+
+ void unlink_desktop(monitor_t *m, desktop_t *d)
+ {
+- desktop_t *prev = d->prev;
+- desktop_t *next = d->next;
+- desktop_t *last_desk = history_get_desktop(m, d);
+- 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 = (last_desk == NULL ? (prev == NULL ? next : prev) : last_desk);
+- d->prev = d->next = NULL;
++ desktop_t *prev = d->prev;
++ desktop_t *next = d->next;
++ desktop_t *last_desk = history_get_desktop(m, d);
++ 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 = (last_desk == NULL ? (prev == NULL ? next : prev) : last_desk);
++ d->prev = d->next = NULL;
+ }
+
+ void remove_desktop(monitor_t *m, desktop_t *d)
+ {
+- PRINTF("remove desktop %s\n", d->name);
+-
+- unlink_desktop(m, d);
+- history_remove(d, NULL);
+- empty_desktop(d);
+- free(d);
+- num_desktops--;
+- ewmh_update_number_of_desktops();
+- ewmh_update_desktop_names();
+- put_status();
++ PRINTF("remove desktop %s\n", d->name);
++
++ unlink_desktop(m, d);
++ history_remove(d, NULL);
++ empty_desktop(d);
++ free(d);
++ num_desktops--;
++ ewmh_update_number_of_desktops();
++ ewmh_update_desktop_names();
++ put_status();
+ }
+
+ void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
+ {
+- if (ds == NULL || dd == NULL || ds == dd)
+- return;
+- node_t *n = first_extrema(ds->root);
+- while (n != NULL) {
+- node_t *next = next_leaf(n, ds->root);
+- transfer_node(ms, ds, n, md, dd, dd->focus);
+- n = next;
+- }
++ if (ds == NULL || dd == NULL || ds == dd)
++ return;
++ node_t *n = first_extrema(ds->root);
++ while (n != NULL) {
++ node_t *next = next_leaf(n, ds->root);
++ transfer_node(ms, ds, n, md, dd, dd->focus);
++ n = next;
++ }
+ }
+
+ void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
+ {
+- if (d1 == NULL || d2 == NULL || d1 == d2)
+- return;
+-
+- PRINTF("swap desktops %s %s\n", d1->name, d2->name);
+-
+- 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_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) {
+- for (node_t *n = first_extrema(d1->root); n != NULL; n = next_leaf(n, d1->root))
+- translate_client(m1, m2, n->client);
+- for (node_t *n = first_extrema(d2->root); n != NULL; n = next_leaf(n, d2->root))
+- translate_client(m2, m1, n->client);
+- 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);
+- }
+- }
+-
+- update_input_focus();
+- ewmh_update_wm_desktops();
+- ewmh_update_desktop_names();
+- ewmh_update_current_desktop();
+- put_status();
++ if (d1 == NULL || d2 == NULL || d1 == d2)
++ return;
++
++ PRINTF("swap desktops %s %s\n", d1->name, d2->name);
++
++ 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_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) {
++ for (node_t *n = first_extrema(d1->root); n != NULL; n = next_leaf(n, d1->root))
++ translate_client(m1, m2, n->client);
++ for (node_t *n = first_extrema(d2->root); n != NULL; n = next_leaf(n, d2->root))
++ translate_client(m2, m1, n->client);
++ 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);
++ }
++ }
++
++ update_input_focus();
++ ewmh_update_wm_desktops();
++ ewmh_update_desktop_names();
++ ewmh_update_current_desktop();
++ put_status();
+ }
+
+ void show_desktop(desktop_t *d)
+ {
+- if (!visible)
+- return;
+- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+- window_show(n->client->window);
++ if (!visible)
++ return;
++ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
++ window_show(n->client->window);
+ }
+
+ void hide_desktop(desktop_t *d)
+ {
+- if (!visible)
+- return;
+- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+- window_hide(n->client->window);
++ if (!visible)
++ return;
++ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
++ window_hide(n->client->window);
+ }
+
+ 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->urgent)
+- return true;
+- return false;
++ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
++ if (n->client->urgent)
++ return true;
++ return false;
+ }
+diff --git a/desktop.h b/desktop.h
+index 5931cd2..7b7b6fa 100644
+--- a/desktop.h
++++ b/desktop.h
+@@ -1,39 +1,42 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_DESKTOP_H
+ #define BSPWM_DESKTOP_H
+
+ #define DEFAULT_DESK_NAME "Desktop"
+-#define WINDOW_GAP 6
+-#define BORDER_WIDTH 1
+
+ void focus_desktop(monitor_t *m, desktop_t *d);
+ desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel);
+ void change_layout(monitor_t *m, desktop_t *d, layout_t l);
+ void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
+ desktop_t *make_desktop(const char *name);
++void initialize_desktop(desktop_t *d);
+ void insert_desktop(monitor_t *m, desktop_t *d);
+ void add_desktop(monitor_t *m, desktop_t *d);
+ void empty_desktop(desktop_t *d);
+diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
+index 8151d22..8f5534c 100644
+--- a/doc/CONTRIBUTING.md
++++ b/doc/CONTRIBUTING.md
+@@ -4,14 +4,10 @@ You must be comfortable with [C][1], [XCB][2] and [Git][3].
+
+ ## Coding Style
+
+-I follow the [Linux Coding Style][4] with the following exceptions:
+-- One *Tab* equals 4 spaces.
+-- Always use `typedef ...` for structures.
++I follow the [Linux Coding Style][4].
+
+ ## Browsing the Code
+
+-The first files you might want to look at are `types.h`, `bspwm.c` and `events.c`.
+-
+ If you use `vim`:
+ - Hitting *K* will lead you to the manual page of the function under the cursor (works with most `xcb_*` functions), sometimes you'll have to explicitly specify the section of the manual you're interested in with *3K* (e.g.: `open`).
+ - Install `ctags` and run `ctags *.{c,h}` in the directory holding the source. Then, hitting *Ctrl-]* will lead you to the definition of the function/variable/structure under the cursor (to go back: *Ctrl-T*).
+@@ -24,11 +20,11 @@ To produce debug executables, issue:
+ make debug
+ ```
+
+-If you use `systemd`, X might be started on the same virtual terminal as `bspwm` and you won't see its output, hence use something like `startx -- vt08` to start X (you can switch to the virtual terminal number *n* with *Ctrl-Alt-Fn*).
++If you use `systemd`, X might be started on the same virtual terminal as `bspwm` and you won't see its output, hence use something like `startx -- vt08` to start X (you can switch to the virtual terminal number *<n>* with *Ctrl-Alt-F<n>*).
+
+ The debug messages are generated by the `PRINTF` and `PUTS` macros: feel free to use them.
+
+-If you want to use [`gdb`][5], switch to a free virtual terminal, e.g. *Ctrl-Alt-F2* and issue:
++If you want to use [`gdb`][5], switch to a free virtual terminal and issue:
+
+ ```
+ gdb bspwm $(pgrep -x bspwm)
+diff --git a/doc/INSTALL.md b/doc/INSTALL.md
+index 1ea2bf8..e21c015 100644
+--- a/doc/INSTALL.md
++++ b/doc/INSTALL.md
+@@ -6,17 +6,17 @@
+
+ # Installation
+
+- make && make install
++ make && make install
+
+ # Removal
+
+- make uninstall
++ make uninstall
+
+ # Packages
+
+ - Arch Linux
+- - [bspwm-git](https://aur.archlinux.org/packages/bspwm-git)
+- - [bspwm](https://aur.archlinux.org/packages/bspwm)
++ - [bspwm-git](https://aur.archlinux.org/packages/bspwm-git)
++ - [bspwm](https://aur.archlinux.org/packages/bspwm)
+
+ - Gentoo Linux
+- - [bspwm-git](https://github.com/milomouse/ebuilds)
++ - [bspwm-git](https://github.com/milomouse/ebuilds)
+diff --git a/doc/MISC.md b/doc/MISC.md
+index 9f3e76c..8e067e2 100644
+--- a/doc/MISC.md
++++ b/doc/MISC.md
+@@ -8,7 +8,7 @@ If a node has two children it is an internal node, otherwise a leaf.
+
+ Fundamental theorem:
+ Let I be the number of internal nodes and L the number of leaves, then:
+- L = I + 1
++ L = I + 1
+
+ (It can be proved by induction on the number of internal nodes.)
+
+diff --git a/doc/TODO.md b/doc/TODO.md
+index 4757461..3785005 100644
+--- a/doc/TODO.md
++++ b/doc/TODO.md
+@@ -1,5 +1,5 @@
+-- Desktops as nodes?
++- Set more attributes in `make_client` (instead of doing it in `apply_rules`) and don't pass `XCB_NONE` as argument.
++- Internal nodes selectors/actions: labels?
+ - Invisible state.
+ - Restore built-in pointer grabbing?
+-- `FILE *` instead of `char *` for writing the server response?
+ - Use BSD `sys/{queue/tree}.h` for {list,tree} structures?
+diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf
+deleted file mode 100644
+index 68d4d6d..0000000
+--- a/doc/asciidoc.conf
++++ /dev/null
+@@ -1,39 +0,0 @@
+-#
+-# Borrowed from pacman
+-#
+-
+-[macros]
+-(?su)[\\]?(?P<name>linkman):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+-
+-[attributes]
+-asterisk=&#42;
+-plus=&#43;
+-caret=&#94;
+-startsb=&#91;
+-endsb=&#93;
+-backslash=&#92;
+-tilde=&#126;
+-apostrophe=&#39;
+-backtick=&#96;
+-litdd=&#45;&#45;
+-
+-ifdef::backend-docbook[]
+-[linkman-inlinemacro]
+-{0%{target}}
+-{0#<citerefentry>}
+-{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
+-{0#</citerefentry>}
+-endif::backend-docbook[]
+-
+-ifdef::backend-docbook[]
+-ifndef::docbook-xsl-172[]
+-# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
+-# v1.72 breaks with this because it replaces dots not in roff requests.
+-[listingblock]
+-<example><title>{title}</title>
+-<literallayout>
+-|
+-</literallayout>
+-{title#}</example>
+-endif::docbook-xsl-172[]
+-endif::backend-docbook[]
+diff --git a/doc/bspwm.1 b/doc/bspwm.1
+index 0248bd5..1c78b7c 100644
+--- a/doc/bspwm.1
++++ b/doc/bspwm.1
+@@ -2,12 +2,12 @@
+ .\" Title: bspwm
+ .\" Author: [see the "Author" section]
+ .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+-.\" Date: 01/03/2014
++.\" Date: 05/09/2014
+ .\" Manual: Bspwm Manual
+ .\" Source: Bspwm 0.8.8
+ .\" Language: English
+ .\"
+-.TH "BSPWM" "1" "01/03/2014" "Bspwm 0\&.8\&.8" "Bspwm Manual"
++.TH "BSPWM" "1" "05/09/2014" "Bspwm 0\&.8\&.8" "Bspwm Manual"
+ .\" -----------------------------------------------------------------
+ .\" * Define some portability stuff
+ .\" -----------------------------------------------------------------
+@@ -167,7 +167,7 @@ Select a window\&.
+ .\}
+ .nf
+ WINDOW_SEL := <window_id>
+- | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.floating|\&.tiled][\&.like|\&.unlike][\&.manual][\&.urgent][\&.local]
++ | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[\&.floating|\&.tiled][\&.like|\&.unlike][\&.manual|\&.automatic][\&.urgent][\&.local]
+ .fi
+ .if n \{\
+ .RE
+@@ -247,8 +247,12 @@ Only consider windows that have a different class than the current window\&.
+ .PP
+ manual
+ .RS 4
+-Only consider windows in manual splitting mode (see
+-\fB\-\-presel\fR)\&.
++Only consider windows in manual splitting mode\&.
++.RE
++.PP
++automatic
++.RS 4
++Only consider windows in automatic splitting mode\&.
+ .RE
+ .PP
+ local
+@@ -270,8 +274,8 @@ Select a desktop\&.
+ .\}
+ .nf
+ DESKTOP_SEL := <desktop_name>
+- | ^<n>
+- | (CYCLE_DIR|last|focused[:MONITOR_SEL]|older|newer)[\&.occupied|\&.free][\&.urgent][\&.local]
++ | [MONITOR_SEL:]^<n>
++ | (CYCLE_DIR|last|[MONITOR_SEL:]focused|older|newer)[\&.occupied|\&.free][\&.urgent][\&.local]
+ .fi
+ .if n \{\
+ .RE
+@@ -620,6 +624,11 @@ Flip the tree of the selected desktop\&.
+ Rotate the tree of the selected desktop\&.
+ .RE
+ .PP
++\fB\-E\fR, \fB\-\-equalize\fR
++.RS 4
++Reset the split ratios of the tree of the selected desktop\&.
++.RE
++.PP
+ \fB\-B\fR, \fB\-\-balance\fR
+ .RS 4
+ Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area\&.
+@@ -678,7 +687,7 @@ Reorder the desktops of the selected monitor to match the given order\&.
+ .PP
+ \fB\-d\fR, \fB\-\-reset\-desktops\fR <name>\&...
+ .RS 4
+-Rename, add or remove desktops depending on whether the number of given names is equal, superior or inferior to the number of existing desktops\&.
++Rename, add or remove desktops depending on whether the number of given names is equal, superior or inferior to the number of existing desktops\&. Incidentally reset the settings of the existing desktops\&.
+ .RE
+ .PP
+ \fB\-n\fR, \fB\-\-rename\fR <new_name>
+@@ -821,7 +830,12 @@ Enable or disable the recording of window focus history\&.
+ .PP
+ \fB\-\-subscribe\fR
+ .RS 4
+-Continuously print status informations on standard output\&.
++Continuously print status informations\&.
++.RE
++.PP
++\fB\-\-get\-status\fR
++.RS 4
++Print the current status informations\&.
+ .RE
+ .RE
+ .SS "Pointer"
+@@ -881,7 +895,7 @@ rule \fIOPTIONS\fR
+ \fBOptions\fR
+ .RS 4
+ .PP
+-\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name>|* [\fB\-o\fR|\fB\-\-one\-shot\fR] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)]
++\fB\-a\fR, \fB\-\-add\fR <class_name>|<instance_name>|* [\fB\-o\fR|\fB\-\-one\-shot\fR] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|follow|manage|focus)=(true|false)] [split_dir=DIR]
+ .RS 4
+ Create a new rule\&.
+ .RE
+@@ -906,7 +920,7 @@ List the rules\&.
+ \fBGeneral Syntax\fR
+ .RS 4
+ .PP
+-config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR] <key> [<value>]
++config [\-m \fIMONITOR_SEL\fR|\-d \fIDESKTOP_SEL\fR|\-w \fIWINDOW_SEL\fR] <key> [<value>]
+ .RS 4
+ Get or set the value of <key>\&.
+ .RE
+@@ -926,6 +940,24 @@ quit [<status>]
+ Quit with an optional exit status\&.
+ .RE
+ .RE
++.SH "EXIT CODES"
++.sp
++If the server can\(cqt handle a message, \fBbspc\fR will return with one of the following exit codes:
++.PP
++1
++.RS 4
++Failure\&.
++.RE
++.PP
++2
++.RS 4
++Syntax error\&.
++.RE
++.PP
++3
++.RS 4
++Unknown command\&.
++.RE
+ .SH "SETTINGS"
+ .sp
+ Colors are either X color names or \fI#RRGGBB\fR, booleans are \fItrue\fR or \fIfalse\fR\&.
+@@ -1076,29 +1108,40 @@ atom of each window according to its floating state\&.
+ .PP
+ \fIignore_ewmh_focus\fR
+ .RS 4
+-Ignore EWMH requests to focus a window\&.
++Ignore EWMH focus requests coming from applications\&.
+ .RE
+ .PP
+-\fIremove_disabled_monitor\fR
++\fIremove_disabled_monitors\fR
+ .RS 4
+ Consider disabled monitors as disconnected\&.
+ .RE
++.PP
++\fIremove_unplugged_monitors\fR
++.RS 4
++Remove unplugged monitors\&.
++.RE
++.PP
++\fImerge_overlapping_monitors\fR
++.RS 4
++Merge overlapping monitors (the bigger remains)\&.
++.RE
+ .SS "Monitor and Desktop Settings"
+ .PP
+ \fItop_padding\fR, \fIright_padding\fR, \fIbottom_padding\fR, \fIleft_padding\fR
+ .RS 4
+ Padding space added at the sides of the monitor or desktop\&.
+ .RE
+-.SS "Desktop Settings"
++.SS "Default, Desktop Default and Window Settings"
+ .PP
+-\fIwindow_gap\fR
++\fIborder_width\fR
+ .RS 4
+-Size of the gap that separates windows\&.
++Window border width\&.
+ .RE
++.SS "Default and Desktop Settings"
+ .PP
+-\fIborder_width\fR
++\fIwindow_gap\fR
+ .RS 4
+-Window border width\&.
++Size of the gap that separates windows\&.
+ .RE
+ .SH "STATUS FORMAT"
+ .sp
+diff --git a/doc/bspwm.1.txt b/doc/bspwm.1.txt
+index 278d089..f550ad6 100644
+--- a/doc/bspwm.1.txt
++++ b/doc/bspwm.1.txt
+@@ -29,13 +29,13 @@ Options
+ -------
+
+ *-h*::
+- Print the synopsis and exit.
++ Print the synopsis and exit.
+
+ *-v*::
+- Print the version and exit.
++ Print the version and exit.
+
+ *-c* 'CONFIG_PATH'::
+- Use the given configuration file.
++ Use the given configuration file.
+
+ Configuration
+ -------------
+@@ -122,7 +122,7 @@ can either describe the target relatively or name it globally.
+ Descriptive (relative) selectors consist of a primary selector and any number
+ of non-conflicting modifiers as follows:
+
+- PRIMARY_SELECTOR[.MODIFIER]*
++ PRIMARY_SELECTOR[.MODIFIER]*
+
+ For obvious reasons, neither desktops nor monitors names may be valid
+ descriptive selectors.
+@@ -134,57 +134,59 @@ Select a window.
+
+ ----
+ WINDOW_SEL := <window_id>
+- | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[.floating|.tiled][.like|.unlike][.manual][.urgent][.local]
++ | (DIR|CYCLE_DIR|biggest|last|focused|older|newer)[.floating|.tiled][.like|.unlike][.manual|.automatic][.urgent][.local]
+ ----
+
+ Primary Selectors
+ ^^^^^^^^^^^^^^^^^
+
+ 'DIR'::
+- Selects the window in the given (spacial) direction relative to the active
+- window.
++ Selects the window in the given (spacial) direction relative to the active window.
+
+ 'CYCLE_DIR'::
+- Selects the window in the given (cyclic) direction.
++ Selects the window in the given (cyclic) direction.
+
+ biggest::
+- Selects the biggest window on the current desktop.
++ Selects the biggest window on the current desktop.
+
+ last::
+- Selects the previously focused window.
++ Selects the previously focused window.
+
+ focused::
+- Selects the currently focused window.
++ Selects the currently focused window.
+
+ older::
+- Selects the window older than the focused window in the history.
++ Selects the window older than the focused window in the history.
+
+ newer::
+- Selects the window newer than the focused window in the history.
++ Selects the window newer than the focused window in the history.
+
+ Modifiers
+ ^^^^^^^^^
+
+ floating::
+- Only consider floating windows.
++ Only consider floating windows.
+
+ tiled::
+- Only consider tiled windows.
++ Only consider tiled windows.
+
+ like::
+- Only consider windows that have the same class as the current window.
+-
++ Only consider windows that have the same class as the current window.
++
+ unlike::
+- Only consider windows that have a different class than the current window.
++ Only consider windows that have a different class than the current window.
+
+ manual::
+- Only consider windows in manual splitting mode (see *--presel*).
++ Only consider windows in manual splitting mode.
++
++automatic::
++ Only consider windows in automatic splitting mode.
+
+ local::
+- Only consider windows of the current desktop.
++ Only consider windows of the current desktop.
+
+ urgent::
+- Only consider urgent windows.
++ Only consider urgent windows.
+
+ Desktop
+ ~~~~~~~
+@@ -193,48 +195,48 @@ Select a desktop.
+
+ ----
+ DESKTOP_SEL := <desktop_name>
+- | ^<n>
+- | (CYCLE_DIR|last|focused[:MONITOR_SEL]|older|newer)[.occupied|.free][.urgent][.local]
++ | [MONITOR_SEL:]^<n>
++ | (CYCLE_DIR|last|[MONITOR_SEL:]focused|older|newer)[.occupied|.free][.urgent][.local]
+ ----
+
+ Primary Selectors
+ ^^^^^^^^^^^^^^^^^
+
+ <desktop_name>::
+- Selects the desktop with the given name.
++ Selects the desktop with the given name.
+
+ ^<n>::
+- Selects the nth desktop.
++ Selects the nth desktop.
+
+ 'CYCLE_DIR'::
+- Selects the desktop in the given direction relative to the active desktop.
++ Selects the desktop in the given direction relative to the active desktop.
+
+ last::
+- Selects the previously focused desktop.
++ Selects the previously focused desktop.
+
+ focused::
+- Selects the currently focused desktop.
++ Selects the currently focused desktop.
+
+ older::
+- Selects the desktop older than the focused desktop in the history.
++ Selects the desktop older than the focused desktop in the history.
+
+ newer::
+- Selects the desktop newer than the focused desktop in the history.
++ Selects the desktop newer than the focused desktop in the history.
+
+ Modifiers
+ ^^^^^^^^^
+
+ occupied::
+- Only consider occupied desktops.
++ Only consider occupied desktops.
+
+ free::
+- Only consider free desktops.
++ Only consider free desktops.
+
+ urgent::
+- Only consider urgent desktops.
++ Only consider urgent desktops.
+
+ local::
+- Only consider desktops of the current monitor.
++ Only consider desktops of the current monitor.
+
+ Monitor
+ ~~~~~~~
+@@ -251,62 +253,62 @@ Primary Selectors
+ ^^^^^^^^^^^^^^^^^
+
+ <monitor_name>::
+- Selects the monitor with the given name.
++ Selects the monitor with the given name.
+
+ ^<n>::
+- Selects the nth monitor.
++ Selects the nth monitor.
+
+ 'DIR'::
+- Selects the monitor in the given (spacial) direction relative to the active monitor.
++ Selects the monitor in the given (spacial) direction relative to the active monitor.
+
+ 'CYCLE_DIR'::
+- Selects the monitor in the given (cyclic) direction relative to the active monitor.
++ Selects the monitor in the given (cyclic) direction relative to the active monitor.
+
+ primary::
+- Selects the primary monitor.
++ Selects the primary monitor.
+
+ last::
+- Selects the previously focused monitor.
++ Selects the previously focused monitor.
+
+ focused::
+- Selects the currently focused monitor.
++ Selects the currently focused monitor.
+
+ older::
+- Selects the monitor older than the focused monitor in the history.
++ Selects the monitor older than the focused monitor in the history.
+
+ newer::
+- Selects the monitor newer than the focused monitor in the history.
++ Selects the monitor newer than the focused monitor in the history.
+
+ Modifiers
+ ^^^^^^^^^
+
+ occupied::
+- Only consider monitors where the focused desktop is occupied.
++ Only consider monitors where the focused desktop is occupied.
+
+ free::
+- Only consider monitors where the focused desktop is free.
++ Only consider monitors where the focused desktop is free.
+
+
+ Window States
+ -------------
+
+ floating::
+- Is above any tiled window and can be moved/resized freely. Although it doesn't occupy any tiling space, it is still part of the window tree.
++ Is above any tiled window and can be moved/resized freely. Although it doesn't occupy any tiling space, it is still part of the window tree.
+
+ pseudo_tiled::
+- Has a libre size while being centered in its tiling space.
++ Has a libre size while being centered in its tiling space.
+
+ fullscreen::
+- Fills its monitor rectangle, is above all the other windows and has no borders.
++ Fills its monitor rectangle, is above all the other windows and has no borders.
+
+ locked::
+- Ignores the *close* message.
++ Ignores the *close* message.
+
+ sticky::
+- Stays in the focused desktop of its monitor.
++ Stays in the focused desktop of its monitor.
+
+ private::
+- Tries to keep the same tiling position/size.
++ Tries to keep the same tiling position/size.
+
+
+ Commands
+@@ -323,40 +325,40 @@ window ['WINDOW_SEL'] 'OPTIONS'
+ Options
+ ^^^^^^^
+ *-f*, *--focus* ['WINDOW_SEL']::
+- Focus the selected or given window.
++ Focus the selected or given window.
+
+ *-d*, *--to-desktop* 'DESKTOP_SEL'::
+- Send the selected window to the given desktop.
++ Send the selected window to the given desktop.
+
+ *-m*, *--to-monitor* 'MONITOR_SEL'::
+- Send the selected window to the given monitor.
++ Send the selected window to the given monitor.
+
+ *-w*, *--to-window* 'WINDOW_SEL'::
+- Transplant the selected window to the given window.
++ Transplant the selected window to the given window.
+
+ *-s*, *--swap* 'WINDOW_SEL'::
+- Swap the selected window with the given window.
++ Swap the selected window with the given window.
+
+ *-p*, *--presel* 'DIR'|cancel::
+- Preselect the splitting area of the selected window (or cancel the preselection).
++ Preselect the splitting area of the selected window (or cancel the preselection).
+
+ *-r*, *--ratio* 'RATIO'::
+- Set the splitting ratio of the selected window (0 < 'RATIO' < 1).
++ Set the splitting ratio of the selected window (0 < 'RATIO' < 1).
+
+ *-e*, *--edge* 'DIR' 'RATIO'|±'PIXELS'::
+- Set or change the splitting ratio of the edge located in the given direction in relation to the selected window.
++ Set or change the splitting ratio of the edge located in the given direction in relation to the selected window.
+
+ *-R*, *--rotate* 'DIR' '90|270|180'::
+- Rotate the tree holding the edge located in the given direction in relation to the selected window.
++ Rotate the tree holding the edge located in the given direction in relation to the selected window.
+
+ *-t*, *--toggle* floating|fullscreen|pseudo_tiled|locked|sticky|private[=on|off]::
+- Set or toggle the given state for the selected window.
++ Set or toggle the given state for the selected window.
+
+ *-c*, *--close*::
+- Close the selected window.
++ Close the selected window.
+
+ *-k*, *--kill*::
+- Kill the selected window.
++ Kill the selected window.
+
+ Desktop
+ ~~~~~~~
+@@ -369,40 +371,43 @@ desktop ['DESKTOP_SEL'] 'OPTIONS'
+ Options
+ ^^^^^^^
+ *-f*, *--focus* ['DESKTOP_SEL']::
+- Focus the selected or given desktop.
++ Focus the selected or given desktop.
+
+ *-m*, *--to-monitor* 'MONITOR_SEL'::
+- Send the selected desktop to the given monitor.
++ Send the selected desktop to the given monitor.
+
+ *-l*, *--layout* 'CYCLE_DIR'|monocle|tiled::
+- Set or cycle the layout of the selected desktop.
++ Set or cycle the layout of the selected desktop.
+
+ *-n*, *--rename* <new_name>::
+- Rename the selected desktop.
++ Rename the selected desktop.
+
+ *-s*, *--swap* 'DESKTOP_SEL'::
+- Swap the selected desktop with the given desktop.
++ Swap the selected desktop with the given desktop.
+
+ *-r*, *--remove*::
+- Remove the selected desktop.
++ Remove the selected desktop.
+
+ *-c*, *--cancel-presel*::
+- Cancel the preselection of all the windows of the selected desktop.
++ Cancel the preselection of all the windows of the selected desktop.
+
+ *-F*, *--flip* 'horizontal|vertical'::
+- Flip the tree of the selected desktop.
++ Flip the tree of the selected desktop.
+
+ *-R*, *--rotate* '90|270|180'::
+- Rotate the tree of the selected desktop.
++ Rotate the tree of the selected desktop.
++
++*-E*, *--equalize*::
++ Reset the split ratios of the tree of the selected desktop.
+
+ *-B*, *--balance*::
+- Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area.
++ Adjust the split ratios of the tree of the selected desktop so that all windows occupy the same area.
+
+ *-C*, *--circulate* forward|backward::
+- Circulate the leaves of the tree of the selected desktop.
++ Circulate the leaves of the tree of the selected desktop.
+
+ *-t*, *--toggle* floating[=on|off]::
+- Set or toggle the given state for the selected desktop.
++ Set or toggle the given state for the selected desktop.
+
+
+ Monitor
+@@ -416,25 +421,25 @@ monitor ['MONITOR_SEL'] 'OPTIONS'
+ Options
+ ^^^^^^^
+ *-f*, *--focus* ['MONITOR_SEL']::
+- Focus the selected or given monitor.
++ Focus the selected or given monitor.
+
+ *-a*, *--add-desktops* <name>...::
+- Create desktops with the given names in the selected monitor.
++ Create desktops with the given names in the selected monitor.
+
+ *-r*, *--remove-desktops* <name>...::
+- Remove desktops with the given names.
++ Remove desktops with the given names.
+
+ *-o*, *--reorder-desktops* <name>...::
+- Reorder the desktops of the selected monitor to match the given order.
++ Reorder the desktops of the selected monitor to match the given order.
+
+ *-d*, *--reset-desktops* <name>...::
+- Rename, add or remove desktops depending on whether the number of given names is equal, superior or inferior to the number of existing desktops.
++ Rename, add or remove desktops depending on whether the number of given names is equal, superior or inferior to the number of existing desktops. Incidentally reset the settings of the existing desktops.
+
+ *-n*, *--rename* <new_name>::
+- Rename the selected monitor.
++ Rename the selected monitor.
+
+ *-s*, *--swap* 'MONITOR_SEL'::
+- Swap the selected monitor with the given monitor.
++ Swap the selected monitor with the given monitor.
+
+ Query
+ ~~~~~
+@@ -447,25 +452,25 @@ query 'OPTIONS'
+ Options
+ ^^^^^^^
+ *-W*, *--windows*::
+- List matching windows.
++ List matching windows.
+
+ *-D*, *--desktops*::
+- List matching desktops.
++ List matching desktops.
+
+ *-M*, *--monitors*::
+- List matching monitors.
++ List matching monitors.
+
+ *-T*, *--tree*::
+- Print tree rooted at query.
++ Print tree rooted at query.
+
+ *-H*, *--history*::
+- Print the history as it relates to the query.
++ Print the history as it relates to the query.
+
+ *-S*, *--stack*::
+- Print the window stacking order.
++ Print the window stacking order.
+
+ [*-m*,*--monitor* ['MONITOR_SEL']] | [*-d*,*--desktop* ['DESKTOP_SEL']] | [*-w*, *--window* ['WINDOW_SEL']]::
+- Constrain matches to the selected monitor, desktop or window.
++ Constrain matches to the selected monitor, desktop or window.
+
+ Restore
+ ~~~~~~~
+@@ -479,13 +484,13 @@ Options
+ ^^^^^^^
+
+ *-T*, *--tree* <file_path>::
+- Load the desktop trees from the given file.
++ Load the desktop trees from the given file.
+
+ *-H*, *--history* <file_path>::
+- Load the focus history from the given file.
++ Load the focus history from the given file.
+
+ *-S*, *--stack* <file_path>::
+- Load the window stacking order from the given file.
++ Load the window stacking order from the given file.
+
+ Control
+ ~~~~~~~
+@@ -499,16 +504,19 @@ Options
+ ^^^^^^^
+
+ *--adopt-orphans*::
+- Manage all the unmanaged windows remaining from a previous session.
++ Manage all the unmanaged windows remaining from a previous session.
+
+ *--toggle-visibility*::
+- Toggle the visibility of all the windows.
++ Toggle the visibility of all the windows.
+
+ *--record-history* on|off::
+- Enable or disable the recording of window focus history.
++ Enable or disable the recording of window focus history.
+
+ *--subscribe*::
+- Continuously print status informations on standard output.
++ Continuously print status informations.
++
++*--get-status*::
++ Print the current status informations.
+
+ Pointer
+ ~~~~~~~
+@@ -522,13 +530,13 @@ Options
+ ^^^^^^^
+
+ *-g*, *--grab* focus|move|resize_side|resize_corner::
+- Initiate the given pointer action.
++ Initiate the given pointer action.
+
+ *-t*, *--track* <x> <y>::
+- Pass the pointer root coordinates for the current pointer action.
++ Pass the pointer root coordinates for the current pointer action.
+
+ *-u*, *--ungrab*::
+- Terminate the current pointer action.
++ Terminate the current pointer action.
+
+ Rule
+ ~~~~
+@@ -541,14 +549,14 @@ rule 'OPTIONS'
+ Options
+ ^^^^^^^
+
+-*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)]::
+- Create a new rule.
++*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|window=WINDOW_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|follow|manage|focus)=(true|false)] [split_dir=DIR]::
++ Create a new rule.
+
+ *-r*, *--remove* ^<n>|head|tail|<class_name>|<instance_name>|*...::
+- Remove the given rules.
++ Remove the given rules.
+
+ *-l*, *--list* [<class_name>|<instance_name>|*]::
+- List the rules.
++ List the rules.
+
+ Config
+ ~~~~~~
+@@ -556,8 +564,8 @@ Config
+ General Syntax
+ ^^^^^^^^^^^^^^
+
+-config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'] <key> [<value>]::
+- Get or set the value of <key>.
++config [-m 'MONITOR_SEL'|-d 'DESKTOP_SEL'|-w 'WINDOW_SEL'] <key> [<value>]::
++ Get or set the value of <key>.
+
+ Quit
+ ~~~~
+@@ -566,7 +574,20 @@ General Syntax
+ ^^^^^^^^^^^^^^
+
+ quit [<status>]::
+- Quit with an optional exit status.
++ Quit with an optional exit status.
++
++Exit Codes
++----------
++
++If the server can't handle a message, *bspc* will return with one of the following exit codes:
++
++1::
++ Failure.
++2::
++ Syntax error.
++3::
++ Unknown command.
++
+
+ Settings
+ --------
+@@ -578,85 +599,91 @@ Global Settings
+ ~~~~~~~~~~~~~~~
+
+ 'focused_border_color'::
+- Color of the border of a focused window of a focused monitor.
++ Color of the border of a focused window of a focused monitor.
+
+ 'active_border_color'::
+- Color of the border of a focused window of an unfocused monitor.
++ Color of the border of a focused window of an unfocused monitor.
+
+ 'normal_border_color'::
+- Color of the border of an unfocused window.
++ Color of the border of an unfocused window.
+
+ 'presel_border_color'::
+- Color of the *presel* message feedback.
++ Color of the *presel* message feedback.
+
+ 'focused_locked_border_color'::
+- Color of the border of a focused locked window of a focused monitor.
++ Color of the border of a focused locked window of a focused monitor.
+
+ 'active_locked_border_color'::
+- Color of the border of a focused locked window of an unfocused monitor.
++ Color of the border of a focused locked window of an unfocused monitor.
+
+ 'normal_locked_border_color'::
+- Color of the border of an unfocused locked window.
++ Color of the border of an unfocused locked window.
+
+ 'focused_sticky_border_color'::
+- Color of the border of a focused sticky window of a focused monitor.
++ Color of the border of a focused sticky window of a focused monitor.
+
+ 'active_sticky_border_color'::
+- Color of the border of a focused sticky window of an unfocused monitor.
++ Color of the border of a focused sticky window of an unfocused monitor.
+
+ 'normal_sticky_border_color'::
+- Color of the border of an unfocused sticky window.
++ Color of the border of an unfocused sticky window.
+
+ 'focused_private_border_color'::
+- Color of the border of a focused private window of a focused monitor.
++ Color of the border of a focused private window of a focused monitor.
+
+ 'active_private_border_color'::
+- Color of the border of a focused private window of an unfocused monitor.
++ Color of the border of a focused private window of an unfocused monitor.
+
+ 'normal_private_border_color'::
+- Color of the border of an unfocused private window.
++ Color of the border of an unfocused private window.
+
+ 'urgent_border_color'::
+- Color of the border of an urgent window.
++ Color of the border of an urgent window.
+
+ 'split_ratio'::
+- Default split ratio.
++ Default split ratio.
+
+ 'status_prefix'::
+- Prefix prepended to each of the status lines.
++ Prefix prepended to each of the status lines.
+
+ 'external_rules_command'::
+- External command used to retrieve rule consequences. The command will receive the the ID of the window being processed as its first argument and the class and instance names as second and third arguments. The output of that command must have the following format: *key1=value1 key2=value2 ...* (the valid key/value pairs are given in the description of the 'rule' command).
++ External command used to retrieve rule consequences. The command will receive the the ID of the window being processed as its first argument and the class and instance names as second and third arguments. The output of that command must have the following format: *key1=value1 key2=value2 ...* (the valid key/value pairs are given in the description of the 'rule' command).
+
+ 'history_aware_focus'::
+- Give priority to the focus history when focusing nodes.
++ Give priority to the focus history when focusing nodes.
+
+ 'borderless_monocle'::
+- Remove borders for tiled windows in monocle mode.
++ Remove borders for tiled windows in monocle mode.
+
+ 'gapless_monocle'::
+- Remove gaps for tiled windows in monocle mode.
++ Remove gaps for tiled windows in monocle mode.
+
+ 'focus_follows_pointer'::
+- Focus the window under the pointer.
++ Focus the window under the pointer.
+
+ 'pointer_follows_monitor'::
+- When focusing a monitor, put the pointer at its center.
++ When focusing a monitor, put the pointer at its center.
+
+ 'auto_alternate'::
+- Interpret two consecutive identical *use* messages as an *alternate* message.
++ Interpret two consecutive identical *use* messages as an *alternate* message.
+
+ 'auto_cancel'::
+- Interpret two consecutive identical *presel* messages as a *cancel* message.
++ Interpret two consecutive identical *presel* messages as a *cancel* message.
+
+ 'apply_floating_atom'::
+- Set the value of the '_BSPWM_FLOATING_WINDOW' atom of each window according to its floating state.
++ Set the value of the '_BSPWM_FLOATING_WINDOW' atom of each window according to its floating state.
+
+ 'ignore_ewmh_focus'::
+- Ignore EWMH requests to focus a window.
++ Ignore EWMH focus requests coming from applications.
++
++'remove_disabled_monitors'::
++ Consider disabled monitors as disconnected.
++
++'remove_unplugged_monitors'::
++ Remove unplugged monitors.
+
+-'remove_disabled_monitor'::
+- Consider disabled monitors as disconnected.
++'merge_overlapping_monitors'::
++ Merge overlapping monitors (the bigger remains).
+
+ Monitor and Desktop Settings
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+@@ -665,16 +692,19 @@ Monitor and Desktop Settings
+ 'right_padding'::
+ 'bottom_padding'::
+ 'left_padding'::
+- Padding space added at the sides of the monitor or desktop.
++ Padding space added at the sides of the monitor or desktop.
+
+-Desktop Settings
+-~~~~~~~~~~~~~~~~
+-
+-'window_gap'::
+- Size of the gap that separates windows.
++Default, Desktop Default and Window Settings
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ 'border_width'::
+- Window border width.
++ Window border width.
++
++Default and Desktop Settings
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++'window_gap'::
++ Size of the gap that separates windows.
+
+
+ Status Format
+@@ -685,37 +715,37 @@ Status informations are composed of items separated by colons.
+ Each item has the form '<type><value>' where '<type>' is the first character of the item.
+
+ 'M<monitor_name>'::
+- Focused monitor.
++ Focused monitor.
+
+ 'm<monitor_name>'::
+- Unfocused monitor.
++ Unfocused monitor.
+
+ 'O<desktop_name>'::
+- Occupied focused desktop.
++ Occupied focused desktop.
+
+ 'o<desktop_name>'::
+- Occupied unfocused desktop.
++ Occupied unfocused desktop.
+
+ 'F<desktop_name>'::
+- Free focused desktop.
++ Free focused desktop.
+
+ 'f<desktop_name>'::
+- Free unfocused desktop.
++ Free unfocused desktop.
+
+ 'U<desktop_name>'::
+- Urgent focused desktop.
++ Urgent focused desktop.
+
+ 'u<desktop_name>'::
+- Urgent unfocused desktop.
++ Urgent unfocused desktop.
+
+ 'L(tiled|monocle)'::
+- Layout of the focused desktop of the focused monitor.
++ Layout of the focused desktop of the focused monitor.
+
+ Environment Variables
+ ---------------------
+
+ 'BSPWM_SOCKET'::
+- The path of the socket used for the communication between *bspc* and *bspwm*. If it isn't defined, then the following path is used: '/tmp/bspwm<display_name>-socket'.
++ The path of the socket used for the communication between *bspc* and *bspwm*. If it isn't defined, then the following path is used: '/tmp/bspwm<display_name>-socket'.
+
+ Panels
+ ------
+diff --git a/events.c b/events.c
+index b41a9a7..2c9cacb 100644
+--- a/events.c
++++ b/events.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -34,313 +38,354 @@
+
+ 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_PROPERTY_NOTIFY:
+- property_notify(evt);
+- break;
+- case XCB_ENTER_NOTIFY:
+- enter_notify(evt);
+- break;
+- case XCB_MOTION_NOTIFY:
+- motion_notify(evt);
+- break;
+- case XCB_FOCUS_IN:
+- focus_in(evt);
+- break;
+- default:
+- if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
+- import_monitors();
+- break;
+- }
++ 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_PROPERTY_NOTIFY:
++ property_notify(evt);
++ break;
++ case XCB_ENTER_NOTIFY:
++ enter_notify(evt);
++ break;
++ case XCB_MOTION_NOTIFY:
++ motion_notify(evt);
++ break;
++ case XCB_FOCUS_IN:
++ focus_in(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;
++ xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
+
+- PRINTF("map request %X\n", e->window);
++ PRINTF("map request %X\n", e->window);
+
+- schedule_window(e->window);
++ schedule_window(e->window);
+ }
+
+ void configure_request(xcb_generic_event_t *evt)
+ {
+- xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
+-
+- PRINTF("configure request %X\n", e->window);
+-
+- coordinates_t loc;
+- bool is_managed = locate_window(e->window, &loc);
+-
+- if (is_managed && !is_floating(loc.node->client)) {
+- if (e->value_mask & XCB_CONFIG_WINDOW_X)
+- loc.node->client->floating_rectangle.x = e->x;
+- if (e->value_mask & XCB_CONFIG_WINDOW_Y)
+- loc.node->client->floating_rectangle.y = e->y;
+- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
+- loc.node->client->floating_rectangle.width = e->width;
+- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
+- loc.node->client->floating_rectangle.height = e->height;
+-
+- xcb_configure_notify_event_t evt;
+- xcb_rectangle_t rect;
+- xcb_window_t win = loc.node->client->window;
+- unsigned int bw = loc.node->client->border_width;
+-
+- if (loc.node->client->fullscreen)
+- rect = loc.monitor->rectangle;
+- else
+- rect = loc.node->client->tiled_rectangle;
+-
+- evt.response_type = XCB_CONFIGURE_NOTIFY;
+- evt.event = win;
+- evt.window = win;
+- evt.above_sibling = XCB_NONE;
+- evt.x = rect.x;
+- evt.y = rect.y;
+- evt.width = rect.width;
+- evt.height = rect.height;
+- evt.border_width = bw;
+- evt.override_redirect = false;
+-
+- xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
+-
+- if (loc.node->client->pseudo_tiled)
+- arrange(loc.monitor, loc.desktop);
+- } else {
+- 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 (is_managed)
+- loc.node->client->floating_rectangle.x = e->x;
+- }
+-
+- if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
+- mask |= XCB_CONFIG_WINDOW_Y;
+- values[i++] = e->y;
+- if (is_managed)
+- loc.node->client->floating_rectangle.y = e->y;
+- }
+-
+- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+- mask |= XCB_CONFIG_WINDOW_WIDTH;
+- values[i++] = e->width;
+- if (is_managed)
+- loc.node->client->floating_rectangle.width = e->width;
+- }
+-
+- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+- mask |= XCB_CONFIG_WINDOW_HEIGHT;
+- values[i++] = e->height;
+- if (is_managed)
+- loc.node->client->floating_rectangle.height = e->height;
+- }
+-
+- if (!is_managed && 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);
+- }
+-
+- if (is_managed)
+- translate_client(monitor_from_client(loc.node->client), loc.monitor, loc.node->client);
++ xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
++
++ PRINTF("configure request %X\n", e->window);
++
++ coordinates_t loc;
++ bool is_managed = locate_window(e->window, &loc);
++ client_t *c = (is_managed ? loc.node->client : NULL);
++ int w = 0, h = 0;
++
++ if (is_managed && !is_floating(c)) {
++ 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)
++ w = e->width;
++ if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
++ h = e->height;
++
++ if (w != 0) {
++ restrain_floating_width(c, &w);
++ c->floating_rectangle.width = w;
++ }
++
++ if (h != 0) {
++ restrain_floating_height(c, &h);
++ c->floating_rectangle.height = h;
++ }
++
++ xcb_configure_notify_event_t evt;
++ xcb_rectangle_t rect;
++ xcb_window_t win = c->window;
++ unsigned int bw = c->border_width;
++
++ if (c->fullscreen)
++ rect = loc.monitor->rectangle;
++ else
++ rect = c->tiled_rectangle;
++
++ evt.response_type = XCB_CONFIGURE_NOTIFY;
++ evt.event = win;
++ evt.window = win;
++ evt.above_sibling = XCB_NONE;
++ evt.x = rect.x;
++ evt.y = rect.y;
++ evt.width = rect.width;
++ evt.height = rect.height;
++ evt.border_width = bw;
++ evt.override_redirect = false;
++
++ xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
++
++ if (c->pseudo_tiled)
++ arrange(loc.monitor, loc.desktop);
++ } else {
++ 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 (is_managed)
++ c->floating_rectangle.x = e->x;
++ }
++
++ if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
++ mask |= XCB_CONFIG_WINDOW_Y;
++ values[i++] = e->y;
++ if (is_managed)
++ c->floating_rectangle.y = e->y;
++ }
++
++ if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
++ mask |= XCB_CONFIG_WINDOW_WIDTH;
++ w = e->width;
++ if (is_managed) {
++ restrain_floating_width(c, &w);
++ c->floating_rectangle.width = w;
++ }
++ values[i++] = w;
++ }
++
++ if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
++ mask |= XCB_CONFIG_WINDOW_HEIGHT;
++ h = e->height;
++ if (is_managed) {
++ restrain_floating_height(c, &h);
++ c->floating_rectangle.height = h;
++ }
++ values[i++] = h;
++ }
++
++ if (!is_managed && 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);
++ }
++
++ if (is_managed)
++ translate_client(monitor_from_client(c), loc.monitor, c);
+ }
+
+ void destroy_notify(xcb_generic_event_t *evt)
+ {
+- xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
++ xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
+
+- PRINTF("destroy notify %X\n", e->window);
++ PRINTF("destroy notify %X\n", e->window);
+
+- unmanage_window(e->window);
++ unmanage_window(e->window);
+ }
+
+ void unmap_notify(xcb_generic_event_t *evt)
+ {
+- xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
++ xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
+
+- PRINTF("unmap notify %X\n", e->window);
++ PRINTF("unmap notify %X\n", e->window);
+
+- unmanage_window(e->window);
++ unmanage_window(e->window);
+ }
+
+ void property_notify(xcb_generic_event_t *evt)
+ {
+- xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
+- xcb_icccm_wm_hints_t hints;
+-
+- /* PRINTF("property notify %X\n", e->window); */
+-
+- if (e->atom != XCB_ATOM_WM_HINTS)
+- return;
+-
+- coordinates_t loc;
+- if (locate_window(e->window, &loc)
+- && xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1)
+- set_urgency(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
++ xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
++
++ /* PRINTF("property notify %X\n", e->window); */
++
++ 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_urgency(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;
++ xcb_size_hints_t size_hints;
++ if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &size_hints, NULL) == 1 &&
++ (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE))) {
++ c->min_width = size_hints.min_width;
++ c->max_width = size_hints.max_width;
++ c->min_height = size_hints.min_height;
++ c->max_height = size_hints.max_height;
++ int w = c->floating_rectangle.width;
++ int h = c->floating_rectangle.height;
++ restrain_floating_size(c, &w, &h);
++ c->floating_rectangle.width = w;
++ c->floating_rectangle.height = h;
++ 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;
+-
+- PRINTF("client message %X %u\n", e->window, e->type);
+-
+- 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 || loc.node == mon->desk->focus)
+- return;
+- if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node) {
+- set_fullscreen(loc.desktop->focus, false);
+- arrange(loc.monitor, loc.desktop);
+- }
+- 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) {
+- window_close(loc.node);
+- }
++ xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
++
++ PRINTF("client message %X %u\n", e->window, e->type);
++
++ 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;
++ if (loc.desktop->focus->client->fullscreen && loc.desktop->focus != loc.node) {
++ set_fullscreen(loc.desktop->focus, false);
++ arrange(loc.monitor, loc.desktop);
++ }
++ 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) {
++ window_close(loc.node);
++ }
+ }
+
+ void focus_in(xcb_generic_event_t *evt)
+ {
+- xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
+-
+- /* PRINTF("focus in %X %d %d\n", e->event, e->mode, e->detail); */
+-
+- if (e->mode == XCB_NOTIFY_MODE_GRAB
+- || e->mode == XCB_NOTIFY_MODE_UNGRAB)
+- return;
+- /* prevent focus stealing */
+- if ((e->detail == XCB_NOTIFY_DETAIL_ANCESTOR ||
+- e->detail == XCB_NOTIFY_DETAIL_INFERIOR ||
+- e->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL ||
+- e->detail == XCB_NOTIFY_DETAIL_NONLINEAR) &&
+- (mon->desk->focus == NULL
+- || mon->desk->focus->client->window != e->event))
+- update_input_focus();
++ xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
++
++ /* PRINTF("focus in %X %d %d\n", e->event, e->mode, e->detail); */
++
++ if (e->mode == XCB_NOTIFY_MODE_GRAB ||
++ e->mode == XCB_NOTIFY_MODE_UNGRAB)
++ return;
++ /* prevent focus stealing */
++ if ((e->detail == XCB_NOTIFY_DETAIL_ANCESTOR ||
++ e->detail == XCB_NOTIFY_DETAIL_INFERIOR ||
++ e->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL ||
++ e->detail == XCB_NOTIFY_DETAIL_NONLINEAR) &&
++ (mon->desk->focus == NULL ||
++ mon->desk->focus->client->window != e->event))
++ update_input_focus();
+ }
+
+ 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;
++ xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
++ xcb_window_t win = e->event;
+
+- PRINTF("enter notify %X %d %d\n", win, e->mode, e->detail);
++ PRINTF("enter notify %X %d %d\n", win, e->mode, e->detail);
+
+- if (e->mode != XCB_NOTIFY_MODE_NORMAL
+- || (mon->desk->focus != NULL && mon->desk->focus->client->window == win))
+- return;
++ if (e->mode != XCB_NOTIFY_MODE_NORMAL ||
++ (mon->desk->focus != NULL &&
++ mon->desk->focus->client->window == win))
++ return;
+
+- enable_motion_recorder();
++ enable_motion_recorder();
+ }
+
+ void motion_notify(xcb_generic_event_t *evt)
+ {
+- PUTS("motion notify");
+-
+- xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
+-
+- int dtime = e->time - last_motion_time;
+- if (dtime > 1000) {
+- last_motion_time = e->time;
+- last_motion_x = e->event_x;
+- last_motion_y = e->event_y;
+- return;
+- }
+-
+- int mdist = abs(e->event_x - last_motion_x) + abs(e->event_y - last_motion_y);
+- if (mdist < 10)
+- return;
+-
+- disable_motion_recorder();
+-
+- xcb_window_t win = XCB_NONE;
+- xcb_point_t pt = {e->root_x, e->root_y};
+- query_pointer(&win, NULL);
+-
+- bool backup = pointer_follows_monitor;
+- auto_raise = false;
+- pointer_follows_monitor = false;
+- if (!window_focus(win)) {
+- monitor_t *m = monitor_from_point(pt);
+- if (m != NULL && m != mon)
+- focus_node(m, m->desk, m->desk->focus);
+- }
+- pointer_follows_monitor = backup;
+- auto_raise = true;
++ PUTS("motion notify");
++
++ xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
++
++ int dtime = e->time - last_motion_time;
++ if (dtime > 1000) {
++ last_motion_time = e->time;
++ last_motion_x = e->event_x;
++ last_motion_y = e->event_y;
++ return;
++ }
++
++ int mdist = abs(e->event_x - last_motion_x) + abs(e->event_y - last_motion_y);
++ if (mdist < 10)
++ return;
++
++ disable_motion_recorder();
++
++ xcb_window_t win = XCB_NONE;
++ xcb_point_t pt = {e->root_x, e->root_y};
++ query_pointer(&win, NULL);
++
++ bool backup = pointer_follows_monitor;
++ auto_raise = false;
++ pointer_follows_monitor = false;
++ if (!window_focus(win)) {
++ monitor_t *m = monitor_from_point(pt);
++ if (m != NULL && m != mon)
++ focus_node(m, m->desk, m->desk->focus);
++ }
++ pointer_follows_monitor = backup;
++ 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_fullscreen(n, true);
+- else if (action == XCB_EWMH_WM_STATE_REMOVE)
+- set_fullscreen(n, false);
+- else if (action == XCB_EWMH_WM_STATE_TOGGLE)
+- set_fullscreen(n, !n->client->fullscreen);
+- arrange(m, d);
+- } 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->client->sticky);
+- } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
+- if (action == XCB_EWMH_WM_STATE_ADD)
+- set_urgency(m, d, n, true);
+- else if (action == XCB_EWMH_WM_STATE_REMOVE)
+- set_urgency(m, d, n, false);
+- else if (action == XCB_EWMH_WM_STATE_TOGGLE)
+- set_urgency(m, d, n, !n->client->urgent);
+- }
++ if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
++ if (action == XCB_EWMH_WM_STATE_ADD)
++ set_fullscreen(n, true);
++ else if (action == XCB_EWMH_WM_STATE_REMOVE)
++ set_fullscreen(n, false);
++ else if (action == XCB_EWMH_WM_STATE_TOGGLE)
++ set_fullscreen(n, !n->client->fullscreen);
++ arrange(m, d);
++ } 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->client->sticky);
++ } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
++ if (action == XCB_EWMH_WM_STATE_ADD)
++ set_urgency(m, d, n, true);
++ else if (action == XCB_EWMH_WM_STATE_REMOVE)
++ set_urgency(m, d, n, false);
++ else if (action == XCB_EWMH_WM_STATE_TOGGLE)
++ set_urgency(m, d, n, !n->client->urgent);
++ }
+ }
+diff --git a/events.h b/events.h
+index 7812dad..1d718da 100644
+--- a/events.h
++++ b/events.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_EVENTS_H
+diff --git a/ewmh.c b/ewmh.c
+index 25d86d3..f7d1466 100644
+--- a/ewmh.c
++++ b/ewmh.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <string.h>
+@@ -31,142 +35,142 @@
+
+ void ewmh_init(void)
+ {
+- ewmh = malloc(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");
++ ewmh = malloc(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 ? XCB_NONE : mon->desk->focus->client->window);
+- xcb_ewmh_set_active_window(ewmh, default_screen, win);
++ xcb_window_t win = (mon->desk->focus == NULL ? XCB_NONE : mon->desk->focus->client->window);
++ xcb_ewmh_set_active_window(ewmh, default_screen, win);
+ }
+
+ void ewmh_update_number_of_desktops(void)
+ {
+- xcb_ewmh_set_number_of_desktops(ewmh, default_screen, num_desktops);
++ xcb_ewmh_set_number_of_desktops(ewmh, default_screen, num_desktops);
+ }
+
+ 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;
++ 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;
++ 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)
+ {
+- uint32_t i = ewmh_get_desktop_index(mon->desk);
+- xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
++ 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);
+- xcb_ewmh_set_wm_desktop(ewmh, n->client->window, i);
++ uint32_t i = ewmh_get_desktop_index(d);
++ xcb_ewmh_set_wm_desktop(ewmh, n->client->window, 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))
+- xcb_ewmh_set_wm_desktop(ewmh, n->client->window, i);
+- }
++ 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))
++ xcb_ewmh_set_wm_desktop(ewmh, n->client->window, 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)
+- return;
+-
+- names_len = i - 1;
+- xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
++ 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)
++ return;
++
++ names_len = i - 1;
++ xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
+ }
+
+ void ewmh_update_client_list(void)
+ {
+- if (num_clients == 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[num_clients];
+- unsigned int 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 (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+- wins[i++] = n->client->window;
+-
+- xcb_ewmh_set_client_list(ewmh, default_screen, num_clients, wins);
+- xcb_ewmh_set_client_list_stacking(ewmh, default_screen, num_clients, wins);
++ if (num_clients == 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[num_clients];
++ unsigned int 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 (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
++ wins[i++] = n->client->window;
++
++ xcb_ewmh_set_client_list(ewmh, default_screen, num_clients, wins);
++ xcb_ewmh_set_client_list_stacking(ewmh, default_screen, num_clients, wins);
+ }
+
+ bool ewmh_wm_state_add(client_t *c, xcb_atom_t state)
+ {
+- if (c->num_states >= MAX_STATE)
+- return false;
+- for (int i = 0; i < c->num_states; i++)
+- if (c->wm_state[i] == state)
+- return false;
+- c->wm_state[c->num_states] = state;
+- c->num_states++;
+- xcb_ewmh_set_wm_state(ewmh, c->window, c->num_states, c->wm_state);
+- return true;
++ if (c->num_states >= MAX_STATE)
++ return false;
++ for (int i = 0; i < c->num_states; i++)
++ if (c->wm_state[i] == state)
++ return false;
++ c->wm_state[c->num_states] = state;
++ c->num_states++;
++ xcb_ewmh_set_wm_state(ewmh, c->window, c->num_states, c->wm_state);
++ return true;
+ }
+
+ bool ewmh_wm_state_remove(client_t *c, xcb_atom_t state)
+ {
+- for (int i = 0; i < c->num_states; i++)
+- if (c->wm_state[i] == state)
+- {
+- for (int j = i; j < (c->num_states - 1); j++)
+- c->wm_state[j] = c->wm_state[j + 1];
+- c->num_states--;
+- xcb_ewmh_set_wm_state(ewmh, c->window, c->num_states, c->wm_state);
+- return true;
+- }
+- return false;
++ for (int i = 0; i < c->num_states; i++)
++ if (c->wm_state[i] == state)
++ {
++ for (int j = i; j < (c->num_states - 1); j++)
++ c->wm_state[j] = c->wm_state[j + 1];
++ c->num_states--;
++ xcb_ewmh_set_wm_state(ewmh, c->window, c->num_states, c->wm_state);
++ return true;
++ }
++ return false;
+ }
+
+ 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);
++ 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
+index b41c4ee..9a757e6 100644
+--- a/ewmh.h
++++ b/ewmh.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_EWMH_H
+diff --git a/examples/bspwmrc b/examples/bspwmrc
+index e50baae..ab98df7 100755
+--- a/examples/bspwmrc
++++ b/examples/bspwmrc
+@@ -1,13 +1,14 @@
+ #! /bin/sh
+
+-bspc monitor -d I II III IV V VI VII VIII IX X
+-
+ bspc config border_width 2
+-bspc config window_gap 12
++bspc config window_gap 12
++
+ bspc config split_ratio 0.52
+ bspc config borderless_monocle true
+ bspc config gapless_monocle true
+
++bspc monitor -d I II III IV V VI VII VIII IX X
++
+ bspc rule -a Gimp desktop=^8 follow=on floating=on
+ bspc rule -a Chromium desktop=^2
+ bspc rule -a mplayer2 floating=on
+diff --git a/examples/external_rules/external_rules b/examples/external_rules/external_rules
+index c3efb78..1a31f43 100755
+--- a/examples/external_rules/external_rules
++++ b/examples/external_rules/external_rules
+@@ -5,10 +5,10 @@ class=$2
+ instance=$3
+
+ if [ "$instance" = fontforge ] ; then
+- title=$(xtitle "$wid")
+- case "$title" in
+- Layers|Tools|Warning)
+- echo "focus = off"
+- ;;
+- esac
++ title=$(xtitle "$wid")
++ case "$title" in
++ Layers|Tools|Warning)
++ echo "focus = off"
++ ;;
++ esac
+ fi
+diff --git a/examples/external_rules/lua/README.asciidoc b/examples/external_rules/lua/README.asciidoc
+index 17ae1cf..24b5392 100644
+--- a/examples/external_rules/lua/README.asciidoc
++++ b/examples/external_rules/lua/README.asciidoc
+@@ -18,25 +18,25 @@ Shared
+ ~~~~~~
+
+ *-h*::
+- Print the synopsis and exit.
++ Print the synopsis and exit.
+
+ *-p* 'PORT'::
+- Set the socket port.
++ Set the socket port.
+
+ Client
+ ~~~~~~
+
+ *-a* 'HYPOT' 'CONSEQ' ['[duration=DURATION][,delay=DELAY]']::
+- Create a new rule. 'HYPOT' is a Lua expression that represent the rule hypothesis and involves the following strings: 'class', 'instance', 'title', 'type' and 'state'.
++ Create a new rule. 'HYPOT' is a Lua expression that represent the rule hypothesis and involves the following strings: 'class', 'instance', 'title', 'type' and 'state'.
+
+ *-r* 'STRING'|head|tail::
+- Remove the rules containing 'STRING' in their hypothesis or remove the first or last rule.
++ Remove the rules containing 'STRING' in their hypothesis or remove the first or last rule.
+
+ *-l*::
+- List the rules.
++ List the rules.
+
+ *-t* 'CLASS' 'INSTANCE' 'TITLE' 'TYPE' 'STATE'::
+- Test the rules for the given window informations.
++ Test the rules for the given window informations.
+
+ *-q*::
+- Kill the server.
++ Kill the server.
+diff --git a/examples/external_rules/lua/bspwm_rules b/examples/external_rules/lua/bspwm_rules
+index 6e91732..49d28b3 100755
+--- a/examples/external_rules/lua/bspwm_rules
++++ b/examples/external_rules/lua/bspwm_rules
+@@ -1,16 +1,16 @@
+ #! /bin/sh
+
+ if ! rulc -l > /dev/null ; then
+- (setsid ruld &)
+- while ! rulc -l > /dev/null ; do
+- sleep 0.1
+- done
++ (setsid ruld &)
++ while ! rulc -l > /dev/null ; do
++ sleep 0.1
++ done
+ fi
+ rules=$(rulc -l)
+ if [ -z "$rules" ] ; then
+- rulc -a 'class=="Gimp"' 'desktop=^8, follow=on, floating=on'
+- rulc -a 'class=="Chromium"' 'desktop=^2'
+- rulc -a 'instance=="mpv"' 'floating=on'
+- rulc -a 'class=="Kupfer.py"' 'focus=on'
+- rulc -a 'class=="Screenkey"' 'manage=off'
++ rulc -a 'class=="Gimp"' 'desktop=^8, follow=on, floating=on'
++ rulc -a 'class=="Chromium"' 'desktop=^2'
++ rulc -a 'instance=="mpv"' 'floating=on'
++ rulc -a 'class=="Kupfer.py"' 'focus=on'
++ rulc -a 'class=="Screenkey"' 'manage=off'
+ fi
+diff --git a/examples/external_rules/lua/rulc b/examples/external_rules/lua/rulc
+index f0cd633..0e28105 100755
+--- a/examples/external_rules/lua/rulc
++++ b/examples/external_rules/lua/rulc
+@@ -5,64 +5,64 @@ local port = 54321
+
+ local short = "hp:arltq"
+ local long = {
+- {"help", "none", 'h'},
+- {"port", "required", 'p'},
+- {"add", "none", 'a'},
+- {"remove", "none", 'r'},
+- {"list", "none", 'l'},
+- {"test", "none", 't'},
+- {"quit", "none", 'q'}
++ {"help", "none", 'h'},
++ {"port", "required", 'p'},
++ {"add", "none", 'a'},
++ {"remove", "none", 'r'},
++ {"list", "none", 'l'},
++ {"test", "none", 't'},
++ {"quit", "none", 'q'}
+ }
+
+ local cmd_assoc = {
+- a = "add",
+- r = "remove",
+- l = "list",
+- t = "test",
+- q = "quit"
++ a = "add",
++ r = "remove",
++ l = "list",
++ t = "test",
++ q = "quit"
+ }
+
+ local cmd
+ local data_idx = 1
+ for opt, optarg, optind, longind in p.getopt(arg, short, long) do
+- if opt == '?' then
+- print("Unrecognized option")
+- os.exit(1)
+- elseif opt == 'h' then
+- print("Usage: rulc [-h|-p PORT|-a|-r|-l|-t|-q] DATA ...")
+- os.exit(0)
+- elseif opt == 'p' then
+- port = optarg
+- else
+- cmd = cmd_assoc[opt]
+- end
+- data_idx = optind
++ if opt == '?' then
++ print("Unrecognized option")
++ os.exit(1)
++ elseif opt == 'h' then
++ print("Usage: rulc [-h|-p PORT|-a|-r|-l|-t|-q] DATA ...")
++ os.exit(0)
++ elseif opt == 'p' then
++ port = optarg
++ else
++ cmd = cmd_assoc[opt]
++ end
++ data_idx = optind
+ end
+
+ if not cmd then
+- os.exit(1)
++ os.exit(1)
+ end
+
+ local msg = cmd
+ if cmd == "test" then
+- msg = string.format("%s {class=%q, instance=%q, title=%q, type=%q, state=%q}", msg, arg[data_idx], arg[data_idx+1], arg[data_idx+2], arg[data_idx+3], arg[data_idx+4])
++ msg = string.format("%s {class=%q, instance=%q, title=%q, type=%q, state=%q}", msg, arg[data_idx], arg[data_idx+1], arg[data_idx+2], arg[data_idx+3], arg[data_idx+4])
+ elseif cmd == "add" then
+- msg = string.format("%s {%q, %q, %s}", msg, arg[data_idx], arg[data_idx+1], arg[data_idx+2] and string.format("{%s}", arg[data_idx+2]) or "")
++ msg = string.format("%s {%q, %q, %s}", msg, arg[data_idx], arg[data_idx+1], arg[data_idx+2] and string.format("{%s}", arg[data_idx+2]) or "")
+ elseif cmd == "remove" then
+- msg = string.format("%s %s", msg, arg[data_idx])
++ msg = string.format("%s %s", msg, arg[data_idx])
+ end
+
+ local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
+ local s = p.connect(fd, {family=p.AF_INET, addr="127.0.0.1", port=port})
+ if not s then
+- p.close(fd)
+- os.exit(1)
++ p.close(fd)
++ os.exit(1)
+ end
+ p.send(fd, msg)
+ rsp = p.recv(fd, p.BUFSIZ)
+
+ if rsp and #rsp > 0 then
+- print(rsp)
++ print(rsp)
+ end
+
+ p.close(fd)
+diff --git a/examples/external_rules/lua/ruld b/examples/external_rules/lua/ruld
+index e4a8674..47bd531 100755
+--- a/examples/external_rules/lua/ruld
++++ b/examples/external_rules/lua/ruld
+@@ -5,64 +5,64 @@ local port = 54321
+
+ local short = "hp:"
+ local long = {
+- {"help", "none", 'h'},
+- {"port", "required", 'p'},
++ {"help", "none", 'h'},
++ {"port", "required", 'p'},
+ }
+
+ local rules = {}
+
+ for opt, optarg, optind, longind in p.getopt(arg, short, long) do
+- if opt == '?' then
+- print("Unrecognized option")
+- os.exit(1)
+- elseif opt == 'h' then
+- print("Usage: ruld [-h|-p PORT]")
+- os.exit(0)
+- elseif opt == 'p' then
+- port = optarg
+- end
++ if opt == '?' then
++ print("Unrecognized option")
++ os.exit(1)
++ elseif opt == 'h' then
++ print("Usage: ruld [-h|-p PORT]")
++ os.exit(0)
++ elseif opt == 'p' then
++ port = optarg
++ end
+ end
+
+ function eval(exp, env)
+- local f
+- local value = "return " .. exp
+- if env then
+- f = load(value, nil, nil, env)
+- else
+- f = load(value)
+- end
+- return f and f()
++ local f
++ local value = "return " .. exp
++ if env then
++ f = load(value, nil, nil, env)
++ else
++ f = load(value)
++ end
++ return f and f()
+ end
+
+ function test(env)
+- local rsp = ""
+- for index = #rules, 1, -1 do
+- local entry = rules[index]
+- if eval(entry[1], env) then
+- local delay, duration
+- if entry[3] then
+- delay = entry[3].delay
+- duration = entry[3].duration
+- if delay and delay > 0 then
+- entry[3].delay = delay - 1
+- end
+- if ((not delay) or delay == 0) and duration and duration > 0 then
+- entry[3].duration = duration - 1
+- end
+- end
+- if (((not delay) or delay == 0) and ((not duration) or duration > 0)) then
+- if #rsp > 0 then
+- rsp = rsp .. " " .. entry[2]
+- else
+- rsp = entry[2]
+- end
+- end
+- if duration and duration <= 1 then
+- table.remove(rules, index)
+- end
+- end
+- end
+- return rsp
++ local rsp = ""
++ for index = #rules, 1, -1 do
++ local entry = rules[index]
++ if eval(entry[1], env) then
++ local delay, duration
++ if entry[3] then
++ delay = entry[3].delay
++ duration = entry[3].duration
++ if delay and delay > 0 then
++ entry[3].delay = delay - 1
++ end
++ if ((not delay) or delay == 0) and duration and duration > 0 then
++ entry[3].duration = duration - 1
++ end
++ end
++ if (((not delay) or delay == 0) and ((not duration) or duration > 0)) then
++ if #rsp > 0 then
++ rsp = rsp .. " " .. entry[2]
++ else
++ rsp = entry[2]
++ end
++ end
++ if duration and duration <= 1 then
++ table.remove(rules, index)
++ end
++ end
++ end
++ return rsp
+ end
+
+ local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
+@@ -72,64 +72,64 @@ p.listen(fd, p.SOMAXCONN)
+ local running = true
+
+ while running do
+- ret_fd = p.accept(fd)
+- if ret_fd then
+- local msg = p.recv(ret_fd, p.BUFSIZ)
+- if msg then
+- local cmd, data = nil
+- sep = msg:find(" ")
+- if sep then
+- cmd = msg:sub(1, sep - 1)
+- data = msg:sub(sep + 1)
+- else
+- cmd = msg
+- end
+- if cmd == "test" then
+- local env = eval(data)
+- if env then
+- rsp = test(env)
+- p.send(ret_fd, rsp)
+- end
+- elseif cmd == "add" then
+- local value = eval(data)
+- if value then
+- table.insert(rules, value)
+- end
+- elseif cmd == "remove" then
+- if data == "tail" then
+- table.remove(rules, #rules)
+- elseif data == "head" then
+- table.remove(rules, 1)
+- else
+- for index = #rules, 1, -1 do
+- if rules[index][1]:find(data) then
+- table.remove(rules, index)
+- end
+- end
+- end
+- elseif cmd == "quit" then
+- running = false
+- elseif cmd == "list" then
+- local rsp = ""
+- for index, entry in pairs(rules) do
+- rsp = rsp .. string.format("%s => %s", entry[1], entry[2])
+- if entry[3] then
+- if entry[3].delay then
+- rsp = rsp .. string.format(" @%i", entry[3].delay)
+- end
+- if entry[3].duration then
+- rsp = rsp .. string.format(" +%i", entry[3].duration)
+- end
+- end
+- if index < #rules then
+- rsp = rsp .. "\n"
+- end
+- end
+- p.send(ret_fd, rsp)
+- end
+- end
+- p.close(ret_fd)
+- end
++ ret_fd = p.accept(fd)
++ if ret_fd then
++ local msg = p.recv(ret_fd, p.BUFSIZ)
++ if msg then
++ local cmd, data = nil
++ sep = msg:find(" ")
++ if sep then
++ cmd = msg:sub(1, sep - 1)
++ data = msg:sub(sep + 1)
++ else
++ cmd = msg
++ end
++ if cmd == "test" then
++ local env = eval(data)
++ if env then
++ rsp = test(env)
++ p.send(ret_fd, rsp)
++ end
++ elseif cmd == "add" then
++ local value = eval(data)
++ if value then
++ table.insert(rules, value)
++ end
++ elseif cmd == "remove" then
++ if data == "tail" then
++ table.remove(rules, #rules)
++ elseif data == "head" then
++ table.remove(rules, 1)
++ else
++ for index = #rules, 1, -1 do
++ if rules[index][1]:find(data) then
++ table.remove(rules, index)
++ end
++ end
++ end
++ elseif cmd == "quit" then
++ running = false
++ elseif cmd == "list" then
++ local rsp = ""
++ for index, entry in pairs(rules) do
++ rsp = rsp .. string.format("%s => %s", entry[1], entry[2])
++ if entry[3] then
++ if entry[3].delay then
++ rsp = rsp .. string.format(" @%i", entry[3].delay)
++ end
++ if entry[3].duration then
++ rsp = rsp .. string.format(" +%i", entry[3].duration)
++ end
++ end
++ if index < #rules then
++ rsp = rsp .. "\n"
++ end
++ end
++ p.send(ret_fd, rsp)
++ end
++ end
++ p.close(ret_fd)
++ end
+ end
+
+ p.close(fd)
+diff --git a/examples/loop/bspwmrc b/examples/loop/bspwmrc
+index 5238e43..7a7988f 100755
+--- a/examples/loop/bspwmrc
++++ b/examples/loop/bspwmrc
+@@ -1,6 +1,6 @@
+ #! /bin/sh
+
+ if [ -e "$BSPWM_TREE" ] ; then
+- bspc restore -T "$BSPWM_TREE" -H "$BSPWM_HISTORY" -S "$BSPWM_STACK"
+- rm "$BSPWM_TREE" "$BSPWM_HISTORY" "$BSPWM_STACK"
++ bspc restore -T "$BSPWM_TREE" -H "$BSPWM_HISTORY" -S "$BSPWM_STACK"
++ rm "$BSPWM_TREE" "$BSPWM_HISTORY" "$BSPWM_STACK"
+ fi
+diff --git a/examples/loop/sxhkdrc b/examples/loop/sxhkdrc
+index b9a461c..67feb78 100644
+--- a/examples/loop/sxhkdrc
++++ b/examples/loop/sxhkdrc
+@@ -1,5 +1,5 @@
+ super + alt + {_,shift + }Escape
+- {bspc query -T > "$BSPWM_TREE"; \
+- bspc query -H > "$BSPWM_HISTORY"; \
+- bspc quit,\
+- bspc quit 1}
++ {bspc query -T > "$BSPWM_TREE"; \
++ bspc query -H > "$BSPWM_HISTORY"; \
++ bspc quit,\
++ bspc quit 1}
+diff --git a/examples/loop/wm b/examples/loop/wm
+index 7dbf264..489893e 100755
+--- a/examples/loop/wm
++++ b/examples/loop/wm
+@@ -1,5 +1,5 @@
+ #! /bin/sh
+
+ while true ; do
+- bspwm || break
++ bspwm || break
+ done
+diff --git a/examples/overlapping_borders/bspwmrc b/examples/overlapping_borders/bspwmrc
+index 5ea828d..aa03d3f 100755
+--- a/examples/overlapping_borders/bspwmrc
++++ b/examples/overlapping_borders/bspwmrc
+@@ -4,5 +4,5 @@ BW=3
+ bspc config border_width $BW
+ bspc config window_gap -$BW
+ for side in top right bottom left ; do
+- bspc config ${side}_padding $BW
++ bspc config ${side}_padding $BW
+ done
+diff --git a/examples/panel/panel b/examples/panel/panel
+index 0e1ff56..d51e1ae 100755
+--- a/examples/panel/panel
++++ b/examples/panel/panel
+@@ -1,8 +1,8 @@
+ #! /bin/sh
+
+ if [ $(pgrep -cx panel) -gt 1 ] ; then
+- printf "%s\n" "The panel is already running." >&2
+- exit 1
++ printf "%s\n" "The panel is already running." >&2
++ exit 1
+ fi
+
+ trap 'trap - TERM; kill 0' INT TERM QUIT EXIT
+@@ -18,16 +18,18 @@ bspc control --subscribe > "$PANEL_FIFO" &
+ xtitle -sf 'T%s' > "$PANEL_FIFO" &
+ clock -sf 'S%a %H:%M' > "$PANEL_FIFO" &
+
++. panel_colors
++
+ case "$flavor" in
+- bar)
+- cat "$PANEL_FIFO" | panel_bar | bar &
+- ;;
+- dzen2)
+- . panel_colors
+- FONT_FAMILY='DejaVu Sans'
+- FONT_SIZE=11
+- cat "$PANEL_FIFO" | panel_dzen2 -f "$FONT_FAMILY" -s "$FONT_SIZE" | dzen2 -h $PANEL_HEIGHT -dock -ta l -title-name panel -fn "${FONT_FAMILY}:pixelsize=${FONT_SIZE}" -fg "$COLOR_FOREGROUND" -bg "$COLOR_BACKGROUND" &
+- ;;
++ bar)
++ FONT_FAMILY='-*-terminus-medium-r-normal-*-12-*-*-*-c-*-*-1'
++ cat "$PANEL_FIFO" | panel_bar | bar -g x$PANEL_HEIGHT -f "$FONT_FAMILY" -F "$COLOR_FOREGROUND" -B "$COLOR_BACKGROUND" &
++ ;;
++ dzen2)
++ FONT_FAMILY='DejaVu Sans'
++ FONT_SIZE=11
++ cat "$PANEL_FIFO" | panel_dzen2 -f "$FONT_FAMILY" -s "$FONT_SIZE" | dzen2 -h $PANEL_HEIGHT -dock -ta l -title-name panel -fn "${FONT_FAMILY}:pixelsize=${FONT_SIZE}" -fg "$COLOR_FOREGROUND" -bg "$COLOR_BACKGROUND" &
++ ;;
+ esac
+
+ wait
+diff --git a/examples/panel/panel_bar b/examples/panel/panel_bar
+index 7cbd2fa..70d951f 100755
+--- a/examples/panel/panel_bar
++++ b/examples/panel/panel_bar
+@@ -2,58 +2,60 @@
+ #
+ # Example panel for LemonBoy's bar
+
++. panel_colors
++
+ while read -r line ; do
+- case $line in
+- S*)
+- # clock output
+- sys_infos="\\br\\f6${line#?}"
+- ;;
+- T*)
+- # xtitle output
+- title="\\br\\f7${line#?}"
+- ;;
+- W*)
+- # bspwm internal state
+- wm_infos=""
+- IFS=':'
+- set -- ${line#?}
+- while [ $# -gt 0 ] ; do
+- item=$1
+- name=${item#?}
+- case $item in
+- O*)
+- # focused occupied desktop
+- wm_infos="$wm_infos \\u3\\br\\fr${name}\\ur"
+- ;;
+- F*)
+- # focused free desktop
+- wm_infos="$wm_infos \\u4\\br\\f7${name}\\ur"
+- ;;
+- U*)
+- # focused urgent desktop
+- wm_infos="$wm_infos \\u9\\br\\fr${name}\\ur"
+- ;;
+- o*)
+- # occupied desktop
+- wm_infos="$wm_infos \\br\\fr${name}"
+- ;;
+- f*)
+- # free desktop
+- wm_infos="$wm_infos \\br\\f7${name}"
+- ;;
+- u*)
+- # urgent desktop
+- wm_infos="$wm_infos \\b1\\fr${name}"
+- ;;
+- L*)
+- # layout
+- layout=$(printf "%s" "${name}" | sed 's/\(.\).*/\U\1/')
+- wm_infos="$wm_infos \\br\\f6$layout"
+- ;;
+- esac
+- shift
+- done
+- ;;
+- esac
+- printf "%s\n" "\\l $wm_infos \\c $title \\r $sys_infos "
++ case $line in
++ S*)
++ # clock output
++ sys_infos="%{F$COLOR_STATUS_FG}%{B$COLOR_STATUS_BG}${line#?}"
++ ;;
++ T*)
++ # xtitle output
++ title="%{F$COLOR_TITLE_FG}%{B$COLOR_TITLE_BG}${line#?}"
++ ;;
++ W*)
++ # bspwm internal state
++ wm_infos=""
++ IFS=':'
++ set -- ${line#?}
++ while [ $# -gt 0 ] ; do
++ item=$1
++ name=${item#?}
++ case $item in
++ O*)
++ # focused occupied desktop
++ wm_infos="$wm_infos %{F$COLOR_FOCUSED_OCCUPIED_FG}%{B$COLOR_FOCUSED_OCCUPIED_BG}%{U$COLOR_FOREGROUND}%{+u}${name}%{-u}"
++ ;;
++ F*)
++ # focused free desktop
++ wm_infos="$wm_infos %{F$COLOR_FOCUSED_FREE_FG}%{B$COLOR_FOCUSED_FREE_BG}%{U$COLOR_FOREGROUND}%{+u}${name}%{-u}"
++ ;;
++ U*)
++ # focused urgent desktop
++ wm_infos="$wm_infos %{F$COLOR_FOCUSED_URGENT_FG}%{B$COLOR_FOCUSED_URGENT_BG}%{U$COLOR_FOREGROUND}%{+u}${name}%{-u}"
++ ;;
++ o*)
++ # occupied desktop
++ wm_infos="$wm_infos %{F$COLOR_OCCUPIED_FG}%{B$COLOR_OCCUPIED_BG}${name}"
++ ;;
++ f*)
++ # free desktop
++ wm_infos="$wm_infos %{F$COLOR_FREE_FG}%{B$COLOR_FREE_BG}${name}"
++ ;;
++ u*)
++ # urgent desktop
++ wm_infos="$wm_infos %{F$COLOR_URGENT_FG}%{B$COLOR_URGENT_BG}${name}"
++ ;;
++ L*)
++ # layout
++ layout=$(printf "%s" "${name}" | sed 's/\(.\).*/\U\1/')
++ wm_infos="$wm_infos %{F$COLOR_LAYOUT_FG}%{B$COLOR_LAYOUT_BG}$layout"
++ ;;
++ esac
++ shift
++ done
++ ;;
++ esac
++ printf "%s\n" "%{l} $wm_infos %{c} $title %{r} $sys_infos "
+ done
+diff --git a/examples/panel/panel_dzen2 b/examples/panel/panel_dzen2
+index ac5cf94..adf1df1 100755
+--- a/examples/panel/panel_dzen2
++++ b/examples/panel/panel_dzen2
+@@ -7,109 +7,107 @@ font_size=11
+
+ . panel_colors
+
+-adaptive_centering=0
+ screen_width=$(sres -W)
+ NORMIFS=$IFS
+ FIELDIFS=':'
+ PADDING=' '
+
+-while getopts 'af:s:' opt ; do
+- case "$opt" in
+- a)
+- adaptive_centering=1
+- ;;
+- f)
+- font_family=$OPTARG
+- ;;
+- s)
+- font_size=$OPTARG
+- ;;
+- esac
++while getopts 'f:s:' opt ; do
++ case "$opt" in
++ f)
++ font_family=$OPTARG
++ ;;
++ s)
++ font_size=$OPTARG
++ ;;
++ esac
+ done
+
+ shift $((OPTIND - 1))
+
+ while read -r line ; do
+- case $line in
+- S*)
+- # clock output
+- sys_infos="^fg($COLOR_STATUS_FG)^bg($COLOR_STATUS_BG)${PADDING}${line#?}${PADDING}^fg()^bg()${PADDING}"
+- ;;
+- T*)
+- # xtitle output
+- title="^fg($COLOR_TITLE_FG)^bg($COLOR_TITLE_BG)${PADDING}${line#?}${PADDING}^fg()^bg()"
+- ;;
+- W*)
+- # bspwm internal state
+- wm_infos="$PADDING"
+- IFS=$FIELDIFS
+- set -- ${line#?}
+- while [ $# -gt 0 ] ; do
+- item=$1
+- case $item in
+- [OoFfUu]*)
+- # desktops
+- name=${item#?}
+- case $item in
+- O*)
+- # focused occupied desktop
+- FG=$COLOR_FOCUSED_OCCUPIED_FG
+- BG=$COLOR_FOCUSED_OCCUPIED_BG
+- ;;
+- F*)
+- # focused free desktop
+- FG=$COLOR_FOCUSED_FREE_FG
+- BG=$COLOR_FOCUSED_FREE_BG
+- ;;
+- U*)
+- # focused urgent desktop
+- FG=$COLOR_FOCUSED_URGENT_FG
+- BG=$COLOR_FOCUSED_URGENT_BG
+- ;;
+- o*)
+- # occupied desktop
+- FG=$COLOR_OCUPPIED_FG
+- BG=$COLOR_OCUPPIED_BG
+- ;;
+- f*)
+- # free desktop
+- FG=$COLOR_FREE_FG
+- BG=$COLOR_FREE_BG
+- ;;
+- u*)
+- # urgent desktop
+- FG=$COLOR_URGENT_FG
+- BG=$COLOR_URGENT_BG
+- ;;
+- esac
+- wm_infos="${wm_infos}^fg(${FG})^bg(${BG})^ca(1, bspc desktop -f ${name})^ca(2, bspc window -d ${name})${PADDING}${name}${PADDING}^ca()^ca()"
+- ;;
+- L*)
+- # layout
+- layout=$(printf "%s" "${item#?}" | sed 's/^\(.\).*/\U\1/')
+- wm_infos="${wm_infos}^fg()^bg()${PADDING}${PADDING}^fg($COLOR_LAYOUT_FG)^bg($COLOR_LAYOUT_BG)^ca(1, bspc desktop -l next)${PADDING}$layout${PADDING}^ca()"
+- ;;
+- esac
+- shift
+- done
+- IFS=$NORMIFS
+- ;;
+- esac
+- set -- $(printf '%s\0%s\0%s' "$wm_infos" "$title" "$sys_infos" | sed 's/\^[a-z]\+([^)]*)//g' | xargs -0 txtw -f "$font_family" -s "$font_size")
+- left_width=$1
+- center_width=$2
+- right_width=$3
+- left_indent=0
+- right_indent=$((screen_width - right_width))
+- available_center=$((screen_width - (left_width + right_width)))
+- if [ $available_center -lt $center_width ] ; then
+- center_indent=$((left_indent + left_width))
+- else
+- if [ $adaptive_centering -eq 1 ] ; then
+- center_indent=$((left_width + (available_center - center_width) / 2))
+- else
+- center_indent=$(( (screen_width - center_width) / 2 ))
+- fi
+- fi
+- printf "%s\n" "^pa($center_indent)$title^pa($left_indent)$wm_infos^pa($right_indent)$sys_infos"
++ case $line in
++ S*)
++ # clock output
++ sys_infos="^fg($COLOR_STATUS_FG)^bg($COLOR_STATUS_BG)${PADDING}${line#?}${PADDING}^fg()^bg()${PADDING}"
++ ;;
++ T*)
++ # xtitle output
++ title="^fg($COLOR_TITLE_FG)^bg($COLOR_TITLE_BG)${PADDING}${line#?}${PADDING}^fg()^bg()"
++ ;;
++ W*)
++ # bspwm internal state
++ wm_infos="$PADDING"
++ IFS=$FIELDIFS
++ set -- ${line#?}
++ while [ $# -gt 0 ] ; do
++ item=$1
++ case $item in
++ [OoFfUu]*)
++ # desktops
++ name=${item#?}
++ case $item in
++ O*)
++ # focused occupied desktop
++ FG=$COLOR_FOCUSED_OCCUPIED_FG
++ BG=$COLOR_FOCUSED_OCCUPIED_BG
++ ;;
++ F*)
++ # focused free desktop
++ FG=$COLOR_FOCUSED_FREE_FG
++ BG=$COLOR_FOCUSED_FREE_BG
++ ;;
++ U*)
++ # focused urgent desktop
++ FG=$COLOR_FOCUSED_URGENT_FG
++ BG=$COLOR_FOCUSED_URGENT_BG
++ ;;
++ o*)
++ # occupied desktop
++ FG=$COLOR_OCCUPIED_FG
++ BG=$COLOR_OCCUPIED_BG
++ ;;
++ f*)
++ # free desktop
++ FG=$COLOR_FREE_FG
++ BG=$COLOR_FREE_BG
++ ;;
++ u*)
++ # urgent desktop
++ FG=$COLOR_URGENT_FG
++ BG=$COLOR_URGENT_BG
++ ;;
++ esac
++ wm_infos="${wm_infos}^fg(${FG})^bg(${BG})^ca(1, bspc desktop -f ${name})^ca(2, bspc window -d ${name})${PADDING}${name}${PADDING}^ca()^ca()"
++ ;;
++ L*)
++ # layout
++ layout=$(printf "%s" "${item#?}" | sed 's/^\(.\).*/\U\1/')
++ wm_infos="${wm_infos}^fg()^bg()${PADDING}${PADDING}^fg($COLOR_LAYOUT_FG)^bg($COLOR_LAYOUT_BG)^ca(1, bspc desktop -l next)${PADDING}$layout${PADDING}^ca()"
++ ;;
++ esac
++ shift
++ done
++ IFS=$NORMIFS
++ ;;
++ esac
++ set -- $(printf '%s\0%s\0%s' "$wm_infos" "$title" "$sys_infos" | sed 's/\^[a-z]\+([^)]*)//g' | xargs -0 txtw -f "$font_family" -s "$font_size")
++ left_width=$1
++ center_width=$2
++ right_width=$3
++ left_indent=0
++ right_indent=$((screen_width - right_width))
++ available_center=$((screen_width - (left_width + right_width)))
++ if [ $available_center -lt $center_width ] ; then
++ center_indent=$left_width
++ else
++ max_left_right_width=$left_width
++ [ $left_width -lt $right_width ] && max_left_right_width=$right_width
++ if [ $((2 * max_left_right_width + center_width)) -gt $screen_width ] ; then
++ center_indent=$((left_width + (available_center - center_width) / 2))
++ else
++ center_indent=$(((screen_width - center_width) / 2))
++ fi
++ fi
++ printf "%s\n" "^pa($center_indent)$title^pa($left_indent)$wm_infos^pa($right_indent)$sys_infos"
+ done
+diff --git a/examples/panel/sxhkdrc b/examples/panel/sxhkdrc
+index 99cda75..b19cd77 100644
+--- a/examples/panel/sxhkdrc
++++ b/examples/panel/sxhkdrc
+@@ -1,2 +1,2 @@
+ super + alt + Escape
+- pkill -x panel; bspc quit
++ pkill -x panel; bspc quit
+diff --git a/examples/sxhkdrc b/examples/sxhkdrc
+index 5497bf7..61eb96e 100644
+--- a/examples/sxhkdrc
++++ b/examples/sxhkdrc
+@@ -3,89 +3,89 @@
+ #
+
+ super + alt + Escape
+- bspc quit
++ bspc quit
+
+ super + w
+- bspc window -c
++ bspc window -c
+
+ super + t
+- bspc desktop -l next
++ bspc desktop -l next
+
+ super + b
+- bspc desktop -B
++ bspc desktop -B
+
+ super + {s,f}
+- bspc window -t {floating,fullscreen}
++ bspc window -t {floating,fullscreen}
+
+ super + {grave,Tab}
+- bspc {window,desktop} -f last
++ bspc {window,desktop} -f last
+
+ super + apostrophe
+- bspc window -s last
++ bspc window -s last
+
+ super + {o,i}
+- bspc control --record-history off; \
+- bspc window {older,newer} -f; \
+- bspc control --record-history on
++ bspc control --record-history off; \
++ bspc window {older,newer} -f; \
++ bspc control --record-history on
+
+ super + y
+- bspc window -w last.manual
++ bspc window -w last.manual
+
+ super + m
+- bspc window -s biggest
++ bspc window -s biggest
+
+ super + {_,shift + }{h,j,k,l}
+- bspc window -{f,s} {left,down,up,right}
++ bspc window -{f,s} {left,down,up,right}
+
+ super + {_,shift + }c
+- bspc window -f {next,prev}
++ bspc window -f {next,prev}
+
+ super + {comma,period}
+- bspc desktop -C {backward,forward}
++ bspc desktop -C {backward,forward}
+
+ super + bracket{left,right}
+- bspc desktop -f {prev,next}
++ bspc desktop -f {prev,next}
+
+ super + ctrl + {h,j,k,l}
+- bspc window -p {left,down,up,right}
++ bspc window -p {left,down,up,right}
+
+ super + ctrl + {_,shift + }space
+- bspc {window -p cancel,desktop -c}
++ bspc {window -p cancel,desktop -c}
+
+ super + alt + {h,j,k,l}
+- bspc window -e {left -10,down +10,up -10,right +10}
++ bspc window -e {left -10,down +10,up -10,right +10}
+
+ super + alt + shift + {h,j,k,l}
+- bspc window -e {right -10,up +10,down -10,left +10}
++ bspc window -e {right -10,up +10,down -10,left +10}
+
+ super + ctrl + {1-9}
+- bspc window -r 0.{1-9}
++ bspc window -r 0.{1-9}
+
+ super + {_,shift + }{1-9,0}
+- bspc {desktop -f,window -d} ^{1-9,10}
++ bspc {desktop -f,window -d} ^{1-9,10}
+
+ ~button1
+- bspc pointer -g focus
++ bspc pointer -g focus
+
+ super + button{1-3}
+- bspc pointer -g {move,resize_side,resize_corner}
++ bspc pointer -g {move,resize_side,resize_corner}
+
+ super + !button{1-3}
+- bspc pointer -t %i %i
++ bspc pointer -t %i %i
+
+ super + @button{1-3}
+- bspc pointer -u
++ bspc pointer -u
+
+ #
+ # wm independent hotkeys
+ #
+
+ super + Return
+- urxvt
++ urxvt
+
+ super + space
+- dmenu_run
++ dmenu_run
+
+ # make sxhkd reload its configuration files:
+ super + Escape
+- pkill -USR1 -x sxhkd
++ pkill -USR1 -x sxhkd
+diff --git a/helpers.c b/helpers.c
+index ccc197a..2e2f23f 100644
+--- a/helpers.c
++++ b/helpers.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -29,63 +33,57 @@
+
+ void warn(char *fmt, ...)
+ {
+- va_list ap;
+- va_start(ap, fmt);
+- vfprintf(stderr, fmt, ap);
+- va_end(ap);
++ 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);
++ va_list ap;
++ va_start(ap, fmt);
++ vfprintf(stderr, fmt, ap);
++ va_end(ap);
++ exit(EXIT_FAILURE);
+ }
+
+ bool get_color(char *col, xcb_window_t win, uint32_t *pxl)
+ {
+- xcb_colormap_t map = screen->default_colormap;
+- xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
+- if (reply != NULL)
+- map = reply->colormap;
+- free(reply);
++ xcb_colormap_t map = screen->default_colormap;
++ xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
++ if (reply != NULL)
++ map = reply->colormap;
++ free(reply);
+
+- if (col[0] == '#') {
+- unsigned int red, green, blue;
+- if (sscanf(col + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
+- /* 2**16 - 1 == 0xffff and 0x101 * 0xij == 0xijij */
+- red *= 0x101;
+- green *= 0x101;
+- blue *= 0x101;
+- xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(dpy, xcb_alloc_color(dpy, map, red, green, blue), NULL);
+- if (reply != NULL) {
+- *pxl = reply->pixel;
+- free(reply);
+- return true;
+- }
+- }
+- } else {
+- xcb_alloc_named_color_reply_t *reply = xcb_alloc_named_color_reply(dpy, xcb_alloc_named_color(dpy, map, strlen(col), col), NULL);
+- if (reply != NULL) {
+- *pxl = reply->pixel;
+- free(reply);
+- return true;
+- }
+- }
+- *pxl = 0;
+- return false;
++ if (col[0] == '#') {
++ unsigned int red, green, blue;
++ if (sscanf(col + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
++ /* 2**16 - 1 == 0xffff and 0x101 * 0xij == 0xijij */
++ red *= 0x101;
++ green *= 0x101;
++ blue *= 0x101;
++ xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(dpy, xcb_alloc_color(dpy, map, red, green, blue), NULL);
++ if (reply != NULL) {
++ *pxl = reply->pixel;
++ free(reply);
++ return true;
++ }
++ }
++ } else {
++ xcb_alloc_named_color_reply_t *reply = xcb_alloc_named_color_reply(dpy, xcb_alloc_named_color(dpy, map, strlen(col), col), NULL);
++ if (reply != NULL) {
++ *pxl = reply->pixel;
++ free(reply);
++ return true;
++ }
++ }
++ *pxl = 0;
++ return false;
+ }
+
+ double distance(xcb_point_t a, xcb_point_t b)
+ {
+- return hypot(a.x - b.x, a.y - b.y);
+-}
+-
+-void center_rectangle(xcb_rectangle_t *src, xcb_rectangle_t dst)
+-{
+- src->x = dst.x + (dst.width - src->width) / 2;
+- src->y = dst.y + (dst.height - src->height) / 2;
++ return hypot(a.x - b.x, a.y - b.y);
+ }
+diff --git a/helpers.h b/helpers.h
+index 81821ad..9a88676 100644
+--- a/helpers.h
++++ b/helpers.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_HELPERS_H
+@@ -44,7 +48,6 @@
+ #define SMALEN 32
+ #define INIT_CAP 8
+
+-#define REMLEN(x) (BUFSIZ - strlen(x) - 1)
+ #define streq(s1, s2) (strcmp((s1), (s2)) == 0)
+
+ #ifdef DEBUG
+@@ -59,6 +62,5 @@ void warn(char *fmt, ...);
+ void err(char *fmt, ...);
+ bool get_color(char *col, xcb_window_t win, uint32_t *pxl);
+ double distance(xcb_point_t a, xcb_point_t b);
+-void center_rectangle(xcb_rectangle_t *src, xcb_rectangle_t dst);
+
+ #endif
+diff --git a/history.c b/history.c
+index 4f6882c..7440a08 100644
+--- a/history.c
++++ b/history.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -28,212 +32,212 @@
+
+ history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
+ {
+- history_t *h = malloc(sizeof(history_t));
+- h->loc = (coordinates_t) {m, d, n};
+- h->prev = h->next = NULL;
+- h->latest = true;
+- return h;
++ history_t *h = malloc(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);
+- }
++ 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 (h->loc.node == n) {
+- h->loc.monitor = m;
+- h->loc.desktop = d;
+- }
++ for (history_t *h = history_head; h != NULL; h = h->next)
++ if (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;
++ 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 (h->loc.node == n1) {
+- h->loc.monitor = m2;
+- h->loc.desktop = d2;
+- } else if (h->loc.node == n2) {
+- h->loc.monitor = m1;
+- h->loc.desktop = d1;
+- }
++ for (history_t *h = history_head; h != NULL; h = h->next)
++ if (h->loc.node == n1) {
++ h->loc.monitor = m2;
++ h->loc.desktop = d2;
++ } else if (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;
++ 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)
+ {
+ /* 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 && n == b->loc.node) || (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 *d = c->prev;
+- if (history_head == c)
+- history_head = history_tail;
+- if (history_needle == c)
+- history_needle = history_tail;
+- free(c);
+- c = d;
+- }
+- 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;
+- }
+- }
++ history_t *b = history_tail;
++ while (b != NULL) {
++ if ((n != NULL && n == b->loc.node) || (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 *d = c->prev;
++ if (history_head == c)
++ history_head = history_tail;
++ if (history_needle == c)
++ history_needle = history_tail;
++ free(c);
++ c = d;
++ }
++ 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;
++ 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_get_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 != n && h->loc.desktop == d)
+- return h->loc.node;
+- return NULL;
++ for (history_t *h = history_tail; h != NULL; h = h->prev)
++ if (h->latest && h->loc.node != NULL && h->loc.node != n && h->loc.desktop == d)
++ return h->loc.node;
++ return NULL;
+ }
+
+ desktop_t *history_get_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;
++ 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_get_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;
++ 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, client_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
+- || !node_matches(&h->loc, ref, sel))
+- continue;
+- if (!record_history)
+- history_needle = h;
+- *dst = h->loc;
+- return true;
+- }
+- return false;
++ 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 ||
++ !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;
++ 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, 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.monitor == ref->monitor
+- || !desktop_matches(&h->loc, ref, sel))
+- continue;
+- if (!record_history)
+- history_needle = h;
+- *dst = h->loc;
+- return true;
+- }
+- return false;
++ 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 ||
++ !desktop_matches(&h->loc, ref, sel))
++ continue;
++ if (!record_history)
++ history_needle = h;
++ *dst = h->loc;
++ return true;
++ }
++ return false;
+ }
+
+ int history_rank(desktop_t *d, node_t *n)
+ {
+- int i = 0;
+- history_t *h = history_tail;
+- while (h != NULL && (!h->latest || h->loc.node != n || h->loc.desktop != d)) {
+- h = h->prev;
+- i++;
+- }
+- if (h == NULL)
+- return -1;
+- else
+- return i;
++ int i = 0;
++ history_t *h = history_tail;
++ while (h != NULL && (!h->latest || h->loc.node != n || h->loc.desktop != d)) {
++ h = h->prev;
++ i++;
++ }
++ if (h == NULL)
++ return -1;
++ else
++ return i;
+ }
+diff --git a/history.h b/history.h
+index b57b94f..7b55516 100644
+--- a/history.h
++++ b/history.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_HISTORY_H
+diff --git a/messages.c b/messages.c
+index 2eb5cd8..ff48673 100644
+--- a/messages.c
++++ b/messages.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <errno.h>
+@@ -38,1169 +42,1202 @@
+ #include "settings.h"
+ #include "tree.h"
+ #include "window.h"
++#include "common.h"
++#include "subscribe.h"
+ #include "messages.h"
+
+-bool handle_message(char *msg, int msg_len, char *rsp)
++int handle_message(char *msg, int msg_len, FILE *rsp)
+ {
+- int cap = INIT_CAP;
+- int num = 0;
+- char **args = malloc(cap * sizeof(char *));
+- if (args == NULL)
+- return false;
++ int cap = INIT_CAP;
++ int num = 0;
++ char **args = malloc(cap * sizeof(char *));
++ if (args == NULL)
++ return MSG_FAILURE;
+
+- 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);
+- return false;
+- } else {
+- args = new;
+- }
+- }
+- }
++ 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);
++ return MSG_FAILURE;
++ } else {
++ args = new;
++ }
++ }
++ }
+
+- if (num < 1) {
+- free(args);
+- return false;
+- }
++ if (num < 1) {
++ free(args);
++ return MSG_SYNTAX;
++ }
+
+- char **args_orig = args;
+- bool ret = process_message(args, num, rsp);
+- free(args_orig);
+- return ret;
++ char **args_orig = args;
++ int ret = process_message(args, num, rsp);
++ free(args_orig);
++ return ret;
+ }
+
+-bool process_message(char **args, int num, char *rsp)
++int process_message(char **args, int num, FILE *rsp)
+ {
+- if (streq("window", *args)) {
+- return cmd_window(++args, --num);
+- } else if (streq("desktop", *args)) {
+- return cmd_desktop(++args, --num);
+- } else if (streq("monitor", *args)) {
+- return cmd_monitor(++args, --num);
+- } else if (streq("query", *args)) {
+- return cmd_query(++args, --num, rsp);
+- } else if (streq("restore", *args)) {
+- return cmd_restore(++args, --num);
+- } else if (streq("control", *args)) {
+- return cmd_control(++args, --num, rsp);
+- } else if (streq("rule", *args)) {
+- return cmd_rule(++args, --num, rsp);
+- } else if (streq("pointer", *args)) {
+- return cmd_pointer(++args, --num);
+- } else if (streq("config", *args)) {
+- return cmd_config(++args, --num, rsp);
+- } else if (streq("quit", *args)) {
+- return cmd_quit(++args, --num);
+- }
++ if (streq("window", *args)) {
++ return cmd_window(++args, --num);
++ } else if (streq("desktop", *args)) {
++ return cmd_desktop(++args, --num);
++ } else if (streq("monitor", *args)) {
++ return cmd_monitor(++args, --num);
++ } else if (streq("query", *args)) {
++ return cmd_query(++args, --num, rsp);
++ } else if (streq("restore", *args)) {
++ return cmd_restore(++args, --num);
++ } else if (streq("control", *args)) {
++ return cmd_control(++args, --num, rsp);
++ } else if (streq("rule", *args)) {
++ return cmd_rule(++args, --num, rsp);
++ } else if (streq("pointer", *args)) {
++ return cmd_pointer(++args, --num);
++ } else if (streq("config", *args)) {
++ return cmd_config(++args, --num, rsp);
++ } else if (streq("quit", *args)) {
++ return cmd_quit(++args, --num);
++ }
+
+- return false;
++ return MSG_UNKNOWN;
+ }
+
+-bool cmd_window(char **args, int num)
++int cmd_window(char **args, int num)
+ {
+- if (num < 1)
+- return false;
++ if (num < 1)
++ return MSG_SYNTAX;
+
+- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+- coordinates_t trg = ref;
++ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
++ coordinates_t trg = ref;
+
+- if ((*args)[0] != OPT_CHR) {
+- if (node_from_desc(*args, &ref, &trg))
+- num--, args++;
+- else
+- return false;
+- }
++ if ((*args)[0] != OPT_CHR) {
++ if (node_from_desc(*args, &ref, &trg))
++ num--, args++;
++ else
++ return MSG_FAILURE;
++ }
+
+- if (trg.node == NULL)
+- return false;
++ if (trg.node == NULL)
++ return MSG_FAILURE;
+
+- bool dirty = false;
++ bool dirty = 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++;
+- if (!node_from_desc(*args, &trg, &dst))
+- return false;
+- }
+- focus_node(dst.monitor, dst.desktop, dst.node);
+- } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
+- num--, args++;
+- coordinates_t dst;
+- if (desktop_from_desc(*args, &trg, &dst)) {
+- 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 {
+- return false;
+- }
+- } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- coordinates_t dst;
+- if (monitor_from_desc(*args, &trg, &dst)) {
+- 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 {
+- return false;
+- }
+- } else if (streq("-w", *args) || streq("--to-window", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- coordinates_t dst;
+- if (node_from_desc(*args, &trg, &dst)) {
+- if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
+- trg.monitor = dst.monitor;
+- trg.desktop = dst.desktop;
+- }
+- } else {
+- return false;
+- }
+- } else if (streq("-s", *args) || streq("--swap", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- coordinates_t dst;
+- if (node_from_desc(*args, &trg, &dst)) {
+- if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
+- if (trg.desktop != dst.desktop)
+- arrange(trg.monitor, trg.desktop);
+- trg.monitor = dst.monitor;
+- trg.desktop = dst.desktop;
+- dirty = true;
+- }
+- } else {
+- return false;
+- }
+- } else if (streq("-t", *args) || streq("--toggle", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- 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
+- return false;
+- }
+- if (streq("fullscreen", key)) {
+- set_fullscreen(trg.node, (a == ALTER_SET ? b : !trg.node->client->fullscreen));
+- dirty = true;
+- } else if (streq("pseudo_tiled", key)) {
+- set_pseudo_tiled(trg.node, (a == ALTER_SET ? b : !trg.node->client->pseudo_tiled));
+- dirty = true;
+- } else if (streq("floating", key)) {
+- set_floating(trg.node, (a == ALTER_SET ? b : !trg.node->client->floating));
+- dirty = true;
+- } else if (streq("locked", key)) {
+- set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->locked));
+- } else if (streq("sticky", key)) {
+- set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->sticky));
+- } else if (streq("private", key)) {
+- set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->private));
+- } else {
+- return false;
+- }
+- } else if (streq("-p", *args) || streq("--presel", *args)) {
+- num--, args++;
+- if (num < 1 || !is_tiled(trg.node->client)
+- || trg.desktop->layout != LAYOUT_TILED)
+- return false;
+- if (streq("cancel", *args)) {
+- reset_mode(&trg);
+- } else {
+- direction_t dir;
+- if (parse_direction(*args, &dir)) {
+- double rat = trg.node->split_ratio;
+- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+- num--, args++;
+- if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1)
+- return false;
+- }
+- if (auto_cancel && trg.node->split_mode == MODE_MANUAL
+- && dir == trg.node->split_dir
+- && rat == trg.node->split_ratio) {
+- reset_mode(&trg);
+- } else {
+- trg.node->split_mode = MODE_MANUAL;
+- trg.node->split_dir = dir;
+- trg.node->split_ratio = rat;
+- }
+- window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
+- } else {
+- return false;
+- }
+- }
+- } else if (streq("-e", *args) || streq("--edge", *args)) {
+- num--, args++;
+- if (num < 2)
+- return false;
+- direction_t dir;
+- if (!parse_direction(*args, &dir))
+- return false;
+- node_t *n = find_fence(trg.node, dir);
+- if (n == NULL)
+- return false;
+- num--, args++;
+- if ((*args)[0] == '+' || (*args)[0] == '-') {
+- int pix;
+- if (sscanf(*args, "%i", &pix) == 1) {
+- int max = (n->split_type == TYPE_HORIZONTAL ? n->rectangle.height : n->rectangle.width);
+- double rat = ((max * n->split_ratio) + pix) / max;
+- if (rat > 0 && rat < 1)
+- n->split_ratio = rat;
+- else
+- return false;
+- } else {
+- return false;
+- }
+- } else {
+- double rat;
+- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1)
+- n->split_ratio = rat;
+- else
+- return false;
+- }
+- dirty = true;
+- } else if (streq("-r", *args) || streq("--ratio", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- double rat;
+- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+- trg.node->split_ratio = rat;
+- window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
+- } else {
+- return false;
+- }
+- } else if (streq("-R", *args) || streq("--rotate", *args)) {
+- num--, args++;
+- if (num < 2)
+- return false;
+- direction_t dir;
+- if (!parse_direction(*args, &dir))
+- return false;
+- node_t *n = find_fence(trg.node, dir);
+- if (n == NULL)
+- return false;
+- num--, args++;
+- int deg;
+- if (parse_degree(*args, &deg)) {
+- rotate_tree(n, deg);
+- dirty = true;
+- } else {
+- return false;
+- }
+- } else if (streq("-c", *args) || streq("--close", *args)) {
+- if (num > 1)
+- return false;
+- window_close(trg.node);
+- } else if (streq("-k", *args) || streq("--kill", *args)) {
+- if (num > 1)
+- return false;
+- window_kill(trg.monitor, trg.desktop, trg.node);
+- dirty = true;
+- } else {
+- return 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++;
++ if (!node_from_desc(*args, &trg, &dst))
++ return MSG_FAILURE;
++ }
++ focus_node(dst.monitor, dst.desktop, dst.node);
++ } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
++ num--, args++;
++ coordinates_t dst;
++ if (desktop_from_desc(*args, &trg, &dst)) {
++ 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 {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ coordinates_t dst;
++ if (monitor_from_desc(*args, &trg, &dst)) {
++ 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 {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-w", *args) || streq("--to-window", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ coordinates_t dst;
++ if (node_from_desc(*args, &trg, &dst)) {
++ if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
++ trg.monitor = dst.monitor;
++ trg.desktop = dst.desktop;
++ }
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-s", *args) || streq("--swap", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ coordinates_t dst;
++ if (node_from_desc(*args, &trg, &dst)) {
++ if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
++ if (trg.desktop != dst.desktop)
++ arrange(trg.monitor, trg.desktop);
++ trg.monitor = dst.monitor;
++ trg.desktop = dst.desktop;
++ dirty = true;
++ }
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-t", *args) || streq("--toggle", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ 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
++ return MSG_FAILURE;
++ }
++ if (streq("fullscreen", key)) {
++ set_fullscreen(trg.node, (a == ALTER_SET ? b : !trg.node->client->fullscreen));
++ dirty = true;
++ } else if (streq("pseudo_tiled", key)) {
++ set_pseudo_tiled(trg.node, (a == ALTER_SET ? b : !trg.node->client->pseudo_tiled));
++ dirty = true;
++ } else if (streq("floating", key)) {
++ set_floating(trg.node, (a == ALTER_SET ? b : !trg.node->client->floating));
++ dirty = true;
++ } else if (streq("locked", key)) {
++ set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->locked));
++ } else if (streq("sticky", key)) {
++ set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->sticky));
++ } else if (streq("private", key)) {
++ set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->client->private));
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-p", *args) || streq("--presel", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ if (!is_tiled(trg.node->client) ||
++ trg.desktop->layout != LAYOUT_TILED)
++ return MSG_FAILURE;
++ if (streq("cancel", *args)) {
++ reset_mode(&trg);
++ } else {
++ direction_t dir;
++ if (parse_direction(*args, &dir)) {
++ double rat = trg.node->split_ratio;
++ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
++ num--, args++;
++ if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1)
++ return MSG_FAILURE;
++ }
++ if (auto_cancel && trg.node->split_mode == MODE_MANUAL &&
++ dir == trg.node->split_dir &&
++ rat == trg.node->split_ratio) {
++ reset_mode(&trg);
++ } else {
++ trg.node->split_mode = MODE_MANUAL;
++ trg.node->split_dir = dir;
++ trg.node->split_ratio = rat;
++ }
++ window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
++ } else {
++ return MSG_FAILURE;
++ }
++ }
++ } else if (streq("-e", *args) || streq("--edge", *args)) {
++ num--, args++;
++ if (num < 2)
++ return MSG_SYNTAX;
++ direction_t dir;
++ if (!parse_direction(*args, &dir))
++ return MSG_FAILURE;
++ node_t *n = find_fence(trg.node, dir);
++ if (n == NULL)
++ return MSG_FAILURE;
++ num--, args++;
++ if ((*args)[0] == '+' || (*args)[0] == '-') {
++ int pix;
++ if (sscanf(*args, "%i", &pix) == 1) {
++ int max = (n->split_type == TYPE_HORIZONTAL ? n->rectangle.height : n->rectangle.width);
++ double rat = ((max * n->split_ratio) + pix) / max;
++ if (rat > 0 && rat < 1)
++ n->split_ratio = rat;
++ else
++ return MSG_FAILURE;
++ } else {
++ return MSG_FAILURE;
++ }
++ } else {
++ double rat;
++ if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1)
++ n->split_ratio = rat;
++ else
++ return MSG_FAILURE;
++ }
++ dirty = true;
++ } else if (streq("-r", *args) || streq("--ratio", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ double rat;
++ if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
++ trg.node->split_ratio = rat;
++ window_draw_border(trg.node, trg.desktop->focus == trg.node, mon == trg.monitor);
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-R", *args) || streq("--rotate", *args)) {
++ num--, args++;
++ if (num < 2)
++ return MSG_SYNTAX;
++ direction_t dir;
++ if (!parse_direction(*args, &dir))
++ return MSG_FAILURE;
++ node_t *n = find_fence(trg.node, dir);
++ if (n == NULL)
++ return MSG_FAILURE;
++ num--, args++;
++ int deg;
++ if (parse_degree(*args, &deg)) {
++ rotate_tree(n, deg);
++ dirty = true;
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-c", *args) || streq("--close", *args)) {
++ if (num > 1)
++ return MSG_SYNTAX;
++ window_close(trg.node);
++ } else if (streq("-k", *args) || streq("--kill", *args)) {
++ if (num > 1)
++ return MSG_SYNTAX;
++ window_kill(trg.monitor, trg.desktop, trg.node);
++ dirty = true;
++ } else {
++ return MSG_SYNTAX;
++ }
+
+- num--, args++;
+- }
++ num--, args++;
++ }
+
+- if (dirty)
+- arrange(trg.monitor, trg.desktop);
++ if (dirty)
++ arrange(trg.monitor, trg.desktop);
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_desktop(char **args, int num)
++int cmd_desktop(char **args, int num)
+ {
+- if (num < 1)
+- return false;
++ if (num < 1)
++ return MSG_SYNTAX;
+
+- coordinates_t ref = {mon, mon->desk, NULL};
+- coordinates_t trg = ref;
++ coordinates_t ref = {mon, mon->desk, NULL};
++ coordinates_t trg = ref;
+
+- if ((*args)[0] != OPT_CHR) {
+- if (desktop_from_desc(*args, &ref, &trg))
+- num--, args++;
+- else
+- return false;
+- }
++ if ((*args)[0] != OPT_CHR) {
++ if (desktop_from_desc(*args, &ref, &trg))
++ num--, args++;
++ else
++ return MSG_FAILURE;
++ }
+
+- bool dirty = false;
++ bool dirty = 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++;
+- if (!desktop_from_desc(*args, &trg, &dst))
+- return false;
+- }
+- if (auto_alternate && dst.desktop == mon->desk) {
+- desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
+- history_find_desktop(HISTORY_OLDER, &trg, &dst, sel);
+- }
+- focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
+- } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+- num--, args++;
+- if (num < 1 || trg.monitor->desk_head == trg.monitor->desk_tail)
+- return false;
+- coordinates_t dst;
+- if (monitor_from_desc(*args, &trg, &dst)) {
+- transfer_desktop(trg.monitor, dst.monitor, trg.desktop);
+- trg.monitor = dst.monitor;
+- update_current();
+- } else {
+- return false;
+- }
+- } else if (streq("-s", *args) || streq("--swap", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- coordinates_t dst;
+- if (desktop_from_desc(*args, &trg, &dst))
+- swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop);
+- else
+- return false;
+- } else if (streq("-l", *args) || streq("--layout", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- layout_t lyt;
+- cycle_dir_t cyc;
+- if (parse_cycle_direction(*args, &cyc))
+- change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
+- else if (parse_layout(*args, &lyt))
+- change_layout(trg.monitor, trg.desktop, lyt);
+- else
+- return false;
+- } else if (streq("-n", *args) || streq("--rename", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- snprintf(trg.desktop->name, sizeof(trg.desktop->name), "%s", *args);
+- ewmh_update_desktop_names();
+- put_status();
+- } else if (streq("-r", *args) || streq("--remove", *args)) {
+- if (trg.desktop->root == NULL
+- && trg.monitor->desk_head != trg.monitor->desk_tail) {
+- remove_desktop(trg.monitor, trg.desktop);
+- show_desktop(trg.monitor->desk);
+- update_current();
+- return true;
+- } else {
+- return false;
+- }
+- } else if (streq("-c", *args) || streq("--cancel-presel", *args)) {
+- reset_mode(&trg);
+- } else if (streq("-F", *args) || streq("--flip", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- flip_t flp;
+- if (parse_flip(*args, &flp)) {
+- flip_tree(trg.desktop->root, flp);
+- dirty = true;
+- } else {
+- return false;
+- }
+- } else if (streq("-R", *args) || streq("--rotate", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- int deg;
+- if (parse_degree(*args, &deg)) {
+- rotate_tree(trg.desktop->root, deg);
+- dirty = true;
+- } else {
+- return false;
+- }
+- } else if (streq("-B", *args) || streq("--balance", *args)) {
+- balance_tree(trg.desktop->root);
+- dirty = true;
+- } else if (streq("-C", *args) || streq("--circulate", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- circulate_dir_t cir;
+- if (parse_circulate_direction(*args, &cir)) {
+- circulate_leaves(trg.monitor, trg.desktop, cir);
+- dirty = true;
+- } else {
+- return false;
+- }
+- } else if (streq("-t", *args) || streq("--toggle", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- 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
+- return false;
+- }
+- if (streq("floating", key))
+- trg.desktop->floating = (a == ALTER_SET ? b : !trg.desktop->floating);
+- else
+- return false;
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ while (num > 0) {
++ if (streq("-f", *args) || streq("--focus", *args)) {
++ coordinates_t dst = trg;
++ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
++ num--, args++;
++ if (!desktop_from_desc(*args, &trg, &dst))
++ return MSG_FAILURE;
++ }
++ if (auto_alternate && dst.desktop == mon->desk) {
++ desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
++ history_find_desktop(HISTORY_OLDER, &trg, &dst, sel);
++ }
++ focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
++ } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ if (trg.monitor->desk_head == trg.monitor->desk_tail)
++ return MSG_FAILURE;
++ coordinates_t dst;
++ if (monitor_from_desc(*args, &trg, &dst)) {
++ transfer_desktop(trg.monitor, dst.monitor, trg.desktop);
++ trg.monitor = dst.monitor;
++ update_current();
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-s", *args) || streq("--swap", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ coordinates_t dst;
++ if (desktop_from_desc(*args, &trg, &dst))
++ swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop);
++ else
++ return MSG_FAILURE;
++ } else if (streq("-l", *args) || streq("--layout", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ layout_t lyt;
++ cycle_dir_t cyc;
++ if (parse_cycle_direction(*args, &cyc))
++ change_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
++ else if (parse_layout(*args, &lyt))
++ change_layout(trg.monitor, trg.desktop, lyt);
++ else
++ return MSG_FAILURE;
++ } else if (streq("-n", *args) || streq("--rename", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ snprintf(trg.desktop->name, sizeof(trg.desktop->name), "%s", *args);
++ ewmh_update_desktop_names();
++ put_status();
++ } else if (streq("-r", *args) || streq("--remove", *args)) {
++ if (trg.desktop->root == NULL &&
++ trg.monitor->desk_head != trg.monitor->desk_tail) {
++ remove_desktop(trg.monitor, trg.desktop);
++ show_desktop(trg.monitor->desk);
++ update_current();
++ return MSG_SUCCESS;
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-c", *args) || streq("--cancel-presel", *args)) {
++ reset_mode(&trg);
++ } else if (streq("-F", *args) || streq("--flip", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ flip_t flp;
++ if (parse_flip(*args, &flp)) {
++ flip_tree(trg.desktop->root, flp);
++ dirty = true;
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-R", *args) || streq("--rotate", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ int deg;
++ if (parse_degree(*args, &deg)) {
++ rotate_tree(trg.desktop->root, deg);
++ dirty = true;
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-E", *args) || streq("--equalize", *args)) {
++ equalize_tree(trg.desktop->root);
++ dirty = true;
++ } else if (streq("-B", *args) || streq("--balance", *args)) {
++ balance_tree(trg.desktop->root);
++ dirty = true;
++ } else if (streq("-C", *args) || streq("--circulate", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ circulate_dir_t cir;
++ if (parse_circulate_direction(*args, &cir)) {
++ circulate_leaves(trg.monitor, trg.desktop, cir);
++ dirty = true;
++ } else {
++ return MSG_FAILURE;
++ }
++ } else if (streq("-t", *args) || streq("--toggle", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ 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
++ return MSG_FAILURE;
++ }
++ if (streq("floating", key))
++ trg.desktop->floating = (a == ALTER_SET ? b : !trg.desktop->floating);
++ else
++ return MSG_FAILURE;
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- if (dirty)
+- arrange(trg.monitor, trg.desktop);
++ if (dirty)
++ arrange(trg.monitor, trg.desktop);
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_monitor(char **args, int num)
++int cmd_monitor(char **args, int num)
+ {
+- if (num < 1)
+- return false;
++ if (num < 1)
++ return MSG_SYNTAX;
+
+- coordinates_t ref = {mon, NULL, NULL};
+- coordinates_t trg = ref;
++ coordinates_t ref = {mon, NULL, NULL};
++ coordinates_t trg = ref;
+
+- if ((*args)[0] != OPT_CHR) {
+- if (monitor_from_desc(*args, &ref, &trg))
+- num--, args++;
+- else
+- return false;
+- }
++ if ((*args)[0] != OPT_CHR) {
++ if (monitor_from_desc(*args, &ref, &trg))
++ num--, args++;
++ else
++ return MSG_FAILURE;
++ }
+
+- while (num > 0) {
+- if (streq("-f", *args) || streq("--focus", *args)) {
+- coordinates_t dst = trg;
+- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+- num--, args++;
+- if (!monitor_from_desc(*args, &trg, &dst))
+- return false;
+- }
+- if (auto_alternate && dst.monitor == mon) {
+- desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
+- history_find_monitor(HISTORY_OLDER, &trg, &dst, sel);
+- }
+- focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
+- } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- desktop_t *d = trg.monitor->desk_head;
+- while (num > 0 && d != NULL) {
+- snprintf(d->name, sizeof(d->name), "%s", *args);
+- d = d->next;
+- num--, args++;
+- }
+- put_status();
+- while (num > 0) {
+- add_desktop(trg.monitor, make_desktop(*args));
+- 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)
+- return false;
+- while (num > 0) {
+- add_desktop(trg.monitor, make_desktop(*args));
+- num--, args++;
+- }
+- } else if (streq("-r", *args) || streq("--remove-desktops", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- while (num > 0) {
+- coordinates_t dst;
+- if (locate_desktop(*args, &dst) && dst.monitor->desk_head != dst.monitor->desk_tail && dst.desktop->root == NULL) {
+- remove_desktop(dst.monitor, dst.desktop);
+- show_desktop(dst.monitor->desk);
+- }
+- num--, args++;
+- }
+- } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- 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("-n", *args) || streq("--rename", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- snprintf(trg.monitor->name, sizeof(trg.monitor->name), "%s", *args);
+- put_status();
+- } else if (streq("-s", *args) || streq("--swap", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- coordinates_t dst;
+- if (monitor_from_desc(*args, &trg, &dst))
+- swap_monitors(trg.monitor, dst.monitor);
+- else
+- return false;
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ while (num > 0) {
++ if (streq("-f", *args) || streq("--focus", *args)) {
++ coordinates_t dst = trg;
++ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
++ num--, args++;
++ if (!monitor_from_desc(*args, &trg, &dst))
++ return MSG_FAILURE;
++ }
++ if (auto_alternate && dst.monitor == mon) {
++ desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
++ history_find_monitor(HISTORY_OLDER, &trg, &dst, sel);
++ }
++ focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
++ } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ desktop_t *d = trg.monitor->desk_head;
++ while (num > 0 && d != NULL) {
++ snprintf(d->name, sizeof(d->name), "%s", *args);
++ initialize_desktop(d);
++ arrange(trg.monitor, d);
++ d = d->next;
++ num--, args++;
++ }
++ put_status();
++ while (num > 0) {
++ add_desktop(trg.monitor, make_desktop(*args));
++ 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)
++ return MSG_SYNTAX;
++ while (num > 0) {
++ add_desktop(trg.monitor, make_desktop(*args));
++ num--, args++;
++ }
++ } else if (streq("-r", *args) || streq("--remove-desktops", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ while (num > 0) {
++ coordinates_t dst;
++ if (locate_desktop(*args, &dst) && dst.monitor->desk_head != dst.monitor->desk_tail && dst.desktop->root == NULL) {
++ remove_desktop(dst.monitor, dst.desktop);
++ show_desktop(dst.monitor->desk);
++ }
++ num--, args++;
++ }
++ } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ 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("-n", *args) || streq("--rename", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ snprintf(trg.monitor->name, sizeof(trg.monitor->name), "%s", *args);
++ put_status();
++ } else if (streq("-s", *args) || streq("--swap", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ coordinates_t dst;
++ if (monitor_from_desc(*args, &trg, &dst))
++ swap_monitors(trg.monitor, dst.monitor);
++ else
++ return MSG_FAILURE;
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_query(char **args, int num, char *rsp)
++int cmd_query(char **args, int num, FILE *rsp)
+ {
+- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+- coordinates_t trg = {NULL, NULL, NULL};
+- domain_t dom = DOMAIN_TREE;
+- int d = 0, t = 0;
++ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
++ coordinates_t trg = {NULL, NULL, NULL};
++ domain_t dom = DOMAIN_TREE;
++ int d = 0, t = 0;
+
+- 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++;
+- } else if (streq("-D", *args) || streq("--desktops", *args)) {
+- dom = DOMAIN_DESKTOP, d++;
+- } else if (streq("-W", *args) || streq("--windows", *args)) {
+- dom = DOMAIN_WINDOW, d++;
+- } else if (streq("-H", *args) || streq("--history", *args)) {
+- dom = DOMAIN_HISTORY, d++;
+- } else if (streq("-S", *args) || streq("--stack", *args)) {
+- dom = DOMAIN_STACK, d++;
+- } else if (streq("-m", *args) || streq("--monitor", *args)) {
+- trg.monitor = ref.monitor;
+- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+- num--, args++;
+- if (!monitor_from_desc(*args, &ref, &trg))
+- return false;
+- }
+- t++;
+- } else if (streq("-d", *args) || streq("--desktop", *args)) {
+- trg.monitor = ref.monitor;
+- trg.desktop = ref.desktop;
+- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+- num--, args++;
+- if (!desktop_from_desc(*args, &ref, &trg))
+- return false;
+- }
+- t++;
+- } else if (streq("-w", *args) || streq("--window", *args)) {
+- trg = ref;
+- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+- num--, args++;
+- if (!node_from_desc(*args, &ref, &trg))
+- return false;
+- }
+- t++;
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ 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++;
++ } else if (streq("-D", *args) || streq("--desktops", *args)) {
++ dom = DOMAIN_DESKTOP, d++;
++ } else if (streq("-W", *args) || streq("--windows", *args)) {
++ dom = DOMAIN_WINDOW, d++;
++ } else if (streq("-H", *args) || streq("--history", *args)) {
++ dom = DOMAIN_HISTORY, d++;
++ } else if (streq("-S", *args) || streq("--stack", *args)) {
++ dom = DOMAIN_STACK, d++;
++ } else if (streq("-m", *args) || streq("--monitor", *args)) {
++ trg.monitor = ref.monitor;
++ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
++ num--, args++;
++ if (!monitor_from_desc(*args, &ref, &trg))
++ return MSG_FAILURE;
++ }
++ t++;
++ } else if (streq("-d", *args) || streq("--desktop", *args)) {
++ trg.monitor = ref.monitor;
++ trg.desktop = ref.desktop;
++ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
++ num--, args++;
++ if (!desktop_from_desc(*args, &ref, &trg))
++ return MSG_FAILURE;
++ }
++ t++;
++ } else if (streq("-w", *args) || streq("--window", *args)) {
++ trg = ref;
++ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
++ num--, args++;
++ if (!node_from_desc(*args, &ref, &trg))
++ return MSG_FAILURE;
++ }
++ t++;
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- if (d != 1 || t > 1)
+- return false;
++ if (d != 1 || t > 1)
++ return MSG_SYNTAX;
+
+- if (dom == DOMAIN_HISTORY)
+- query_history(trg, rsp);
+- else if (dom == DOMAIN_STACK)
+- query_stack(rsp);
+- else if (dom == DOMAIN_WINDOW)
+- query_windows(trg, rsp);
+- else
+- query_monitors(trg, dom, rsp);
++ if (dom == DOMAIN_HISTORY)
++ query_history(trg, rsp);
++ else if (dom == DOMAIN_STACK)
++ query_stack(rsp);
++ else if (dom == DOMAIN_WINDOW)
++ query_windows(trg, rsp);
++ else
++ query_monitors(trg, dom, rsp);
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_rule(char **args, int num, char *rsp)
++int cmd_rule(char **args, int num, FILE *rsp)
+ {
+- if (num < 1)
+- return false;
+- while (num > 0) {
+- if (streq("-a", *args) || streq("--add", *args)) {
+- num--, args++;
+- if (num < 2)
+- return false;
+- rule_t *rule = make_rule();
+- snprintf(rule->cause, sizeof(rule->cause), "%s", *args);
+- 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)
+- return false;
+- int 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)) {
+- num--, args++;
+- list_rules(num > 0 ? *args : NULL, rsp);
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ if (num < 1)
++ return MSG_SYNTAX;
++ while (num > 0) {
++ if (streq("-a", *args) || streq("--add", *args)) {
++ num--, args++;
++ if (num < 2)
++ return MSG_SYNTAX;
++ rule_t *rule = make_rule();
++ snprintf(rule->cause, sizeof(rule->cause), "%s", *args);
++ 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)
++ return MSG_SYNTAX;
++ int 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)) {
++ num--, args++;
++ list_rules(num > 0 ? *args : NULL, rsp);
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_pointer(char **args, int num)
++int cmd_pointer(char **args, int num)
+ {
+- if (num < 1)
+- return false;
+- while (num > 0) {
+- if (streq("-t", *args) || streq("--track", *args)) {
+- num--, args++;
+- if (num < 2)
+- return false;
+- int x, y;
+- if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1)
+- track_pointer(x, y);
+- else
+- return false;
+- } else if (streq("-g", *args) || streq("--grab", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- pointer_action_t pac;
+- if (parse_pointer_action(*args, &pac))
+- grab_pointer(pac);
+- else
+- return false;
+- } else if (streq("-u", *args) || streq("--ungrab", *args)) {
+- ungrab_pointer();
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ if (num < 1)
++ return MSG_SYNTAX;
++ while (num > 0) {
++ if (streq("-t", *args) || streq("--track", *args)) {
++ num--, args++;
++ if (num < 2)
++ return MSG_SYNTAX;
++ int x, y;
++ if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1)
++ track_pointer(x, y);
++ else
++ return MSG_FAILURE;
++ num--, args++;
++ } else if (streq("-g", *args) || streq("--grab", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ pointer_action_t pac;
++ if (parse_pointer_action(*args, &pac))
++ grab_pointer(pac);
++ else
++ return MSG_FAILURE;
++ } else if (streq("-u", *args) || streq("--ungrab", *args)) {
++ ungrab_pointer();
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_restore(char **args, int num)
++int cmd_restore(char **args, int num)
+ {
+- if (num < 1)
+- return false;
+- while (num > 0) {
+- if (streq("-T", *args) || streq("--tree", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- restore_tree(*args);
+- } else if (streq("-H", *args) || streq("--history", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- restore_history(*args);
+- } else if (streq("-S", *args) || streq("--stack", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- restore_stack(*args);
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ if (num < 1)
++ return MSG_SYNTAX;
++ while (num > 0) {
++ if (streq("-T", *args) || streq("--tree", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ restore_tree(*args);
++ } else if (streq("-H", *args) || streq("--history", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ restore_history(*args);
++ } else if (streq("-S", *args) || streq("--stack", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ restore_stack(*args);
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_control(char **args, int num, char *rsp)
++int cmd_control(char **args, int num, FILE *rsp)
+ {
+- if (num < 1)
+- return false;
+- while (num > 0) {
+- if (streq("--adopt-orphans", *args)) {
+- adopt_orphans();
+- } else if (streq("--put-status", *args)) {
+- put_status();
+- } else if (streq("--toggle-visibility", *args)) {
+- toggle_visibility();
+- } else if (streq("--subscribe", *args)) {
+- snprintf(rsp, BUFSIZ, "%c", MESSAGE_SUBSCRIBE);
+- } else if (streq("--record-history", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- bool b;
+- if (parse_bool(*args, &b))
+- record_history = b;
+- else
+- return false;
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
++ if (num < 1)
++ return MSG_SYNTAX;
++ while (num > 0) {
++ if (streq("--adopt-orphans", *args)) {
++ adopt_orphans();
++ } else if (streq("--toggle-visibility", *args)) {
++ toggle_visibility();
++ } else if (streq("--subscribe", *args)) {
++ return MSG_SUBSCRIBE;
++ } else if (streq("--get-status", *args)) {
++ print_status(rsp);
++ } else if (streq("--record-history", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ bool b;
++ if (parse_bool(*args, &b))
++ record_history = b;
++ else
++ return MSG_SYNTAX;
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool cmd_config(char **args, int num, char *rsp)
++int cmd_config(char **args, int num, FILE *rsp)
+ {
+- if (num < 1)
+- return false;
+- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+- coordinates_t trg = {NULL, NULL, NULL};
+- if ((*args)[0] == OPT_CHR) {
+- if (streq("-d", *args) || streq("--desktop", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- if (!desktop_from_desc(*args, &ref, &trg))
+- return false;
+- } else if (streq("-m", *args) || streq("--monitor", *args)) {
+- num--, args++;
+- if (num < 1)
+- return false;
+- if (!monitor_from_desc(*args, &ref, &trg))
+- return false;
+- } else {
+- return false;
+- }
+- num--, args++;
+- }
+- if (num == 2)
+- return set_setting(trg, *args, *(args + 1));
+- else if (num == 1)
+- return get_setting(trg, *args, rsp);
+- else
+- return false;
++ if (num < 1)
++ return MSG_SYNTAX;
++ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
++ coordinates_t trg = {NULL, NULL, NULL};
++ if ((*args)[0] == OPT_CHR) {
++ if (streq("-m", *args) || streq("--monitor", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ if (!monitor_from_desc(*args, &ref, &trg))
++ return MSG_FAILURE;
++ } else if (streq("-d", *args) || streq("--desktop", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ if (!desktop_from_desc(*args, &ref, &trg))
++ return MSG_FAILURE;
++ } else if (streq("-w", *args) || streq("--window", *args)) {
++ num--, args++;
++ if (num < 1)
++ return MSG_SYNTAX;
++ if (!node_from_desc(*args, &ref, &trg))
++ return MSG_FAILURE;
++ } else {
++ return MSG_SYNTAX;
++ }
++ num--, args++;
++ }
++ if (num == 2)
++ return set_setting(trg, *args, *(args + 1));
++ else if (num == 1)
++ return get_setting(trg, *args, rsp);
++ else
++ return MSG_SYNTAX;
+ }
+
+-bool cmd_quit(char **args, int num)
++int cmd_quit(char **args, int num)
+ {
+- if (num > 0 && sscanf(*args, "%i", &exit_status) != 1)
+- return false;
+- running = false;
+- return true;
++ if (num > 0 && sscanf(*args, "%i", &exit_status) != 1)
++ return MSG_FAILURE;
++ running = false;
++ return MSG_SUCCESS;
+ }
+
+-bool set_setting(coordinates_t loc, char *name, char *value)
++int set_setting(coordinates_t loc, char *name, char *value)
+ {
+-#define DESKSET(k, v) \
+- if (loc.desktop != NULL) \
+- loc.desktop->k = v; \
+- else if (loc.monitor != NULL) \
+- for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) \
+- d->k = v; \
+- else \
+- for (monitor_t *m = mon_head; m != NULL; m = m->next) \
+- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) \
+- d->k = v;
+- if (streq("border_width", name)) {
+- unsigned int bw;
+- if (sscanf(value, "%u", &bw) != 1)
+- return false;
+- DESKSET(border_width, bw)
+- } else if (streq("window_gap", name)) {
+- int wg;
+- if (sscanf(value, "%i", &wg) != 1)
+- return false;
+- DESKSET(window_gap, wg)
+-#undef DESKSET
++#define DESKWINDEFSET(k, v) \
++ if (loc.node != NULL) \
++ loc.node->client->k = v; \
++ else if (loc.desktop != NULL) \
++ loc.desktop->k = v; \
++ else if (loc.monitor != NULL) \
++ for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) \
++ d->k = v; \
++ else \
++ k = v;
++ if (streq("border_width", name)) {
++ unsigned int bw;
++ if (sscanf(value, "%u", &bw) != 1)
++ return MSG_FAILURE;
++ DESKWINDEFSET(border_width, bw)
++#undef DESKWINDEFSET
++#define DESKDEFSET(k, v) \
++ if (loc.desktop != NULL) \
++ loc.desktop->k = v; \
++ else if (loc.monitor != NULL) \
++ for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) \
++ d->k = v; \
++ else \
++ k = v;
++ } else if (streq("window_gap", name)) {
++ int wg;
++ if (sscanf(value, "%i", &wg) != 1)
++ return MSG_FAILURE;
++ DESKDEFSET(window_gap, wg)
++#undef DESKDEFSET
+ #define MONDESKSET(k, v) \
+- if (loc.desktop != NULL) \
+- loc.desktop->k = v; \
+- else if (loc.monitor != NULL) \
+- loc.monitor->k = v; \
+- else \
+- 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)
+- return false;
+- MONDESKSET(top_padding, tp)
+- } else if (streq("right_padding", name)) {
+- int rp;
+- if (sscanf(value, "%i", &rp) != 1)
+- return false;
+- MONDESKSET(right_padding, rp)
+- } else if (streq("bottom_padding", name)) {
+- int bp;
+- if (sscanf(value, "%i", &bp) != 1)
+- return false;
+- MONDESKSET(bottom_padding, bp)
+- } else if (streq("left_padding", name)) {
+- int lp;
+- if (sscanf(value, "%i", &lp) != 1)
+- return false;
+- MONDESKSET(left_padding, lp)
++ if (loc.desktop != NULL) \
++ loc.desktop->k = v; \
++ else if (loc.monitor != NULL) \
++ loc.monitor->k = v; \
++ else \
++ 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)
++ return MSG_FAILURE;
++ MONDESKSET(top_padding, tp)
++ } else if (streq("right_padding", name)) {
++ int rp;
++ if (sscanf(value, "%i", &rp) != 1)
++ return MSG_FAILURE;
++ MONDESKSET(right_padding, rp)
++ } else if (streq("bottom_padding", name)) {
++ int bp;
++ if (sscanf(value, "%i", &bp) != 1)
++ return MSG_FAILURE;
++ MONDESKSET(bottom_padding, bp)
++ } else if (streq("left_padding", name)) {
++ int lp;
++ if (sscanf(value, "%i", &lp) != 1)
++ return MSG_FAILURE;
++ MONDESKSET(left_padding, lp)
+ #undef MONDESKSET
+ #define SETSTR(s) \
+- } else if (streq(#s, name)) { \
+- return snprintf(s, sizeof(s), "%s", value) >= 0;
+- SETSTR(external_rules_command)
+- SETSTR(status_prefix)
++ } else if (streq(#s, name)) { \
++ return snprintf(s, sizeof(s), "%s", value) >= 0;
++ SETSTR(external_rules_command)
++ SETSTR(status_prefix)
+ #undef SETSTR
+- } else if (streq("split_ratio", name)) {
+- double r;
+- if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
+- split_ratio = r;
+- else
+- return false;
+- return true;
++ } else if (streq("split_ratio", name)) {
++ double r;
++ if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1)
++ split_ratio = r;
++ else
++ return MSG_FAILURE;
++ return MSG_SUCCESS;
+ #define SETCOLOR(s) \
+- } else if (streq(#s, name)) { \
+- snprintf(s, sizeof(s), "%s", value);
+- SETCOLOR(focused_border_color)
+- SETCOLOR(active_border_color)
+- SETCOLOR(normal_border_color)
+- SETCOLOR(presel_border_color)
+- SETCOLOR(focused_locked_border_color)
+- SETCOLOR(active_locked_border_color)
+- SETCOLOR(normal_locked_border_color)
+- SETCOLOR(focused_sticky_border_color)
+- SETCOLOR(active_sticky_border_color)
+- SETCOLOR(normal_sticky_border_color)
+- SETCOLOR(focused_private_border_color)
+- SETCOLOR(active_private_border_color)
+- SETCOLOR(normal_private_border_color)
+- SETCOLOR(urgent_border_color)
++ } else if (streq(#s, name)) { \
++ snprintf(s, sizeof(s), "%s", value);
++ SETCOLOR(focused_border_color)
++ SETCOLOR(active_border_color)
++ SETCOLOR(normal_border_color)
++ SETCOLOR(presel_border_color)
++ SETCOLOR(focused_locked_border_color)
++ SETCOLOR(active_locked_border_color)
++ SETCOLOR(normal_locked_border_color)
++ SETCOLOR(focused_sticky_border_color)
++ SETCOLOR(active_sticky_border_color)
++ SETCOLOR(normal_sticky_border_color)
++ SETCOLOR(focused_private_border_color)
++ SETCOLOR(active_private_border_color)
++ SETCOLOR(normal_private_border_color)
++ SETCOLOR(urgent_border_color)
+ #undef SETCOLOR
+- } else if (streq("focus_follows_pointer", name)) {
+- bool b;
+- if (parse_bool(value, &b) && b != focus_follows_pointer) {
+- focus_follows_pointer = b;
+- 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)) {
+- uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+- xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
+- }
+- if (focus_follows_pointer) {
+- for (monitor_t *m = mon_head; m != NULL; m = m->next)
+- window_show(m->root);
+- enable_motion_recorder();
+- } else {
+- for (monitor_t *m = mon_head; m != NULL; m = m->next)
+- window_hide(m->root);
+- disable_motion_recorder();
+- }
+- return true;
+- } else {
+- return false;
+- }
++ } else if (streq("focus_follows_pointer", name)) {
++ bool b;
++ if (parse_bool(value, &b) && b != focus_follows_pointer) {
++ focus_follows_pointer = b;
++ 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)) {
++ uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
++ xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
++ }
++ if (focus_follows_pointer) {
++ for (monitor_t *m = mon_head; m != NULL; m = m->next)
++ window_show(m->root);
++ enable_motion_recorder();
++ } else {
++ for (monitor_t *m = mon_head; m != NULL; m = m->next)
++ window_hide(m->root);
++ disable_motion_recorder();
++ }
++ return MSG_SUCCESS;
++ } else {
++ return MSG_FAILURE;
++ }
+ #define SETBOOL(s) \
+- } else if (streq(#s, name)) { \
+- if (!parse_bool(value, &s)) \
+- return false;
+- SETBOOL(borderless_monocle)
+- SETBOOL(gapless_monocle)
+- SETBOOL(pointer_follows_monitor)
+- SETBOOL(apply_floating_atom)
+- SETBOOL(auto_alternate)
+- SETBOOL(auto_cancel)
+- SETBOOL(history_aware_focus)
+- SETBOOL(ignore_ewmh_focus)
+- SETBOOL(remove_disabled_monitor)
++ } else if (streq(#s, name)) { \
++ if (!parse_bool(value, &s)) \
++ return MSG_FAILURE;
++ SETBOOL(borderless_monocle)
++ SETBOOL(gapless_monocle)
++ SETBOOL(pointer_follows_monitor)
++ SETBOOL(apply_floating_atom)
++ SETBOOL(auto_alternate)
++ SETBOOL(auto_cancel)
++ SETBOOL(history_aware_focus)
++ SETBOOL(ignore_ewmh_focus)
++ SETBOOL(remove_disabled_monitors)
++ SETBOOL(remove_unplugged_monitors)
++ SETBOOL(merge_overlapping_monitors)
+ #undef SETBOOL
+- } else {
+- return false;
+- }
++ } else {
++ return MSG_FAILURE;
++ }
+
+- 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);
++ 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);
+
+- return true;
++ return MSG_SUCCESS;
+ }
+
+-bool get_setting(coordinates_t loc, char *name, char* rsp)
++int get_setting(coordinates_t loc, char *name, FILE* rsp)
+ {
+- if (streq("split_ratio", name))
+- snprintf(rsp, BUFSIZ, "%lf", split_ratio);
+- else if (streq("window_gap", name))
+- if (loc.desktop == NULL)
+- return false;
+- else
+- snprintf(rsp, BUFSIZ, "%i", loc.desktop->window_gap);
+- else if (streq("border_width", name))
+- if (loc.desktop == NULL)
+- return false;
+- else
+- snprintf(rsp, BUFSIZ, "%u", loc.desktop->border_width);
+- else if (streq("external_rules_command", name))
+- snprintf(rsp, BUFSIZ, "%s", external_rules_command);
+- else if (streq("status_prefix", name))
+- snprintf(rsp, BUFSIZ, "%s", status_prefix);
++ if (streq("split_ratio", name))
++ fprintf(rsp, "%lf", split_ratio);
++ else if (streq("window_gap", name))
++ if (loc.desktop != NULL)
++ fprintf(rsp, "%i", loc.desktop->window_gap);
++ else
++ fprintf(rsp, "%i", window_gap);
++ else if (streq("border_width", name))
++ if (loc.node != NULL)
++ fprintf(rsp, "%u", loc.node->client->border_width);
++ else if (loc.desktop != NULL)
++ fprintf(rsp, "%u", loc.desktop->border_width);
++ else
++ fprintf(rsp, "%u", border_width);
++ else if (streq("external_rules_command", name))
++ fprintf(rsp, "%s", external_rules_command);
++ else if (streq("status_prefix", name))
++ fprintf(rsp, "%s", status_prefix);
+ #define MONDESKGET(k) \
+- else if (streq(#k, name)) \
+- if (loc.desktop != NULL) \
+- snprintf(rsp, BUFSIZ, "%i", loc.desktop->k); \
+- else if (loc.monitor != NULL) \
+- snprintf(rsp, BUFSIZ, "%i", loc.monitor->k); \
+- else \
+- return false;
+- MONDESKGET(top_padding)
+- MONDESKGET(right_padding)
+- MONDESKGET(bottom_padding)
+- MONDESKGET(left_padding)
++ else if (streq(#k, name)) \
++ if (loc.desktop != NULL) \
++ fprintf(rsp, "%i", loc.desktop->k); \
++ else if (loc.monitor != NULL) \
++ fprintf(rsp, "%i", loc.monitor->k); \
++ else \
++ return MSG_FAILURE;
++ MONDESKGET(top_padding)
++ MONDESKGET(right_padding)
++ MONDESKGET(bottom_padding)
++ MONDESKGET(left_padding)
+ #undef DESKGET
+ #define GETCOLOR(s) \
+- else if (streq(#s, name)) \
+- snprintf(rsp, BUFSIZ, "%s", s);
+- GETCOLOR(focused_border_color)
+- GETCOLOR(active_border_color)
+- GETCOLOR(normal_border_color)
+- GETCOLOR(presel_border_color)
+- GETCOLOR(focused_locked_border_color)
+- GETCOLOR(active_locked_border_color)
+- GETCOLOR(normal_locked_border_color)
+- GETCOLOR(focused_sticky_border_color)
+- GETCOLOR(active_sticky_border_color)
+- GETCOLOR(normal_sticky_border_color)
+- GETCOLOR(urgent_border_color)
++ else if (streq(#s, name)) \
++ fprintf(rsp, "%s", s);
++ GETCOLOR(focused_border_color)
++ GETCOLOR(active_border_color)
++ GETCOLOR(normal_border_color)
++ GETCOLOR(presel_border_color)
++ GETCOLOR(focused_locked_border_color)
++ GETCOLOR(active_locked_border_color)
++ GETCOLOR(normal_locked_border_color)
++ GETCOLOR(focused_sticky_border_color)
++ GETCOLOR(active_sticky_border_color)
++ GETCOLOR(normal_sticky_border_color)
++ GETCOLOR(urgent_border_color)
+ #undef GETCOLOR
+ #define GETBOOL(s) \
+- else if (streq(#s, name)) \
+- snprintf(rsp, BUFSIZ, "%s", BOOLSTR(s));
+- GETBOOL(borderless_monocle)
+- GETBOOL(gapless_monocle)
+- GETBOOL(focus_follows_pointer)
+- GETBOOL(pointer_follows_monitor)
+- GETBOOL(apply_floating_atom)
+- GETBOOL(auto_alternate)
+- GETBOOL(auto_cancel)
+- GETBOOL(history_aware_focus)
+- GETBOOL(ignore_ewmh_focus)
+- GETBOOL(remove_disabled_monitor)
++ else if (streq(#s, name)) \
++ fprintf(rsp, "%s", BOOLSTR(s));
++ GETBOOL(borderless_monocle)
++ GETBOOL(gapless_monocle)
++ GETBOOL(focus_follows_pointer)
++ GETBOOL(pointer_follows_monitor)
++ GETBOOL(apply_floating_atom)
++ GETBOOL(auto_alternate)
++ GETBOOL(auto_cancel)
++ GETBOOL(history_aware_focus)
++ GETBOOL(ignore_ewmh_focus)
++ GETBOOL(remove_disabled_monitors)
++ GETBOOL(remove_unplugged_monitors)
++ GETBOOL(merge_overlapping_monitors)
+ #undef GETBOOL
+- else
+- return false;
+- return true;
++ else
++ return MSG_FAILURE;
++ return MSG_SUCCESS;
+ }
+
+ 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;
++ 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_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;
++ if (streq("monocle", s)) {
++ *l = LAYOUT_MONOCLE;
++ return true;
++ } else if (streq("tiled", s)) {
++ *l = LAYOUT_TILED;
++ return true;
++ }
++ return false;
+ }
+
+ bool parse_direction(char *s, direction_t *d)
+ {
+- if (streq("right", s)) {
+- *d = DIR_RIGHT;
+- return true;
+- } else if (streq("down", s)) {
+- *d = DIR_DOWN;
+- return true;
+- } else if (streq("left", s)) {
+- *d = DIR_LEFT;
+- return true;
+- } else if (streq("up", s)) {
+- *d = DIR_UP;
+- return true;
+- }
+- return false;
++ if (streq("right", s)) {
++ *d = DIR_RIGHT;
++ return true;
++ } else if (streq("down", s)) {
++ *d = DIR_DOWN;
++ return true;
++ } else if (streq("left", s)) {
++ *d = DIR_LEFT;
++ return true;
++ } else if (streq("up", s)) {
++ *d = DIR_UP;
++ 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;
++ 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;
++ 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;
++ 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;
++ if (streq("horizontal", s)) {
++ *f = FLIP_HORIZONTAL;
++ return true;
++ } else if (streq("vertical", s)) {
++ *f = FLIP_VERTICAL;
++ 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;
+- }
+- return false;
++ 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;
++ }
++ 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;
+- }
++ 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_window_id(char *s, long int *i)
+ {
+- char *end;
+- errno = 0;
+- long int ret = strtol(s, &end, 0);
+- if (errno != 0 || *end != '\0')
+- return false;
+- else
+- *i = ret;
+- return true;
++ char *end;
++ errno = 0;
++ long int ret = strtol(s, &end, 0);
++ if (errno != 0 || *end != '\0')
++ return false;
++ else
++ *i = ret;
++ 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;
++ *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, int *i)
+ {
+- int idx;
+- if (sscanf(s, "^%i", &idx) != 1 || idx < 1)
+- return false;
+- *i = idx;
+- return true;
++ int idx;
++ if (sscanf(s, "^%i", &idx) != 1 || idx < 1)
++ return false;
++ *i = idx;
++ return true;
+ }
+diff --git a/messages.h b/messages.h
+index 46a27f4..aa0e447 100644
+--- a/messages.h
++++ b/messages.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_MESSAGES_H
+@@ -31,22 +35,20 @@
+ #define CAT_CHR '.'
+ #define EQL_TOK "="
+
+-#define MESSAGE_SUBSCRIBE '\x01'
+-
+-bool handle_message(char *msg, int msg_len, char *rsp);
+-bool process_message(char **args, int num, char *rsp);
+-bool cmd_window(char **args, int num);
+-bool cmd_desktop(char **args, int num);
+-bool cmd_monitor(char **args, int num);
+-bool cmd_query(char **args, int num, char *rsp);
+-bool cmd_rule(char **args, int num, char *rsp);
+-bool cmd_pointer(char **args, int num);
+-bool cmd_restore(char **args, int num);
+-bool cmd_control(char **args, int num, char *rsp);
+-bool cmd_config(char **args, int num, char *rsp);
+-bool cmd_quit(char **args, int num);
+-bool set_setting(coordinates_t loc, char *name, char *value);
+-bool get_setting(coordinates_t loc, char *name, char* rsp);
++int handle_message(char *msg, int msg_len, FILE *rsp);
++int process_message(char **args, int num, FILE *rsp);
++int cmd_window(char **args, int num);
++int cmd_desktop(char **args, int num);
++int cmd_monitor(char **args, int num);
++int cmd_query(char **args, int num, FILE *rsp);
++int cmd_rule(char **args, int num, FILE *rsp);
++int cmd_pointer(char **args, int num);
++int cmd_restore(char **args, int num);
++int cmd_control(char **args, int num, FILE *rsp);
++int cmd_config(char **args, int num, FILE *rsp);
++int cmd_quit(char **args, int num);
++int set_setting(coordinates_t loc, char *name, char *value);
++int get_setting(coordinates_t loc, char *name, FILE* rsp);
+ bool parse_bool(char *value, bool *b);
+ bool parse_layout(char *s, layout_t *l);
+ bool parse_direction(char *s, direction_t *d);
+diff --git a/monitor.c b/monitor.c
+index ea8e8c1..551ac55 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <limits.h>
+@@ -37,391 +41,404 @@
+
+ monitor_t *make_monitor(xcb_rectangle_t rect)
+ {
+- monitor_t *m = malloc(sizeof(monitor_t));
+- snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
+- m->prev = m->next = NULL;
+- m->desk = m->desk_head = m->desk_tail = NULL;
+- m->rectangle = rect;
+- m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
+- m->wired = true;
+- m->num_sticky = 0;
+- uint32_t mask = XCB_CW_EVENT_MASK;
+- 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, mask, values);
+- window_lower(m->root);
+- if (focus_follows_pointer)
+- window_show(m->root);
+- return m;
++ monitor_t *m = malloc(sizeof(monitor_t));
++ snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
++ m->prev = m->next = NULL;
++ m->desk = m->desk_head = m->desk_tail = NULL;
++ m->rectangle = rect;
++ m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
++ m->wired = true;
++ m->num_sticky = 0;
++ uint32_t mask = XCB_CW_EVENT_MASK;
++ 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, mask, values);
++ window_lower(m->root);
++ if (focus_follows_pointer)
++ window_show(m->root);
++ return m;
+ }
+
+ monitor_t *find_monitor(char *name)
+ {
+- for (monitor_t *m = mon_head; m != NULL; m = m->next)
+- if (streq(m->name, name))
+- return m;
+- return NULL;
++ for (monitor_t *m = mon_head; m != NULL; m = m->next)
++ if (streq(m->name, name))
++ return m;
++ return NULL;
+ }
+
+ monitor_t *get_monitor_by_id(xcb_randr_output_t id)
+ {
+- for (monitor_t *m = mon_head; m != NULL; m = m->next)
+- if (m->id == id)
+- return m;
+- return NULL;
++ for (monitor_t *m = mon_head; m != NULL; m = m->next)
++ if (m->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;
++ 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 translate_client(monitor_t *ms, monitor_t *md, client_t *c)
+ {
+- if (frozen_pointer->action != ACTION_NONE || ms == md)
+- return;
+-
+- /* 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((ms->rectangle.x - c->floating_rectangle.x), 0);
+- int top_adjust = MAX((ms->rectangle.y - c->floating_rectangle.y), 0);
+- int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (ms->rectangle.x + ms->rectangle.width), 0);
+- int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (ms->rectangle.y + ms->rectangle.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 - ms->rectangle.x;
+- int dy_s = c->floating_rectangle.y - ms->rectangle.y;
+-
+- int nume_x = dx_s * (md->rectangle.width - c->floating_rectangle.width);
+- int nume_y = dy_s * (md->rectangle.height - c->floating_rectangle.height);
+-
+- int deno_x = ms->rectangle.width - c->floating_rectangle.width;
+- int deno_y = ms->rectangle.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 = md->rectangle.x + dx_d - left_adjust;
+- c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
++ if (frozen_pointer->action != ACTION_NONE || ms == md)
++ return;
++
++ /* 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((ms->rectangle.x - c->floating_rectangle.x), 0);
++ int top_adjust = MAX((ms->rectangle.y - c->floating_rectangle.y), 0);
++ int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (ms->rectangle.x + ms->rectangle.width), 0);
++ int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (ms->rectangle.y + ms->rectangle.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 - ms->rectangle.x;
++ int dy_s = c->floating_rectangle.y - ms->rectangle.y;
++
++ int nume_x = dx_s * (md->rectangle.width - c->floating_rectangle.width);
++ int nume_y = dy_s * (md->rectangle.height - c->floating_rectangle.height);
++
++ int deno_x = ms->rectangle.width - c->floating_rectangle.width;
++ int deno_y = ms->rectangle.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 = md->rectangle.x + dx_d - left_adjust;
++ c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
+ }
+
+ void update_root(monitor_t *m)
+ {
+- xcb_rectangle_t rect = m->rectangle;
+- window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
++ xcb_rectangle_t rect = m->rectangle;
++ window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
+ }
+
+ void focus_monitor(monitor_t *m)
+ {
+- if (mon == m)
+- return;
++ if (mon == m)
++ return;
+
+- PRINTF("focus monitor %s\n", m->name);
++ PRINTF("focus monitor %s\n", m->name);
+
+- mon = m;
++ mon = m;
+
+- if (pointer_follows_monitor)
+- center_pointer(m);
++ if (pointer_follows_monitor)
++ center_pointer(m);
+
+- ewmh_update_current_desktop();
+- put_status();
++ ewmh_update_current_desktop();
++ put_status();
+ }
+
+ monitor_t *add_monitor(xcb_rectangle_t rect)
+ {
+- monitor_t *m = make_monitor(rect);
+- if (mon == NULL) {
+- mon = m;
+- mon_head = m;
+- mon_tail = m;
+- } else {
+- mon_tail->next = m;
+- m->prev = mon_tail;
+- mon_tail = m;
+- }
+- num_monitors++;
+- return m;
++ monitor_t *m = make_monitor(rect);
++ if (mon == NULL) {
++ mon = m;
++ mon_head = m;
++ mon_tail = m;
++ } else {
++ mon_tail->next = m;
++ m->prev = mon_tail;
++ mon_tail = m;
++ }
++ num_monitors++;
++ return m;
+ }
+
+ void remove_monitor(monitor_t *m)
+ {
+- PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
+-
+- while (m->desk_head != NULL)
+- remove_desktop(m, m->desk_head);
+- monitor_t *prev = m->prev;
+- monitor_t *next = m->next;
+- monitor_t *last_mon = history_get_monitor(m);
+- 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 = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
+- if (mon != NULL && mon->desk != NULL)
+- update_current();
+- }
+- xcb_destroy_window(dpy, m->root);
+- free(m);
+- num_monitors--;
+- put_status();
++ PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
++
++ while (m->desk_head != NULL)
++ remove_desktop(m, m->desk_head);
++ monitor_t *prev = m->prev;
++ monitor_t *next = m->next;
++ monitor_t *last_mon = history_get_monitor(m);
++ 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 = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
++ if (mon != NULL && mon->desk != NULL)
++ update_current();
++ }
++ xcb_destroy_window(dpy, m->root);
++ free(m);
++ num_monitors--;
++ put_status();
+ }
+
+ void merge_monitors(monitor_t *ms, monitor_t *md)
+ {
+- PRINTF("merge %s into %s\n", ms->name, md->name);
+-
+- desktop_t *d = ms->desk_head;
+- while (d != NULL) {
+- desktop_t *next = d->next;
+- if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
+- transfer_desktop(ms, md, d);
+- d = next;
+- }
++ PRINTF("merge %s into %s\n", ms->name, md->name);
++
++ desktop_t *d = ms->desk_head;
++ while (d != NULL) {
++ desktop_t *next = d->next;
++ if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
++ transfer_desktop(ms, md, d);
++ d = next;
++ }
+ }
+
+ void swap_monitors(monitor_t *m1, monitor_t *m2)
+ {
+- if (m1 == NULL || m2 == NULL || m1 == m2)
+- return;
+-
+- 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_current_desktop();
+- put_status();
++ if (m1 == NULL || m2 == NULL || m1 == m2)
++ return;
++
++ 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_current_desktop();
++ put_status();
+ }
+
+ monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_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 = {m, m->desk, NULL};
+- if (desktop_matches(&loc, &loc, sel))
+- return f;
+- f = (dir == CYCLE_PREV ? m->prev : m->next);
+- if (f == NULL)
+- f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+- }
+-
+- return NULL;
++ 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 = {m, m->desk, NULL};
++ if (desktop_matches(&loc, &loc, sel))
++ return f;
++ f = (dir == CYCLE_PREV ? m->prev : m->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)
+ {
+- xcb_rectangle_t r = m->rectangle;
+- return (r.x <= pt.x && pt.x < (r.x + r.width)
+- && r.y <= pt.y && pt.y < (r.y + r.height));
++ xcb_rectangle_t r = m->rectangle;
++ return (r.x <= pt.x && pt.x < (r.x + r.width)
++ && r.y <= pt.y && pt.y < (r.y + r.height));
+ }
+
+ 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;
++ 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)
+ {
+- xcb_point_t pt = {c->floating_rectangle.x, c->floating_rectangle.y};
+- monitor_t *nearest = monitor_from_point(pt);
+- if (nearest == NULL) {
+- int x = (c->floating_rectangle.x + c->floating_rectangle.width) / 2;
+- int y = (c->floating_rectangle.y + c->floating_rectangle.height) / 2;
+- 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) - x) + abs((r.y + r.height / 2) - y);
+- if (d < dmin) {
+- dmin = d;
+- nearest = m;
+- }
+- }
+- }
+- return nearest;
++ xcb_point_t pt = {c->floating_rectangle.x, c->floating_rectangle.y};
++ monitor_t *nearest = monitor_from_point(pt);
++ if (nearest == NULL) {
++ int x = (c->floating_rectangle.x + c->floating_rectangle.width) / 2;
++ int y = (c->floating_rectangle.y + c->floating_rectangle.height) / 2;
++ 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) - x) + abs((r.y + r.height / 2) - y);
++ if (d < dmin) {
++ dmin = d;
++ nearest = m;
++ }
++ }
++ }
++ return nearest;
+ }
+
+ monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
+ {
+- int dmin = INT_MAX;
+- monitor_t *nearest = NULL;
+- xcb_rectangle_t rect = m->rectangle;
+- for (monitor_t *f = mon_head; f != NULL; f = f->next) {
+- if (f == m)
+- continue;
+- coordinates_t loc = {f, f->desk, NULL};
+- if (!desktop_matches(&loc, &loc, sel))
+- continue;
+- xcb_rectangle_t r = f->rectangle;
+- if ((dir == DIR_LEFT && r.x < rect.x) ||
+- (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
+- (dir == DIR_UP && r.y < rect.y) ||
+- (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
+- int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
+- abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
+- if (d < dmin) {
+- dmin = d;
+- nearest = f;
+- }
+- }
+- }
+- return nearest;
++ int dmin = INT_MAX;
++ monitor_t *nearest = NULL;
++ xcb_rectangle_t rect = m->rectangle;
++ for (monitor_t *f = mon_head; f != NULL; f = f->next) {
++ if (f == m)
++ continue;
++ coordinates_t loc = {f, f->desk, NULL};
++ if (!desktop_matches(&loc, &loc, sel))
++ continue;
++ xcb_rectangle_t r = f->rectangle;
++ if ((dir == DIR_LEFT && r.x < rect.x) ||
++ (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
++ (dir == DIR_UP && r.y < rect.y) ||
++ (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
++ int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
++ abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
++ if (d < dmin) {
++ dmin = d;
++ nearest = f;
++ }
++ }
++ }
++ return nearest;
+ }
+
+-bool import_monitors(void)
++bool update_monitors(void)
+ {
+- PUTS("import monitors");
+- xcb_randr_get_screen_resources_current_reply_t *sres = xcb_randr_get_screen_resources_current_reply(dpy, xcb_randr_get_screen_resources_current(dpy, root), NULL);
+- if (sres == NULL)
+- return false;
+-
+- monitor_t *m, *mm = NULL;
+-
+- int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
+- xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_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_id(outputs[i]);
+- if (mm != NULL) {
+- mm->rectangle = rect;
+- update_root(mm);
+- for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
+- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+- translate_client(mm, mm, n->client);
+- arrange(mm, mm->desk);
+- mm->wired = true;
+- PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
+- } else {
+- mm = add_monitor(rect);
+- char *name = (char *)xcb_randr_get_output_info_name(info);
+- size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
+- snprintf(mm->name, name_len, "%s", name);
+- mm->id = outputs[i];
+- PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
+- }
+- }
+- free(cir);
+- } else if (!remove_disabled_monitor && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
+- m = get_monitor_by_id(outputs[i]);
+- if (m != NULL)
+- m->wired = true;
+- }
+- }
+- free(info);
+- }
+-
+- /* initially focus the primary monitor and add the first desktop to it */
+- 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_id(gpo->output);
+- if (!running && pri_mon != NULL) {
+- if (mon != pri_mon)
+- mon = pri_mon;
+- add_desktop(pri_mon, make_desktop(NULL));
+- ewmh_update_current_desktop();
+- }
+- }
+- free(gpo);
+-
+- /* handle overlapping monitors */
+- m = mon_head;
+- while (m != NULL) {
+- monitor_t *next = m->next;
+- if (m->wired) {
+- for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
+- if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
+- && contains(mb->rectangle, m->rectangle)) {
+- if (mm == m)
+- mm = mb;
+- merge_monitors(m, mb);
+- remove_monitor(m);
+- break;
+- }
+- }
+- m = next;
+- }
+-
+- /* merge and remove disconnected 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 && (running || pri_mon == NULL || m != pri_mon))
+- add_desktop(m, make_desktop(NULL));
+-
+- free(sres);
+- update_motion_recorder();
+- return (num_monitors > 0);
++ PUTS("update monitors");
++ xcb_randr_get_screen_resources_current_reply_t *sres = xcb_randr_get_screen_resources_current_reply(dpy, xcb_randr_get_screen_resources_current(dpy, root), NULL);
++ if (sres == NULL)
++ return false;
++
++ monitor_t *m, *mm = NULL;
++
++ int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
++ xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_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_id(outputs[i]);
++ if (mm != NULL) {
++ mm->rectangle = rect;
++ update_root(mm);
++ for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
++ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
++ translate_client(mm, mm, n->client);
++ arrange(mm, mm->desk);
++ mm->wired = true;
++ PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
++ } else {
++ mm = add_monitor(rect);
++ char *name = (char *)xcb_randr_get_output_info_name(info);
++ size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
++ snprintf(mm->name, name_len, "%s", name);
++ mm->id = outputs[i];
++ PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
++ }
++ }
++ free(cir);
++ } else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
++ m = get_monitor_by_id(outputs[i]);
++ if (m != NULL)
++ m->wired = true;
++ }
++ }
++ free(info);
++ }
++
++ /* initially focus the primary monitor and add the first desktop to it */
++ 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_id(gpo->output);
++ if (!running && pri_mon != NULL) {
++ if (mon != pri_mon)
++ mon = pri_mon;
++ add_desktop(pri_mon, make_desktop(NULL));
++ ewmh_update_current_desktop();
++ }
++ }
++ free(gpo);
++
++ /* handle overlapping monitors */
++ if (merge_overlapping_monitors) {
++ m = mon_head;
++ while (m != NULL) {
++ monitor_t *next = m->next;
++ if (m->wired) {
++ for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
++ if (mb != m && mb->wired &&
++ (m->desk == NULL || mb->desk == NULL) &&
++ contains(mb->rectangle, m->rectangle)) {
++ if (mm == m)
++ mm = mb;
++ if (m->desk != NULL && mb->desk == NULL && contains(m->rectangle, mb->rectangle)) {
++ mm = m;
++ remove_monitor(mb);
++ } else {
++ merge_monitors(m, mb);
++ remove_monitor(m);
++ }
++ break;
++ }
++ }
++ 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 && (running || pri_mon == NULL || m != pri_mon))
++ add_desktop(m, make_desktop(NULL));
++
++ if (!running && pri_mon != NULL && mon_head != pri_mon)
++ swap_monitors(mon_head, pri_mon);
++
++ free(sres);
++ update_motion_recorder();
++ return (num_monitors > 0);
+ }
+diff --git a/monitor.h b/monitor.h
+index 3c5bc9f..c5b3dc7 100644
+--- a/monitor.h
++++ b/monitor.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_MONITOR_H
+@@ -43,6 +47,6 @@ 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, desktop_select_t sel);
+-bool import_monitors(void);
++bool update_monitors(void);
+
+ #endif
+diff --git a/pointer.c b/pointer.c
+index c06922e..4527c30 100644
+--- a/pointer.c
++++ b/pointer.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include "bspwm.h"
+@@ -33,303 +37,314 @@
+
+ void grab_pointer(pointer_action_t pac)
+ {
+- PRINTF("grab pointer %u\n", pac);
++ PRINTF("grab pointer %u\n", pac);
+
+- xcb_window_t win = XCB_NONE;
+- xcb_point_t pos;
++ xcb_window_t win = XCB_NONE;
++ xcb_point_t pos;
+
+- query_pointer(&win, &pos);
++ query_pointer(&win, &pos);
+
+- coordinates_t loc;
+- if (locate_window(win, &loc)) {
+- client_t *c = NULL;
+- frozen_pointer->position = pos;
+- frozen_pointer->action = pac;
+- c = loc.node->client;
+- frozen_pointer->monitor = loc.monitor;
+- frozen_pointer->desktop = loc.desktop;
+- frozen_pointer->node = loc.node;
+- frozen_pointer->client = c;
+- frozen_pointer->window = c->window;
+- frozen_pointer->horizontal_fence = NULL;
+- frozen_pointer->vertical_fence = NULL;
++ coordinates_t loc;
++ if (locate_window(win, &loc)) {
++ client_t *c = NULL;
++ frozen_pointer->position = pos;
++ frozen_pointer->action = pac;
++ c = loc.node->client;
++ frozen_pointer->monitor = loc.monitor;
++ frozen_pointer->desktop = loc.desktop;
++ frozen_pointer->node = loc.node;
++ frozen_pointer->client = c;
++ frozen_pointer->window = c->window;
++ frozen_pointer->horizontal_fence = NULL;
++ frozen_pointer->vertical_fence = NULL;
+
+- switch (pac) {
+- case ACTION_FOCUS:
+- if (loc.node != mon->desk->focus) {
+- bool backup = pointer_follows_monitor;
+- pointer_follows_monitor = false;
+- focus_node(loc.monitor, loc.desktop, loc.node);
+- pointer_follows_monitor = backup;
+- } else if (focus_follows_pointer) {
+- stack(loc.node, STACK_ABOVE);
+- }
+- frozen_pointer->action = ACTION_NONE;
+- break;
+- case ACTION_MOVE:
+- case ACTION_RESIZE_SIDE:
+- case ACTION_RESIZE_CORNER:
+- if (is_floating(c)) {
+- frozen_pointer->rectangle = c->floating_rectangle;
+- frozen_pointer->is_tiled = false;
+- } else if (is_tiled(c)) {
+- frozen_pointer->rectangle = c->tiled_rectangle;
+- frozen_pointer->is_tiled = (pac == ACTION_MOVE || !c->pseudo_tiled);
+- } else {
+- frozen_pointer->action = ACTION_NONE;
+- return;
+- }
+- if (pac == ACTION_RESIZE_SIDE) {
+- float W = frozen_pointer->rectangle.width;
+- float H = frozen_pointer->rectangle.height;
+- float ratio = W / H;
+- float x = pos.x - frozen_pointer->rectangle.x;
+- float y = pos.y - frozen_pointer->rectangle.y;
+- float diag_a = ratio * y;
+- float diag_b = W - diag_a;
+- if (x < diag_a) {
+- if (x < diag_b)
+- frozen_pointer->side = SIDE_LEFT;
+- else
+- frozen_pointer->side = SIDE_BOTTOM;
+- } else {
+- if (x < diag_b)
+- frozen_pointer->side = SIDE_TOP;
+- else
+- frozen_pointer->side = SIDE_RIGHT;
+- }
+- } else if (pac == ACTION_RESIZE_CORNER) {
+- int16_t mid_x = frozen_pointer->rectangle.x + (frozen_pointer->rectangle.width / 2);
+- int16_t mid_y = frozen_pointer->rectangle.y + (frozen_pointer->rectangle.height / 2);
+- if (pos.x > mid_x) {
+- if (pos.y > mid_y)
+- frozen_pointer->corner = CORNER_BOTTOM_RIGHT;
+- else
+- frozen_pointer->corner = CORNER_TOP_RIGHT;
+- } else {
+- if (pos.y > mid_y)
+- frozen_pointer->corner = CORNER_BOTTOM_LEFT;
+- else
+- frozen_pointer->corner = CORNER_TOP_LEFT;
+- }
+- }
+- if (frozen_pointer->is_tiled) {
+- if (pac == ACTION_RESIZE_SIDE) {
+- switch (frozen_pointer->side) {
+- case SIDE_TOP:
+- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
+- break;
+- case SIDE_RIGHT:
+- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
+- break;
+- case SIDE_BOTTOM:
+- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
+- break;
+- case SIDE_LEFT:
+- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
+- break;
+- }
+- } else if (pac == ACTION_RESIZE_CORNER) {
+- switch (frozen_pointer->corner) {
+- case CORNER_TOP_LEFT:
+- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
+- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
+- break;
+- case CORNER_TOP_RIGHT:
+- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
+- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
+- break;
+- case CORNER_BOTTOM_RIGHT:
+- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
+- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
+- break;
+- case CORNER_BOTTOM_LEFT:
+- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
+- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
+- break;
+- }
+- }
+- if (frozen_pointer->horizontal_fence != NULL)
+- frozen_pointer->horizontal_ratio = frozen_pointer->horizontal_fence->split_ratio;
+- if (frozen_pointer->vertical_fence != NULL)
+- frozen_pointer->vertical_ratio = frozen_pointer->vertical_fence->split_ratio;
+- }
+- break;
+- case ACTION_NONE:
+- break;
+- }
+- } else {
+- if (pac == ACTION_FOCUS) {
+- monitor_t *m = monitor_from_point(pos);
+- if (m != NULL && m != mon)
+- focus_node(m, m->desk, m->desk->focus);
+- }
+- frozen_pointer->action = ACTION_NONE;
+- }
++ switch (pac) {
++ case ACTION_FOCUS:
++ if (loc.node != mon->desk->focus) {
++ bool backup = pointer_follows_monitor;
++ pointer_follows_monitor = false;
++ focus_node(loc.monitor, loc.desktop, loc.node);
++ pointer_follows_monitor = backup;
++ } else if (focus_follows_pointer) {
++ stack(loc.node, STACK_ABOVE);
++ }
++ frozen_pointer->action = ACTION_NONE;
++ break;
++ case ACTION_MOVE:
++ case ACTION_RESIZE_SIDE:
++ case ACTION_RESIZE_CORNER:
++ if (is_floating(c)) {
++ frozen_pointer->rectangle = c->floating_rectangle;
++ frozen_pointer->is_tiled = false;
++ } else if (is_tiled(c)) {
++ frozen_pointer->rectangle = c->tiled_rectangle;
++ frozen_pointer->is_tiled = (pac == ACTION_MOVE || !c->pseudo_tiled);
++ } else {
++ frozen_pointer->action = ACTION_NONE;
++ return;
++ }
++ if (pac == ACTION_RESIZE_SIDE) {
++ float W = frozen_pointer->rectangle.width;
++ float H = frozen_pointer->rectangle.height;
++ float ratio = W / H;
++ float x = pos.x - frozen_pointer->rectangle.x;
++ float y = pos.y - frozen_pointer->rectangle.y;
++ float diag_a = ratio * y;
++ float diag_b = W - diag_a;
++ if (x < diag_a) {
++ if (x < diag_b)
++ frozen_pointer->side = SIDE_LEFT;
++ else
++ frozen_pointer->side = SIDE_BOTTOM;
++ } else {
++ if (x < diag_b)
++ frozen_pointer->side = SIDE_TOP;
++ else
++ frozen_pointer->side = SIDE_RIGHT;
++ }
++ } else if (pac == ACTION_RESIZE_CORNER) {
++ int16_t mid_x = frozen_pointer->rectangle.x + (frozen_pointer->rectangle.width / 2);
++ int16_t mid_y = frozen_pointer->rectangle.y + (frozen_pointer->rectangle.height / 2);
++ if (pos.x > mid_x) {
++ if (pos.y > mid_y)
++ frozen_pointer->corner = CORNER_BOTTOM_RIGHT;
++ else
++ frozen_pointer->corner = CORNER_TOP_RIGHT;
++ } else {
++ if (pos.y > mid_y)
++ frozen_pointer->corner = CORNER_BOTTOM_LEFT;
++ else
++ frozen_pointer->corner = CORNER_TOP_LEFT;
++ }
++ }
++ if (frozen_pointer->is_tiled) {
++ if (pac == ACTION_RESIZE_SIDE) {
++ switch (frozen_pointer->side) {
++ case SIDE_TOP:
++ frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
++ break;
++ case SIDE_RIGHT:
++ frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
++ break;
++ case SIDE_BOTTOM:
++ frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
++ break;
++ case SIDE_LEFT:
++ frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
++ break;
++ }
++ } else if (pac == ACTION_RESIZE_CORNER) {
++ switch (frozen_pointer->corner) {
++ case CORNER_TOP_LEFT:
++ frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
++ frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
++ break;
++ case CORNER_TOP_RIGHT:
++ frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_UP);
++ frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
++ break;
++ case CORNER_BOTTOM_RIGHT:
++ frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
++ frozen_pointer->vertical_fence = find_fence(loc.node, DIR_RIGHT);
++ break;
++ case CORNER_BOTTOM_LEFT:
++ frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_DOWN);
++ frozen_pointer->vertical_fence = find_fence(loc.node, DIR_LEFT);
++ break;
++ }
++ }
++ if (frozen_pointer->horizontal_fence != NULL)
++ frozen_pointer->horizontal_ratio = frozen_pointer->horizontal_fence->split_ratio;
++ if (frozen_pointer->vertical_fence != NULL)
++ frozen_pointer->vertical_ratio = frozen_pointer->vertical_fence->split_ratio;
++ }
++ break;
++ case ACTION_NONE:
++ break;
++ }
++ } else {
++ if (pac == ACTION_FOCUS) {
++ monitor_t *m = monitor_from_point(pos);
++ if (m != NULL && m != mon)
++ focus_node(m, m->desk, m->desk->focus);
++ }
++ frozen_pointer->action = ACTION_NONE;
++ }
+ }
+
+ void track_pointer(int root_x, int root_y)
+ {
+- if (frozen_pointer->action == ACTION_NONE)
+- return;
++ if (frozen_pointer->action == ACTION_NONE)
++ return;
++
++ int delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
++
++ pointer_action_t pac = frozen_pointer->action;
++ monitor_t *m = frozen_pointer->monitor;
++ desktop_t *d = frozen_pointer->desktop;
++ node_t *n = frozen_pointer->node;
++ client_t *c = frozen_pointer->client;
++ xcb_window_t win = frozen_pointer->window;
++ xcb_rectangle_t rect = frozen_pointer->rectangle;
++ node_t *vertical_fence = frozen_pointer->vertical_fence;
++ node_t *horizontal_fence = frozen_pointer->horizontal_fence;
+
+- int16_t delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
+- uint16_t width, height;
++ delta_x = root_x - frozen_pointer->position.x;
++ delta_y = root_y - frozen_pointer->position.y;
+
+- pointer_action_t pac = frozen_pointer->action;
+- monitor_t *m = frozen_pointer->monitor;
+- desktop_t *d = frozen_pointer->desktop;
+- node_t *n = frozen_pointer->node;
+- client_t *c = frozen_pointer->client;
+- xcb_window_t win = frozen_pointer->window;
+- xcb_rectangle_t rect = frozen_pointer->rectangle;
+- node_t *vertical_fence = frozen_pointer->vertical_fence;
+- node_t *horizontal_fence = frozen_pointer->horizontal_fence;
++ switch (pac) {
++ case ACTION_MOVE:
++ if (frozen_pointer->is_tiled) {
++ xcb_window_t pwin = XCB_NONE;
++ query_pointer(&pwin, NULL);
++ if (pwin == win)
++ return;
++ coordinates_t loc;
++ bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
++ if (is_managed && is_tiled(loc.node->client) && loc.monitor == m) {
++ swap_nodes(m, d, n, m, d, loc.node);
++ arrange(m, d);
++ } else {
++ if (is_managed && loc.monitor == m) {
++ return;
++ } else if (!is_managed) {
++ xcb_point_t pt = (xcb_point_t) {root_x, root_y};
++ monitor_t *pmon = monitor_from_point(pt);
++ if (pmon == NULL || pmon == m) {
++ return;
++ } else {
++ loc.monitor = pmon;
++ loc.desktop = pmon->desk;
++ }
++ }
++ bool focused = (n == mon->desk->focus);
++ transfer_node(m, d, n, loc.monitor, loc.desktop, loc.desktop->focus);
++ if (focused)
++ focus_node(loc.monitor, loc.desktop, n);
++ frozen_pointer->monitor = loc.monitor;
++ frozen_pointer->desktop = loc.desktop;
++ }
++ } else {
++ x = rect.x + delta_x;
++ y = rect.y + delta_y;
++ window_move(win, x, y);
++ c->floating_rectangle.x = x;
++ c->floating_rectangle.y = y;
++ xcb_point_t pt = (xcb_point_t) {root_x, root_y};
++ monitor_t *pmon = monitor_from_point(pt);
++ if (pmon == NULL || pmon == m)
++ return;
++ bool focused = (n == mon->desk->focus);
++ transfer_node(m, d, n, pmon, pmon->desk, pmon->desk->focus);
++ if (focused)
++ focus_node(pmon, pmon->desk, n);
++ frozen_pointer->monitor = pmon;
++ frozen_pointer->desktop = pmon->desk;
++ }
++ break;
++ case ACTION_RESIZE_SIDE:
++ case ACTION_RESIZE_CORNER:
++ if (frozen_pointer->is_tiled) {
++ if (vertical_fence != NULL) {
++ double sr = frozen_pointer->vertical_ratio + (double) delta_x / vertical_fence->rectangle.width;
++ sr = MAX(0, sr);
++ sr = MIN(1, sr);
++ vertical_fence->split_ratio = sr;
++ }
++ if (horizontal_fence != NULL) {
++ double sr = frozen_pointer->horizontal_ratio + (double) delta_y / horizontal_fence->rectangle.height;
++ sr = MAX(0, sr);
++ sr = MIN(1, sr);
++ horizontal_fence->split_ratio = sr;
++ }
++ arrange(m, d);
++ } else {
++ if (pac == ACTION_RESIZE_SIDE) {
++ switch (frozen_pointer->side) {
++ case SIDE_TOP:
++ x = rect.x;
++ y = rect.y + delta_y;
++ w = rect.width;
++ h = rect.height - delta_y;
++ break;
++ case SIDE_RIGHT:
++ x = rect.x;
++ y = rect.y;
++ w = rect.width + delta_x;
++ h = rect.height;
++ break;
++ case SIDE_BOTTOM:
++ x = rect.x;
++ y = rect.y;
++ w = rect.width;
++ h = rect.height + delta_y;
++ break;
++ case SIDE_LEFT:
++ x = rect.x + delta_x;
++ y = rect.y;
++ w = rect.width - delta_x;
++ h = rect.height;
++ break;
++ }
++ } else if (pac == ACTION_RESIZE_CORNER) {
++ switch (frozen_pointer->corner) {
++ case CORNER_TOP_LEFT:
++ x = rect.x + delta_x;
++ y = rect.y + delta_y;
++ w = rect.width - delta_x;
++ h = rect.height - delta_y;
++ break;
++ case CORNER_TOP_RIGHT:
++ x = rect.x;
++ y = rect.y + delta_y;
++ w = rect.width + delta_x;
++ h = rect.height - delta_y;
++ break;
++ case CORNER_BOTTOM_LEFT:
++ x = rect.x + delta_x;
++ y = rect.y;
++ w = rect.width - delta_x;
++ h = rect.height + delta_y;
++ break;
++ case CORNER_BOTTOM_RIGHT:
++ x = rect.x;
++ y = rect.y;
++ w = rect.width + delta_x;
++ h = rect.height + delta_y;
++ break;
++ }
++ }
+
+- delta_x = root_x - frozen_pointer->position.x;
+- delta_y = root_y - frozen_pointer->position.y;
++ int oldw = w, oldh = h;
++ restrain_floating_size(c, &w, &h);
+
+- switch (pac) {
+- case ACTION_MOVE:
+- if (frozen_pointer->is_tiled) {
+- xcb_window_t pwin = XCB_NONE;
+- query_pointer(&pwin, NULL);
+- if (pwin == win)
+- return;
+- coordinates_t loc;
+- bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
+- if (is_managed && is_tiled(loc.node->client) && loc.monitor == m) {
+- swap_nodes(m, d, n, m, d, loc.node);
+- arrange(m, d);
+- } else {
+- if (is_managed && loc.monitor == m) {
+- return;
+- } else if (!is_managed) {
+- xcb_point_t pt = (xcb_point_t) {root_x, root_y};
+- monitor_t *pmon = monitor_from_point(pt);
+- if (pmon == NULL || pmon == m) {
+- return;
+- } else {
+- loc.monitor = pmon;
+- loc.desktop = pmon->desk;
+- }
+- }
+- bool focused = (n == mon->desk->focus);
+- transfer_node(m, d, n, loc.monitor, loc.desktop, loc.desktop->focus);
+- if (focused)
+- focus_node(loc.monitor, loc.desktop, n);
+- frozen_pointer->monitor = loc.monitor;
+- frozen_pointer->desktop = loc.desktop;
+- }
+- } else {
+- x = rect.x + delta_x;
+- y = rect.y + delta_y;
+- window_move(win, x, y);
+- c->floating_rectangle.x = x;
+- c->floating_rectangle.y = y;
+- xcb_point_t pt = (xcb_point_t) {root_x, root_y};
+- monitor_t *pmon = monitor_from_point(pt);
+- if (pmon == NULL || pmon == m)
+- return;
+- bool focused = (n == mon->desk->focus);
+- transfer_node(m, d, n, pmon, pmon->desk, pmon->desk->focus);
+- if (focused)
+- focus_node(pmon, pmon->desk, n);
+- frozen_pointer->monitor = pmon;
+- frozen_pointer->desktop = pmon->desk;
+- }
+- break;
+- case ACTION_RESIZE_SIDE:
+- case ACTION_RESIZE_CORNER:
+- if (frozen_pointer->is_tiled) {
+- if (vertical_fence != NULL) {
+- double sr = frozen_pointer->vertical_ratio + (double) delta_x / vertical_fence->rectangle.width;
+- sr = MAX(0, sr);
+- sr = MIN(1, sr);
+- vertical_fence->split_ratio = sr;
+- }
+- if (horizontal_fence != NULL) {
+- double sr = frozen_pointer->horizontal_ratio + (double) delta_y / horizontal_fence->rectangle.height;
+- sr = MAX(0, sr);
+- sr = MIN(1, sr);
+- horizontal_fence->split_ratio = sr;
+- }
+- arrange(m, d);
+- } else {
+- if (pac == ACTION_RESIZE_SIDE) {
+- switch (frozen_pointer->side) {
+- case SIDE_TOP:
+- x = rect.x;
+- y = rect.y + delta_y;
+- w = rect.width;
+- h = rect.height - delta_y;
+- break;
+- case SIDE_RIGHT:
+- x = rect.x;
+- y = rect.y;
+- w = rect.width + delta_x;
+- h = rect.height;
+- break;
+- case SIDE_BOTTOM:
+- x = rect.x;
+- y = rect.y;
+- w = rect.width;
+- h = rect.height + delta_y;
+- break;
+- case SIDE_LEFT:
+- x = rect.x + delta_x;
+- y = rect.y;
+- w = rect.width - delta_x;
+- h = rect.height;
+- break;
+- }
+- } else if (pac == ACTION_RESIZE_CORNER) {
+- switch (frozen_pointer->corner) {
+- case CORNER_TOP_LEFT:
+- x = rect.x + delta_x;
+- y = rect.y + delta_y;
+- w = rect.width - delta_x;
+- h = rect.height - delta_y;
+- break;
+- case CORNER_TOP_RIGHT:
+- x = rect.x;
+- y = rect.y + delta_y;
+- w = rect.width + delta_x;
+- h = rect.height - delta_y;
+- break;
+- case CORNER_BOTTOM_LEFT:
+- x = rect.x + delta_x;
+- y = rect.y;
+- w = rect.width - delta_x;
+- h = rect.height + delta_y;
+- break;
+- case CORNER_BOTTOM_RIGHT:
+- x = rect.x;
+- y = rect.y;
+- w = rect.width + delta_x;
+- h = rect.height + delta_y;
+- break;
+- }
+- }
+- width = MAX(1, w);
+- height = MAX(1, h);
+- if (c->pseudo_tiled) {
+- c->floating_rectangle.width = width;
+- c->floating_rectangle.height = height;
+- arrange(m, d);
+- } else {
+- c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
+- window_move_resize(win, x, y, width, height);
+- }
+- }
+- break;
+- case ACTION_FOCUS:
+- case ACTION_NONE:
+- break;
+- }
++ if (c->pseudo_tiled) {
++ c->floating_rectangle.width = w;
++ c->floating_rectangle.height = h;
++ arrange(m, d);
++ } else {
++ if (oldw == w) {
++ c->floating_rectangle.x = x;
++ c->floating_rectangle.width = w;
++ }
++ if (oldh == h) {
++ c->floating_rectangle.y = y;
++ c->floating_rectangle.height = h;
++ }
++ window_move_resize(win, c->floating_rectangle.x,
++ c->floating_rectangle.y,
++ c->floating_rectangle.width,
++ c->floating_rectangle.height);
++ }
++ }
++ break;
++ case ACTION_FOCUS:
++ case ACTION_NONE:
++ break;
++ }
+ }
+
+ void ungrab_pointer(void)
+ {
+- frozen_pointer->action = ACTION_NONE;
++ frozen_pointer->action = ACTION_NONE;
+ }
+diff --git a/pointer.h b/pointer.h
+index 534c66e..e156dfa 100644
+--- a/pointer.h
++++ b/pointer.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_POINTER_H
+diff --git a/query.c b/query.c
+index 13c3910..2f61882 100644
+--- a/query.c
++++ b/query.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdio.h>
+@@ -33,380 +37,390 @@
+ #include "tree.h"
+ #include "query.h"
+
+-void query_monitors(coordinates_t loc, domain_t dom, char *rsp)
++void query_monitors(coordinates_t loc, domain_t dom, FILE *rsp)
+ {
+- char line[MAXLEN];
+- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+- if (loc.monitor != NULL && m != loc.monitor)
+- continue;
+- if (dom != DOMAIN_DESKTOP) {
+- if (dom == DOMAIN_MONITOR) {
+- snprintf(line, sizeof(line), "%s\n", m->name);
+- strncat(rsp, line, REMLEN(rsp));
+- continue;
+- } else {
+- snprintf(line, sizeof(line), "%s %ux%u%+i%+i %i,%i,%i,%i", m->name, m->rectangle.width, m->rectangle.height, m->rectangle.x, m->rectangle.y, m->top_padding, m->right_padding, m->bottom_padding, m->left_padding);
+- strncat(rsp, line, REMLEN(rsp));
+- if (m == mon)
+- strncat(rsp, " *", REMLEN(rsp));
+- strncat(rsp, "\n", REMLEN(rsp));
+- }
+- }
+- query_desktops(m, dom, loc, (dom == DOMAIN_DESKTOP ? 0 : 1), rsp);
+- }
++ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
++ if (loc.monitor != NULL && m != loc.monitor)
++ continue;
++ if (dom != DOMAIN_DESKTOP) {
++ if (dom == DOMAIN_MONITOR) {
++ fprintf(rsp, "%s\n", m->name);
++ continue;
++ } else {
++ fprintf(rsp, "%s %ux%u%+i%+i %i,%i,%i,%i%s\n", m->name,
++ m->rectangle.width,m->rectangle.height, m->rectangle.x, m->rectangle.y,
++ m->top_padding, m->right_padding, m->bottom_padding, m->left_padding,
++ (m == mon ? " *" : ""));
++ }
++ }
++ query_desktops(m, dom, loc, (dom == DOMAIN_DESKTOP ? 0 : 1), rsp);
++ }
+ }
+
+-void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, char *rsp)
++void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, FILE *rsp)
+ {
+- char line[MAXLEN];
+- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+- if (loc.desktop != NULL && d != loc.desktop)
+- continue;
+- for (unsigned int i = 0; i < depth; i++)
+- strncat(rsp, " ", REMLEN(rsp));
+- if (dom == DOMAIN_DESKTOP) {
+- snprintf(line, sizeof(line), "%s\n", d->name);
+- strncat(rsp, line, REMLEN(rsp));
+- continue;
+- } else {
+- snprintf(line, sizeof(line), "%s %u %i %i,%i,%i,%i %c %c", d->name, d->border_width, d->window_gap, d->top_padding, d->right_padding, d->bottom_padding, d->left_padding, (d->layout == LAYOUT_TILED ? 'T' : 'M'), (d->floating ? 'f' : '-'));
+- strncat(rsp, line, REMLEN(rsp));
+- if (d == m->desk)
+- strncat(rsp, " *", REMLEN(rsp));
+- strncat(rsp, "\n", REMLEN(rsp));
+- }
+- query_tree(d, d->root, rsp, depth + 1);
+- }
++ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
++ if (loc.desktop != NULL && d != loc.desktop)
++ continue;
++ for (unsigned int i = 0; i < depth; i++)
++ fprintf(rsp, "\t");
++ if (dom == DOMAIN_DESKTOP) {
++ fprintf(rsp, "%s\n", d->name);
++ continue;
++ } else {
++ fprintf(rsp, "%s %u %i %i,%i,%i,%i %c %c%s\n", d->name, d->border_width,
++ d->window_gap,
++ d->top_padding, d->right_padding, d->bottom_padding, d->left_padding,
++ (d->layout == LAYOUT_TILED ? 'T' : 'M'), (d->floating ? 'f' : '-'),
++ (d == m->desk ? " *" : ""));
++ }
++ query_tree(d, d->root, rsp, depth + 1);
++ }
+ }
+
+-void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
++void query_tree(desktop_t *d, node_t *n, FILE *rsp, unsigned int depth)
+ {
+- if (n == NULL)
+- return;
+-
+- char line[MAXLEN];
+-
+- for (unsigned int i = 0; i < depth; i++)
+- strncat(rsp, " ", REMLEN(rsp));
+-
+- if (is_leaf(n)) {
+- client_t *c = n->client;
+- snprintf(line, sizeof(line), "%c %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))), (c->floating ? 'f' : '-'), (c->pseudo_tiled ? 'd' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-'));
+- } else {
+- snprintf(line, sizeof(line), "%c %c %lf", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
+- }
+-
+- strncat(rsp, line, REMLEN(rsp));
+-
+- if (n == d->focus)
+- strncat(rsp, " *", REMLEN(rsp));
+- strncat(rsp, "\n", REMLEN(rsp));
+-
+- query_tree(d, n->first_child, rsp, depth + 1);
+- query_tree(d, n->second_child, rsp, depth + 1);
++ if (n == NULL)
++ return;
++
++ for (unsigned int i = 0; i < depth; i++)
++ fprintf(rsp, "\t");
++
++ if (is_leaf(n)) {
++ client_t *c = n->client;
++ fprintf(rsp, "%c %s %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c%c%s\n",
++ (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')),
++ c->class_name, c->instance_name, c->window, c->border_width,
++ c->floating_rectangle.width, c->floating_rectangle.height,
++ c->floating_rectangle.x, c->floating_rectangle.y,
++ (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))),
++ (c->floating ? 'f' : '-'), (c->pseudo_tiled ? 'd' : '-'), (c->fullscreen ? 'F' : '-'),
++ (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'),
++ (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-'),
++ (n == d->focus ? " *" : ""));
++ } else {
++ fprintf(rsp, "%c %c %lf\n", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'),
++ (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
++ }
++
++ query_tree(d, n->first_child, rsp, depth + 1);
++ query_tree(d, n->second_child, rsp, depth + 1);
+ }
+
+-void query_history(coordinates_t loc, char *rsp)
++void query_history(coordinates_t loc, FILE *rsp)
+ {
+- char line[MAXLEN];
+- for (history_t *h = history_head; h != NULL; h = h->next) {
+- if ((loc.monitor != NULL && h->loc.monitor != loc.monitor)
+- || (loc.desktop != NULL && h->loc.desktop != loc.desktop))
+- continue;
+- xcb_window_t win = XCB_NONE;
+- if (h->loc.node != NULL)
+- win = h->loc.node->client->window;
+- snprintf(line, sizeof(line), "%s %s 0x%X", h->loc.monitor->name, h->loc.desktop->name, win);
+- strncat(rsp, line, REMLEN(rsp));
+- strncat(rsp, "\n", REMLEN(rsp));
+- }
++ for (history_t *h = history_head; h != NULL; h = h->next) {
++ if ((loc.monitor != NULL && h->loc.monitor != loc.monitor)
++ || (loc.desktop != NULL && h->loc.desktop != loc.desktop))
++ continue;
++ xcb_window_t win = XCB_NONE;
++ if (h->loc.node != NULL)
++ win = h->loc.node->client->window;
++ fprintf(rsp, "%s %s 0x%X\n", h->loc.monitor->name, h->loc.desktop->name, win);
++ }
+ }
+
+-void query_stack(char *rsp)
++void query_stack(FILE *rsp)
+ {
+- char line[MAXLEN];
+- for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+- snprintf(line, sizeof(line), "0x%X", s->node->client->window);
+- strncat(rsp, line, REMLEN(rsp));
+- strncat(rsp, "\n", REMLEN(rsp));
+- }
++ for (stacking_list_t *s = stack_head; s != NULL; s = s->next)
++ fprintf(rsp, "0x%X\n", s->node->client->window);
+ }
+
+-void query_windows(coordinates_t loc, char *rsp)
++void query_windows(coordinates_t loc, FILE *rsp)
+ {
+- char line[MAXLEN];
+-
+- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+- if (loc.monitor != NULL && m != loc.monitor)
+- continue;
+- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+- if (loc.desktop != NULL && d != loc.desktop)
+- continue;
+- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+- if (loc.node != NULL && n != loc.node)
+- continue;
+- snprintf(line, sizeof(line), "0x%X\n", n->client->window);
+- strncat(rsp, line, REMLEN(rsp));
+- }
+- }
+- }
++ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
++ if (loc.monitor != NULL && m != loc.monitor)
++ continue;
++ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
++ if (loc.desktop != NULL && d != loc.desktop)
++ continue;
++ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
++ if (loc.node != NULL && n != loc.node)
++ continue;
++ fprintf(rsp, "0x%X\n", n->client->window);
++ }
++ }
++ }
+ }
+
+ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+ {
+- client_select_t sel = {CLIENT_TYPE_ALL, CLIENT_CLASS_ALL, false, false, false};
+- char *tok;
+- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+- tok[0] = '\0';
+- tok++;
+- if (streq("tiled", tok)) {
+- sel.type = CLIENT_TYPE_TILED;
+- } else if (streq("floating", tok)) {
+- sel.type = CLIENT_TYPE_FLOATING;
+- } else if (streq("like", tok)) {
+- sel.class = CLIENT_CLASS_EQUAL;
+- } else if (streq("unlike", tok)) {
+- sel.class = CLIENT_CLASS_DIFFER;
+- } else if (streq("urgent", tok)) {
+- sel.urgent = true;
+- } else if (streq("manual", tok)) {
+- sel.manual = true;
+- } else if (streq("local", tok)) {
+- sel.local = true;
+- }
+- }
+-
+- dst->monitor = ref->monitor;
+- dst->desktop = ref->desktop;
+- dst->node = NULL;
+-
+- direction_t dir;
+- cycle_dir_t cyc;
+- history_dir_t hdi;
+- if (parse_direction(desc, &dir)) {
+- dst->node = nearest_neighbor(ref->monitor, ref->desktop, ref->node, dir, sel);
+- } else if (parse_cycle_direction(desc, &cyc)) {
+- dst->node = closest_node(ref->monitor, ref->desktop, ref->node, 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)) {
+- dst->node = find_biggest(ref->monitor, ref->desktop, ref->node, sel);
+- } else if (streq("focused", desc)) {
+- coordinates_t loc = {mon, mon->desk, mon->desk->focus};
+- if (node_matches(&loc, ref, sel)) {
+- dst->monitor = mon;
+- dst->desktop = mon->desk;
+- dst->node = mon->desk->focus;
+- }
+- } else {
+- long int wid;
+- if (parse_window_id(desc, &wid))
+- locate_window(wid, dst);
+- }
+-
+- return (dst->node != NULL);
++ client_select_t sel = {CLIENT_TYPE_ALL, CLIENT_CLASS_ALL, CLIENT_MODE_ALL, false, false};
++ char *tok;
++ while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
++ tok[0] = '\0';
++ tok++;
++ if (streq("tiled", tok)) {
++ sel.type = CLIENT_TYPE_TILED;
++ } else if (streq("floating", tok)) {
++ sel.type = CLIENT_TYPE_FLOATING;
++ } else if (streq("like", tok)) {
++ sel.class = CLIENT_CLASS_EQUAL;
++ } else if (streq("unlike", tok)) {
++ sel.class = CLIENT_CLASS_DIFFER;
++ } else if (streq("manual", tok)) {
++ sel.mode = CLIENT_MODE_MANUAL;
++ } else if (streq("automatic", tok)) {
++ sel.mode = CLIENT_MODE_AUTOMATIC;
++ } else if (streq("urgent", tok)) {
++ sel.urgent = true;
++ } else if (streq("local", tok)) {
++ sel.local = true;
++ }
++ }
++
++ dst->monitor = ref->monitor;
++ dst->desktop = ref->desktop;
++ dst->node = NULL;
++
++ direction_t dir;
++ cycle_dir_t cyc;
++ history_dir_t hdi;
++ if (parse_direction(desc, &dir)) {
++ dst->node = nearest_neighbor(ref->monitor, ref->desktop, ref->node, dir, sel);
++ if (dst->node == NULL && num_monitors > 1) {
++ monitor_t *m = nearest_monitor(ref->monitor, dir, (desktop_select_t) {DESKTOP_STATUS_ALL, false, false});
++ if (m != NULL) {
++ dst->monitor = m;
++ dst->desktop = m->desk;
++ dst->node = m->desk->focus;
++ }
++ }
++ } else if (parse_cycle_direction(desc, &cyc)) {
++ dst->node = closest_node(ref->monitor, ref->desktop, ref->node, 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)) {
++ dst->node = find_biggest(ref->monitor, ref->desktop, ref->node, sel);
++ } else if (streq("focused", desc)) {
++ coordinates_t loc = {mon, mon->desk, mon->desk->focus};
++ if (node_matches(&loc, ref, sel)) {
++ dst->monitor = mon;
++ dst->desktop = mon->desk;
++ dst->node = mon->desk->focus;
++ }
++ } else {
++ long int wid;
++ if (parse_window_id(desc, &wid))
++ locate_window(wid, dst);
++ }
++
++ return (dst->node != NULL);
+ }
+
+ bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+ {
+- desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
+- char *tok;
+- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+- tok[0] = '\0';
+- tok++;
+- if (streq("free", tok)) {
+- sel.status = DESKTOP_STATUS_FREE;
+- } else if (streq("occupied", tok)) {
+- sel.status = DESKTOP_STATUS_OCCUPIED;
+- } else if (streq("urgent", tok)) {
+- sel.urgent = true;
+- } else if (streq("local", tok)) {
+- sel.local = true;
+- }
+- }
+-
+- dst->desktop = NULL;
+-
+- cycle_dir_t cyc;
+- history_dir_t hdi;
+- char *colon;
+- int idx;
+- if (parse_cycle_direction(desc, &cyc)) {
+- dst->monitor = ref->monitor;
+- dst->desktop = closest_desktop(ref->monitor, ref->desktop, 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->monitor = mon;
+- dst->desktop = mon->desk;
+- }
+- } else if ((colon = index(desc, ':')) != NULL) {
+- *colon = '\0';
+- if (streq("focused", desc))
+- if (monitor_from_desc(colon + 1, ref, dst))
+- dst->desktop = dst->monitor->desk;
+- } else if (parse_index(desc, &idx)) {
+- desktop_from_index(idx, dst);
+- } else {
+- locate_desktop(desc, dst);
+- }
+-
+- return (dst->desktop != NULL);
++ desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
++ char *tok;
++ while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
++ tok[0] = '\0';
++ tok++;
++ if (streq("free", tok)) {
++ sel.status = DESKTOP_STATUS_FREE;
++ } else if (streq("occupied", tok)) {
++ sel.status = DESKTOP_STATUS_OCCUPIED;
++ } else if (streq("urgent", tok)) {
++ sel.urgent = true;
++ } else if (streq("local", tok)) {
++ sel.local = true;
++ }
++ }
++
++ dst->desktop = NULL;
++
++ cycle_dir_t cyc;
++ history_dir_t hdi;
++ char *colon;
++ int idx;
++ if (parse_cycle_direction(desc, &cyc)) {
++ dst->monitor = ref->monitor;
++ dst->desktop = closest_desktop(ref->monitor, ref->desktop, 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->monitor = mon;
++ dst->desktop = mon->desk;
++ }
++ } else if ((colon = strchr(desc, ':')) != NULL) {
++ *colon = '\0';
++ if (monitor_from_desc(desc, ref, dst)) {
++ if (streq("focused", colon + 1)) {
++ dst->desktop = dst->monitor->desk;
++ } else if (parse_index(colon + 1, &idx)) {
++ desktop_from_index(idx, dst, dst->monitor);
++ }
++ }
++ } else if (parse_index(desc, &idx)) {
++ desktop_from_index(idx, dst, NULL);
++ } else {
++ locate_desktop(desc, dst);
++ }
++
++ return (dst->desktop != NULL);
+ }
+
+ bool monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+ {
+- desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
+- char *tok;
+- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+- tok[0] = '\0';
+- tok++;
+- if (streq("free", tok)) {
+- sel.status = DESKTOP_STATUS_FREE;
+- } else if (streq("occupied", tok)) {
+- sel.status = DESKTOP_STATUS_OCCUPIED;
+- }
+- }
+-
+- dst->monitor = NULL;
+-
+- direction_t dir;
+- cycle_dir_t cyc;
+- history_dir_t hdi;
+- int idx;
+- 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, pri_mon->desk, NULL};
+- if (desktop_matches(&loc, ref, sel))
+- dst->monitor = pri_mon;
+- }
+- } else if (streq("focused", desc)) {
+- coordinates_t loc = {mon, mon->desk, NULL};
+- if (desktop_matches(&loc, ref, sel))
+- dst->monitor = mon;
+- } else if (parse_index(desc, &idx)) {
+- monitor_from_index(idx, dst);
+- } else {
+- locate_monitor(desc, dst);
+- }
+-
+- return (dst->monitor != NULL);
++ desktop_select_t sel = {DESKTOP_STATUS_ALL, false, false};
++ char *tok;
++ while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
++ tok[0] = '\0';
++ tok++;
++ if (streq("free", tok)) {
++ sel.status = DESKTOP_STATUS_FREE;
++ } else if (streq("occupied", tok)) {
++ sel.status = DESKTOP_STATUS_OCCUPIED;
++ }
++ }
++
++ dst->monitor = NULL;
++
++ direction_t dir;
++ cycle_dir_t cyc;
++ history_dir_t hdi;
++ int idx;
++ 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, pri_mon->desk, NULL};
++ if (desktop_matches(&loc, ref, sel))
++ dst->monitor = pri_mon;
++ }
++ } else if (streq("focused", desc)) {
++ coordinates_t loc = {mon, mon->desk, NULL};
++ if (desktop_matches(&loc, ref, sel))
++ dst->monitor = mon;
++ } else if (parse_index(desc, &idx)) {
++ monitor_from_index(idx, dst);
++ } else {
++ locate_monitor(desc, dst);
++ }
++
++ return (dst->monitor != NULL);
+ }
+
+ 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->window == win) {
+- loc->monitor = m;
+- loc->desktop = d;
+- loc->node = n;
+- return true;
+- }
+- return false;
++ 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->window == 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;
++ 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;
++ 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_index(int i, coordinates_t *loc)
++bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm)
+ {
+- 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 == 1) {
+- loc->monitor = m;
+- loc->desktop = d;
+- loc->node = NULL;
+- return true;
+- }
+- return false;
++ 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, i--)
++ if (i == 1) {
++ loc->monitor = m;
++ loc->desktop = d;
++ loc->node = NULL;
++ return true;
++ }
++ }
++ return false;
+ }
+
+ bool monitor_from_index(int i, coordinates_t *loc)
+ {
+- for (monitor_t *m = mon_head; m != NULL; m = m->next, i--)
+- if (i == 1) {
+- loc->monitor = m;
+- loc->desktop = NULL;
+- loc->node = NULL;
+- return true;
+- }
+- return false;
++ for (monitor_t *m = mon_head; m != NULL; m = m->next, i--)
++ if (i == 1) {
++ loc->monitor = m;
++ loc->desktop = NULL;
++ loc->node = NULL;
++ return true;
++ }
++ return false;
+ }
+
+ bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel)
+ {
+- if (sel.type != CLIENT_TYPE_ALL &&
+- is_tiled(loc->node->client)
+- ? sel.type == CLIENT_TYPE_FLOATING
+- : sel.type == CLIENT_TYPE_TILED)
+- return false;
+-
+- if (sel.class != CLIENT_CLASS_ALL &&
+- streq(loc->node->client->class_name, ref->node->client->class_name)
+- ? sel.class == CLIENT_CLASS_DIFFER
+- : sel.class == CLIENT_CLASS_EQUAL)
+- return false;
+-
+- if (sel.manual && loc->node->split_mode != MODE_MANUAL)
+- return false;
+-
+- if (sel.local && loc->desktop != ref->desktop)
+- return false;
+-
+- if (sel.urgent && !loc->node->client->urgent)
+- return false;
+-
+- return true;
++ if (ref->node == NULL || loc->node == NULL)
++ return false;
++
++ if (sel.type != CLIENT_TYPE_ALL &&
++ is_tiled(loc->node->client)
++ ? sel.type == CLIENT_TYPE_FLOATING
++ : sel.type == CLIENT_TYPE_TILED)
++ return false;
++
++ if (sel.class != CLIENT_CLASS_ALL &&
++ streq(loc->node->client->class_name, ref->node->client->class_name)
++ ? sel.class == CLIENT_CLASS_DIFFER
++ : sel.class == CLIENT_CLASS_EQUAL)
++ return false;
++
++ if (sel.mode != CLIENT_MODE_ALL &&
++ loc->node->split_mode == MODE_MANUAL
++ ? sel.mode == CLIENT_MODE_AUTOMATIC
++ : sel.mode == CLIENT_MODE_MANUAL)
++ return false;
++
++ if (sel.local && loc->desktop != ref->desktop)
++ return false;
++
++ if (sel.urgent && !loc->node->client->urgent)
++ return false;
++
++ return true;
+ }
+
+ bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel)
+ {
+- if (sel.status != DESKTOP_STATUS_ALL &&
+- loc->desktop->root == NULL
+- ? sel.status == DESKTOP_STATUS_OCCUPIED
+- : sel.status == DESKTOP_STATUS_FREE)
+- return false;
++ if (sel.status != DESKTOP_STATUS_ALL &&
++ loc->desktop->root == NULL
++ ? sel.status == DESKTOP_STATUS_OCCUPIED
++ : sel.status == DESKTOP_STATUS_FREE)
++ return false;
+
+- if (sel.urgent && !is_urgent(loc->desktop))
+- return false;
++ if (sel.urgent && !is_urgent(loc->desktop))
++ return false;
+
+- if (sel.local && ref->monitor != loc->monitor)
+- return false;
++ if (sel.local && ref->monitor != loc->monitor)
++ return false;
+
+- return true;
++ return true;
+ }
+diff --git a/query.h b/query.h
+index bb2db30..8cd2ee7 100644
+--- a/query.h
++++ b/query.h
+@@ -1,52 +1,56 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_QUERY_H
+ #define BSPWM_QUERY_H
+
+ typedef enum {
+- DOMAIN_MONITOR,
+- DOMAIN_DESKTOP,
+- DOMAIN_WINDOW,
+- DOMAIN_TREE,
+- DOMAIN_HISTORY,
+- DOMAIN_STACK
++ DOMAIN_MONITOR,
++ DOMAIN_DESKTOP,
++ DOMAIN_WINDOW,
++ DOMAIN_TREE,
++ DOMAIN_HISTORY,
++ DOMAIN_STACK
+ } domain_t;
+
+-void query_monitors(coordinates_t loc, domain_t dom, char *rsp);
+-void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, char *rsp);
+-void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth);
+-void query_history(coordinates_t loc, char *rsp);
+-void query_stack(char *rsp);
+-void query_windows(coordinates_t loc, char *rsp);
++void query_monitors(coordinates_t loc, domain_t dom, FILE *rsp);
++void query_desktops(monitor_t *m, domain_t dom, coordinates_t loc, unsigned int depth, FILE *rsp);
++void query_tree(desktop_t *d, node_t *n, FILE *rsp, unsigned int depth);
++void query_history(coordinates_t loc, FILE *rsp);
++void query_stack(FILE *rsp);
++void query_windows(coordinates_t loc, FILE *rsp);
+ bool node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+ bool desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+ bool 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_index(int i, coordinates_t *loc);
++bool desktop_from_index(int i, coordinates_t *loc, monitor_t *mm);
+ bool monitor_from_index(int i, coordinates_t *loc);
+ bool node_matches(coordinates_t *loc, coordinates_t *ref, client_select_t sel);
+ bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
+diff --git a/restore.c b/restore.c
+index f136156..24ae84d 100644
+--- a/restore.c
++++ b/restore.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <ctype.h>
+@@ -37,229 +41,236 @@
+
+ void restore_tree(char *file_path)
+ {
+- if (file_path == NULL)
+- return;
++ if (file_path == NULL)
++ return;
+
+- FILE *snapshot = fopen(file_path, "r");
+- if (snapshot == NULL) {
+- warn("Restore tree: can't open file\n");
+- return;
+- }
++ FILE *snapshot = fopen(file_path, "r");
++ if (snapshot == NULL) {
++ warn("Restore tree: can't open file\n");
++ return;
++ }
+
+- PUTS("restore tree");
++ PUTS("restore tree");
+
+- char line[MAXLEN];
+- char name[MAXLEN];
+- coordinates_t loc;
+- monitor_t *m = NULL;
+- desktop_t *d = NULL;
+- node_t *n = NULL;
+- unsigned int level, last_level = 0;
++ char line[MAXLEN];
++ char name[MAXLEN];
++ coordinates_t loc;
++ monitor_t *m = NULL;
++ desktop_t *d = NULL;
++ node_t *n = NULL;
++ unsigned int level, last_level = 0;
+
+- while (fgets(line, sizeof(line), snapshot) != NULL) {
+- unsigned int len = strlen(line);
+- level = 0;
++ while (fgets(line, sizeof(line), snapshot) != NULL) {
++ unsigned int len = strlen(line);
++ level = 0;
+
+- while (level < len && isspace(line[level]))
+- level++;
++ while (level < len && isspace(line[level]))
++ level++;
+
+- if (level == 0) {
+- int x, y, top, right, bottom, left;
+- unsigned int w, h;
+- char end = 0;
+- name[0] = '\0';
+- sscanf(line + level, "%s %ux%u%i%i %i,%i,%i,%i %c", name, &w, &h, &x, &y, &top, &right, &bottom, &left, &end);
+- m = find_monitor(name);
+- if (m == NULL)
+- continue;
+- m->rectangle = (xcb_rectangle_t) {x, y, w, h};
+- m->top_padding = top;
+- m->right_padding = right;
+- m->bottom_padding = bottom;
+- m->left_padding = left;
+- if (end != 0)
+- mon = m;
+- } else if (level == 2) {
+- if (m == NULL)
+- continue;
+- int wg, top, right, bottom, left;
+- unsigned int bw;
+- char floating, layout = 0, end = 0;
+- name[0] = '\0';
+- loc.desktop = NULL;
+- sscanf(line + level, "%s %u %i %i,%i,%i,%i %c %c %c", name, &bw, &wg, &top, &right, &bottom, &left, &layout, &floating, &end);
+- locate_desktop(name, &loc);
+- d = loc.desktop;
+- if (d == NULL)
+- continue;
+- d->border_width = bw;
+- d->window_gap = wg;
+- d->top_padding = top;
+- d->right_padding = right;
+- d->bottom_padding = bottom;
+- d->left_padding = left;
+- if (layout == 'M')
+- d->layout = LAYOUT_MONOCLE;
+- else if (layout == 'T')
+- d->layout = LAYOUT_TILED;
+- d->floating = (floating == '-' ? false : true);
+- if (end != 0)
+- m->desk = d;
+- } else {
+- if (m == NULL || d == NULL)
+- continue;
+- node_t *birth = make_node();
+- if (level == 4) {
+- empty_desktop(d);
+- d->root = birth;
+- } else if (n != NULL) {
+- if (level > last_level) {
+- n->first_child = birth;
+- } else {
+- do {
+- n = n->parent;
+- } while (n != NULL && n->second_child != NULL);
+- if (n == NULL)
+- continue;
+- n->second_child = birth;
+- }
+- birth->parent = n;
+- }
+- n = birth;
+- char br;
+- if (isupper(line[level])) {
+- char st;
+- sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio);
+- if (st == 'H')
+- n->split_type = TYPE_HORIZONTAL;
+- else if (st == 'V')
+- n->split_type = TYPE_VERTICAL;
+- } else {
+- client_t *c = make_client(XCB_NONE);
+- num_clients++;
+- char floating, pseudo_tiled, fullscreen, urgent, locked, sticky, private, sd, sm, end = 0;
+- sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &pseudo_tiled, &fullscreen, &urgent, &locked, &sticky, &private, &sm, &end);
+- c->floating = (floating == '-' ? false : true);
+- c->pseudo_tiled = (pseudo_tiled == '-' ? false : true);
+- c->fullscreen = (fullscreen == '-' ? false : true);
+- c->urgent = (urgent == '-' ? false : true);
+- c->locked = (locked == '-' ? false : true);
+- c->sticky = (sticky == '-' ? false : true);
+- c->private = (private == '-' ? false : true);
+- n->split_mode = (sm == '-' ? MODE_AUTOMATIC : MODE_MANUAL);
+- if (sd == 'U')
+- n->split_dir = DIR_UP;
+- else if (sd == 'R')
+- n->split_dir = DIR_RIGHT;
+- else if (sd == 'D')
+- n->split_dir = DIR_DOWN;
+- else if (sd == 'L')
+- n->split_dir = DIR_LEFT;
+- n->client = c;
+- if (end != 0)
+- d->focus = n;
+- if (c->sticky)
+- m->num_sticky++;
+- }
+- if (br == 'a')
+- n->birth_rotation = 90;
+- else if (br == 'c')
+- n->birth_rotation = 270;
+- else if (br == 'm')
+- n->birth_rotation = 0;
+- }
+- last_level = level;
+- }
++ if (level == 0) {
++ int x, y, top, right, bottom, left;
++ unsigned int w, h;
++ char end = 0;
++ name[0] = '\0';
++ sscanf(line + level, "%s %ux%u%i%i %i,%i,%i,%i %c", name, &w, &h, &x, &y,
++ &top, &right, &bottom, &left, &end);
++ m = find_monitor(name);
++ if (m == NULL)
++ continue;
++ m->rectangle = (xcb_rectangle_t) {x, y, w, h};
++ m->top_padding = top;
++ m->right_padding = right;
++ m->bottom_padding = bottom;
++ m->left_padding = left;
++ if (end != 0)
++ mon = m;
++ } else if (level == 1) {
++ if (m == NULL)
++ continue;
++ int wg, top, right, bottom, left;
++ unsigned int bw;
++ char floating, layout = 0, end = 0;
++ name[0] = '\0';
++ loc.desktop = NULL;
++ sscanf(line + level, "%s %u %i %i,%i,%i,%i %c %c %c", name,
++ &bw, &wg, &top, &right, &bottom, &left, &layout, &floating, &end);
++ locate_desktop(name, &loc);
++ d = loc.desktop;
++ if (d == NULL)
++ continue;
++ d->border_width = bw;
++ d->window_gap = wg;
++ d->top_padding = top;
++ d->right_padding = right;
++ d->bottom_padding = bottom;
++ d->left_padding = left;
++ if (layout == 'M')
++ d->layout = LAYOUT_MONOCLE;
++ else if (layout == 'T')
++ d->layout = LAYOUT_TILED;
++ d->floating = (floating == '-' ? false : true);
++ if (end != 0)
++ m->desk = d;
++ } else {
++ if (m == NULL || d == NULL)
++ continue;
++ node_t *birth = make_node();
++ if (level == 2) {
++ empty_desktop(d);
++ d->root = birth;
++ } else if (n != NULL) {
++ if (level > last_level) {
++ n->first_child = birth;
++ } else {
++ do {
++ n = n->parent;
++ } while (n != NULL && n->second_child != NULL);
++ if (n == NULL)
++ continue;
++ n->second_child = birth;
++ }
++ birth->parent = n;
++ }
++ n = birth;
++ char br;
++ if (isupper(line[level])) {
++ char st;
++ sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio);
++ if (st == 'H')
++ n->split_type = TYPE_HORIZONTAL;
++ else if (st == 'V')
++ n->split_type = TYPE_VERTICAL;
++ } else {
++ client_t *c = make_client(XCB_NONE, d->border_width);
++ num_clients++;
++ char floating, pseudo_tiled, fullscreen, urgent, locked, sticky, private, sd, sm, end = 0;
++ sscanf(line + level, "%c %s %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c %c", &br,
++ c->class_name, c->instance_name, &c->window, &c->border_width,
++ &c->floating_rectangle.width, &c->floating_rectangle.height,
++ &c->floating_rectangle.x, &c->floating_rectangle.y,
++ &sd, &floating, &pseudo_tiled, &fullscreen, &urgent,
++ &locked, &sticky, &private, &sm, &end);
++ c->floating = (floating == '-' ? false : true);
++ c->pseudo_tiled = (pseudo_tiled == '-' ? false : true);
++ c->fullscreen = (fullscreen == '-' ? false : true);
++ c->urgent = (urgent == '-' ? false : true);
++ c->locked = (locked == '-' ? false : true);
++ c->sticky = (sticky == '-' ? false : true);
++ c->private = (private == '-' ? false : true);
++ n->split_mode = (sm == '-' ? MODE_AUTOMATIC : MODE_MANUAL);
++ if (sd == 'U')
++ n->split_dir = DIR_UP;
++ else if (sd == 'R')
++ n->split_dir = DIR_RIGHT;
++ else if (sd == 'D')
++ n->split_dir = DIR_DOWN;
++ else if (sd == 'L')
++ n->split_dir = DIR_LEFT;
++ n->client = c;
++ if (end != 0)
++ d->focus = n;
++ if (c->sticky)
++ m->num_sticky++;
++ }
++ if (br == 'a')
++ n->birth_rotation = 90;
++ else if (br == 'c')
++ n->birth_rotation = 270;
++ else if (br == 'm')
++ n->birth_rotation = 0;
++ }
++ last_level = level;
++ }
+
+- fclose(snapshot);
++ fclose(snapshot);
+
+- 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)) {
+- uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+- xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
+- if (n->client->floating) {
+- n->vacant = true;
+- update_vacant_state(n->parent);
+- }
+- if (n->client->private)
+- update_privacy_level(n, true);
+- }
+- ewmh_update_current_desktop();
++ 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)) {
++ uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
++ xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
++ if (n->client->floating) {
++ n->vacant = true;
++ update_vacant_state(n->parent);
++ }
++ if (n->client->private)
++ update_privacy_level(n, true);
++ }
++ ewmh_update_current_desktop();
+ }
+
+ void restore_history(char *file_path)
+ {
+- if (file_path == NULL)
+- return;
++ if (file_path == NULL)
++ return;
+
+- FILE *snapshot = fopen(file_path, "r");
+- if (snapshot == NULL) {
+- warn("Restore history: can't open '%s'.\n", file_path);
+- return;
+- }
++ FILE *snapshot = fopen(file_path, "r");
++ if (snapshot == NULL) {
++ warn("Restore history: can't open '%s'.\n", file_path);
++ return;
++ }
+
+- PUTS("restore history");
++ PUTS("restore history");
+
+- char line[MAXLEN];
+- char mnm[SMALEN];
+- char dnm[SMALEN];
+- xcb_window_t win;
++ char line[MAXLEN];
++ char mnm[SMALEN];
++ char dnm[SMALEN];
++ xcb_window_t win;
+
+- while (fgets(line, sizeof(line), snapshot) != NULL) {
+- if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
+- coordinates_t loc;
+- if (win != XCB_NONE && !locate_window(win, &loc)) {
+- warn("Can't locate window 0x%X.\n", win);
+- continue;
+- }
+- node_t *n = (win == XCB_NONE ? NULL : loc.node);
+- if (!locate_desktop(dnm, &loc)) {
+- warn("Can't locate desktop '%s'.\n", dnm);
+- continue;
+- }
+- desktop_t *d = loc.desktop;
+- if (!locate_monitor(mnm, &loc)) {
+- warn("Can't locate monitor '%s'.\n", mnm);
+- continue;
+- }
+- monitor_t *m = loc.monitor;
+- history_add(m, d, n);
+- } else {
+- warn("Can't parse history entry: '%s'\n", line);
+- }
+- }
++ while (fgets(line, sizeof(line), snapshot) != NULL) {
++ if (sscanf(line, "%s %s %X", mnm, dnm, &win) == 3) {
++ coordinates_t loc;
++ if (win != XCB_NONE && !locate_window(win, &loc)) {
++ warn("Can't locate window 0x%X.\n", win);
++ continue;
++ }
++ node_t *n = (win == XCB_NONE ? NULL : loc.node);
++ if (!locate_desktop(dnm, &loc)) {
++ warn("Can't locate desktop '%s'.\n", dnm);
++ continue;
++ }
++ desktop_t *d = loc.desktop;
++ if (!locate_monitor(mnm, &loc)) {
++ warn("Can't locate monitor '%s'.\n", mnm);
++ continue;
++ }
++ monitor_t *m = loc.monitor;
++ history_add(m, d, n);
++ } else {
++ warn("Can't parse history entry: '%s'\n", line);
++ }
++ }
+
+- fclose(snapshot);
++ fclose(snapshot);
+ }
+
+ void restore_stack(char *file_path)
+ {
+- if (file_path == NULL)
+- return;
++ if (file_path == NULL)
++ return;
+
+- FILE *snapshot = fopen(file_path, "r");
+- if (snapshot == NULL) {
+- warn("Restore stack: can't open '%s'.\n", file_path);
+- return;
+- }
++ FILE *snapshot = fopen(file_path, "r");
++ if (snapshot == NULL) {
++ warn("Restore stack: can't open '%s'.\n", file_path);
++ return;
++ }
+
+- PUTS("restore stack");
++ PUTS("restore stack");
+
+- char line[MAXLEN];
+- xcb_window_t win;
++ char line[MAXLEN];
++ xcb_window_t win;
+
+- while (fgets(line, sizeof(line), snapshot) != NULL) {
+- if (sscanf(line, "%X", &win) == 1) {
+- coordinates_t loc;
+- if (locate_window(win, &loc))
+- stack_insert_after(stack_tail, loc.node);
+- else
+- warn("Can't locate window 0x%X.\n", win);
+- } else {
+- warn("Can't parse stack entry: '%s'\n", line);
+- }
+- }
++ while (fgets(line, sizeof(line), snapshot) != NULL) {
++ if (sscanf(line, "%X", &win) == 1) {
++ coordinates_t loc;
++ if (locate_window(win, &loc))
++ stack_insert_after(stack_tail, loc.node);
++ else
++ warn("Can't locate window 0x%X.\n", win);
++ } else {
++ warn("Can't parse stack entry: '%s'\n", line);
++ }
++ }
+
+- fclose(snapshot);
++ fclose(snapshot);
+ }
+diff --git a/restore.h b/restore.h
+index 7f0ae21..7debd57 100644
+--- a/restore.h
++++ b/restore.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_RESTORE_H
+diff --git a/rule.c b/rule.c
+index e886343..b669cb6 100644
+--- a/rule.c
++++ b/rule.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdio.h>
+@@ -34,268 +38,276 @@
+
+ rule_t *make_rule(void)
+ {
+- rule_t *r = malloc(sizeof(rule_t));
+- r->cause[0] = r->effect[0] = '\0';
+- r->next = r->prev = NULL;
+- r->one_shot = false;
+- return r;
++ rule_t *r = malloc(sizeof(rule_t));
++ r->cause[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;
+- }
++ 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);
++ 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;
+- while (r != NULL) {
+- rule_t *next = r->next;
+- if (streq(r->cause, cause))
+- remove_rule(r);
+- r = next;
+- }
++ rule_t *r = rule_head;
++ while (r != NULL) {
++ rule_t *next = r->next;
++ if (streq(r->cause, cause))
++ 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;
++ 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 = true;
+- return rc;
++ rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
++ rc->manage = rc->focus = true;
++ return rc;
+ }
+
+ pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
+ {
+- pending_rule_t *pr = malloc(sizeof(pending_rule_t));
+- pr->prev = pr->next = NULL;
+- pr->fd = fd;
+- pr->win = win;
+- pr->csq = csq;
+- return pr;
++ pending_rule_t *pr = malloc(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;
+- PRINTF("add pending rule %i\n", pr->fd);
+- 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;
+- }
++ if (pr == NULL)
++ return;
++ PRINTF("add pending rule %i\n", pr->fd);
++ 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;
+- PRINTF("remove pending rule %i\n", pr->fd);
+- 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);
++ if (pr == NULL)
++ return;
++ PRINTF("remove pending rule %i\n", pr->fd);
++ 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;
++ 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) {
+- csq->floating = 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);
+- }
++ 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) {
++ csq->floating = true;
++ 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;
++ 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)
+- csq->fullscreen = true;
+- else if (a == ewmh->_NET_WM_STATE_STICKY)
+- csq->sticky = true;
+- }
+- xcb_ewmh_get_atoms_reply_wipe(&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)
++ csq->fullscreen = true;
++ else if (a == ewmh->_NET_WM_STATE_STICKY)
++ csq->sticky = true;
++ }
++ xcb_ewmh_get_atoms_reply_wipe(&win_state);
++ }
+
+- 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.min_width > 0 && size_hints.min_height > 0
+- && size_hints.min_width == size_hints.max_width
+- && size_hints.min_height == size_hints.max_height)
+- csq->floating = true;
+- }
++ 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.min_width > 0 && size_hints.min_height > 0 &&
++ size_hints.min_width == size_hints.max_width &&
++ size_hints.min_height == size_hints.max_height)
++ csq->floating = true;
++ csq->min_width = size_hints.min_width;
++ csq->max_width = size_hints.max_width;
++ csq->min_height = size_hints.min_height;
++ csq->max_height = size_hints.max_height;
++ }
+
+- 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)
+- csq->floating = true;
++ 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)
++ csq->floating = true;
+
+- 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);
+- }
++ 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->cause, MATCH_ANY)
+- || streq(rule->cause, csq->class_name)
+- || streq(rule->cause, 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);
+- }
+- rule = next;
+- }
++ rule_t *rule = rule_head;
++ while (rule != NULL) {
++ rule_t *next = rule->next;
++ if (streq(rule->cause, MATCH_ANY) ||
++ streq(rule->cause, csq->class_name) ||
++ streq(rule->cause, 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);
++ }
++ 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, 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);
++ 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, 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);
+- }
+- }
++ 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("desktop", key)) {
+- snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
+- } else if (streq("monitor", key)) {
+- snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
+- } else if (parse_bool(value, &v)) {
+- if (streq("floating", key))
+- csq->floating = v;
++ 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("window", 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 (parse_bool(value, &v)) {
++ if (streq("floating", key))
++ csq->floating = v;
+ #define SETCSQ(name) \
+- else if (streq(#name, key)) \
+- csq->name = v;
+- SETCSQ(pseudo_tiled)
+- SETCSQ(fullscreen)
+- SETCSQ(locked)
+- SETCSQ(sticky)
+- SETCSQ(private)
+- SETCSQ(center)
+- SETCSQ(lower)
+- SETCSQ(follow)
+- SETCSQ(manage)
+- SETCSQ(focus)
++ else if (streq(#name, key)) \
++ csq->name = v;
++ SETCSQ(pseudo_tiled)
++ SETCSQ(fullscreen)
++ SETCSQ(locked)
++ SETCSQ(sticky)
++ SETCSQ(private)
++ SETCSQ(center)
++ SETCSQ(follow)
++ SETCSQ(manage)
++ SETCSQ(focus)
+ #undef SETCSQ
+- }
++ }
+ }
+
+-void list_rules(char *pattern, char *rsp)
++void list_rules(char *pattern, FILE *rsp)
+ {
+- char line[MAXLEN];
+- for (rule_t *r = rule_head; r != NULL; r = r->next) {
+- if (pattern != NULL && !streq(pattern, r->cause))
+- continue;
+- snprintf(line, sizeof(line), "%s => %s\n", r->cause, r->effect);
+- strncat(rsp, line, REMLEN(rsp));
+- }
++ for (rule_t *r = rule_head; r != NULL; r = r->next) {
++ if (pattern != NULL && !streq(pattern, r->cause))
++ continue;
++ fprintf(rsp, "%s => %s\n", r->cause, r->effect);
++ }
+ }
+diff --git a/rule.h b/rule.h
+index 6467aaf..f57301d 100644
+--- a/rule.h
++++ b/rule.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_RULE_H
+@@ -41,6 +45,6 @@ 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(char *pattern, char *rsp);
++void list_rules(char *pattern, FILE *rsp);
+
+ #endif
+diff --git a/settings.c b/settings.c
+index 997ef93..99f2f76 100644
+--- a/settings.c
++++ b/settings.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <unistd.h>
+@@ -28,45 +32,49 @@
+
+ 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");
+- }
++ 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(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(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
+- snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
+- snprintf(presel_border_color, sizeof(presel_border_color), "%s", PRESEL_BORDER_COLOR);
+- snprintf(focused_locked_border_color, sizeof(focused_locked_border_color), "%s", FOCUSED_LOCKED_BORDER_COLOR);
+- snprintf(active_locked_border_color, sizeof(active_locked_border_color), "%s", ACTIVE_LOCKED_BORDER_COLOR);
+- snprintf(normal_locked_border_color, sizeof(normal_locked_border_color), "%s", NORMAL_LOCKED_BORDER_COLOR);
+- snprintf(focused_sticky_border_color, sizeof(focused_sticky_border_color), "%s", FOCUSED_STICKY_BORDER_COLOR);
+- snprintf(active_sticky_border_color, sizeof(active_sticky_border_color), "%s", ACTIVE_STICKY_BORDER_COLOR);
+- snprintf(normal_sticky_border_color, sizeof(normal_sticky_border_color), "%s", NORMAL_STICKY_BORDER_COLOR);
+- snprintf(focused_private_border_color, sizeof(focused_private_border_color), "%s", FOCUSED_PRIVATE_BORDER_COLOR);
+- snprintf(active_private_border_color, sizeof(active_private_border_color), "%s", ACTIVE_PRIVATE_BORDER_COLOR);
+- snprintf(normal_private_border_color, sizeof(normal_private_border_color), "%s", NORMAL_PRIVATE_BORDER_COLOR);
+- snprintf(urgent_border_color, sizeof(urgent_border_color), "%s", URGENT_BORDER_COLOR);
++ snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
++ snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
++ snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
++ snprintf(presel_border_color, sizeof(presel_border_color), "%s", PRESEL_BORDER_COLOR);
++ snprintf(focused_locked_border_color, sizeof(focused_locked_border_color), "%s", FOCUSED_LOCKED_BORDER_COLOR);
++ snprintf(active_locked_border_color, sizeof(active_locked_border_color), "%s", ACTIVE_LOCKED_BORDER_COLOR);
++ snprintf(normal_locked_border_color, sizeof(normal_locked_border_color), "%s", NORMAL_LOCKED_BORDER_COLOR);
++ snprintf(focused_sticky_border_color, sizeof(focused_sticky_border_color), "%s", FOCUSED_STICKY_BORDER_COLOR);
++ snprintf(active_sticky_border_color, sizeof(active_sticky_border_color), "%s", ACTIVE_STICKY_BORDER_COLOR);
++ snprintf(normal_sticky_border_color, sizeof(normal_sticky_border_color), "%s", NORMAL_STICKY_BORDER_COLOR);
++ snprintf(focused_private_border_color, sizeof(focused_private_border_color), "%s", FOCUSED_PRIVATE_BORDER_COLOR);
++ snprintf(active_private_border_color, sizeof(active_private_border_color), "%s", ACTIVE_PRIVATE_BORDER_COLOR);
++ snprintf(normal_private_border_color, sizeof(normal_private_border_color), "%s", NORMAL_PRIVATE_BORDER_COLOR);
++ snprintf(urgent_border_color, sizeof(urgent_border_color), "%s", URGENT_BORDER_COLOR);
+
+- split_ratio = SPLIT_RATIO;
++ split_ratio = SPLIT_RATIO;
++ window_gap = WINDOW_GAP;
++ border_width = BORDER_WIDTH;
+
+- borderless_monocle = BORDERLESS_MONOCLE;
+- gapless_monocle = GAPLESS_MONOCLE;
+- focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
+- pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
+- apply_floating_atom = APPLY_FLOATING_ATOM;
+- auto_alternate = AUTO_ALTERNATE;
+- auto_cancel = AUTO_CANCEL;
+- history_aware_focus = HISTORY_AWARE_FOCUS;
+- ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
+- remove_disabled_monitor = REMOVE_DISABLED_MONITOR;
++ borderless_monocle = BORDERLESS_MONOCLE;
++ gapless_monocle = GAPLESS_MONOCLE;
++ focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
++ pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
++ apply_floating_atom = APPLY_FLOATING_ATOM;
++ auto_alternate = AUTO_ALTERNATE;
++ auto_cancel = AUTO_CANCEL;
++ history_aware_focus = HISTORY_AWARE_FOCUS;
++ ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
++ 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
+index 372347e..0a85b51 100644
+--- a/settings.h
++++ b/settings.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_SETTINGS_H
+@@ -49,17 +53,21 @@
+ #define URGENT_BORDER_COLOR "#EFA29A"
+
+ #define SPLIT_RATIO 0.5
++#define WINDOW_GAP 6
++#define BORDER_WIDTH 1
+
+-#define HISTORY_AWARE_FOCUS false
+-#define BORDERLESS_MONOCLE false
+-#define GAPLESS_MONOCLE false
+-#define FOCUS_FOLLOWS_POINTER false
+-#define POINTER_FOLLOWS_MONITOR false
+-#define AUTO_ALTERNATE false
+-#define AUTO_CANCEL false
+-#define APPLY_FLOATING_ATOM false
+-#define IGNORE_EWMH_FOCUS false
+-#define REMOVE_DISABLED_MONITOR false
++#define HISTORY_AWARE_FOCUS false
++#define BORDERLESS_MONOCLE false
++#define GAPLESS_MONOCLE false
++#define FOCUS_FOLLOWS_POINTER false
++#define POINTER_FOLLOWS_MONITOR false
++#define AUTO_ALTERNATE false
++#define AUTO_CANCEL false
++#define APPLY_FLOATING_ATOM false
++#define IGNORE_EWMH_FOCUS 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];
+@@ -80,6 +88,8 @@ char normal_private_border_color[MAXLEN];
+ char urgent_border_color[MAXLEN];
+
+ double split_ratio;
++int window_gap;
++unsigned int border_width;
+
+ bool borderless_monocle;
+ bool gapless_monocle;
+@@ -90,7 +100,9 @@ bool auto_alternate;
+ bool auto_cancel;
+ bool history_aware_focus;
+ bool ignore_ewmh_focus;
+-bool remove_disabled_monitor;
++bool remove_disabled_monitors;
++bool remove_unplugged_monitors;
++bool merge_overlapping_monitors;
+
+ void run_config(void);
+ void load_settings(void);
+diff --git a/stack.c b/stack.c
+index 3c4f6be..7351bd1 100644
+--- a/stack.c
++++ b/stack.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -29,120 +33,136 @@
+
+ stacking_list_t *make_stack(node_t *n)
+ {
+- stacking_list_t *s = malloc(sizeof(stacking_list_t));
+- s->node = n;
+- s->prev = s->next = NULL;
+- return s;
++ stacking_list_t *s = malloc(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 {
+- 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;
+- }
++ stacking_list_t *s = make_stack(n);
++ if (a == NULL) {
++ stack_head = stack_tail = s;
++ } else {
++ if (a->node == n)
++ 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 {
+- 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;
+- }
++ stacking_list_t *s = make_stack(n);
++ if (a == NULL) {
++ stack_head = stack_tail = s;
++ } else {
++ if (a->node == n)
++ 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);
++ 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 (stacking_list_t *s = stack_head; s != NULL; s = s->next)
+- if (s->node == n) {
+- remove_stack(s);
+- return;
+- }
++ for (stacking_list_t *s = stack_head; s != NULL; s = s->next)
++ if (s->node == n) {
++ remove_stack(s);
++ return;
++ }
+ }
+
+ void stack(node_t *n, stack_flavor_t f)
+ {
+- PRINTF("stack %X\n", n->client->window);
++ PRINTF("stack %X\n", n->client->window);
+
+- if (stack_head == NULL) {
+- stack_insert_after(NULL, n);
+- } else if (n->client->fullscreen) {
+- if (f == STACK_ABOVE) {
+- stack_insert_after(stack_tail, n);
+- window_raise(n->client->window);
+- }
+- } else {
+- if (f == STACK_ABOVE && n->client->floating && !auto_raise)
+- return;
+- stacking_list_t *latest_tiled = NULL;
+- stacking_list_t *oldest_floating = NULL;
+- for (stacking_list_t *s = (f == STACK_ABOVE ? stack_tail : stack_head); s != NULL; s = (f == STACK_ABOVE ? s->prev : s->next)) {
+- if (s->node != n) {
+- if (s->node->client->floating == n->client->floating) {
+- if (f == STACK_ABOVE) {
+- stack_insert_after(s, n);
+- window_above(n->client->window, s->node->client->window);
+- } else {
+- stack_insert_before(s, n);
+- window_below(n->client->window, s->node->client->window);
+- }
+- return;
+- } else if ((f != STACK_ABOVE || latest_tiled == NULL) && !s->node->client->floating) {
+- latest_tiled = s;
+- } else if ((f == STACK_ABOVE || oldest_floating == NULL) && s->node->client->floating) {
+- oldest_floating = s;
+- }
+- }
+- }
+- if (latest_tiled == NULL && oldest_floating == NULL)
+- return;
+- if (n->client->floating) {
+- if (latest_tiled == NULL)
+- return;
+- window_above(n->client->window, latest_tiled->node->client->window);
+- stack_insert_after(latest_tiled, n);
+- } else {
+- if (oldest_floating == NULL)
+- return;
+- window_below(n->client->window, oldest_floating->node->client->window);
+- stack_insert_before(oldest_floating, n);
+- }
+- }
++ if (stack_head == NULL) {
++ stack_insert_after(NULL, n);
++ } else if (n->client->fullscreen) {
++ if (f == STACK_ABOVE) {
++ stack_insert_after(stack_tail, n);
++ window_raise(n->client->window);
++ }
++ } else {
++ if (f == STACK_ABOVE && n->client->floating && !auto_raise)
++ return;
++ stacking_list_t *latest_tiled = NULL;
++ stacking_list_t *oldest_floating = NULL;
++ stacking_list_t *oldest_fullscreen = NULL;
++ for (stacking_list_t *s = (f == STACK_ABOVE ? stack_tail : stack_head); s != NULL; s = (f == STACK_ABOVE ? s->prev : s->next)) {
++ if (s->node != n) {
++ if (s->node->client->fullscreen) {
++ if (oldest_fullscreen == NULL)
++ oldest_fullscreen = s;
++ continue;
++ }
++ if (s->node->client->floating == n->client->floating) {
++ if (f == STACK_ABOVE) {
++ stack_insert_after(s, n);
++ window_above(n->client->window, s->node->client->window);
++ } else {
++ stack_insert_before(s, n);
++ window_below(n->client->window, s->node->client->window);
++ }
++ return;
++ } else if ((f != STACK_ABOVE || latest_tiled == NULL) && !s->node->client->floating) {
++ latest_tiled = s;
++ } else if ((f == STACK_ABOVE || oldest_floating == NULL) && s->node->client->floating) {
++ oldest_floating = s;
++ }
++ }
++ }
++ if (latest_tiled == NULL && oldest_floating == NULL && oldest_fullscreen == NULL)
++ return;
++ if (n->client->floating) {
++ if (latest_tiled != NULL) {
++ window_above(n->client->window, latest_tiled->node->client->window);
++ stack_insert_after(latest_tiled, n);
++ } else if (oldest_fullscreen != NULL) {
++ window_below(n->client->window, oldest_fullscreen->node->client->window);
++ stack_insert_before(oldest_fullscreen, n);
++ }
++ } else {
++ if (oldest_floating != NULL) {
++ window_below(n->client->window, oldest_floating->node->client->window);
++ stack_insert_before(oldest_floating, n);
++ } else if (oldest_fullscreen != NULL) {
++ window_below(n->client->window, oldest_fullscreen->node->client->window);
++ stack_insert_before(oldest_fullscreen, n);
++ }
++ }
++ }
+ }
+diff --git a/stack.h b/stack.h
+index 259a201..83f767f 100644
+--- a/stack.h
++++ b/stack.h
+@@ -1,33 +1,37 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_STACK_H
+ #define BSPWM_STACK_H
+
+ typedef enum {
+- STACK_ABOVE,
+- STACK_BELOW
++ STACK_ABOVE,
++ STACK_BELOW
+ } stack_flavor_t;
+
+ stacking_list_t *make_stack(node_t *n);
+diff --git a/subscribe.c b/subscribe.c
+index d876052..104c873 100644
+--- a/subscribe.c
++++ b/subscribe.c
+@@ -1,3 +1,31 @@
++/* Copyright (c) 2012-2014, 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
++ */
++
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <ctype.h>
+@@ -6,73 +34,62 @@
+ #include "settings.h"
+ #include "subscribe.h"
+
+-subscriber_list_t *make_subscriber_list(int fd)
++subscriber_list_t *make_subscriber_list(FILE *stream)
+ {
+- subscriber_list_t *sb = malloc(sizeof(subscriber_list_t));
+- sb->prev = sb->next = NULL;
+- sb->fd = fd;
+- sb->stream = fdopen(fd, "w");
+- if (sb->stream == NULL) {
+- warn("Can't open subscriber %i\n", fd);
+- close(fd);
+- free(sb);
+- return NULL;
+- }
+- return sb;
++ subscriber_list_t *sb = malloc(sizeof(subscriber_list_t));
++ sb->prev = sb->next = NULL;
++ sb->stream = stream;
++ 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);
++ 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(int fd)
++void add_subscriber(FILE *stream)
+ {
+- subscriber_list_t *sb = make_subscriber_list(fd);
+- if (sb == NULL)
+- return;
+- if (subscribe_head == NULL) {
+- subscribe_head = subscribe_tail = sb;
+- } else {
+- subscribe_tail->next = sb;
+- sb->prev = subscribe_tail;
+- subscribe_tail = sb;
+- }
+- feed_subscriber(sb);
++ subscriber_list_t *sb = make_subscriber_list(stream);
++ if (subscribe_head == NULL) {
++ subscribe_head = subscribe_tail = sb;
++ } else {
++ subscribe_tail->next = sb;
++ sb->prev = subscribe_tail;
++ subscribe_tail = sb;
++ }
++ print_status(sb->stream);
+ }
+
+-void feed_subscriber(subscriber_list_t *sb)
++int print_status(FILE *stream)
+ {
+- fprintf(sb->stream, "%s", status_prefix);
+- bool urgent = false;
+- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+- fprintf(sb->stream, "%c%s:", (mon == m ? 'M' : 'm'), m->name);
+- for (desktop_t *d = m->desk_head; d != NULL; d = d->next, urgent = false) {
+- for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
+- urgent |= n->client->urgent;
+- char c = (urgent ? 'u' : (d->root == NULL ? 'f' : 'o'));
+- if (m->desk == d)
+- c = toupper(c);
+- fprintf(sb->stream, "%c%s:", c, d->name);
+- }
+- }
+- if (mon != NULL && mon->desk != NULL)
+- fprintf(sb->stream, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
+- fprintf(sb->stream, "%s", "\n");
+- int ret = fflush(sb->stream);
+- if (ret != 0)
+- remove_subscriber(sb);
++ fprintf(stream, "%s", status_prefix);
++ bool urgent = false;
++ 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, urgent = false) {
++ for (node_t *n = first_extrema(d->root); n != NULL && !urgent; n = next_leaf(n, d->root))
++ urgent |= n->client->urgent;
++ char c = (urgent ? 'u' : (d->root == NULL ? 'f' : 'o'));
++ if (m->desk == d)
++ c = toupper(c);
++ fprintf(stream, "%c%s:", c, d->name);
++ }
++ }
++ if (mon != NULL && mon->desk != NULL)
++ fprintf(stream, "L%s", (mon->desk->layout == LAYOUT_TILED ? "tiled" : "monocle"));
++ fprintf(stream, "%s", "\n");
++ return fflush(stream);
+ }
+diff --git a/subscribe.h b/subscribe.h
+index e5ce7de..31bb8c4 100644
+--- a/subscribe.h
++++ b/subscribe.h
+@@ -1,9 +1,37 @@
++/* Copyright (c) 2012-2014, 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
++ */
++
+ #ifndef BSPWM_SUBSCRIBE_H
+ #define BSPWM_SUBSCRIBE_H
+
+-subscriber_list_t *make_subscriber_list(int fd);
++subscriber_list_t *make_subscriber_list(FILE *stream);
+ void remove_subscriber(subscriber_list_t *sb);
+-void add_subscriber(int fd);
+-void feed_subscriber(subscriber_list_t *sb);
++void add_subscriber(FILE *stream);
++int print_status(FILE *stream);
+
+ #endif
+diff --git a/tree.c b/tree.c
+index b9c7950..0756547 100644
+--- a/tree.c
++++ b/tree.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <float.h>
+@@ -37,1060 +41,1082 @@
+
+ void arrange(monitor_t *m, desktop_t *d)
+ {
+- if (d->root == NULL)
+- return;
+-
+- PRINTF("arrange %s %s\n", m->name, d->name);
+-
+- xcb_rectangle_t rect = m->rectangle;
+- int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
+- rect.x += m->left_padding + d->left_padding + wg;
+- rect.y += m->top_padding + d->top_padding + wg;
+- rect.width -= m->left_padding + d->left_padding + d->right_padding + m->right_padding + wg;
+- rect.height -= m->top_padding + d->top_padding + d->bottom_padding + m->bottom_padding + wg;
+- apply_layout(m, d, d->root, rect, rect);
++ if (d->root == NULL)
++ return;
++
++ PRINTF("arrange %s %s\n", m->name, d->name);
++
++ xcb_rectangle_t rect = m->rectangle;
++ int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
++ rect.x += m->left_padding + d->left_padding + wg;
++ rect.y += m->top_padding + d->top_padding + wg;
++ rect.width -= m->left_padding + d->left_padding + d->right_padding + m->right_padding + wg;
++ rect.height -= m->top_padding + d->top_padding + d->bottom_padding + m->bottom_padding + wg;
++ apply_layout(m, d, d->root, rect, rect);
+ }
+
+ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
+ {
+- if (n == NULL)
+- return;
+-
+- n->rectangle = rect;
+-
+- if (is_leaf(n)) {
+-
+- if ((borderless_monocle && is_tiled(n->client) && d->layout == LAYOUT_MONOCLE)
+- || n->client->fullscreen)
+- n->client->border_width = 0;
+- else
+- n->client->border_width = d->border_width;
+-
+- xcb_rectangle_t r;
+- if (!n->client->fullscreen) {
+- if (!n->client->floating) {
+- if (n->client->pseudo_tiled) {
+- /* pseudo-tiled clients */
+- r = n->client->floating_rectangle;
+- center_rectangle(&r, rect);
+- } else {
+- /* tiled clients */
+- r = rect;
+- int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
+- int bleed = wg + 2 * n->client->border_width;
+- r.width = (bleed < r.width ? r.width - bleed : 1);
+- r.height = (bleed < r.height ? r.height - bleed : 1);
+- }
+- n->client->tiled_rectangle = r;
+- } else {
+- /* floating clients */
+- r = n->client->floating_rectangle;
+- }
+- } else {
+- /* fullscreen clients */
+- r = m->rectangle;
+- }
+-
+- window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
+- window_border_width(n->client->window, n->client->border_width);
+- window_draw_border(n, d->focus == n, m == mon);
+-
+- } else {
+- xcb_rectangle_t first_rect;
+- xcb_rectangle_t second_rect;
+-
+- if (d->layout == 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 if (n->split_type == TYPE_HORIZONTAL) {
+- 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, first_rect, root_rect);
+- apply_layout(m, d, n->second_child, second_rect, root_rect);
+- }
++ if (n == NULL)
++ return;
++
++ n->rectangle = rect;
++
++ if (is_leaf(n)) {
++
++ unsigned int bw;
++ if ((borderless_monocle && is_tiled(n->client) &&
++ !n->client->pseudo_tiled &&
++ d->layout == LAYOUT_MONOCLE) ||
++ n->client->fullscreen)
++ bw = 0;
++ else
++ bw = n->client->border_width;
++
++ xcb_rectangle_t r;
++ if (!n->client->fullscreen) {
++ if (!n->client->floating) {
++ int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap);
++ if (n->client->pseudo_tiled) {
++ /* pseudo-tiled clients */
++ r = n->client->floating_rectangle;
++ r.x = rect.x - bw + (rect.width - wg - r.width) / 2;
++ r.y = rect.y - bw + (rect.height - wg - r.height) / 2;
++ } else {
++ /* tiled clients */
++ 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);
++ }
++ n->client->tiled_rectangle = r;
++ } else {
++ /* floating clients */
++ r = n->client->floating_rectangle;
++ }
++ } else {
++ /* fullscreen clients */
++ r = m->rectangle;
++ }
++
++ window_move_resize(n->client->window, r.x, r.y, r.width, r.height);
++ window_border_width(n->client->window, bw);
++ window_draw_border(n, d->focus == n, m == mon);
++
++ } else {
++ xcb_rectangle_t first_rect;
++ xcb_rectangle_t second_rect;
++
++ if (d->layout == 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, first_rect, root_rect);
++ apply_layout(m, d, n->second_child, second_rect, root_rect);
++ }
+ }
+
+ void insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+ {
+- if (d == NULL || n == NULL)
+- return;
+-
+- PRINTF("insert node %X\n", n->client->window);
+-
+- /* n: new leaf node */
+- /* c: new container 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 {
+- node_t *c = make_node();
+- node_t *p = f->parent;
+- if (p != NULL && f->split_mode == MODE_AUTOMATIC
+- && (p->first_child->vacant || p->second_child->vacant)) {
+- f = p;
+- p = f->parent;
+- }
+- if (((f->client != NULL && f->client->private) || (p != NULL && p->privacy_level > 0))
+- && f->split_mode == MODE_AUTOMATIC) {
+- node_t *closest = NULL;
+- node_t *public = NULL;
+- closest_public(d, f, &closest, &public);
+- if (public != NULL) {
+- f = public;
+- p = f->parent;
+- } else {
+- if (closest != NULL) {
+- f = closest;
+- p = f->parent;
+- }
+- f->split_mode = MODE_MANUAL;
+- xcb_rectangle_t rect = f->client->tiled_rectangle;
+- f->split_dir = (rect.width >= rect.height ? DIR_LEFT : DIR_UP);
+- if (f->client->private) {
+- get_opposite(f->split_dir, &f->split_dir);
+- update_privacy_level(f, false);
+- }
+- }
+- }
+- n->parent = c;
+- c->birth_rotation = f->birth_rotation;
+- switch (f->split_mode) {
+- case MODE_AUTOMATIC:
+- if (p == NULL) {
+- c->first_child = n;
+- c->second_child = f;
+- 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;
+- }
+- if (!is_floating(n->client))
+- rotate_tree(p, rot);
+- n->birth_rotation = rot;
+- }
+- break;
+- case MODE_MANUAL:
+- if (p != NULL) {
+- if (is_first_child(f))
+- p->first_child = c;
+- else
+- p->second_child = c;
+- }
+- c->split_ratio = f->split_ratio;
+- c->parent = p;
+- f->parent = c;
+- f->birth_rotation = 0;
+- switch (f->split_dir) {
+- case DIR_LEFT:
+- c->split_type = TYPE_VERTICAL;
+- c->first_child = n;
+- c->second_child = f;
+- break;
+- case DIR_RIGHT:
+- c->split_type = TYPE_VERTICAL;
+- c->first_child = f;
+- c->second_child = n;
+- break;
+- case DIR_UP:
+- c->split_type = TYPE_HORIZONTAL;
+- c->first_child = n;
+- c->second_child = f;
+- break;
+- case DIR_DOWN:
+- c->split_type = TYPE_HORIZONTAL;
+- c->first_child = f;
+- c->second_child = n;
+- break;
+- }
+- if (d->root == f)
+- d->root = c;
+- f->split_mode = MODE_AUTOMATIC;
+- break;
+- }
+- if (f->vacant)
+- update_vacant_state(f->parent);
+- if (f->client != NULL && f->client->private)
+- update_privacy_level(f, true);
+- }
+- if (n->client->private)
+- update_privacy_level(n, true);
+- if (d->focus == NULL)
+- d->focus = n;
+- if (n->client->sticky)
+- m->num_sticky++;
+- put_status();
++ if (d == NULL || n == NULL)
++ return;
++
++ PRINTF("insert node %X\n", n->client->window);
++
++ /* n: new leaf node */
++ /* c: new container 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 {
++ node_t *c = make_node();
++ node_t *p = f->parent;
++ if (p != NULL && f->split_mode == MODE_AUTOMATIC &&
++ (p->first_child->vacant || p->second_child->vacant)) {
++ f = p;
++ p = f->parent;
++ }
++ if (((f->client != NULL && f->client->private) ||
++ (p != NULL && p->privacy_level > 0)) &&
++ f->split_mode == MODE_AUTOMATIC) {
++ node_t *closest = NULL;
++ node_t *public = NULL;
++ closest_public(d, f, &closest, &public);
++ if (public != NULL) {
++ f = public;
++ p = f->parent;
++ } else {
++ if (closest != NULL) {
++ f = closest;
++ p = f->parent;
++ }
++ f->split_mode = MODE_MANUAL;
++ xcb_rectangle_t rect = f->client->tiled_rectangle;
++ f->split_dir = (rect.width >= rect.height ? DIR_LEFT : DIR_UP);
++ if (f->client->private) {
++ get_opposite(f->split_dir, &f->split_dir);
++ update_privacy_level(f, false);
++ }
++ }
++ }
++ n->parent = c;
++ c->birth_rotation = f->birth_rotation;
++ switch (f->split_mode) {
++ case MODE_AUTOMATIC:
++ if (p == NULL) {
++ c->first_child = n;
++ c->second_child = f;
++ 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;
++ }
++ if (!is_floating(n->client))
++ rotate_tree(p, rot);
++ n->birth_rotation = rot;
++ }
++ break;
++ case MODE_MANUAL:
++ if (p != NULL) {
++ if (is_first_child(f))
++ p->first_child = c;
++ else
++ p->second_child = c;
++ }
++ c->split_ratio = f->split_ratio;
++ c->parent = p;
++ f->parent = c;
++ f->birth_rotation = 0;
++ switch (f->split_dir) {
++ case DIR_LEFT:
++ c->split_type = TYPE_VERTICAL;
++ c->first_child = n;
++ c->second_child = f;
++ break;
++ case DIR_RIGHT:
++ c->split_type = TYPE_VERTICAL;
++ c->first_child = f;
++ c->second_child = n;
++ break;
++ case DIR_UP:
++ c->split_type = TYPE_HORIZONTAL;
++ c->first_child = n;
++ c->second_child = f;
++ break;
++ case DIR_DOWN:
++ c->split_type = TYPE_HORIZONTAL;
++ c->first_child = f;
++ c->second_child = n;
++ break;
++ }
++ if (d->root == f)
++ d->root = c;
++ f->split_mode = MODE_AUTOMATIC;
++ break;
++ }
++ if (f->vacant)
++ update_vacant_state(f->parent);
++ if (f->client != NULL && f->client->private)
++ update_privacy_level(f, true);
++ }
++ if (n->client->private)
++ update_privacy_level(n, true);
++ if (d->focus == NULL)
++ d->focus = n;
++ if (n->client->sticky)
++ m->num_sticky++;
++ put_status();
+ }
+
+ void pseudo_focus(monitor_t *m, desktop_t *d, node_t *n)
+ {
+- if (n != NULL) {
+- stack(n, STACK_ABOVE);
+- if (d->focus != n) {
+- window_draw_border(d->focus, false, m == mon);
+- window_draw_border(n, true, m == mon);
+- }
+- }
+- d->focus = n;
++ if (n != NULL) {
++ stack(n, STACK_ABOVE);
++ if (d->focus != n) {
++ window_draw_border(d->focus, false, m == mon);
++ window_draw_border(n, true, m == mon);
++ }
++ }
++ d->focus = n;
+ }
+
+ void focus_node(monitor_t *m, desktop_t *d, node_t *n)
+ {
+- if (mon->desk != d || n == NULL)
+- clear_input_focus();
+-
+- if (m->num_sticky > 0 && d != m->desk) {
+- node_t *a = first_extrema(m->desk->root);
+- sticky_still = false;
+- while (a != NULL) {
+- node_t *b = next_leaf(a, m->desk->root);
+- if (a->client->sticky)
+- transfer_node(m, m->desk, a, m, d, d->focus);
+- a = b;
+- }
+- sticky_still = true;
+- if (n == NULL && d->focus != NULL)
+- n = d->focus;
+- }
+-
+- if (n != NULL && d->focus != NULL && n != d->focus && d->focus->client->fullscreen) {
+- set_fullscreen(d->focus, false);
+- arrange(m, d);
+- }
+-
+- if (mon != m) {
+- for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
+- window_draw_border(cd->focus, true, false);
+- for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next)
+- if (cd != d)
+- window_draw_border(cd->focus, true, true);
+- if (d->focus == n)
+- window_draw_border(n, true, true);
+- }
+-
+- if (d->focus != n) {
+- window_draw_border(d->focus, false, true);
+- window_draw_border(n, true, true);
+- }
+-
+- focus_desktop(m, d);
+-
+- d->focus = n;
+-
+- if (n == NULL) {
+- history_add(m, d, NULL);
+- ewmh_update_active_window();
+- return;
+- } else {
+- stack(n, STACK_ABOVE);
+- }
+-
+- PRINTF("focus node %X\n", n->client->window);
+-
+- n->client->urgent = false;
+-
+- history_add(m, d, n);
+- set_input_focus(n);
+-
+- if (focus_follows_pointer) {
+- xcb_window_t win = XCB_NONE;
+- query_pointer(&win, NULL);
+- if (win != n->client->window)
+- enable_motion_recorder();
+- else
+- disable_motion_recorder();
+- }
+-
+- ewmh_update_active_window();
++ if (mon->desk != d || n == NULL)
++ clear_input_focus();
++
++ if (m->num_sticky > 0 && d != m->desk) {
++ node_t *a = first_extrema(m->desk->root);
++ sticky_still = false;
++ while (a != NULL) {
++ node_t *b = next_leaf(a, m->desk->root);
++ if (a->client->sticky)
++ transfer_node(m, m->desk, a, m, d, d->focus);
++ a = b;
++ }
++ sticky_still = true;
++ if (n == NULL && d->focus != NULL)
++ n = d->focus;
++ }
++
++ if (n != NULL) {
++ if (d->focus != NULL && n != d->focus && d->focus->client->fullscreen) {
++ set_fullscreen(d->focus, false);
++ arrange(m, d);
++ }
++ if (n->client->urgent) {
++ n->client->urgent = false;
++ put_status();
++ }
++ }
++
++ if (mon != m) {
++ for (desktop_t *cd = mon->desk_head; cd != NULL; cd = cd->next)
++ window_draw_border(cd->focus, true, false);
++ for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next)
++ if (cd != d)
++ window_draw_border(cd->focus, true, true);
++ if (d->focus == n)
++ window_draw_border(n, true, true);
++ }
++
++ if (d->focus != n) {
++ window_draw_border(d->focus, false, true);
++ window_draw_border(n, true, true);
++ }
++
++ focus_desktop(m, d);
++
++ d->focus = n;
++
++ if (n == NULL) {
++ history_add(m, d, NULL);
++ ewmh_update_active_window();
++ return;
++ } else {
++ stack(n, STACK_ABOVE);
++ }
++
++ PRINTF("focus node %X\n", n->client->window);
++
++ history_add(m, d, n);
++ set_input_focus(n);
++
++ if (focus_follows_pointer) {
++ xcb_window_t win = XCB_NONE;
++ query_pointer(&win, NULL);
++ if (win != n->client->window)
++ enable_motion_recorder();
++ else
++ disable_motion_recorder();
++ }
++
++ ewmh_update_active_window();
+ }
+
+ void update_current(void)
+ {
+- focus_node(mon, mon->desk, mon->desk->focus);
++ focus_node(mon, mon->desk, mon->desk->focus);
+ }
+
+ node_t *make_node(void)
+ {
+- node_t *n = malloc(sizeof(node_t));
+- n->parent = n->first_child = n->second_child = NULL;
+- n->split_ratio = split_ratio;
+- n->split_mode = MODE_AUTOMATIC;
+- n->split_type = TYPE_VERTICAL;
+- n->birth_rotation = 0;
+- n->privacy_level = 0;
+- n->client = NULL;
+- n->vacant = false;
+- return n;
++ node_t *n = malloc(sizeof(node_t));
++ n->parent = n->first_child = n->second_child = NULL;
++ n->split_ratio = split_ratio;
++ n->split_mode = MODE_AUTOMATIC;
++ n->split_type = TYPE_VERTICAL;
++ n->birth_rotation = 0;
++ n->privacy_level = 0;
++ n->client = NULL;
++ n->vacant = false;
++ return n;
+ }
+
+-client_t *make_client(xcb_window_t win)
++client_t *make_client(xcb_window_t win, unsigned int border_width)
+ {
+- client_t *c = malloc(sizeof(client_t));
+- snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
+- c->border_width = BORDER_WIDTH;
+- c->window = win;
+- c->pseudo_tiled = c->floating = c->fullscreen = false;
+- c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false;
+- xcb_icccm_get_wm_protocols_reply_t protocols;
+- if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
+- if (has_proto(WM_TAKE_FOCUS, &protocols))
+- c->icccm_focus = true;
+- xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
+- }
+- c->num_states = 0;
+- 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_STATE; i++)
+- ewmh_wm_state_add(c, wm_state.atoms[i]);
+- xcb_ewmh_get_atoms_reply_wipe(&wm_state);
+- }
+- return c;
++ client_t *c = malloc(sizeof(client_t));
++ c->window = win;
++ 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->pseudo_tiled = c->floating = c->fullscreen = false;
++ c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false;
++ xcb_icccm_get_wm_protocols_reply_t protocols;
++ if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
++ if (has_proto(WM_TAKE_FOCUS, &protocols))
++ c->icccm_focus = true;
++ xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
++ }
++ c->num_states = 0;
++ 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_STATE; i++)
++ ewmh_wm_state_add(c, wm_state.atoms[i]);
++ xcb_ewmh_get_atoms_reply_wipe(&wm_state);
++ }
++ return c;
+ }
+
+ bool is_leaf(node_t *n)
+ {
+- return (n != NULL && n->first_child == NULL && n->second_child == NULL);
++ return (n != NULL && n->first_child == NULL && n->second_child == NULL);
+ }
+
+ bool is_tiled(client_t *c)
+ {
+- if (c == NULL)
+- return false;
+- return (!c->floating && !c->fullscreen);
++ if (c == NULL)
++ return false;
++ return (!c->floating && !c->fullscreen);
+ }
+
+ bool is_floating(client_t *c)
+ {
+- if (c == NULL)
+- return false;
+- return (c->floating && !c->fullscreen);
++ if (c == NULL)
++ return false;
++ return (c->floating && !c->fullscreen);
+ }
+
+ bool is_first_child(node_t *n)
+ {
+- return (n != NULL && n->parent != NULL && n->parent->first_child == 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);
++ return (n != NULL && n->parent != NULL && n->parent->second_child == n);
+ }
+
+ void reset_mode(coordinates_t *loc)
+ {
+- if (loc->node != NULL) {
+- loc->node->split_mode = MODE_AUTOMATIC;
+- window_draw_border(loc->node, loc->desktop->focus == loc->node, mon == loc->monitor);
+- } else if (loc->desktop != NULL) {
+- for (node_t *a = first_extrema(loc->desktop->root); a != NULL; a = next_leaf(a, loc->desktop->root)) {
+- a->split_mode = MODE_AUTOMATIC;
+- window_draw_border(a, loc->desktop->focus == a, mon == loc->monitor);
+- }
+- }
++ if (loc->node != NULL) {
++ loc->node->split_mode = MODE_AUTOMATIC;
++ window_draw_border(loc->node, loc->desktop->focus == loc->node, mon == loc->monitor);
++ } else if (loc->desktop != NULL) {
++ for (node_t *a = first_extrema(loc->desktop->root); a != NULL; a = next_leaf(a, loc->desktop->root)) {
++ a->split_mode = MODE_AUTOMATIC;
++ window_draw_border(a, loc->desktop->focus == a, mon == loc->monitor);
++ }
++ }
+ }
+
+ 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;
++ if (n == NULL || n->parent == NULL)
++ return NULL;
++ if (is_first_child(n))
++ return n->parent->second_child;
++ else
++ return n->parent->first_child;
+ }
+
+ void closest_public(desktop_t *d, node_t *n, node_t **closest, node_t **public)
+ {
+- if (n == NULL)
+- return;
+- node_t *prev = prev_leaf(n, d->root);
+- node_t *next = next_leaf(n, d->root);
+- while (prev != NULL || next != NULL) {
++ if (n == NULL)
++ return;
++ node_t *prev = prev_leaf(n, d->root);
++ node_t *next = next_leaf(n, d->root);
++ while (prev != NULL || next != NULL) {
+ #define TESTLOOP(n) \
+- if (n != NULL) { \
+- if (is_tiled(n->client)) { \
+- if (n->privacy_level == 0) { \
+- if (n->parent == NULL || n->parent->privacy_level == 0) { \
+- *public = n; \
+- return; \
+- } else if (*closest == NULL) { \
+- *closest = n; \
+- } \
+- } \
+- } \
+- n = n##_leaf(n, d->root); \
+- }
+- TESTLOOP(prev)
+- TESTLOOP(next)
++ if (n != NULL) { \
++ if (is_tiled(n->client)) { \
++ if (n->privacy_level == 0) { \
++ if (n->parent == NULL || n->parent->privacy_level == 0) { \
++ *public = n; \
++ return; \
++ } else if (*closest == NULL) { \
++ *closest = n; \
++ } \
++ } \
++ } \
++ n = n##_leaf(n, d->root); \
++ }
++ TESTLOOP(prev)
++ TESTLOOP(next)
+ #undef TESTLOOP
+- }
++ }
+ }
+
+ 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);
++ 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);
++ if (n == NULL)
++ return NULL;
++ else if (n->second_child == NULL)
++ return n;
++ else
++ return second_extrema(n->second_child);
+ }
+
+ 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);
++ 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);
++ 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(desktop_t *d, node_t *n, node_t *r)
+ {
+- node_t *next = next_leaf(n, r);
+- if (next == NULL || is_tiled(next->client))
+- return next;
+- else
+- return next_tiled_leaf(d, next, r);
++ node_t *next = next_leaf(n, r);
++ if (next == NULL || is_tiled(next->client))
++ return next;
++ else
++ return next_tiled_leaf(d, next, r);
+ }
+
+ node_t *prev_tiled_leaf(desktop_t *d, node_t *n, node_t *r)
+ {
+- node_t *prev = prev_leaf(n, r);
+- if (prev == NULL || is_tiled(prev->client))
+- return prev;
+- else
+- return prev_tiled_leaf(d, prev, r);
++ node_t *prev = prev_leaf(n, r);
++ if (prev == NULL || is_tiled(prev->client))
++ return prev;
++ else
++ return prev_tiled_leaf(d, prev, r);
+ }
+
+ /* bool is_adjacent(node_t *a, node_t *r) */
+ /* { */
+-/* node_t *f = r->parent; */
+-/* node_t *p = a; */
+-/* bool first_child = is_first_child(r); */
+-/* while (p != r) { */
+-/* if (p->parent->split_type == f->split_type && is_first_child(p) == first_child) */
+-/* return false; */
+-/* p = p->parent; */
+-/* } */
+-/* return true; */
++/* node_t *f = r->parent; */
++/* node_t *p = a; */
++/* bool first_child = is_first_child(r); */
++/* while (p != r) { */
++/* if (p->parent->split_type == f->split_type && is_first_child(p) == first_child) */
++/* return false; */
++/* p = p->parent; */
++/* } */
++/* return true; */
+ /* } */
+
+ /* 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_RIGHT:
+- return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
+- break;
+- case DIR_DOWN:
+- return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
+- break;
+- case DIR_LEFT:
+- return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
+- break;
+- case DIR_UP:
+- return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
+- break;
+- }
+- return false;
++ switch (dir) {
++ case DIR_RIGHT:
++ return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
++ break;
++ case DIR_DOWN:
++ return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
++ break;
++ case DIR_LEFT:
++ return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
++ break;
++ case DIR_UP:
++ 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;
++ node_t *p;
+
+- if (n == NULL)
+- return NULL;
++ if (n == NULL)
++ return NULL;
+
+- p = n->parent;
++ p = n->parent;
+
+- while (p != NULL) {
+- if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y)
+- || (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x)
+- || (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height))
+- || (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
+- return p;
+- p = p->parent;
+- }
++ while (p != NULL) {
++ if ((dir == DIR_UP && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
++ (dir == DIR_LEFT && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
++ (dir == DIR_DOWN && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
++ (dir == DIR_RIGHT && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
++ return p;
++ p = p->parent;
++ }
+
+- return NULL;
++ return NULL;
+ }
+
+ node_t *nearest_neighbor(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+ {
+- if (n == NULL || n->client->fullscreen
+- || (d->layout == LAYOUT_MONOCLE && is_tiled(n->client)))
+- return NULL;
+-
+- node_t *nearest = NULL;
+- if (history_aware_focus)
+- nearest = nearest_from_history(m, d, n, dir, sel);
+- if (nearest == NULL)
+- nearest = nearest_from_distance(m, d, n, dir, sel);
+- return nearest;
++ if (n == NULL || n->client->fullscreen ||
++ (d->layout == LAYOUT_MONOCLE && is_tiled(n->client)))
++ return NULL;
++
++ node_t *nearest = NULL;
++ if (history_aware_focus)
++ nearest = nearest_from_history(m, d, n, dir, sel);
++ if (nearest == NULL)
++ nearest = nearest_from_distance(m, d, n, dir, sel);
++ return nearest;
+ }
+
+ node_t *nearest_from_history(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+ {
+- if (n == NULL || !is_tiled(n->client))
+- return NULL;
+-
+- node_t *target = find_fence(n, dir);
+- if (target == NULL)
+- return NULL;
+- if (dir == DIR_UP || dir == DIR_LEFT)
+- target = target->first_child;
+- else if (dir == DIR_DOWN || dir == DIR_RIGHT)
+- target = target->second_child;
+-
+- node_t *nearest = NULL;
+- int min_rank = INT_MAX;
+- coordinates_t ref = {m, d, n};
+-
+- for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
+- if (a->vacant || !is_adjacent(n, a, dir) || a == n)
+- continue;
+- coordinates_t loc = {m, d, a};
+- if (!node_matches(&loc, &ref, sel))
+- continue;
+-
+- int rank = history_rank(d, a);
+- if (rank >= 0 && rank < min_rank) {
+- nearest = a;
+- min_rank = rank;
+- }
+- }
+-
+- return nearest;
++ if (n == NULL || !is_tiled(n->client))
++ return NULL;
++
++ node_t *target = find_fence(n, dir);
++ if (target == NULL)
++ return NULL;
++ if (dir == DIR_UP || dir == DIR_LEFT)
++ target = target->first_child;
++ else if (dir == DIR_DOWN || dir == DIR_RIGHT)
++ target = target->second_child;
++
++ node_t *nearest = NULL;
++ int min_rank = INT_MAX;
++ coordinates_t ref = {m, d, n};
++
++ for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
++ if (a->vacant || !is_adjacent(n, a, dir) || a == n)
++ continue;
++ coordinates_t loc = {m, d, a};
++ if (!node_matches(&loc, &ref, sel))
++ continue;
++
++ int rank = history_rank(d, a);
++ if (rank >= 0 && rank < min_rank) {
++ nearest = a;
++ min_rank = rank;
++ }
++ }
++
++ return nearest;
+ }
+
+ node_t *nearest_from_distance(monitor_t *m, desktop_t *d, node_t *n, direction_t dir, client_select_t sel)
+ {
+- if (n == NULL)
+- return NULL;
+-
+- node_t *target = NULL;
+-
+- if (is_tiled(n->client)) {
+- target = find_fence(n, dir);
+- if (target == NULL)
+- return NULL;
+- if (dir == DIR_UP || dir == DIR_LEFT)
+- target = target->first_child;
+- else if (dir == DIR_DOWN || dir == DIR_RIGHT)
+- target = target->second_child;
+- } else {
+- target = d->root;
+- }
+-
+- node_t *nearest = NULL;
+- direction_t dir2;
+- xcb_point_t pt;
+- xcb_point_t pt2;
+- get_side_handle(n->client, dir, &pt);
+- get_opposite(dir, &dir2);
+- double ds = DBL_MAX;
+- coordinates_t ref = {m, d, n};
+-
+- for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
+- coordinates_t loc = {m, d, a};
+- if (a == n ||
+- !node_matches(&loc, &ref, sel) ||
+- is_tiled(a->client) != is_tiled(n->client) ||
+- (is_tiled(a->client) && !is_adjacent(n, a, dir)))
+- continue;
+-
+- get_side_handle(a->client, dir2, &pt2);
+- double ds2 = distance(pt, pt2);
+- if (ds2 < ds) {
+- ds = ds2;
+- nearest = a;
+- }
+- }
+-
+- return nearest;
++ if (n == NULL)
++ return NULL;
++
++ node_t *target = NULL;
++
++ if (is_tiled(n->client)) {
++ target = find_fence(n, dir);
++ if (target == NULL)
++ return NULL;
++ if (dir == DIR_UP || dir == DIR_LEFT)
++ target = target->first_child;
++ else if (dir == DIR_DOWN || dir == DIR_RIGHT)
++ target = target->second_child;
++ } else {
++ target = d->root;
++ }
++
++ node_t *nearest = NULL;
++ direction_t dir2;
++ xcb_point_t pt;
++ xcb_point_t pt2;
++ get_side_handle(n->client, dir, &pt);
++ get_opposite(dir, &dir2);
++ double ds = DBL_MAX;
++ coordinates_t ref = {m, d, n};
++
++ for (node_t *a = first_extrema(target); a != NULL; a = next_leaf(a, target)) {
++ coordinates_t loc = {m, d, a};
++ if (a == n ||
++ !node_matches(&loc, &ref, sel) ||
++ is_tiled(a->client) != is_tiled(n->client) ||
++ (is_tiled(a->client) && !is_adjacent(n, a, dir)))
++ continue;
++
++ get_side_handle(a->client, dir2, &pt2);
++ double ds2 = distance(pt, pt2);
++ if (ds2 < ds) {
++ ds = ds2;
++ nearest = a;
++ }
++ }
++
++ return nearest;
+ }
+
+ void get_opposite(direction_t src, direction_t *dst)
+ {
+- switch (src) {
+- case DIR_RIGHT:
+- *dst = DIR_LEFT;
+- break;
+- case DIR_DOWN:
+- *dst = DIR_UP;
+- break;
+- case DIR_LEFT:
+- *dst = DIR_RIGHT;
+- break;
+- case DIR_UP:
+- *dst = DIR_DOWN;
+- break;
+- }
++ switch (src) {
++ case DIR_RIGHT:
++ *dst = DIR_LEFT;
++ break;
++ case DIR_DOWN:
++ *dst = DIR_UP;
++ break;
++ case DIR_LEFT:
++ *dst = DIR_RIGHT;
++ break;
++ case DIR_UP:
++ *dst = DIR_DOWN;
++ break;
++ }
+ }
+
+ int tiled_area(node_t *n)
+ {
+- if (n == NULL)
+- return -1;
+- xcb_rectangle_t rect = n->client->tiled_rectangle;
+- return rect.width * rect.height;
++ if (n == NULL)
++ return -1;
++ xcb_rectangle_t rect = n->client->tiled_rectangle;
++ return rect.width * rect.height;
+ }
+
+ node_t *find_biggest(monitor_t *m, desktop_t *d, node_t *n, client_select_t sel)
+ {
+- if (d == NULL)
+- return NULL;
+-
+- node_t *r = NULL;
+- int r_area = tiled_area(r);
+- coordinates_t ref = {m, d, n};
+-
+- for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+- coordinates_t loc = {m, d, f};
+- if (!is_tiled(f->client) || !node_matches(&loc, &ref, sel))
+- continue;
+- int f_area = tiled_area(f);
+- if (r == NULL) {
+- r = f;
+- r_area = f_area;
+- } else if (f_area > r_area) {
+- r = f;
+- r_area = f_area;
+- }
+- }
+-
+- return r;
++ if (d == NULL)
++ return NULL;
++
++ node_t *r = NULL;
++ int r_area = tiled_area(r);
++ coordinates_t ref = {m, d, n};
++
++ for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
++ coordinates_t loc = {m, d, f};
++ if (!is_tiled(f->client) || !node_matches(&loc, &ref, sel))
++ continue;
++ int f_area = tiled_area(f);
++ if (r == NULL) {
++ r = f;
++ r_area = f_area;
++ } else if (f_area > r_area) {
++ r = f;
++ r_area = f_area;
++ }
++ }
++
++ return r;
+ }
+
+ 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);
++ 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);
++ 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);
++ if (rot == 0)
++ return;
++ rotate_tree(n, 360 - rot);
+ }
+
+ void unrotate_brother(node_t *n)
+ {
+- unrotate_tree(brother_tree(n), n->birth_rotation);
++ unrotate_tree(brother_tree(n), n->birth_rotation);
+ }
+
+ void flip_tree(node_t *n, flip_t flp)
+ {
+- if (n == NULL || is_leaf(n))
+- return;
++ if (n == NULL || is_leaf(n))
++ return;
++
++ node_t *tmp;
+
+- 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;
++ }
+
+- 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);
++}
+
+- 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;
+- }
++ 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;
+-
+- PRINTF("unlink node %X\n", n->client->window);
+-
+- node_t *p = n->parent;
+-
+- if (p == NULL) {
+- d->root = NULL;
+- d->focus = NULL;
+- } else {
+- if (n->client->private)
+- update_privacy_level(n, false);
+-
+- node_t *b;
+- node_t *g = p->parent;
+-
+- if (is_first_child(n)) {
+- b = p->second_child;
+- if (!n->vacant)
+- unrotate_tree(b, n->birth_rotation);
+- } else {
+- b = p->first_child;
+- 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;
+- n->parent = NULL;
+- free(p);
+- update_vacant_state(b->parent);
+-
+- if (n == d->focus) {
+- d->focus = history_get_node(d, n);
+- // fallback to the first extrema (`n` is not reachable)
+- if (d->focus == NULL)
+- d->focus = first_extrema(d->root);
+- }
+- }
+- if (n->client->sticky)
+- m->num_sticky--;
+- put_status();
++ if (d == NULL || n == NULL)
++ return;
++
++ PRINTF("unlink node %X\n", n->client->window);
++
++ node_t *p = n->parent;
++
++ if (p == NULL) {
++ d->root = NULL;
++ d->focus = NULL;
++ } else {
++ if (n->client->private)
++ update_privacy_level(n, false);
++
++ node_t *b;
++ node_t *g = p->parent;
++
++ if (is_first_child(n)) {
++ b = p->second_child;
++ if (!n->vacant)
++ unrotate_tree(b, n->birth_rotation);
++ } else {
++ b = p->first_child;
++ 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;
++ n->parent = NULL;
++ free(p);
++ update_vacant_state(b->parent);
++
++ if (n == d->focus) {
++ d->focus = history_get_node(d, n);
++ // fallback to the first extrema (`n` is not reachable)
++ if (d->focus == NULL)
++ d->focus = first_extrema(d->root);
++ }
++ }
++ if (n->client->sticky)
++ m->num_sticky--;
++ put_status();
+ }
+
+ void remove_node(monitor_t *m, desktop_t *d, node_t *n)
+ {
+- if (n == NULL)
+- return;
++ if (n == NULL)
++ return;
+
+- PRINTF("remove node %X\n", n->client->window);
++ PRINTF("remove node %X\n", n->client->window);
+
+- bool focused = (n == mon->desk->focus);
+- unlink_node(m, d, n);
+- history_remove(d, n);
+- remove_stack_node(n);
+- free(n->client);
+- free(n);
++ bool focused = (n == mon->desk->focus);
++ unlink_node(m, d, n);
++ history_remove(d, n);
++ remove_stack_node(n);
++ free(n->client);
++ free(n);
+
+- num_clients--;
+- ewmh_update_client_list();
++ num_clients--;
++ ewmh_update_client_list();
+
+- if (focused)
+- update_current();
++ if (focused)
++ update_current();
+ }
+
+ void destroy_tree(node_t *n)
+ {
+- if (n == NULL)
+- return;
+- node_t *first_tree = n->first_child;
+- node_t *second_tree = n->second_child;
+- if (n->client != NULL) {
+- free(n->client);
+- num_clients--;
+- }
+- free(n);
+- destroy_tree(first_tree);
+- destroy_tree(second_tree);
++ if (n == NULL)
++ return;
++ node_t *first_tree = n->first_child;
++ node_t *second_tree = n->second_child;
++ if (n->client != NULL) {
++ free(n->client);
++ num_clients--;
++ }
++ free(n);
++ destroy_tree(first_tree);
++ destroy_tree(second_tree);
+ }
+
+ 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 || (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
+- return false;
+-
+- PRINTF("swap nodes %X %X\n", n1->client->window, n2->client->window);
+-
+- 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;
+- int pl1 = n1->privacy_level;
+- int pl2 = n2->privacy_level;
+-
+- 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;
+- n1->privacy_level = pl2;
+- n2->privacy_level = pl1;
+-
+- if (n1->vacant != n2->vacant) {
+- update_vacant_state(n1->parent);
+- update_vacant_state(n2->parent);
+- }
+-
+- if (n1->client->private != n2->client->private) {
+- n1->client->private = !n1->client->private;
+- n2->client->private = !n2->client->private;
+- }
+-
+- if (d1 != d2) {
+- if (d1->root == n1)
+- d1->root = n2;
+- if (d1->focus == n1)
+- d1->focus = n2;
+- if (d2->root == n2)
+- d2->root = n1;
+- if (d2->focus == n2)
+- d2->focus = n1;
+-
+- if (m1 != m2) {
+- translate_client(m2, m1, n2->client);
+- translate_client(m1, m2, n1->client);
+- }
+-
+- 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) {
+- window_show(n1->client->window);
+- window_hide(n2->client->window);
+- } else if (m1->desk == d1 && m2->desk != d2) {
+- window_hide(n1->client->window);
+- window_show(n2->client->window);
+- }
+-
+- update_input_focus();
+- }
+-
+- return true;
++ if (n1 == NULL || n2 == NULL ||n1 == n2 ||
++ (d1 != d2 && (n1->client->sticky || n2->client->sticky)))
++ return false;
++
++ PRINTF("swap nodes %X %X\n", n1->client->window, n2->client->window);
++
++ 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;
++ int pl1 = n1->privacy_level;
++ int pl2 = n2->privacy_level;
++
++ 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;
++ n1->privacy_level = pl2;
++ n2->privacy_level = pl1;
++
++ if (n1->vacant != n2->vacant) {
++ update_vacant_state(n1->parent);
++ update_vacant_state(n2->parent);
++ }
++
++ if (n1->client->private != n2->client->private) {
++ n1->client->private = !n1->client->private;
++ n2->client->private = !n2->client->private;
++ }
++
++ if (d1 != d2) {
++ if (d1->root == n1)
++ d1->root = n2;
++ if (d1->focus == n1)
++ d1->focus = n2;
++ if (d2->root == n2)
++ d2->root = n1;
++ if (d2->focus == n2)
++ d2->focus = n1;
++
++ if (m1 != m2) {
++ translate_client(m2, m1, n2->client);
++ translate_client(m1, m2, n1->client);
++ }
++
++ 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) {
++ window_show(n1->client->window);
++ window_hide(n2->client->window);
++ } else if (m1->desk == d1 && m2->desk != d2) {
++ window_hide(n1->client->window);
++ window_show(n2->client->window);
++ }
++
++ update_input_focus();
++ }
++
++ 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 || (sticky_still && ns->client->sticky))
+- return false;
+-
+- PRINTF("transfer node %X\n", ns->client->window);
+-
+- bool focused = (ns == mon->desk->focus);
+- bool active = (ns == ds->focus);
+-
+- if (focused)
+- clear_input_focus();
+-
+- unlink_node(ms, ds, ns);
+- insert_node(md, dd, ns, nd);
+-
+- if (md != ms)
+- translate_client(ms, md, ns->client);
+-
+- if (ds != dd) {
+- ewmh_set_wm_desktop(ns, dd);
+- if (!ns->client->sticky) {
+- if (ds == ms->desk && dd != md->desk)
+- window_hide(ns->client->window);
+- else if (ds != ms->desk && dd == md->desk)
+- window_show(ns->client->window);
+- }
+- if (ns->client->fullscreen && dd->focus != ns)
+- set_fullscreen(ns, false);
+- }
+-
+- history_transfer_node(md, dd, ns);
+- stack(ns, STACK_BELOW);
+-
+- if (ds == dd) {
+- if (focused)
+- focus_node(md, dd, ns);
+- else if (active)
+- pseudo_focus(md, dd, ns);
+- } else {
+- if (focused)
+- update_current();
+- else if (ns == mon->desk->focus)
+- update_input_focus();
+- }
+-
+- arrange(ms, ds);
+- if (ds != dd)
+- arrange(md, dd);
+-
+- return true;
++ if (ns == NULL || ns == nd || (sticky_still && ns->client->sticky))
++ return false;
++
++ PRINTF("transfer node %X\n", ns->client->window);
++
++ bool focused = (ns == mon->desk->focus);
++ bool active = (ns == ds->focus);
++
++ if (focused)
++ clear_input_focus();
++
++ unlink_node(ms, ds, ns);
++ insert_node(md, dd, ns, nd);
++
++ if (md != ms)
++ translate_client(ms, md, ns->client);
++
++ if (ds != dd) {
++ ewmh_set_wm_desktop(ns, dd);
++ if (!ns->client->sticky) {
++ if (ds == ms->desk && dd != md->desk)
++ window_hide(ns->client->window);
++ else if (ds != ms->desk && dd == md->desk)
++ window_show(ns->client->window);
++ }
++ if (ns->client->fullscreen && dd->focus != ns)
++ set_fullscreen(ns, false);
++ }
++
++ history_transfer_node(md, dd, ns);
++ stack(ns, STACK_BELOW);
++
++ if (ds == dd) {
++ if (focused)
++ focus_node(md, dd, ns);
++ else if (active)
++ pseudo_focus(md, dd, ns);
++ } else {
++ if (focused)
++ update_current();
++ else if (ns == mon->desk->focus)
++ update_input_focus();
++ }
++
++ arrange(ms, ds);
++ if (ds != dd)
++ arrange(md, dd);
++
++ return true;
+ }
+
+ node_t *closest_node(monitor_t *m, desktop_t *d, node_t *n, cycle_dir_t dir, client_select_t sel)
+ {
+- if (n == NULL)
+- return NULL;
+-
+- node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
+- if (f == NULL)
+- f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
+-
+- coordinates_t ref = {m, d, n};
+- while (f != n) {
+- coordinates_t loc = {m, d, f};
+- if (node_matches(&loc, &ref, sel))
+- return f;
+- f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
+- if (f == NULL)
+- f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
+- }
+- return NULL;
++ if (n == NULL)
++ return NULL;
++
++ node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
++ if (f == NULL)
++ f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
++
++ coordinates_t ref = {m, d, n};
++ while (f != n) {
++ coordinates_t loc = {m, d, f};
++ if (node_matches(&loc, &ref, sel))
++ return f;
++ f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
++ if (f == NULL)
++ f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root));
++ }
++ return NULL;
+ }
+
+ void circulate_leaves(monitor_t *m, desktop_t *d, circulate_dir_t dir)
+ {
+- if (d == NULL || d->root == NULL || d->focus == NULL || is_leaf(d->root))
+- return;
+- node_t *p = d->focus->parent;
+- bool focus_first_child = is_first_child(d->focus);
+- if (dir == CIRCULATE_FORWARD)
+- for (node_t *s = second_extrema(d->root), *f = prev_tiled_leaf(d, s, d->root); f != NULL; s = prev_tiled_leaf(d, f, d->root), f = prev_tiled_leaf(d, s, d->root))
+- swap_nodes(m, d, f, m, d, s);
+- else
+- for (node_t *f = first_extrema(d->root), *s = next_tiled_leaf(d, f, d->root); s != NULL; f = next_tiled_leaf(d, s, d->root), s = next_tiled_leaf(d, f, d->root))
+- 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);
++ if (d == NULL || d->root == NULL || d->focus == NULL || is_leaf(d->root))
++ return;
++ node_t *p = d->focus->parent;
++ bool focus_first_child = is_first_child(d->focus);
++ if (dir == CIRCULATE_FORWARD)
++ for (node_t *s = second_extrema(d->root), *f = prev_tiled_leaf(d, s, d->root); f != NULL; s = prev_tiled_leaf(d, f, d->root), f = prev_tiled_leaf(d, s, d->root))
++ swap_nodes(m, d, f, m, d, s);
++ else
++ for (node_t *f = first_extrema(d->root), *s = next_tiled_leaf(d, f, d->root); s != NULL; f = next_tiled_leaf(d, s, d->root), s = next_tiled_leaf(d, f, d->root))
++ 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 update_vacant_state(node_t *n)
+ {
+- if (n == NULL)
+- return;
++ if (n == NULL)
++ return;
+
+- PUTS("update vacant state");
++ PUTS("update vacant state");
+
+- /* n is not a leaf */
+- node_t *p = n;
++ /* n is not a leaf */
++ node_t *p = n;
+
+- while (p != NULL) {
+- p->vacant = (p->first_child->vacant && p->second_child->vacant);
+- p = p->parent;
+- }
++ while (p != NULL) {
++ p->vacant = (p->first_child->vacant && p->second_child->vacant);
++ p = p->parent;
++ }
+ }
+
+ void update_privacy_level(node_t *n, bool value)
+ {
+- int v = (value ? 1 : -1);
+- for (node_t *p = n; p != NULL; p = p->parent)
+- p->privacy_level += v;
++ int v = (value ? 1 : -1);
++ for (node_t *p = n; p != NULL; p = p->parent)
++ p->privacy_level += v;
+ }
+diff --git a/tree.h b/tree.h
+index 3a82a2e..3f0ee5d 100644
+--- a/tree.h
++++ b/tree.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_TREE_H
+@@ -32,7 +36,7 @@ void pseudo_focus(monitor_t *m, desktop_t *d, node_t *n);
+ void focus_node(monitor_t *m, desktop_t *d, node_t *n);
+ void update_current(void);
+ node_t *make_node(void);
+-client_t *make_client(xcb_window_t win);
++client_t *make_client(xcb_window_t win, unsigned int border_width);
+ bool is_leaf(node_t *n);
+ bool is_tiled(client_t *c);
+ bool is_floating(client_t *c);
+@@ -60,6 +64,7 @@ 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 remove_node(monitor_t *m, desktop_t *d, node_t *n);
+diff --git a/types.h b/types.h
+index 6495f0a..6c57713 100644
+--- a/types.h
++++ b/types.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_TYPES_H
+@@ -35,268 +39,284 @@
+ #define MAX_STATE 4
+
+ typedef enum {
+- TYPE_HORIZONTAL,
+- TYPE_VERTICAL
++ TYPE_HORIZONTAL,
++ TYPE_VERTICAL
+ } split_type_t;
+
+ typedef enum {
+- MODE_AUTOMATIC,
+- MODE_MANUAL
++ MODE_AUTOMATIC,
++ MODE_MANUAL
+ } split_mode_t;
+
+ typedef enum {
+- CLIENT_TYPE_ALL,
+- CLIENT_TYPE_FLOATING,
+- CLIENT_TYPE_TILED
++ CLIENT_TYPE_ALL,
++ CLIENT_TYPE_FLOATING,
++ CLIENT_TYPE_TILED
+ } client_type_t;
+
+ typedef enum {
+- CLIENT_CLASS_ALL,
+- CLIENT_CLASS_EQUAL,
+- CLIENT_CLASS_DIFFER
++ CLIENT_CLASS_ALL,
++ CLIENT_CLASS_EQUAL,
++ CLIENT_CLASS_DIFFER
+ } client_class_t;
+
++typedef enum {
++ CLIENT_MODE_ALL,
++ CLIENT_MODE_AUTOMATIC,
++ CLIENT_MODE_MANUAL
++} client_mode_t;
++
+ typedef struct {
+- client_type_t type;
+- client_class_t class;
+- bool urgent;
+- bool manual;
+- bool local;
++ client_type_t type;
++ client_class_t class;
++ client_mode_t mode;
++ bool urgent;
++ bool local;
+ } client_select_t;
+
+ typedef enum {
+- ALTER_TOGGLE,
+- ALTER_SET
++ ALTER_TOGGLE,
++ ALTER_SET
+ } alter_state_t;
+
+ typedef enum {
+- CYCLE_NEXT,
+- CYCLE_PREV
++ CYCLE_NEXT,
++ CYCLE_PREV
+ } cycle_dir_t;
+
+ typedef enum {
+- CIRCULATE_FORWARD,
+- CIRCULATE_BACKWARD
++ CIRCULATE_FORWARD,
++ CIRCULATE_BACKWARD
+ } circulate_dir_t;
+
+ typedef enum {
+- HISTORY_OLDER,
+- HISTORY_NEWER
++ HISTORY_OLDER,
++ HISTORY_NEWER
+ } history_dir_t;
+
+ typedef enum {
+- DIR_RIGHT,
+- DIR_DOWN,
+- DIR_LEFT,
+- DIR_UP
++ DIR_RIGHT,
++ DIR_DOWN,
++ DIR_LEFT,
++ DIR_UP
+ } direction_t;
+
+ typedef enum {
+- CORNER_TOP_LEFT,
+- CORNER_TOP_RIGHT,
+- CORNER_BOTTOM_RIGHT,
+- CORNER_BOTTOM_LEFT
++ CORNER_TOP_LEFT,
++ CORNER_TOP_RIGHT,
++ CORNER_BOTTOM_RIGHT,
++ CORNER_BOTTOM_LEFT
+ } corner_t;
+
+ typedef enum {
+- SIDE_LEFT,
+- SIDE_TOP,
+- SIDE_RIGHT,
+- SIDE_BOTTOM
++ SIDE_LEFT,
++ SIDE_TOP,
++ SIDE_RIGHT,
++ SIDE_BOTTOM
+ } side_t;
+
+ typedef enum {
+- ACTION_NONE,
+- ACTION_FOCUS,
+- ACTION_MOVE,
+- ACTION_RESIZE_SIDE,
+- ACTION_RESIZE_CORNER
++ ACTION_NONE,
++ ACTION_FOCUS,
++ ACTION_MOVE,
++ ACTION_RESIZE_SIDE,
++ ACTION_RESIZE_CORNER
+ } pointer_action_t;
+
+ typedef enum {
+- LAYOUT_TILED,
+- LAYOUT_MONOCLE
++ LAYOUT_TILED,
++ LAYOUT_MONOCLE
+ } layout_t;
+
+ typedef enum {
+- FLIP_HORIZONTAL,
+- FLIP_VERTICAL
++ FLIP_HORIZONTAL,
++ FLIP_VERTICAL
+ } flip_t;
+
+ typedef enum {
+- DESKTOP_STATUS_ALL,
+- DESKTOP_STATUS_FREE,
+- DESKTOP_STATUS_OCCUPIED
++ DESKTOP_STATUS_ALL,
++ DESKTOP_STATUS_FREE,
++ DESKTOP_STATUS_OCCUPIED
+ } desktop_status_t;
+
+ typedef enum {
+- DESKTOP_URGENCY_ALL,
+- DESKTOP_URGENCY_ON,
+- DESKTOP_URGENCY_OFF
++ DESKTOP_URGENCY_ALL,
++ DESKTOP_URGENCY_ON,
++ DESKTOP_URGENCY_OFF
+ } desktop_urgency_t;
+
+ typedef struct {
+- desktop_status_t status;
+- bool urgent;
+- bool local;
++ desktop_status_t status;
++ bool urgent;
++ bool local;
+ } desktop_select_t;
+
+ typedef struct {
+- xcb_window_t window;
+- char class_name[SMALEN];
+- unsigned int border_width;
+- bool pseudo_tiled;
+- bool floating;
+- bool fullscreen;
+- bool locked; /* protects window from being closed */
+- bool sticky;
+- bool urgent;
+- bool private;
+- bool icccm_focus;
+- xcb_rectangle_t floating_rectangle;
+- xcb_rectangle_t tiled_rectangle;
+- xcb_atom_t wm_state[MAX_STATE];
+- int num_states;
++ xcb_window_t window;
++ char class_name[3 * SMALEN / 2];
++ char instance_name[3 * SMALEN / 2];
++ unsigned int border_width;
++ bool pseudo_tiled;
++ bool floating;
++ bool fullscreen;
++ bool locked; /* protects window from being closed */
++ bool sticky;
++ bool urgent;
++ bool private;
++ bool icccm_focus;
++ xcb_rectangle_t floating_rectangle;
++ xcb_rectangle_t tiled_rectangle;
++ uint16_t min_width;
++ uint16_t max_width;
++ uint16_t min_height;
++ uint16_t max_height;
++ xcb_atom_t wm_state[MAX_STATE];
++ int num_states;
+ } client_t;
+
+ typedef struct node_t node_t;
+ struct node_t {
+- split_type_t split_type;
+- double split_ratio;
+- split_mode_t split_mode;
+- direction_t split_dir;
+- int birth_rotation;
+- xcb_rectangle_t rectangle;
+- bool vacant; /* vacant nodes only hold floating clients */
+- int privacy_level;
+- node_t *first_child;
+- node_t *second_child;
+- node_t *parent;
+- client_t *client; /* NULL except for leaves */
++ split_type_t split_type;
++ double split_ratio;
++ split_mode_t split_mode;
++ direction_t split_dir;
++ int birth_rotation;
++ xcb_rectangle_t rectangle;
++ bool vacant; /* vacant nodes only hold floating clients */
++ int privacy_level;
++ node_t *first_child;
++ node_t *second_child;
++ node_t *parent;
++ client_t *client; /* NULL except for leaves */
+ };
+
+ typedef struct desktop_t desktop_t;
+ struct desktop_t {
+- char name[SMALEN];
+- layout_t layout;
+- node_t *root;
+- node_t *focus;
+- desktop_t *prev;
+- desktop_t *next;
+- int top_padding;
+- int right_padding;
+- int bottom_padding;
+- int left_padding;
+- int window_gap;
+- unsigned int border_width;
+- bool floating;
++ char name[SMALEN];
++ layout_t layout;
++ node_t *root;
++ node_t *focus;
++ desktop_t *prev;
++ desktop_t *next;
++ int top_padding;
++ int right_padding;
++ int bottom_padding;
++ int left_padding;
++ int window_gap;
++ unsigned int border_width;
++ bool floating;
+ };
+
+ typedef struct monitor_t monitor_t;
+ struct monitor_t {
+- char name[SMALEN];
+- xcb_randr_output_t id;
+- xcb_rectangle_t rectangle;
+- xcb_window_t root;
+- bool wired;
+- int top_padding;
+- int right_padding;
+- int bottom_padding;
+- int left_padding;
+- desktop_t *desk;
+- desktop_t *desk_head;
+- desktop_t *desk_tail;
+- monitor_t *prev;
+- monitor_t *next;
+- int num_sticky;
++ char name[SMALEN];
++ xcb_randr_output_t id;
++ xcb_rectangle_t rectangle;
++ xcb_window_t root;
++ bool wired;
++ int top_padding;
++ int right_padding;
++ int bottom_padding;
++ int left_padding;
++ desktop_t *desk;
++ desktop_t *desk_head;
++ desktop_t *desk_tail;
++ monitor_t *prev;
++ monitor_t *next;
++ int num_sticky;
+ };
+
+ typedef struct {
+- monitor_t *monitor;
+- desktop_t *desktop;
+- node_t *node;
++ 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;
++ 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;
++ 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;
+- subscriber_list_t *prev;
+- subscriber_list_t *next;
++ int fd;
++ FILE *stream;
++ subscriber_list_t *prev;
++ subscriber_list_t *next;
+ };
+
+ typedef struct rule_t rule_t;
+ struct rule_t {
+- char cause[MAXLEN];
+- char effect[MAXLEN];
+- bool one_shot;
+- rule_t *prev;
+- rule_t *next;
++ char cause[MAXLEN];
++ char effect[MAXLEN];
++ bool one_shot;
++ rule_t *prev;
++ rule_t *next;
+ };
+
+ typedef struct {
+- char class_name[SMALEN];
+- char instance_name[SMALEN];
+- char desktop_desc[MAXLEN];
+- char monitor_desc[MAXLEN];
+- bool pseudo_tiled;
+- bool floating;
+- bool fullscreen;
+- bool locked;
+- bool sticky;
+- bool private;
+- bool center;
+- bool lower;
+- bool follow;
+- bool manage;
+- bool focus;
++ 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];
++ uint16_t min_width;
++ uint16_t max_width;
++ uint16_t min_height;
++ uint16_t max_height;
++ bool pseudo_tiled;
++ bool floating;
++ bool fullscreen;
++ bool locked;
++ bool sticky;
++ bool private;
++ bool center;
++ bool follow;
++ bool manage;
++ bool focus;
+ } 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;
++ int fd;
++ xcb_window_t win;
++ rule_consequence_t *csq;
++ pending_rule_t *prev;
++ pending_rule_t *next;
+ };
+
+ typedef struct {
+- xcb_point_t position;
+- pointer_action_t action;
+- xcb_rectangle_t rectangle;
+- node_t *vertical_fence;
+- node_t *horizontal_fence;
+- monitor_t *monitor;
+- desktop_t *desktop;
+- node_t *node;
+- client_t *client;
+- xcb_window_t window;
+- bool is_tiled;
+- double vertical_ratio;
+- double horizontal_ratio;
+- corner_t corner;
+- side_t side;
++ xcb_point_t position;
++ pointer_action_t action;
++ xcb_rectangle_t rectangle;
++ node_t *vertical_fence;
++ node_t *horizontal_fence;
++ monitor_t *monitor;
++ desktop_t *desktop;
++ node_t *node;
++ client_t *client;
++ xcb_window_t window;
++ bool is_tiled;
++ double vertical_ratio;
++ double horizontal_ratio;
++ corner_t corner;
++ side_t side;
+ } pointer_state_t;
+
+ typedef struct {
+- node_t *fence;
+- unsigned int distance;
++ node_t *fence;
++ unsigned int distance;
+ } fence_distance_t;
+
+ #endif
+diff --git a/window.c b/window.c
+index 4b1ed3f..b750a2d 100644
+--- a/window.c
++++ b/window.c
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #include <stdlib.h>
+@@ -32,722 +36,775 @@
+ #include "settings.h"
+ #include "stack.h"
+ #include "tree.h"
++#include "messages.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);
++ 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 (wa != NULL) {
++ override_redirect = wa->override_redirect;
++ free(wa);
++ }
+
+- if (override_redirect || locate_window(win, &loc))
+- return;
++ 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;
++ /* 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);
+- }
++ 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;
+-
+- parse_rule_consequence(fd, csq);
+-
+- if (csq->lower)
+- window_lower(win);
+-
+- if (!csq->manage) {
+- disable_floating_atom(win);
+- window_show(win);
+- return;
+- }
+-
+- PRINTF("manage %X\n", win);
+-
+- 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)) {
+- m = trg.monitor;
+- d = trg.desktop;
+- }
+- } 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)) {
+- m = trg.monitor;
+- d = trg.monitor->desk;
+- }
+- }
+-
+- if (csq->sticky) {
+- m = mon;
+- d = mon->desk;
+- }
+-
+- client_t *c = make_client(win);
+- update_floating_rectangle(c);
+- monitor_t *mm = monitor_from_client(c);
+- embrace_client(mm, c);
+- translate_client(mm, m, c);
+- if (csq->center)
+- window_center(m, c);
+-
+- snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
+-
+- csq->floating = csq->floating || d->floating;
+-
+- node_t *n = make_node();
+- n->client = c;
+-
+- insert_node(m, d, n, d->focus);
+-
+- disable_floating_atom(c->window);
+- set_pseudo_tiled(n, csq->pseudo_tiled);
+- set_floating(n, csq->floating);
+- set_locked(m, d, n, csq->locked);
+- set_sticky(m, d, n, csq->sticky);
+- set_private(m, d, n, csq->private);
+-
+- if (d->focus != NULL && d->focus->client->fullscreen)
+- set_fullscreen(d->focus, false);
+-
+- set_fullscreen(n, csq->fullscreen);
+-
+- arrange(m, d);
+-
+- bool give_focus = (csq->focus && (d == mon->desk || csq->follow));
+-
+- if (give_focus)
+- focus_node(m, d, n);
+- else if (csq->focus)
+- pseudo_focus(m, d, n);
+- else
+- stack(n, STACK_ABOVE);
+-
+- uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+- xcb_change_window_attributes(dpy, c->window, XCB_CW_EVENT_MASK, values);
+-
+- if (visible) {
+- if (d == m->desk)
+- window_show(n->client->window);
+- else
+- window_hide(n->client->window);
+- }
+-
+- /* the same function is already called in `focus_node` but has no effects on unmapped windows */
+- if (give_focus)
+- xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
+-
+- num_clients++;
+- ewmh_set_wm_desktop(n, d);
+- ewmh_update_client_list();
++ monitor_t *m = mon;
++ desktop_t *d = mon->desk;
++ node_t *f = mon->desk->focus;
++
++ parse_rule_consequence(fd, csq);
++
++ if (!csq->manage) {
++ disable_floating_atom(win);
++ window_show(win);
++ return;
++ }
++
++ PRINTF("manage %X\n", win);
++
++ 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)) {
++ 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)) {
++ 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)) {
++ 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)) {
++ f->split_mode = MODE_MANUAL;
++ f->split_dir = dir;
++ }
++ }
++
++ client_t *c = make_client(win, d->border_width);
++ update_floating_rectangle(c);
++ if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0)
++ csq->center = true;
++ c->min_width = csq->min_width;
++ c->max_width = csq->max_width;
++ c->min_height = csq->min_height;
++ c->max_height = csq->max_height;
++ monitor_t *mm = monitor_from_client(c);
++ embrace_client(mm, c);
++ translate_client(mm, m, c);
++ 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);
++
++ csq->floating = csq->floating || d->floating;
++
++ node_t *n = make_node();
++ n->client = c;
++
++ insert_node(m, d, n, f);
++
++ disable_floating_atom(c->window);
++ set_pseudo_tiled(n, csq->pseudo_tiled);
++ set_floating(n, csq->floating);
++ set_locked(m, d, n, csq->locked);
++ set_sticky(m, d, n, csq->sticky);
++ set_private(m, d, n, csq->private);
++
++ if (d->focus != NULL && d->focus->client->fullscreen)
++ set_fullscreen(d->focus, false);
++
++ set_fullscreen(n, csq->fullscreen);
++
++ arrange(m, d);
++
++ bool give_focus = (csq->focus && (d == mon->desk || csq->follow));
++
++ if (give_focus)
++ focus_node(m, d, n);
++ else if (csq->focus)
++ pseudo_focus(m, d, n);
++ else
++ stack(n, STACK_ABOVE);
++
++ uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
++ xcb_change_window_attributes(dpy, c->window, XCB_CW_EVENT_MASK, values);
++
++ if (visible) {
++ if (d == m->desk)
++ window_show(n->client->window);
++ else
++ window_hide(n->client->window);
++ }
++
++ /* the same function is already called in `focus_node` but has no effects on unmapped windows */
++ if (give_focus)
++ xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME);
++
++ num_clients++;
++ ewmh_set_wm_desktop(n, d);
++ ewmh_update_client_list();
+ }
+
+ void unmanage_window(xcb_window_t win)
+ {
+- coordinates_t loc;
+- if (locate_window(win, &loc)) {
+- PRINTF("unmanage %X\n", win);
+- remove_node(loc.monitor, loc.desktop, loc.node);
+- 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;
+- }
+- }
+- }
++ coordinates_t loc;
++ if (locate_window(win, &loc)) {
++ PRINTF("unmanage %X\n", win);
++ remove_node(loc.monitor, loc.desktop, loc.node);
++ if (frozen_pointer->window == win)
++ frozen_pointer->action = ACTION_NONE;
++ 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;
++ }
++ }
++ }
+ }
+
+ void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
+ {
+- if (n == NULL || n->client->border_width < 1) {
+- return;
+- }
+-
+- xcb_window_t win = n->client->window;
+- uint32_t border_color_pxl = get_border_color(n->client, focused_window, focused_monitor);
+-
+- if (n->split_mode == MODE_AUTOMATIC) {
+- xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
+- } else {
+- unsigned int border_width = n->client->border_width;
+- uint32_t presel_border_color_pxl;
+- get_color(presel_border_color, win, &presel_border_color_pxl);
+-
+- xcb_rectangle_t actual_rectangle = get_rectangle(n->client);
+-
+- uint16_t width = actual_rectangle.width;
+- uint16_t height = actual_rectangle.height;
+-
+- uint16_t full_width = width + 2 * border_width;
+- uint16_t full_height = height + 2 * border_width;
+-
+- xcb_rectangle_t border_rectangles[] =
+- {
+- { width, 0, 2 * border_width, height + 2 * border_width },
+- { 0, height, width + 2 * border_width, 2 * border_width }
+- };
+-
+- xcb_rectangle_t *presel_rectangles;
+-
+- uint8_t win_depth = root_depth;
+- xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);
+- if (geo != NULL)
+- win_depth = geo->depth;
+- free(geo);
+-
+- xcb_pixmap_t pixmap = xcb_generate_id(dpy);
+- xcb_create_pixmap(dpy, win_depth, pixmap, win, full_width, full_height);
+-
+- xcb_gcontext_t gc = xcb_generate_id(dpy);
+- xcb_create_gc(dpy, gc, pixmap, 0, NULL);
+-
+- xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &border_color_pxl);
+- xcb_poly_fill_rectangle(dpy, pixmap, gc, LENGTH(border_rectangles), border_rectangles);
+-
+- uint16_t fence = (int16_t) (n->split_ratio * ((n->split_dir == DIR_UP || n->split_dir == DIR_DOWN) ? height : width));
+- presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
+- switch (n->split_dir) {
+- case DIR_UP:
+- presel_rectangles[0] = (xcb_rectangle_t) {width, 0, 2 * border_width, fence};
+- presel_rectangles[1] = (xcb_rectangle_t) {0, height + border_width, full_width, border_width};
+- break;
+- case DIR_DOWN:
+- presel_rectangles[0] = (xcb_rectangle_t) {width, fence + 1, 2 * border_width, height + border_width - (fence + 1)};
+- presel_rectangles[1] = (xcb_rectangle_t) {0, height, full_width, border_width};
+- break;
+- case DIR_LEFT:
+- presel_rectangles[0] = (xcb_rectangle_t) {0, height, fence, 2 * border_width};
+- presel_rectangles[1] = (xcb_rectangle_t) {width + border_width, 0, border_width, full_height};
+- break;
+- case DIR_RIGHT:
+- presel_rectangles[0] = (xcb_rectangle_t) {fence + 1, height, width + border_width - (fence + 1), 2 * border_width};
+- presel_rectangles[1] = (xcb_rectangle_t) {width, 0, border_width, full_height};
+- break;
+- }
+- xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
+- xcb_poly_fill_rectangle(dpy, pixmap, gc, 2, presel_rectangles);
+- xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXMAP, &pixmap);
+- free(presel_rectangles);
+- xcb_free_gc(dpy, gc);
+- xcb_free_pixmap(dpy, pixmap);
+- }
++ if (n == NULL || n->client->border_width < 1) {
++ return;
++ }
++
++ xcb_window_t win = n->client->window;
++ uint32_t border_color_pxl = get_border_color(n->client, focused_window, focused_monitor);
++
++ if (n->split_mode == MODE_AUTOMATIC) {
++ xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
++ } else {
++ unsigned int border_width = n->client->border_width;
++ uint32_t presel_border_color_pxl;
++ get_color(presel_border_color, win, &presel_border_color_pxl);
++
++ xcb_rectangle_t actual_rectangle = get_rectangle(n->client);
++
++ uint16_t width = actual_rectangle.width;
++ uint16_t height = actual_rectangle.height;
++
++ uint16_t full_width = width + 2 * border_width;
++ uint16_t full_height = height + 2 * border_width;
++
++ xcb_rectangle_t border_rectangles[] =
++ {
++ { width, 0, 2 * border_width, height + 2 * border_width },
++ { 0, height, width + 2 * border_width, 2 * border_width }
++ };
++
++ xcb_rectangle_t *presel_rectangles;
++
++ uint8_t win_depth = root_depth;
++ xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, win), NULL);
++ if (geo != NULL)
++ win_depth = geo->depth;
++ free(geo);
++
++ xcb_pixmap_t pixmap = xcb_generate_id(dpy);
++ xcb_create_pixmap(dpy, win_depth, pixmap, win, full_width, full_height);
++
++ xcb_gcontext_t gc = xcb_generate_id(dpy);
++ xcb_create_gc(dpy, gc, pixmap, 0, NULL);
++
++ xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &border_color_pxl);
++ xcb_poly_fill_rectangle(dpy, pixmap, gc, LENGTH(border_rectangles), border_rectangles);
++
++ uint16_t fence = (int16_t) (n->split_ratio * ((n->split_dir == DIR_UP || n->split_dir == DIR_DOWN) ? height : width));
++ presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
++ switch (n->split_dir) {
++ case DIR_UP:
++ presel_rectangles[0] = (xcb_rectangle_t) {width, 0, 2 * border_width, fence};
++ presel_rectangles[1] = (xcb_rectangle_t) {0, height + border_width, full_width, border_width};
++ break;
++ case DIR_DOWN:
++ presel_rectangles[0] = (xcb_rectangle_t) {width, fence + 1, 2 * border_width, height + border_width - (fence + 1)};
++ presel_rectangles[1] = (xcb_rectangle_t) {0, height, full_width, border_width};
++ break;
++ case DIR_LEFT:
++ presel_rectangles[0] = (xcb_rectangle_t) {0, height, fence, 2 * border_width};
++ presel_rectangles[1] = (xcb_rectangle_t) {width + border_width, 0, border_width, full_height};
++ break;
++ case DIR_RIGHT:
++ presel_rectangles[0] = (xcb_rectangle_t) {fence + 1, height, width + border_width - (fence + 1), 2 * border_width};
++ presel_rectangles[1] = (xcb_rectangle_t) {width, 0, border_width, full_height};
++ break;
++ }
++ xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
++ xcb_poly_fill_rectangle(dpy, pixmap, gc, 2, presel_rectangles);
++ xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXMAP, &pixmap);
++ free(presel_rectangles);
++ xcb_free_gc(dpy, gc);
++ xcb_free_pixmap(dpy, pixmap);
++ }
+ }
+
+ pointer_state_t *make_pointer_state(void)
+ {
+- pointer_state_t *p = malloc(sizeof(pointer_state_t));
+- p->monitor = NULL;
+- p->desktop = NULL;
+- p->node = p->vertical_fence = p->horizontal_fence = NULL;
+- p->client = NULL;
+- p->window = XCB_NONE;
+- p->action = ACTION_NONE;
+- return p;
++ pointer_state_t *p = malloc(sizeof(pointer_state_t));
++ p->monitor = NULL;
++ p->desktop = NULL;
++ p->node = p->vertical_fence = p->horizontal_fence = NULL;
++ p->client = NULL;
++ p->window = XCB_NONE;
++ p->action = ACTION_NONE;
++ return p;
+ }
+
++/* 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));
++ 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(client_t *c)
+ {
+- if (is_tiled(c))
+- return c->tiled_rectangle;
+- else
+- return c->floating_rectangle;
++ if (is_tiled(c))
++ return c->tiled_rectangle;
++ else
++ return c->floating_rectangle;
+ }
+
+ void get_side_handle(client_t *c, direction_t dir, xcb_point_t *pt)
+ {
+- xcb_rectangle_t rect = get_rectangle(c);
+- switch (dir) {
+- case DIR_RIGHT:
+- pt->x = rect.x + rect.width;
+- pt->y = rect.y + (rect.height / 2);
+- break;
+- case DIR_DOWN:
+- pt->x = rect.x + (rect.width / 2);
+- pt->y = rect.y + rect.height;
+- break;
+- case DIR_LEFT:
+- pt->x = rect.x;
+- pt->y = rect.y + (rect.height / 2);
+- break;
+- case DIR_UP:
+- pt->x = rect.x + (rect.width / 2);
+- pt->y = rect.y;
+- break;
+- }
++ xcb_rectangle_t rect = get_rectangle(c);
++ switch (dir) {
++ case DIR_RIGHT:
++ pt->x = rect.x + rect.width;
++ pt->y = rect.y + (rect.height / 2);
++ break;
++ case DIR_DOWN:
++ pt->x = rect.x + (rect.width / 2);
++ pt->y = rect.y + rect.height;
++ break;
++ case DIR_LEFT:
++ pt->x = rect.x;
++ pt->y = rect.y + (rect.height / 2);
++ break;
++ case DIR_UP:
++ pt->x = rect.x + (rect.width / 2);
++ pt->y = rect.y;
++ break;
++ }
+ }
+
+ 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;
++ xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
++ if (qtr == NULL)
++ return;
+
+- PUTS("adopt orphans");
++ PUTS("adopt orphans");
+
+- 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);
+- }
++ 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);
++ free(qtr);
+ }
+
+ void window_close(node_t *n)
+ {
+- if (n == NULL || n->client->locked)
+- return;
++ if (n == NULL || n->client->locked)
++ return;
+
+- PRINTF("close window %X\n", n->client->window);
++ PRINTF("close window %X\n", n->client->window);
+
+- send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
++ send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
+ }
+
+ void window_kill(monitor_t *m, desktop_t *d, node_t *n)
+ {
+- if (n == NULL)
+- return;
++ if (n == NULL)
++ return;
+
+- xcb_window_t win = n->client->window;
+- PRINTF("kill window %X\n", win);
++ xcb_window_t win = n->client->window;
++ PRINTF("kill window %X\n", win);
+
+- xcb_kill_client(dpy, win);
+- remove_node(m, d, n);
++ xcb_kill_client(dpy, win);
++ remove_node(m, d, n);
+ }
+
+ void set_fullscreen(node_t *n, bool value)
+ {
+- if (n == NULL || n->client->fullscreen == value)
+- return;
++ if (n == NULL || n->client->fullscreen == value)
++ return;
+
+- client_t *c = n->client;
++ client_t *c = n->client;
+
+- PRINTF("fullscreen %X: %s\n", c->window, BOOLSTR(value));
++ PRINTF("fullscreen %X: %s\n", c->window, BOOLSTR(value));
+
+- c->fullscreen = value;
+- if (value)
+- ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_FULLSCREEN);
+- else
+- ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_FULLSCREEN);
+- stack(n, STACK_ABOVE);
++ c->fullscreen = value;
++ if (value)
++ ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_FULLSCREEN);
++ else
++ ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_FULLSCREEN);
++ stack(n, STACK_ABOVE);
+ }
+
+ void set_pseudo_tiled(node_t *n, bool value)
+ {
+- if (n == NULL || n->client->pseudo_tiled == value)
+- return;
++ if (n == NULL || n->client->pseudo_tiled == value)
++ return;
+
+- PRINTF("pseudo-tiled %X: %s\n", n->client->window, BOOLSTR(value));
++ PRINTF("pseudo-tiled %X: %s\n", n->client->window, BOOLSTR(value));
+
+- n->client->pseudo_tiled = value;
++ n->client->pseudo_tiled = value;
+ }
+
+ void set_floating(node_t *n, bool value)
+ {
+- if (n == NULL || n->client->fullscreen || n->client->floating == value)
+- return;
++ if (n == NULL || n->client->fullscreen || n->client->floating == value)
++ return;
+
+- PRINTF("floating %X: %s\n", n->client->window, BOOLSTR(value));
++ PRINTF("floating %X: %s\n", n->client->window, BOOLSTR(value));
+
+- n->split_mode = MODE_AUTOMATIC;
+- client_t *c = n->client;
+- c->floating = n->vacant = value;
+- update_vacant_state(n->parent);
++ n->split_mode = MODE_AUTOMATIC;
++ client_t *c = n->client;
++ c->floating = n->vacant = value;
++ update_vacant_state(n->parent);
+
+- if (value) {
+- enable_floating_atom(c->window);
+- unrotate_brother(n);
+- } else {
+- disable_floating_atom(c->window);
+- rotate_brother(n);
+- }
++ if (value) {
++ enable_floating_atom(c->window);
++ unrotate_brother(n);
++ } else {
++ disable_floating_atom(c->window);
++ rotate_brother(n);
++ }
+
+- stack(n, STACK_ABOVE);
++ stack(n, STACK_ABOVE);
+ }
+
+ void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
+ {
+- if (n == NULL || n->client->locked == value)
+- return;
++ if (n == NULL || n->client->locked == value)
++ return;
+
+- client_t *c = n->client;
++ client_t *c = n->client;
+
+- PRINTF("set locked %X: %s\n", c->window, BOOLSTR(value));
++ PRINTF("set locked %X: %s\n", c->window, BOOLSTR(value));
+
+- c->locked = value;
+- window_draw_border(n, d->focus == n, m == mon);
++ c->locked = value;
++ window_draw_border(n, d->focus == n, m == mon);
+ }
+
+ void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+ {
+- if (n == NULL || n->client->sticky == value)
+- return;
++ if (n == NULL || n->client->sticky == value)
++ return;
+
+- client_t *c = n->client;
++ client_t *c = n->client;
+
+- PRINTF("set sticky %X: %s\n", c->window, BOOLSTR(value));
++ PRINTF("set sticky %X: %s\n", c->window, BOOLSTR(value));
+
+- if (d != m->desk)
+- transfer_node(m, d, n, m, m->desk, m->desk->focus);
++ if (d != m->desk)
++ transfer_node(m, d, n, m, m->desk, m->desk->focus);
+
+- c->sticky = value;
+- if (value) {
+- ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_STICKY);
+- m->num_sticky++;
+- } else {
+- ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_STICKY);
+- m->num_sticky--;
+- }
++ c->sticky = value;
++ if (value) {
++ ewmh_wm_state_add(c, ewmh->_NET_WM_STATE_STICKY);
++ m->num_sticky++;
++ } else {
++ ewmh_wm_state_remove(c, ewmh->_NET_WM_STATE_STICKY);
++ m->num_sticky--;
++ }
+
+- window_draw_border(n, d->focus == n, m == mon);
++ window_draw_border(n, d->focus == n, m == mon);
+ }
+
+ void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
+ {
+- if (n == NULL || n->client->private == value)
+- return;
++ if (n == NULL || n->client->private == value)
++ return;
+
+- client_t *c = n->client;
++ client_t *c = n->client;
+
+- PRINTF("set private %X: %s\n", c->window, BOOLSTR(value));
++ PRINTF("set private %X: %s\n", c->window, BOOLSTR(value));
+
+- c->private = value;
+- update_privacy_level(n, value);
+- window_draw_border(n, d->focus == n, m == mon);
++ c->private = value;
++ update_privacy_level(n, value);
++ window_draw_border(n, d->focus == n, m == mon);
+ }
+
+ void set_urgency(monitor_t *m, desktop_t *d, node_t *n, bool value)
+ {
+- if (value && mon->desk->focus == n)
+- return;
+- n->client->urgent = value;
+- window_draw_border(n, d->focus == n, m == mon);
+- put_status();
++ if (value && mon->desk->focus == n)
++ return;
++ n->client->urgent = value;
++ window_draw_border(n, d->focus == n, m == mon);
++ put_status();
+ }
+
+ void set_floating_atom(xcb_window_t win, uint32_t value)
+ {
+- if (!apply_floating_atom)
+- return;
+- set_atom(win, _BSPWM_FLOATING_WINDOW, value);
++ if (!apply_floating_atom)
++ return;
++ set_atom(win, _BSPWM_FLOATING_WINDOW, value);
+ }
+
+ void enable_floating_atom(xcb_window_t win)
+ {
+- set_floating_atom(win, 1);
++ set_floating_atom(win, 1);
+ }
+
+ void disable_floating_atom(xcb_window_t win)
+ {
+- set_floating_atom(win, 0);
++ set_floating_atom(win, 0);
+ }
+
+ uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor)
+ {
+- if (c == NULL)
+- return 0;
+-
+- uint32_t pxl = 0;
+-
+- if (focused_monitor && focused_window) {
+- if (c->locked)
+- get_color(focused_locked_border_color, c->window, &pxl);
+- else if (c->sticky)
+- get_color(focused_sticky_border_color, c->window, &pxl);
+- else if (c->private)
+- get_color(focused_private_border_color, c->window, &pxl);
+- else
+- get_color(focused_border_color, c->window, &pxl);
+- } else if (focused_window) {
+- if (c->urgent)
+- get_color(urgent_border_color, c->window, &pxl);
+- else if (c->locked)
+- get_color(active_locked_border_color, c->window, &pxl);
+- else if (c->sticky)
+- get_color(active_sticky_border_color, c->window, &pxl);
+- else if (c->private)
+- get_color(active_private_border_color, c->window, &pxl);
+- else
+- get_color(active_border_color, c->window, &pxl);
+- } else {
+- if (c->urgent)
+- get_color(urgent_border_color, c->window, &pxl);
+- else if (c->locked)
+- get_color(normal_locked_border_color, c->window, &pxl);
+- else if (c->sticky)
+- get_color(normal_sticky_border_color, c->window, &pxl);
+- else if (c->private)
+- get_color(normal_private_border_color, c->window, &pxl);
+- else
+- get_color(normal_border_color, c->window, &pxl);
+- }
+-
+- return pxl;
++ if (c == NULL)
++ return 0;
++
++ uint32_t pxl = 0;
++
++ if (focused_monitor && focused_window) {
++ if (c->locked)
++ get_color(focused_locked_border_color, c->window, &pxl);
++ else if (c->sticky)
++ get_color(focused_sticky_border_color, c->window, &pxl);
++ else if (c->private)
++ get_color(focused_private_border_color, c->window, &pxl);
++ else
++ get_color(focused_border_color, c->window, &pxl);
++ } else if (focused_window) {
++ if (c->urgent)
++ get_color(urgent_border_color, c->window, &pxl);
++ else if (c->locked)
++ get_color(active_locked_border_color, c->window, &pxl);
++ else if (c->sticky)
++ get_color(active_sticky_border_color, c->window, &pxl);
++ else if (c->private)
++ get_color(active_private_border_color, c->window, &pxl);
++ else
++ get_color(active_border_color, c->window, &pxl);
++ } else {
++ if (c->urgent)
++ get_color(urgent_border_color, c->window, &pxl);
++ else if (c->locked)
++ get_color(normal_locked_border_color, c->window, &pxl);
++ else if (c->sticky)
++ get_color(normal_sticky_border_color, c->window, &pxl);
++ else if (c->private)
++ get_color(normal_private_border_color, c->window, &pxl);
++ else
++ get_color(normal_border_color, c->window, &pxl);
++ }
++
++ return pxl;
+ }
+
+ void update_floating_rectangle(client_t *c)
+ {
+- xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), NULL);
++ xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), NULL);
+
+- if (geo != NULL)
+- c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
+- else
+- c->floating_rectangle = (xcb_rectangle_t) {0, 0, 32, 24};
++ if (geo != NULL)
++ c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
+
+- free(geo);
++ free(geo);
+ }
+
++void restrain_floating_width(client_t *c, int *width)
++{
++ if (*width < 1)
++ *width = 1;
++ if (c->min_width > 0 && *width < c->min_width)
++ *width = c->min_width;
++ else if (c->max_width > 0 && *width > c->max_width)
++ *width = c->max_width;
++}
++
++void restrain_floating_height(client_t *c, int *height)
++{
++ if (*height < 1)
++ *height = 1;
++ if (c->min_height > 0 && *height < c->min_height)
++ *height = c->min_height;
++ else if (c->max_height > 0 && *height > c->max_height)
++ *height = c->max_height;
++}
++
++void restrain_floating_size(client_t *c, int *width, int *height)
++{
++ restrain_floating_width(c, width);
++ restrain_floating_height(c, height);
++}
+
+ void query_pointer(xcb_window_t *win, xcb_point_t *pt)
+ {
+- window_lower(motion_recorder);
++ window_lower(motion_recorder);
+
+- xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
++ 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;
+- if (pt != NULL)
+- *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
+- free(qpr);
+- }
++ if (qpr != NULL) {
++ if (win != NULL)
++ *win = qpr->child;
++ if (pt != NULL)
++ *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
++ free(qpr);
++ }
+
+- window_raise(motion_recorder);
++ window_raise(motion_recorder);
+ }
+
+ bool window_focus(xcb_window_t win)
+ {
+- coordinates_t loc;
+- if (locate_window(win, &loc)) {
+- if (loc.node != mon->desk->focus)
+- focus_node(loc.monitor, loc.desktop, loc.node);
+- return true;
+- }
+- return false;
++ coordinates_t loc;
++ if (locate_window(win, &loc)) {
++ if (loc.node != mon->desk->focus)
++ focus_node(loc.monitor, loc.desktop, loc.node);
++ return true;
++ }
++ return false;
+ }
+
+ 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);
++ 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);
++ 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);
++ 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);
++ uint32_t values[] = {x, y, w, h};
++ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
+ }
+
+ void window_raise(xcb_window_t win)
+ {
+- uint32_t values[] = {XCB_STACK_MODE_ABOVE};
+- xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
++ uint32_t values[] = {XCB_STACK_MODE_ABOVE};
++ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, 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;
++ 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);
++ 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);
++ 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);
++ 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);
++ 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);
++ 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)
+ {
+- PRINTF("window hide %X\n", win);
+- window_set_visibility(win, false);
++ PRINTF("window hide %X\n", win);
++ window_set_visibility(win, false);
+ }
+
+ void window_show(xcb_window_t win)
+ {
+- PRINTF("window show %X\n", win);
+- window_set_visibility(win, true);
++ PRINTF("window show %X\n", win);
++ window_set_visibility(win, true);
+ }
+
+ void toggle_visibility(void)
+ {
+- visible = !visible;
+- if (!visible)
+- clear_input_focus();
+- for (monitor_t *m = mon_head; m != NULL; m = m->next)
+- for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n, m->desk->root))
+- window_set_visibility(n->client->window, visible);
+- if (visible)
+- update_input_focus();
++ visible = !visible;
++ if (!visible)
++ clear_input_focus();
++ for (monitor_t *m = mon_head; m != NULL; m = m->next)
++ for (node_t *n = first_extrema(m->desk->root); n != NULL; n = next_leaf(n, m->desk->root))
++ window_set_visibility(n->client->window, visible);
++ if (visible)
++ update_input_focus();
+ }
+
+ void enable_motion_recorder(void)
+ {
+- PUTS("enable motion recorder");
+- window_raise(motion_recorder);
+- window_show(motion_recorder);
++ PUTS("enable motion recorder");
++ window_raise(motion_recorder);
++ window_show(motion_recorder);
+ }
+
+ void disable_motion_recorder(void)
+ {
+- PUTS("disable motion recorder");
+- window_hide(motion_recorder);
++ PUTS("disable motion recorder");
++ window_hide(motion_recorder);
+ }
+
+ void update_motion_recorder(void)
+ {
+- xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, root), NULL);
++ xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, root), NULL);
+
+- if (geo != NULL) {
+- window_resize(motion_recorder, geo->width, geo->height);
+- PRINTF("update motion recorder size: %ux%u\n", geo->width, geo->height);
+- }
++ if (geo != NULL) {
++ window_resize(motion_recorder, geo->width, geo->height);
++ PRINTF("update motion recorder size: %ux%u\n", geo->width, geo->height);
++ }
+
+- free(geo);
++ free(geo);
+ }
+
+ void update_input_focus(void)
+ {
+- set_input_focus(mon->desk->focus);
++ set_input_focus(mon->desk->focus);
+ }
+
+ void set_input_focus(node_t *n)
+ {
+- if (n == NULL) {
+- clear_input_focus();
+- } else {
+- if (n->client->icccm_focus)
+- send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
+- xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
+- }
++ if (n == NULL) {
++ clear_input_focus();
++ } else {
++ if (n->client->icccm_focus)
++ send_client_message(n->client->window, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
++ xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME);
++ }
+ }
+
+ void clear_input_focus(void)
+ {
+- xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
++ xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+ }
+
+ void center_pointer(monitor_t *m)
+ {
+- int16_t cx = m->rectangle.x + m->rectangle.width / 2;
+- int16_t cy = m->rectangle.y + m->rectangle.height / 2;
+- window_lower(motion_recorder);
+- xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
+- window_raise(motion_recorder);
++ int16_t cx = m->rectangle.x + m->rectangle.width / 2;
++ int16_t cy = m->rectangle.y + m->rectangle.height / 2;
++ window_lower(motion_recorder);
++ xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
++ window_raise(motion_recorder);
+ }
+
+ 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);
++ 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);
++ xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
+ }
+
+ bool has_proto(xcb_atom_t atom, xcb_icccm_get_wm_protocols_reply_t *protocols)
+ {
+- for (uint32_t i = 0; i < protocols->atoms_len; i++)
+- if (protocols->atoms[i] == atom)
+- return true;
+- return false;
++ for (uint32_t i = 0; i < protocols->atoms_len; i++)
++ if (protocols->atoms[i] == atom)
++ return true;
++ return false;
+ }
+
+ void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
+ {
+- xcb_client_message_event_t e;
++ xcb_client_message_event_t e;
+
+- e.response_type = XCB_CLIENT_MESSAGE;
+- e.window = win;
+- e.format = 32;
+- e.sequence = 0;
+- e.type = property;
+- e.data.data32[0] = value;
+- e.data.data32[1] = XCB_CURRENT_TIME;
++ e.response_type = XCB_CLIENT_MESSAGE;
++ e.window = win;
++ e.format = 32;
++ e.sequence = 0;
++ e.type = property;
++ 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_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) &e);
+ }
+diff --git a/window.h b/window.h
+index 12bc117..9688ef3 100644
+--- a/window.h
++++ b/window.h
+@@ -1,25 +1,29 @@
+-/* * Copyright (c) 2012-2013 Bastien Dejean
++/* Copyright (c) 2012-2014, 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:
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
+ *
+- * * Redistributions of source code must retain the above copyright notice, this
+- * list of conditions and the following disclaimer.
+- * * 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.
++ * 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR
++ * 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
++ * 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.
++ *
++ * The views and conclusions contained in the software and documentation are those
++ * of the authors and should not be interpreted as representing official policies,
++ * either expressed or implied, of the FreeBSD Project.
+ */
+
+ #ifndef BSPWM_WINDOW_H
+@@ -54,6 +58,9 @@ void enable_floating_atom(xcb_window_t win);
+ void disable_floating_atom(xcb_window_t win);
+ uint32_t get_border_color(client_t *c, bool focused_window, bool focused_monitor);
+ void update_floating_rectangle(client_t *c);
++void restrain_floating_width(client_t *c, int *width);
++void restrain_floating_height(client_t *c, int *height);
++void restrain_floating_size(client_t *c, int *width, int *height);
+ void query_pointer(xcb_window_t *win, xcb_point_t *pt);
+ bool window_focus(xcb_window_t win);
+ void window_border_width(xcb_window_t win, uint32_t bw);