summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Vogel <jvogel4@stny.rr.com>2014-03-31 11:59:14 +0700
committerWilly Sudiarto Raharjo <willysr@slackbuilds.org>2014-04-07 14:43:36 +0700
commitf714a15f4b84df6eda6e1dd0dc933342d5181d1d (patch)
tree6f33e722c4d5ac66e2f750de17f22706014b50c4
parent581b0ac67e9191678de1841aef8e135c73240346 (diff)
downloadslackbuilds-f714a15f4b84df6eda6e1dd0dc933342d5181d1d.tar.gz
desktop/bspwm: Updated for version 0.8.8.
Signed-off-by: Willy Sudiarto Raharjo <willysr@slackbuilds.org>
-rw-r--r--desktop/bspwm/README9
-rw-r--r--desktop/bspwm/bspwm.SlackBuild16
-rw-r--r--desktop/bspwm/bspwm.info8
-rw-r--r--desktop/bspwm/doinst.sh14
-rw-r--r--desktop/bspwm/patches/bspwm-0.8.7_8eb599400a.patch1617
-rw-r--r--desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch4719
-rw-r--r--desktop/bspwm/slack-desc2
7 files changed, 4730 insertions, 1655 deletions
diff --git a/desktop/bspwm/README b/desktop/bspwm/README
index 208d78dd9c..081d837df7 100644
--- a/desktop/bspwm/README
+++ b/desktop/bspwm/README
@@ -1,10 +1,5 @@
+bspwm (Binary space partitioning window manager)
+
bspwm is a tiling window manager that represents windows as the
leaves of a full binary tree. It is controlled and configured
via bspc.
-
-before launching it, you need some configuration files: you can
-can find some example ones in /usr/doc/bspwm-$VERSION/examples:
-- bspwmrc, should go to ~/.config/bspwm/bspwmrc
-- sxhkdrc, should go to ~/.config/sxhkd/sxhkdrc
-
-dmenu is an optional dependency.
diff --git a/desktop/bspwm/bspwm.SlackBuild b/desktop/bspwm/bspwm.SlackBuild
index 2b094bda69..f5ce09ca24 100644
--- a/desktop/bspwm/bspwm.SlackBuild
+++ b/desktop/bspwm/bspwm.SlackBuild
@@ -23,7 +23,7 @@
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PRGNAM=bspwm
-VERSION=${VERSION:-0.8.7}
+VERSION=${VERSION:-0.8.8}
BUILD=${BUILD:-1}
TAG=${TAG:-_SBo}
@@ -69,10 +69,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 8eb599400a
-patch -p1 < $CWD/patches/bspwm-0.8.7_8eb599400a.patch
-
-sed -i "s|/lib$|/lib$LIBDIRSUFFIX|" Makefile
+# 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
CFLAGS="$SLKCFLAGS" \
CXXFLAGS="$SLKCFLAGS" \
@@ -85,18 +83,12 @@ find $PKG -print0 | xargs -0 file | grep -e "executable" -e "shared object" | gr
find $PKG/usr/man -type f -exec gzip -9 {} \;
for i in $( find $PKG/usr/man -type l ) ; do ln -s $( readlink $i ).gz $i.gz ; rm $i ; done
-# install a xinitrc file
-install -m 0755 -D $CWD/xinitrc.$PRGNAM $PKG/etc/X11/xinit/xinitrc.$PRGNAM.new
-
mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
-cp -a \
- LICENSE doc/*.md doc/*.txt contrib examples \
- $PKG/usr/doc/$PRGNAM-$VERSION
+cp -a LICENSE doc/*.md doc/*.txt contrib examples $PKG/usr/doc/$PRGNAM-$VERSION
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc
-cat $CWD/doinst.sh > $PKG/install/doinst.sh
cd $PKG
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.${PKGTYPE:-tgz}
diff --git a/desktop/bspwm/bspwm.info b/desktop/bspwm/bspwm.info
index d6d8f15eef..db938c97dd 100644
--- a/desktop/bspwm/bspwm.info
+++ b/desktop/bspwm/bspwm.info
@@ -1,10 +1,10 @@
PRGNAM="bspwm"
-VERSION="0.8.7"
+VERSION="0.8.8"
HOMEPAGE="https://github.com/baskerville/bspwm"
-DOWNLOAD="https://github.com/baskerville/bspwm/archive/0.8.7.tar.gz"
-MD5SUM="8d9521b449ad9fc77cefbe0ecba9e2ae"
+DOWNLOAD="https://github.com/baskerville/bspwm/archive/0.8.8.tar.gz"
+MD5SUM="3f9fa85c48282605b54321e042eeaabf"
DOWNLOAD_x86_64=""
MD5SUM_x86_64=""
-REQUIRES="%README% sxhkd"
+REQUIRES="sxhkd"
MAINTAINER="John Vogel"
EMAIL="jvogel4@stny.rr.com"
diff --git a/desktop/bspwm/doinst.sh b/desktop/bspwm/doinst.sh
deleted file mode 100644
index e6b2addd81..0000000000
--- a/desktop/bspwm/doinst.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-config() {
- NEW="$1"
- OLD="$(dirname $NEW)/$(basename $NEW .new)"
- # If there's no config file by that name, mv it over:
- if [ ! -r $OLD ]; then
- mv $NEW $OLD
- elif [ "$(cat $OLD | md5sum)" = "$(cat $NEW | md5sum)" ]; then
- # toss the redundant copy
- rm $NEW
- fi
- # Otherwise, we leave the .new copy for the admin to consider...
-}
-
-config etc/X11/xinit/xinitrc.bspwm.new
diff --git a/desktop/bspwm/patches/bspwm-0.8.7_8eb599400a.patch b/desktop/bspwm/patches/bspwm-0.8.7_8eb599400a.patch
deleted file mode 100644
index 4d674b4479..0000000000
--- a/desktop/bspwm/patches/bspwm-0.8.7_8eb599400a.patch
+++ /dev/null
@@ -1,1617 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 3aa582e..e263b0d 100644
---- a/Makefile
-+++ b/Makefile
-@@ -1,7 +1,7 @@
- VERSION = 0.8.7
-
- CC ?= gcc
--LIBS = -lm -lxcb -lxcb-icccm -lxcb-ewmh -lxcb-randr
-+LIBS = -lm -lxcb -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama
- CFLAGS += -std=c99 -pedantic -Wall -Wextra -I$(PREFIX)/include
- CFLAGS += -D_POSIX_C_SOURCE=200112L -DVERSION=\"$(VERSION)\"
- LDFLAGS += -L$(PREFIX)/lib
-diff --git a/Sourcedeps b/Sourcedeps
-index 754d0ba..d22d6cf 100644
---- a/Sourcedeps
-+++ b/Sourcedeps
-@@ -1,7 +1,7 @@
- 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 bspwm.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 \
-@@ -10,7 +10,7 @@ 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 restore.h settings.h tree.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
-diff --git a/bspwm.c b/bspwm.c
-index ef4c7de..86daa52 100644
---- a/bspwm.c
-+++ b/bspwm.c
-@@ -34,6 +34,7 @@
- #include <sys/un.h>
- #include <signal.h>
- #include <unistd.h>
-+#include <xcb/xinerama.h>
- #include "types.h"
- #include "desktop.h"
- #include "monitor.h"
-@@ -193,6 +194,7 @@ void init(void)
- 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;
-@@ -261,9 +263,32 @@ void setup(void)
- } else {
- randr = false;
- warn("Couldn't retrieve monitors via RandR.\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));
-+ 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();
-@@ -295,6 +320,8 @@ 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)
-diff --git a/bspwm.h b/bspwm.h
-index f65bbeb..ba50f65 100644
---- a/bspwm.h
-+++ b/bspwm.h
-@@ -49,6 +49,8 @@ monitor_t *pri_mon;
- history_t *history_head;
- history_t *history_tail;
- history_t *history_needle;
-+rule_t *rule_head;
-+rule_t *rule_tail;
- stacking_list_t *stack_head;
- stacking_list_t *stack_tail;
- subscriber_list_t *subscribe_head;
-diff --git a/contrib/bash_completion b/contrib/bash_completion
-index 0ca1d47..0f3e301 100644
---- a/contrib/bash_completion
-+++ b/contrib/bash_completion
-@@ -1,7 +1,7 @@
- _bspc() {
-- local commands='window desktop monitor query pointer restore control config quit'
-+ local commands='window desktop monitor query pointer rule restore control config quit'
-
-- local settings='rule_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 focused_frame_opacity active_frame_opacity normal_frame_opacity border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio growth_factor 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 focused_frame_opacity active_frame_opacity normal_frame_opacity border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio growth_factor 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'
-
- COMPREPLY=()
-
-diff --git a/contrib/zsh_completion b/contrib/zsh_completion
-index 0feeeca..e48fa8d 100644
---- a/contrib/zsh_completion
-+++ b/contrib/zsh_completion
-@@ -2,8 +2,8 @@
-
- _bspc() {
- local -a commands settings
-- commands=('window' 'desktop' 'monitor' 'query' 'pointer' 'restore' 'control' 'config' 'quit')
-- settings=('rule_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' 'focused_frame_opacity' 'active_frame_opacity' 'normal_frame_opacity' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'growth_factor' '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')
-+ 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' 'focused_frame_opacity' 'active_frame_opacity' 'normal_frame_opacity' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'growth_factor' '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
-diff --git a/doc/bspwm.1 b/doc/bspwm.1
-index 8ef7eb5..0b6ce2b 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: 12/05/2013
-+.\" Date: 12/16/2013
- .\" Manual: Bspwm Manual
- .\" Source: Bspwm 0.8.7
- .\" Language: English
- .\"
--.TH "BSPWM" "1" "12/05/2013" "Bspwm 0\&.8\&.7" "Bspwm Manual"
-+.TH "BSPWM" "1" "12/16/2013" "Bspwm 0\&.8\&.7" "Bspwm Manual"
- .\" -----------------------------------------------------------------
- .\" * Define some portability stuff
- .\" -----------------------------------------------------------------
-@@ -437,6 +437,34 @@ free
- Only consider monitors where the focused desktop is free\&.
- .RE
- .RE
-+.SH "WINDOW STATES"
-+.PP
-+floating
-+.RS 4
-+Is above any tiled window and can be moved/resized freely\&. Although it doesn\(cqt occupy any tiling space, it is still part of the window tree\&.
-+.RE
-+.PP
-+fullscreen
-+.RS 4
-+Fills its monitor rectangle, is above all the other windows and has no borders\&.
-+.RE
-+.PP
-+locked
-+.RS 4
-+Ignores the
-+\fBclose\fR
-+message\&.
-+.RE
-+.PP
-+sticky
-+.RS 4
-+Stays in the focused desktop of its monitor\&.
-+.RE
-+.PP
-+private
-+.RS 4
-+Tries to keep the same tiling position/size\&.
-+.RE
- .SH "COMMANDS"
- .SS "Window"
- .sp
-@@ -496,9 +524,9 @@ Set the splitting ratio of the selected window (0 <
- < 1)\&.
- .RE
- .PP
--\fB\-e\fR, \fB\-\-edge\fR \fIDIR\fR \fIRATIO\fR|pull|push
-+\fB\-e\fR, \fB\-\-edge\fR \fIDIR\fR \fIRATIO\fR|\(+-\fIPIXELS\fR|pull|push
- .RS 4
--Set the splitting ratio (or pull, or push) the edge located in the given direction in relation to the selected window\&.
-+Set and change the splitting ratio of (or pull, or push) the edge located in the given direction in relation to the selected window\&.
- .RE
- .PP
- \fB\-R\fR, \fB\-\-rotate\fR \fIDIR\fR \fI90|270|180\fR
-@@ -827,6 +855,42 @@ Pass the pointer root coordinates for the current pointer action\&.
- Terminate the current pointer action\&.
- .RE
- .RE
-+.SS "Rule"
-+.sp
-+.it 1 an-trap
-+.nr an-no-space-flag 1
-+.nr an-break-flag 1
-+.br
-+.ps +1
-+\fBGeneral Syntax\fR
-+.RS 4
-+.sp
-+rule \fIOPTIONS\fR
-+.RE
-+.sp
-+.it 1 an-trap
-+.nr an-no-space-flag 1
-+.nr an-break-flag 1
-+.br
-+.ps +1
-+\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|locked|sticky|private|frame|center|lower|follow|manage|focus)=(true|false)]
-+.RS 4
-+Create a new rule\&.
-+.RE
-+.PP
-+\fB\-r\fR, \fB\-\-remove\fR ^<n>|head|tail|<class_name>|<instance_name>|*\&...
-+.RS 4
-+Remove the given rules\&.
-+.RE
-+.PP
-+\fB\-l\fR, \fB\-\-list\fR [<class_name>|<instance_name>|*]
-+.RS 4
-+List the rules\&.
-+.RE
-+.RE
- .SS "Config"
- .sp
- .it 1 an-trap
-@@ -864,33 +928,6 @@ Colors are either X color names or \fI#RRGGBB\fR, booleans are \fItrue\fR or \fI
- All the boolean settings are \fIfalse\fR by default\&.
- .SS "Global Settings"
- .PP
--\fIrule_command\fR
--.RS 4
--External command used to retrieve rule consequences\&. The command will receive the the ID of the window being processed as its first argument\&. The output of that command must have the following format:
--\fBkey1=value1 key2=value2 \&...\fR, where
--\fBkeyN\fR
--is one of
--\fIfloating\fR,
--\fIfullscreen\fR,
--\fIlocked\fR,
--\fIsticky\fR,
--\fIprivate\fR,
--\fIframe\fR,
--\fIcenter\fR,
--\fIlower\fR,
--\fIfollow\fR,
--\fImanage\fR,
--\fIfocus\fR,
--\fIdesktop\fR
--or
--\fImonitor\fR\&.
--.RE
--.PP
--\fIstatus_prefix\fR
--.RS 4
--Prefix prepended to each of the status lines\&.
--.RE
--.PP
- \fIfocused_border_color\fR
- .RS 4
- Color of the border of a focused window of a focused monitor\&.
-@@ -988,6 +1025,20 @@ Default split ratio\&.
- Intensity of the growth involved in pulling or pushing an edge\&.
- .RE
- .PP
-+\fIstatus_prefix\fR
-+.RS 4
-+Prefix prepended to each of the status lines\&.
-+.RE
-+.PP
-+\fIexternal_rules_command\fR
-+.RS 4
-+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:
-+\fBkey1=value1 key2=value2 \&...\fR
-+(the valid key/value pairs are given in the description of the
-+\fIrule\fR
-+command)\&.
-+.RE
-+.PP
- \fIhistory_aware_focus\fR
- .RS 4
- Give priority to the focus history when focusing nodes\&.
-diff --git a/doc/bspwm.1.txt b/doc/bspwm.1.txt
-index 4f0480f..d241ddb 100644
---- a/doc/bspwm.1.txt
-+++ b/doc/bspwm.1.txt
-@@ -287,6 +287,25 @@ 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.
-+
-+fullscreen::
-+ Fills its monitor rectangle, is above all the other windows and has no borders.
-+
-+locked::
-+ Ignores the *close* message.
-+
-+sticky::
-+ Stays in the focused desktop of its monitor.
-+
-+private::
-+ Tries to keep the same tiling position/size.
-+
-+
- Commands
- --------
-
-@@ -321,8 +340,8 @@ Options
- *-r*, *--ratio* 'RATIO'::
- Set the splitting ratio of the selected window (0 < 'RATIO' < 1).
-
--*-e*, *--edge* 'DIR' 'RATIO'|pull|push::
-- Set the splitting ratio (or pull, or push) the edge located in the given direction in relation to the selected window.
-+*-e*, *--edge* 'DIR' 'RATIO'|±'PIXELS'|pull|push::
-+ Set and change the splitting ratio of (or pull, or push) 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.
-@@ -508,6 +527,26 @@ Options
- *-u*, *--ungrab*::
- Terminate the current pointer action.
-
-+Rule
-+~~~~
-+
-+General Syntax
-+^^^^^^^^^^^^^^
-+
-+rule 'OPTIONS'
-+
-+Options
-+^^^^^^^
-+
-+*-a*, *--add* <class_name>|<instance_name>|* [*-o*|*--one-shot*] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|locked|sticky|private|frame|center|lower|follow|manage|focus)=(true|false)]::
-+ Create a new rule.
-+
-+*-r*, *--remove* ^<n>|head|tail|<class_name>|<instance_name>|*...::
-+ Remove the given rules.
-+
-+*-l*, *--list* [<class_name>|<instance_name>|*]::
-+ List the rules.
-+
- Config
- ~~~~~~
-
-@@ -535,12 +574,6 @@ All the boolean settings are 'false' by default.
- Global Settings
- ~~~~~~~~~~~~~~~
-
--'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. The output of that command must have the following format: *key1=value1 key2=value2 ...*, where *keyN* is one of 'floating', 'fullscreen', 'locked', 'sticky', 'private', 'frame', 'center', 'lower', 'follow', 'manage', 'focus', 'desktop' or 'monitor'.
--
--'status_prefix'::
-- Prefix prepended to each of the status lines.
--
- 'focused_border_color'::
- Color of the border of a focused window of a focused monitor.
-
-@@ -598,6 +631,12 @@ Global Settings
- 'growth_factor'::
- Intensity of the growth involved in pulling or pushing an edge.
-
-+'status_prefix'::
-+ 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).
-+
- 'history_aware_focus'::
- Give priority to the focus history when focusing nodes.
-
-diff --git a/examples/bspwmrc b/examples/bspwmrc
-index 36500b2..e50baae 100755
---- a/examples/bspwmrc
-+++ b/examples/bspwmrc
-@@ -7,3 +7,9 @@ bspc config window_gap 12
- bspc config split_ratio 0.52
- bspc config borderless_monocle true
- bspc config gapless_monocle true
-+
-+bspc rule -a Gimp desktop=^8 follow=on floating=on
-+bspc rule -a Chromium desktop=^2
-+bspc rule -a mplayer2 floating=on
-+bspc rule -a Kupfer.py focus=on
-+bspc rule -a Screenkey manage=off
-diff --git a/examples/external_rules/README.asciidoc b/examples/external_rules/README.asciidoc
-deleted file mode 100644
-index 17ae1cf..0000000
---- a/examples/external_rules/README.asciidoc
-+++ /dev/null
-@@ -1,42 +0,0 @@
--Synopsis
----------
--
--Server
--~~~~~~
--
--*ruld* [*-h*|*-p* 'PORT']
--
--Client
--~~~~~~
--
--*rulc* [*-h*|*-p* 'PORT'|*-a*|*-r*|*-l*|*-t*|*-q*] 'DATA' ...
--
--Options
---------
--
--Shared
--~~~~~~
--
--*-h*::
-- Print the synopsis and exit.
--
--*-p* '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'.
--
--*-r* 'STRING'|head|tail::
-- Remove the rules containing 'STRING' in their hypothesis or remove the first or last rule.
--
--*-l*::
-- List the rules.
--
--*-t* 'CLASS' 'INSTANCE' 'TITLE' 'TYPE' 'STATE'::
-- Test the rules for the given window informations.
--
--*-q*::
-- Kill the server.
-diff --git a/examples/external_rules/bspwm_rules b/examples/external_rules/bspwm_rules
-deleted file mode 100755
-index 6e91732..0000000
---- a/examples/external_rules/bspwm_rules
-+++ /dev/null
-@@ -1,16 +0,0 @@
--#! /bin/sh
--
--if ! rulc -l > /dev/null ; then
-- (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'
--fi
-diff --git a/examples/external_rules/bspwmrc b/examples/external_rules/bspwmrc
-index 5ce0369..9454e77 100755
---- a/examples/external_rules/bspwmrc
-+++ b/examples/external_rules/bspwmrc
-@@ -1,4 +1,3 @@
- #! /bin/sh
-
--bspc config rule_command "$(which rule_command)"
--bspwm_rules
-+bspc config external_rules_command "$(which external_rules)"
-diff --git a/examples/external_rules/external_rules b/examples/external_rules/external_rules
-new file mode 100755
-index 0000000..c3efb78
---- /dev/null
-+++ b/examples/external_rules/external_rules
-@@ -0,0 +1,14 @@
-+#! /bin/sh
-+
-+wid=$1
-+class=$2
-+instance=$3
-+
-+if [ "$instance" = fontforge ] ; then
-+ 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
-new file mode 100644
-index 0000000..17ae1cf
---- /dev/null
-+++ b/examples/external_rules/lua/README.asciidoc
-@@ -0,0 +1,42 @@
-+Synopsis
-+--------
-+
-+Server
-+~~~~~~
-+
-+*ruld* [*-h*|*-p* 'PORT']
-+
-+Client
-+~~~~~~
-+
-+*rulc* [*-h*|*-p* 'PORT'|*-a*|*-r*|*-l*|*-t*|*-q*] 'DATA' ...
-+
-+Options
-+-------
-+
-+Shared
-+~~~~~~
-+
-+*-h*::
-+ Print the synopsis and exit.
-+
-+*-p* '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'.
-+
-+*-r* 'STRING'|head|tail::
-+ Remove the rules containing 'STRING' in their hypothesis or remove the first or last rule.
-+
-+*-l*::
-+ List the rules.
-+
-+*-t* 'CLASS' 'INSTANCE' 'TITLE' 'TYPE' 'STATE'::
-+ Test the rules for the given window informations.
-+
-+*-q*::
-+ Kill the server.
-diff --git a/examples/external_rules/lua/bspwm_rules b/examples/external_rules/lua/bspwm_rules
-new file mode 100755
-index 0000000..6e91732
---- /dev/null
-+++ b/examples/external_rules/lua/bspwm_rules
-@@ -0,0 +1,16 @@
-+#! /bin/sh
-+
-+if ! rulc -l > /dev/null ; then
-+ (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'
-+fi
-diff --git a/examples/external_rules/lua/bspwmrc b/examples/external_rules/lua/bspwmrc
-new file mode 100755
-index 0000000..35206a5
---- /dev/null
-+++ b/examples/external_rules/lua/bspwmrc
-@@ -0,0 +1,4 @@
-+#! /bin/sh
-+
-+bspc config external_rules_command "$(which external_rules)"
-+bspwm_rules
-diff --git a/examples/external_rules/lua/external_rules b/examples/external_rules/lua/external_rules
-new file mode 100755
-index 0000000..c1318cb
---- /dev/null
-+++ b/examples/external_rules/lua/external_rules
-@@ -0,0 +1,3 @@
-+#! /bin/sh
-+
-+xwinfo -cints $1 | xargs -d '\n' rulc -t
-diff --git a/examples/external_rules/lua/rulc b/examples/external_rules/lua/rulc
-new file mode 100755
-index 0000000..f0cd633
---- /dev/null
-+++ b/examples/external_rules/lua/rulc
-@@ -0,0 +1,68 @@
-+#! /usr/bin/env lua
-+
-+local p = require "posix"
-+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'}
-+}
-+
-+local cmd_assoc = {
-+ 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
-+end
-+
-+if not cmd then
-+ 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])
-+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 "")
-+elseif cmd == "remove" then
-+ 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)
-+end
-+p.send(fd, msg)
-+rsp = p.recv(fd, p.BUFSIZ)
-+
-+if rsp and #rsp > 0 then
-+ print(rsp)
-+end
-+
-+p.close(fd)
-diff --git a/examples/external_rules/lua/ruld b/examples/external_rules/lua/ruld
-new file mode 100755
-index 0000000..e4a8674
---- /dev/null
-+++ b/examples/external_rules/lua/ruld
-@@ -0,0 +1,135 @@
-+#! /usr/bin/env lua
-+
-+local p = require("posix")
-+local port = 54321
-+
-+local short = "hp:"
-+local long = {
-+ {"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
-+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()
-+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
-+end
-+
-+local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
-+p.setsockopt(fd, p.SOL_SOCKET, p.SO_REUSEADDR, 1)
-+p.bind(fd, {family=p.AF_INET, addr="127.0.0.1", port=port})
-+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
-+end
-+
-+p.close(fd)
-diff --git a/examples/external_rules/rulc b/examples/external_rules/rulc
-deleted file mode 100755
-index f0cd633..0000000
---- a/examples/external_rules/rulc
-+++ /dev/null
-@@ -1,68 +0,0 @@
--#! /usr/bin/env lua
--
--local p = require "posix"
--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'}
--}
--
--local cmd_assoc = {
-- 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
--end
--
--if not cmd then
-- 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])
--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 "")
--elseif cmd == "remove" then
-- 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)
--end
--p.send(fd, msg)
--rsp = p.recv(fd, p.BUFSIZ)
--
--if rsp and #rsp > 0 then
-- print(rsp)
--end
--
--p.close(fd)
-diff --git a/examples/external_rules/ruld b/examples/external_rules/ruld
-deleted file mode 100755
-index e4a8674..0000000
---- a/examples/external_rules/ruld
-+++ /dev/null
-@@ -1,135 +0,0 @@
--#! /usr/bin/env lua
--
--local p = require("posix")
--local port = 54321
--
--local short = "hp:"
--local long = {
-- {"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
--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()
--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
--end
--
--local fd = p.socket(p.AF_INET, p.SOCK_STREAM, 0)
--p.setsockopt(fd, p.SOL_SOCKET, p.SO_REUSEADDR, 1)
--p.bind(fd, {family=p.AF_INET, addr="127.0.0.1", port=port})
--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
--end
--
--p.close(fd)
-diff --git a/examples/external_rules/rule_command b/examples/external_rules/rule_command
-deleted file mode 100755
-index c1318cb..0000000
---- a/examples/external_rules/rule_command
-+++ /dev/null
-@@ -1,3 +0,0 @@
--#! /bin/sh
--
--xwinfo -cints $1 | xargs -d '\n' rulc -t
-diff --git a/messages.c b/messages.c
-index f598bc7..10428b2 100644
---- a/messages.c
-+++ b/messages.c
-@@ -33,6 +33,7 @@
- #include "monitor.h"
- #include "pointer.h"
- #include "query.h"
-+#include "rule.h"
- #include "restore.h"
- #include "settings.h"
- #include "tree.h"
-@@ -89,6 +90,8 @@ bool process_message(char **args, int num, char *rsp)
- 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)) {
-@@ -108,7 +111,7 @@ bool cmd_window(char **args, int num)
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = ref;
-
-- if (*args[0] != OPT_CHR) {
-+ if ((*args)[0] != OPT_CHR) {
- if (node_from_desc(*args, &ref, &trg))
- num--, args++;
- else
-@@ -258,11 +261,25 @@ bool cmd_window(char **args, int num)
- if (parse_fence_move(*args, &fmo)) {
- move_fence(n, dir, fmo);
- } else {
-- double rat;
-- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1)
-- n->split_ratio = rat;
-- else
-- return false;
-+ 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)) {
-@@ -324,7 +341,7 @@ bool cmd_desktop(char **args, int num)
- coordinates_t ref = {mon, mon->desk, NULL};
- coordinates_t trg = ref;
-
-- if (*args[0] != OPT_CHR) {
-+ if ((*args)[0] != OPT_CHR) {
- if (desktop_from_desc(*args, &ref, &trg))
- num--, args++;
- else
-@@ -474,7 +491,7 @@ bool cmd_monitor(char **args, int num)
- coordinates_t ref = {mon, NULL, NULL};
- coordinates_t trg = ref;
-
-- if (*args[0] != OPT_CHR) {
-+ if ((*args)[0] != OPT_CHR) {
- if (monitor_from_desc(*args, &ref, &trg))
- num--, args++;
- else
-@@ -643,6 +660,60 @@ bool cmd_query(char **args, int num, char *rsp)
- return true;
- }
-
-+bool cmd_rule(char **args, int num, char *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++;
-+ }
-+
-+ return true;
-+}
-+
- bool cmd_pointer(char **args, int num)
- {
- if (num < 1)
-@@ -743,7 +814,7 @@ bool cmd_config(char **args, int num, char *rsp)
- return false;
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = {NULL, NULL, NULL};
-- if (*args[0] == OPT_CHR) {
-+ if ((*args)[0] == OPT_CHR) {
- if (streq("-d", *args) || streq("--desktop", *args)) {
- num--, args++;
- if (num < 1)
-@@ -818,7 +889,7 @@ bool set_setting(coordinates_t loc, char *name, char *value)
- #define SETSTR(s) \
- } else if (streq(#s, name)) { \
- return snprintf(s, sizeof(s), "%s", value) >= 0;
-- SETSTR(rule_command)
-+ SETSTR(external_rules_command)
- SETSTR(status_prefix)
- #undef SETSTR
- } else if (streq("split_ratio", name)) {
-@@ -928,8 +999,8 @@ bool get_setting(coordinates_t loc, char *name, char* rsp)
- return false;
- else
- snprintf(rsp, BUFSIZ, "%u", loc.desktop->border_width);
-- else if (streq("rule_command", name))
-- snprintf(rsp, BUFSIZ, "%s", rule_command);
-+ 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);
- #define MONGET(k) \
-diff --git a/messages.h b/messages.h
-index 7da3ed5..0dee489 100644
---- a/messages.h
-+++ b/messages.h
-@@ -39,6 +39,7 @@ 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);
-diff --git a/query.c b/query.c
-index e29bd88..b35e50d 100644
---- a/query.c
-+++ b/query.c
-@@ -92,7 +92,7 @@ void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth)
- 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%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->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (c->frame ? 'e' : '-'), (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-'));
- } else {
-- snprintf(line, sizeof(line), "%c %c %.2f", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio);
-+ 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));
-diff --git a/rule.c b/rule.c
-index 78f2e25..e7ca0b1 100644
---- a/rule.c
-+++ b/rule.c
-@@ -33,6 +33,64 @@
- #include "query.h"
- #include "rule.h"
-
-+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;
-+}
-+
-+void add_rule(rule_t *r)
-+ {
-+ if (rule_head == NULL) {
-+ rule_head = rule_tail = r;
-+ } else {
-+ rule_tail->next = r;
-+ r->prev = rule_tail;
-+ rule_tail = r;
-+ }
-+}
-+
-+void remove_rule(rule_t *r)
-+{
-+ if (r == NULL)
-+ return;
-+ rule_t *prev = r->prev;
-+ rule_t *next = r->next;
-+ if (prev != NULL)
-+ prev->next = next;
-+ if (next != NULL)
-+ next->prev = prev;
-+ if (r == rule_head)
-+ rule_head = next;
-+ if (r == rule_tail)
-+ rule_tail = prev;
-+ free(r);
-+}
-+
-+void remove_rule_by_cause(char *cause)
-+{
-+ rule_t *r = rule_head;
-+ 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;
-+}
-+
- rule_consequence_t *make_rule_conquence(void)
- {
- rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
-@@ -130,10 +188,40 @@ void apply_rules(xcb_window_t win, rule_consequence_t *csq)
- 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->transient = 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);
-+ }
-+
-+ 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;
-@@ -146,7 +234,7 @@ bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
- char wid[SMALEN];
- snprintf(wid, sizeof(wid), "%i", win);
- setsid();
-- execl(rule_command, rule_command, wid, NULL);
-+ 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]);
-@@ -156,59 +244,59 @@ bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
- return (pid != -1);
- }
-
--void parse_rule_consequence(int fd, rule_consequence_t *csq, monitor_t **m, desktop_t **d)
-+void parse_rule_consequence(int fd, rule_consequence_t *csq)
- {
- if (fd == -1)
- return;
- char data[BUFSIZ];
- int nb;
-- bool v;
- 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) {
-- PRINTF("%s = %s\n", key, value);
-- if (streq("desktop", key)) {
-- coordinates_t ref = {mon, mon->desk, NULL};
-- coordinates_t trg = {NULL, NULL, NULL};
-- if (desktop_from_desc(value, &ref, &trg)) {
-- *m = trg.monitor;
-- *d = trg.desktop;
-- }
-- } else if (streq("monitor", key)) {
-- coordinates_t ref = {mon, NULL, NULL};
-- coordinates_t trg = {NULL, NULL, NULL};
-- if (monitor_from_desc(value, &ref, &trg)) {
-- *m = trg.monitor;
-- *d = trg.monitor->desk;
-- }
-- } 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(fullscreen)
-- SETCSQ(locked)
-- SETCSQ(sticky)
-- SETCSQ(private)
-- SETCSQ(frame)
-- SETCSQ(center)
-- SETCSQ(lower)
-- SETCSQ(follow)
-- SETCSQ(manage)
-- SETCSQ(focus)
--#undef SETCSQ
--
-- }
-+ parse_key_value(key, value, csq);
- key = strtok(NULL, CSQ_BLK);
- value = strtok(NULL, CSQ_BLK);
- }
- }
-- if (csq->sticky) {
-- *m = mon;
-- *d = mon->desk;
-+}
-+
-+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;
-+#define SETCSQ(name) \
-+ else if (streq(#name, key)) \
-+ csq->name = v;
-+ SETCSQ(fullscreen)
-+ SETCSQ(locked)
-+ SETCSQ(sticky)
-+ SETCSQ(private)
-+ SETCSQ(frame)
-+ SETCSQ(center)
-+ SETCSQ(lower)
-+ SETCSQ(follow)
-+ SETCSQ(manage)
-+ SETCSQ(focus)
-+#undef SETCSQ
-+ }
-+}
-+
-+void list_rules(char *pattern, char *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));
- }
- }
-diff --git a/rule.h b/rule.h
-index fd2bfb2..6467aaf 100644
---- a/rule.h
-+++ b/rule.h
-@@ -25,14 +25,22 @@
- #ifndef BSPWM_RULE_H
- #define BSPWM_RULE_H
-
--#define CSQ_BLK " =,\n"
-+#define MATCH_ANY "*"
-+#define CSQ_BLK " =,\n"
-
-+rule_t *make_rule(void);
-+void add_rule(rule_t *r);
-+void remove_rule(rule_t *r);
-+void remove_rule_by_cause(char *cause);
-+bool remove_rule_by_index(int idx);
- rule_consequence_t *make_rule_conquence(void);
- pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);
- void add_pending_rule(pending_rule_t *pr);
- void remove_pending_rule(pending_rule_t *pr);
- void apply_rules(xcb_window_t win, rule_consequence_t *csq);
- bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
--void parse_rule_consequence(int fd, rule_consequence_t *csq, monitor_t **m, desktop_t **d);
-+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);
-
- #endif
-diff --git a/settings.c b/settings.c
-index 1cfaadf..7dcaa94 100644
---- a/settings.c
-+++ b/settings.c
-@@ -39,7 +39,7 @@ void run_config(void)
-
- void load_settings(void)
- {
-- snprintf(rule_command, sizeof(rule_command), "%s", RULE_COMMAND);
-+ 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);
-diff --git a/settings.h b/settings.h
-index 4b68e90..e747547 100644
---- a/settings.h
-+++ b/settings.h
-@@ -27,11 +27,11 @@
-
- #include "types.h"
-
--#define WM_NAME "bspwm"
--#define CONFIG_NAME WM_NAME "rc"
--#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
--#define RULE_COMMAND "/bin/true"
--#define STATUS_PREFIX "W"
-+#define WM_NAME "bspwm"
-+#define CONFIG_NAME WM_NAME "rc"
-+#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
-+#define EXTERNAL_RULES_COMMAND ""
-+#define STATUS_PREFIX "W"
-
- #define FOCUSED_BORDER_COLOR "#7E7F89"
- #define ACTIVE_BORDER_COLOR "#545350"
-@@ -66,7 +66,7 @@
- #define IGNORE_EWMH_FOCUS false
- #define REMOVE_DISABLED_MONITOR false
-
--char rule_command[MAXLEN];
-+char external_rules_command[MAXLEN];
- char status_prefix[MAXLEN];
-
- char focused_border_color[MAXLEN];
-diff --git a/types.h b/types.h
-index e3a8bcf..37e6516 100644
---- a/types.h
-+++ b/types.h
-@@ -247,7 +247,18 @@ struct subscriber_list_t {
- 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;
-+};
-+
- typedef struct {
-+ char class_name[SMALEN];
-+ char instance_name[SMALEN];
- char desktop_desc[MAXLEN];
- char monitor_desc[MAXLEN];
- bool floating;
-diff --git a/window.c b/window.c
-index f1a581c..038bd43 100644
---- a/window.c
-+++ b/window.c
-@@ -48,11 +48,10 @@ void schedule_window(xcb_window_t win)
- if (override_redirect || locate_window(win, &loc))
- return;
-
-- /* Ignore pending windows */
-- for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
-+ /* 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);
-@@ -67,7 +66,7 @@ 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, &m, &d);
-+ parse_rule_consequence(fd, csq);
-
- if (csq->lower)
- window_lower(win);
-@@ -80,6 +79,27 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
-
- 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);
-@@ -89,11 +109,7 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
- window_center(m, c);
- c->frame = csq->frame;
-
-- 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(c->class_name, sizeof(c->class_name), "%s", reply.class_name);
-- xcb_icccm_get_wm_class_reply_wipe(&reply);
-- }
-+ snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
-
- csq->floating = csq->floating || d->floating;
-
diff --git a/desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch b/desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch
new file mode 100644
index 0000000000..3e7dec1736
--- /dev/null
+++ b/desktop/bspwm/patches/bspwm-0.8.8_0ca5bf3.patch
@@ -0,0 +1,4719 @@
+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/slack-desc b/desktop/bspwm/slack-desc
index 0982ff254c..0297653337 100644
--- a/desktop/bspwm/slack-desc
+++ b/desktop/bspwm/slack-desc
@@ -12,7 +12,7 @@ bspwm: bspwm is a tiling window manager that represents windows as the
bspwm: leaves of a full binary tree. It is controlled and configured
bspwm: via bspc.
bspwm:
-bspwm: homepage: https://github.com/baskerville/bspwm
+bspwm:
bspwm:
bspwm:
bspwm: