diff --git a/.clang-format b/.clang-format
index 6b542c18..4a15e88e 100644
--- a/.clang-format
+++ b/.clang-format
@@ -10,3 +10,4 @@ ColumnLimit: 0
 SpaceBeforeParens: ControlStatements
 SortIncludes: false
 ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE, FOREACH_NONINTERNAL]
+TypenameMacros: [ SLIST_HEAD, SLIST_ENTRY, LIST_HEAD, LIST_ENTRY, SIMPLEQ_HEAD, SIMPLEQ_ENTRY, TAILQ_HEAD, TAILQ_ENTRY, CIRCLEQ_HEAD, CIRCLEQ_ENTRY ]
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..6b8710a7
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+.git
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 00000000..40fd8399
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,41 @@
+# Contributing
+
+## i3status/i3lock bug reports and feature requests
+
+Note that bug reports and feature requests for related projects should be filed in the corresponding repositories for [i3status](https://github.com/i3/i3status) and [i3lock](https://github.com/i3/i3lock).
+
+## i3 bug reports and feature requests
+
+1. Read the [debugging instructions](https://i3wm.org/docs/debugging.html).
+2. Make sure you include a link to your logfile in your report (section 3).
+3. Make sure you include the i3 version number in your report (section 1).
+4. Please be aware that we cannot support compatibility issues with
+   closed-source software, as digging into compatibility problems without
+   having access to the source code is too time-consuming. Additionally,
+   experience has shown that often, the software in question is responsible for
+   the issue. Please raise an issue with the software in question, not i3.
+5. Please note that i3 does not support compositors (e.g. compton). If you
+   encountered the issue you are about to report while using a compositor,
+   please try reproducing it without a compositor.
+
+## Pull requests
+
+* Before sending a pull request for new features, please check with us that the
+  feature is something we want to see in i3 by opening an issue which has
+  ”feature request” or ”enhancement” in its title.
+* Use the `next` branch for developing and sending your pull request.
+* Use `clang-format` to format your code.
+* Run the [testsuite](https://i3wm.org/docs/testsuite.html)
+* If your changes should be reported on the next release's changelog, also
+  update the [RELEASE-notes-next](../RELEASE-notes-next) file in the root
+  folder. Example of changes that should be reported are bug fixes present in
+  the latest stable version of i3 and new enhancements. Example of changes that
+  should not be reported are minor code improvements, documentation, regression
+  and fixes for bugs that were introduced in the `next` branch.
+
+## Finding something to do
+
+* Find a [reproducible bug](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Areproducible+label%3Abug+) from the issue tracker. These issues have been reviewed and confirmed by a project contributor.
+* Find an [accepted enhancement](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Aaccepted+label%3Aenhancement) from the issue tracker. These have been approved and are ok to start working on.
+
+There's an [overview of the codebase](https://i3wm.org/docs/hacking-howto.html) available to get you started.
diff --git a/.github/GOVERNANCE.md b/.github/GOVERNANCE.md
new file mode 100644
index 00000000..44e13345
--- /dev/null
+++ b/.github/GOVERNANCE.md
@@ -0,0 +1,30 @@
+# i3 project governance
+
+## Overview
+
+The i3 project uses a governance model commonly described as Benevolent
+Dictator For Life (BDFL). This document outlines our understanding of what this
+means.
+
+## Roles
+
+* user: anyone who interacts with the i3 project
+* core contributor: a handful of people who have contributed significantly to
+  the project by any means (issue triage, support, documentation, code, etc.).
+  Core contributors are recognizable via GitHub’s “Member” badge.
+* BDFL: a single individual who makes decisions when consensus cannot be
+  reached. i3’s current BDFL is [@stapelberg](https://github.com/stapelberg).
+
+## Decision making process
+
+In general, we try to reach consensus in discussions. In case consensus cannot
+be reached, the BDFL makes a decision.
+
+For feature requests and code contributions specifically, the values with which
+we consider them can be found on the bottom of https://i3wm.org/. These values
+are not set in stone and are to be treated as guiding principles, not absolute
+rules that must be followed in every case.
+
+## Contribution process
+
+Please see [CONTRIBUTING](CONTRIBUTING.md).
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..726b4007
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,71 @@
+<!--
+PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
+
+IMPORTANT NOTE: If your issue is not specific to any feature provided by i3-gaps but
+                to i3 in general, please open the issue upstream against i3/i3.
+-->
+
+## I'm submitting a…
+<!-- Please check one of the following options with "x" -->
+<pre>
+[ ] Bug
+[ ] Feature Request
+[ ] Documentation Request
+[ ] Other (Please describe in detail)
+</pre>
+
+## Current Behavior
+<!--
+Describe the current behavior,
+e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
+-->
+
+## Expected Behavior
+<!--
+Describe the desired behavior you expect after mitigation of the issue,
+e.g., »The window left next to the current window should be focused.«
+-->
+
+## Reproduction Instructions
+<!--
+For bug reports, please provide detailed instructions on how the bug can be reproduced.
+For feature requests you can remove this section.
+
+E.g., »Open three windows in a V[A H[B C]] layout on a new workspace«
+-->
+
+## Environment
+<!--
+Please include your exact i3 version.
+Note that we only support the latest major release and the current development version. If you are using an older version of i3, please first update to the current release version and reproduce the issue there.
+-->
+Output of `i3 --moreversion 2>&-`:
+<pre>
+i3 version: 
+</pre>
+
+<!--
+For bug reports, please include your (complete) i3 config with which the issue occurs. You can either paste the file directly or provide a link to a service such as pastebin.
+
+If you would like to help debugging the issue, please try to reduce the config such that it is as close to the default config as possible while still reproducing the issue. This can help us bisect the root cause.
+-->
+<pre>
+</pre>
+
+<!--
+Providing a logfile can help us trace the root cause of an issue much quicker. You can learn how to generate the logfile here:
+https://i3wm.org/docs/debugging.html
+
+Providing the logfile is optional.
+-->
+<pre>
+Logfile URL:
+</pre>
+
+<!--
+Please also answer the questions below to help us process your issue faster. If you have any other information to share, please add it here as well.
+-->
+<pre>
+- Linux Distribution & Version:
+- Are you using a compositor (e.g., xcompmgr or compton):
+</pre>
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..a6df0a95
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,72 @@
+---
+name: Bug report
+about: Create a report to help us improve
+---
+
+<!--
+PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
+-->
+
+## I'm submitting a…
+<!-- Please check one of the following options with "x" -->
+<pre>
+[x] Bug
+[ ] Feature Request
+[ ] Documentation Request
+[ ] Other (Please describe in detail)
+</pre>
+
+## Current Behavior
+<!--
+Describe the current behavior,
+e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
+-->
+
+## Expected Behavior
+<!--
+Describe the desired behavior you expect after mitigation of the issue,
+e.g., »The window left next to the current window should be focused.«
+-->
+
+## Reproduction Instructions
+<!--
+Please provide detailed instructions on how the bug can be reproduced.
+E.g., »Open three windows in a V[A H[B C]] layout on a new workspace«
+-->
+
+## Environment
+<!--
+Please include your exact i3 version.
+Note that we only support the latest major release and the current development version. If you are using an older version of i3, please first update to the current release version and reproduce the issue there.
+-->
+Output of `i3 --moreversion 2>&-`:
+<pre>
+i3 version: 
+</pre>
+
+<!--
+Please include your (complete) i3 config with which the issue occurs. You can either paste the file directly or provide a link to a service such as pastebin.
+
+If you would like to help debugging the issue, please try to reduce the config such that it is as close to the default config as possible while still reproducing the issue. This can help us bisect the root cause.
+-->
+<details><summary>Config file</summary><pre>
+</pre>
+</details>
+
+<!--
+Providing a logfile can help us trace the root cause of an issue much quicker. You can learn how to generate the logfile here:
+https://i3wm.org/docs/debugging.html
+
+Providing the logfile is optional.
+-->
+<pre>
+Logfile URL:
+</pre>
+
+<!--
+Please also answer the questions below to help us process your issue faster. If you have any other information to share, please add it here as well.
+-->
+<pre>
+- Linux Distribution & Version:
+- Are you using a compositor (e.g., xcompmgr or compton):
+</pre>
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..e1c169a5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,47 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+---
+
+<!--
+PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
+-->
+
+## I'm submitting a…
+<!-- Please check one of the following options with "x" -->
+<pre>
+[ ] Bug
+[x] Feature Request
+[ ] Documentation Request
+[ ] Other (Please describe in detail)
+</pre>
+
+## Current Behavior
+<!--
+Describe the current behavior,
+e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
+-->
+
+## Desired Behavior
+<!--
+Describe the desired behavior you expect after mitigation of the issue,
+e.g., »The window left next to the current window should be focused.«
+-->
+
+## Environment
+<!--
+Please include your exact i3 version.
+Note that we only support the latest major release and the current development version. If you are using an older version of i3, please first update to the current release version and reproduce the issue there.
+-->
+Output of `i3 --moreversion 2>&-`:
+<pre>
+i3 version: 
+</pre>
+
+<!--
+Please also answer the questions below to help us process your issue faster. If you have any other information to share, please add it here as well.
+-->
+<pre>
+- Linux Distribution & Version:
+- Are you using a compositor (e.g., xcompmgr or compton):
+</pre>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..51baa22d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,55 @@
+*.o
+tags
+include/GENERATED_*.h
+include/all.h.pch
+*~
+*.swp
+*.gcda
+*.gcno
+*.dSYM
+test.commands_parser
+test.config_parser
+testcases/MYMETA.json
+testcases/MYMETA.yml
+testcases/blib/
+testcases/pm_to_blib
+AnyEvent-I3/Makefile
+AnyEvent-I3/META.yml
+AnyEvent-I3/MYMETA.json
+AnyEvent-I3/MYMETA.yml
+AnyEvent-I3/blib/
+AnyEvent-I3/inc/
+AnyEvent-I3/pm_to_blib
+*.output
+*.tab.*
+*.yy.c
+man/*.1
+man/*.xml
+man/*.html
+*.tar.bz2*
+*.tar.xz*
+i3
+i3-input/i3-input
+i3-nagbar/i3-nagbar
+i3-msg/i3-msg
+i3-config-wizard/i3-config-wizard
+i3-dump-log/i3-dump-log
+libi3.a
+docs/*.pdf
+docs/*.html
+!/docs/refcard.html
+i3-command-parser.stamp
+i3-config-parser.stamp
+.clang_complete
+compile_commands.json
+/.ccls-cache
+/.clangd
+LAST_VERSION
+build
+
+# We recommend building in a subdirectory called build.
+# If you chose a different directory name,
+# it is up to you to arrange for it to be ignored by git,
+# e.g. by listing your directory in .git/info/exclude.
+/build
+
diff --git a/.travis.yml b/.travis.yml
index b56bf684..9935cb63 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,9 +31,10 @@ install:
 script:
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh
-  - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common"'
+  - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'rm -rf build; mkdir -p build && cd build && CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common" meson .. -Ddocs=true -Dmans=true -Db_sanitize=address && ninja -v'
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-spelling.pl
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} ./travis/run-tests.sh
+  - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'rm -rf distbuild; mkdir distbuild && cd distbuild && meson .. -Ddocs=true -Dmans=true && ninja -v dist'
   - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/debian-build.sh deb/debian-amd64/DIST
   - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU} ./travis/debian-build.sh deb/ubuntu-amd64/DIST
   - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_386} linux32 ./travis/debian-build.sh deb/debian-i386/DIST
diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm
index ae9e5bea..1f4e5bd3 100644
--- a/AnyEvent-I3/lib/AnyEvent/I3.pm
+++ b/AnyEvent-I3/lib/AnyEvent/I3.pm
@@ -101,11 +101,13 @@ use constant TYPE_GET_BINDING_MODES => 8;
 use constant TYPE_GET_CONFIG => 9;
 use constant TYPE_SEND_TICK => 10;
 use constant TYPE_SYNC => 11;
+use constant TYPE_GET_BINDING_STATE => 12;
 
 our %EXPORT_TAGS = ( 'all' => [
     qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
        TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
-       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC)
+       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC
+       TYPE_GET_BINDING_STATE)
 ] );
 
 our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
diff --git a/I3_VERSION b/I3_VERSION
deleted file mode 100644
index 60f610c0..00000000
--- a/I3_VERSION
+++ /dev/null
@@ -1 +0,0 @@
-4.18.1-non-git
diff --git a/LICENSE b/LICENSE
index 6354f065..1f0872c7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,26 @@
 Copyright © 2009, Michael Stapelberg and contributors
-All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+modification, are permitted provided that the following conditions
+are met:
 
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
+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.
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
 
-    * Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in the
-      documentation and/or other materials provided with the distribution.
-
-    * Neither the name of Michael Stapelberg nor the
-      names of contributors may be used to endorse or promote products
-      derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY Michael Stapelberg ''AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL Michael Stapelberg 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
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 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 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.
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 1b9c092e..00000000
--- a/Makefile.am
+++ /dev/null
@@ -1,644 +0,0 @@
-@CODE_COVERAGE_RULES@
-
-echo-version:
-	@echo "@I3_VERSION@"
-
-bin_PROGRAMS = \
-	i3 \
-	i3bar/i3bar \
-	i3-config-wizard/i3-config-wizard \
-	i3-dump-log/i3-dump-log \
-	i3-input/i3-input \
-	i3-msg/i3-msg \
-	i3-nagbar/i3-nagbar
-
-install-exec-hook:
-	$(LN_S) -f i3 $(DESTDIR)$(bindir)/i3-with-shmlog
-
-uninstall-hook:
-	rm -f $(DESTDIR)$(bindir)/i3-with-shmlog
-
-i3includedir=$(includedir)/i3
-i3include_HEADERS = \
-	include/i3/ipc.h
-
-dist_bin_SCRIPTS = \
-	i3-dmenu-desktop \
-	i3-migrate-config-to-v4 \
-	i3-save-tree \
-	i3-sensible-editor \
-	i3-sensible-pager \
-	i3-sensible-terminal
-
-i3confdir = $(sysconfdir)/i3
-dist_i3conf_DATA = \
-	etc/config \
-	etc/config.keycodes
-
-I3STATUS_INSTALL_NAME = $(shell echo i3status | sed '@program_transform_name@')
-
-etc/config: etc/$(dirstamp)
-	$(AM_V_GEN) sed "s,status_command i3status,status_command $(I3STATUS_INSTALL_NAME),g" $(top_srcdir)/etc/config > etc/config
-
-etc/config.keycodes: etc/$(dirstamp)
-	$(AM_V_GEN) sed "s,status_command i3status,status_command $(I3STATUS_INSTALL_NAME),g" $(top_srcdir)/etc/config.keycodes > etc/config.keycodes
-
-applicationsdir = $(datarootdir)/applications
-xsessionsdir = $(datarootdir)/xsessions
-dist_applications_DATA = \
-	share/applications/i3.desktop
-dist_xsessions_DATA = \
-	share/xsessions/i3.desktop \
-	share/xsessions/i3-with-shmlog.desktop
-
-noinst_LIBRARIES = libi3.a
-
-check_PROGRAMS = \
-	test.commands_parser \
-	test.config_parser \
-	test.inject_randr15
-
-check_SCRIPTS = \
-	testcases/complete-run.pl
-
-check_DATA = \
-	anyevent-i3.stamp
-
-clean-check:
-	rm -rf testsuite-* latest i3-cfg-for-* _Inline
-clean-local: clean-check
-
-TESTS = testcases/complete-run.pl
-
-EXTRA_DIST = \
-	$(dist_docs_toc_DATA:.html=) \
-	$(dist_docs_notoc_DATA:.html=) \
-	AnyEvent-I3/Changes \
-	AnyEvent-I3/MANIFEST \
-	AnyEvent-I3/MANIFEST.SKIP \
-	AnyEvent-I3/Makefile.PL \
-	AnyEvent-I3/README \
-	AnyEvent-I3/lib/AnyEvent/I3.pm \
-	AnyEvent-I3/t/00-load.t \
-	AnyEvent-I3/t/01-workspaces.t \
-	AnyEvent-I3/t/02-sugar.t \
-	AnyEvent-I3/t/boilerplate.t \
-	AnyEvent-I3/t/manifest.t \
-	AnyEvent-I3/t/pod-coverage.t \
-	AnyEvent-I3/t/pod.t \
-	contrib/dump-asy.pl \
-	contrib/gtk-tree-watch.pl \
-	contrib/i3-wsbar \
-	contrib/per-workspace-layout.pl \
-	contrib/trivial-bar-script.sh \
-	docs/asciidoc-git.conf \
-	docs/bigpicture.png \
-	docs/i3-pod2html \
-	docs/i3-sync.dia \
-	docs/i3-sync.png \
-	docs/i3-sync-working.dia \
-	docs/i3-sync-working.png \
-	docs/keyboard-layer1.png \
-	docs/keyboard-layer2.png \
-	docs/layout-saving-1.png \
-	docs/logo-30.png \
-	docs/modes.png \
-	docs/refcard.html \
-	docs/refcard_style.css \
-	docs/single_terminal.png \
-	docs/snapping.png \
-	docs/tree-layout1.png \
-	docs/tree-layout2.png \
-	docs/tree-shot1.png \
-	docs/tree-shot2.png \
-	docs/tree-shot3.png \
-	docs/tree-shot4.png \
-	docs/two_columns.png \
-	docs/two_terminals.png \
-	docs/wsbar.dia \
-	docs/wsbar.png \
-	i3bar/LICENSE \
-	libi3/README \
-	$(asciidoc_MANS:.1=.man) \
-	$(asciidoc_MANS:.1=.man) \
-	man/asciidoc.conf.in \
-	DEPENDS \
-	I3_VERSION \
-	LICENSE \
-	PACKAGE-MAINTAINER \
-	RELEASE-NOTES-4.18.1 \
-	generate-command-parser.pl \
-	parser-specs/commands.spec \
-	parser-specs/config.spec \
-	parser-specs/highlighting.vim \
-	pseudo-doc.doxygen \
-	testcases/complete-run.pl.in \
-	testcases/i3-test.config \
-	testcases/lib/i3test/Test.pm \
-	testcases/lib/i3test/Util.pm \
-	testcases/lib/i3test/XTEST.pm \
-	testcases/lib/i3test.pm.in \
-	testcases/lib/SocketActivation.pm \
-	testcases/lib/StartXServer.pm \
-	testcases/lib/StatusLine.pm \
-	testcases/lib/TestWorker.pm \
-	testcases/Makefile.PL \
-	testcases/new-test \
-	testcases/restart-state.golden \
-	testcases/t \
-	testcases/valgrind.supp
-
-# dirstamps contains directories which we want to be created in $(top_builddir)
-# so that our custom rules can store files in them.
-dirstamp = .dirstamp
-dirstamps = \
-	docs/$(dirstamp) \
-	man/$(dirstamp) \
-	parser/$(dirstamp) \
-	etc/$(dirstamp)
-DISTCLEANFILES = $(dirstamps)
-
-$(dirstamps):
-	@stamp='$@'; $(MKDIR_P) "$${stamp%/*}"
-	@: > $@
-
-################################################################################
-# docs generation
-################################################################################
-
-docs_tocdir = ${docdir}
-docs_notocdir = ${docdir}
-docs_poddir = ${docdir}
-if BUILD_DOCS
-dist_docs_toc_DATA = \
-	docs/hacking-howto.html \
-	docs/userguide.html \
-	docs/ipc.html \
-	docs/multi-monitor.html \
-	docs/wsbar.html \
-	docs/testsuite.html \
-	docs/i3bar-protocol.html \
-	docs/layout-saving.html
-
-dist_docs_notoc_DATA = \
-	docs/debugging.html
-
-dist_docs_pod_DATA = \
-	docs/lib-i3test.html \
-	docs/lib-i3test-test.html
-
-$(dist_docs_toc_DATA): docs/%.html: docs/% docs/$(dirstamp)
-	$(AM_V_GEN) @PATH_ASCIIDOC@ -a toc -n -o $@ $<
-
-$(dist_docs_notoc_DATA): docs/%.html: docs/% docs/$(dirstamp)
-	$(AM_V_GEN) @PATH_ASCIIDOC@ -n -o $@ $<
-
-docs/lib-i3test.html: testcases/lib/i3test.pm docs/$(dirstamp)
-	$(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@
-
-docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm docs/$(dirstamp)
-	$(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@
-
-else
-dist_docs_toc_DATA =
-dist_docs_notoc_DATA =
-dist_docs_pod_DATA =
-endif
-
-################################################################################
-# manpage generation
-################################################################################
-
-if BUILD_MANS
-dist_man1_MANS = \
-	$(asciidoc_MANS) \
-	$(pod_MANS)
-
-asciidoc_MANS = \
-	man/i3.1 \
-	man/i3bar.1 \
-	man/i3-msg.1 \
-	man/i3-input.1 \
-	man/i3-nagbar.1 \
-	man/i3-config-wizard.1 \
-	man/i3-migrate-config-to-v4.1 \
-	man/i3-sensible-editor.1 \
-	man/i3-sensible-pager.1 \
-	man/i3-sensible-terminal.1 \
-	man/i3-dump-log.1
-
-pod_MANS = \
-	man/i3-dmenu-desktop.1 \
-	man/i3-save-tree.1
-
-$(asciidoc_MANS): man/%.1: man/%.xml man/$(dirstamp)
-	$(AM_V_GEN) out='$@'; @PATH_XMLTO@ man -o "$${out%/*}" $<
-	@stamp='$@'; $(MKDIR_P) "$${stamp%/*}"
-
-man/%.xml: man/%.man man/asciidoc.conf man/$(dirstamp)
-	$(AM_V_GEN) @PATH_ASCIIDOC@ -d manpage -b docbook -f $(top_builddir)/man/asciidoc.conf -o $@ $<
-
-$(pod_MANS): man/%.1: % man/$(dirstamp)
-	$(AM_V_GEN) @PATH_POD2MAN@ --utf8 $< > $@
-else
-asciidoc_MANS =
-endif
-
-AM_CPPFLAGS = \
-	-DSYSCONFDIR="\"$(sysconfdir)\"" \
-	-I$(top_builddir)/parser \
-	-I$(top_srcdir)/include \
-	@AX_EXTEND_SRCDIR_CPPFLAGS@
-
-i3_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(libi3_CFLAGS) \
-	$(LIBSN_CFLAGS) \
-	$(XCB_CFLAGS) \
-	$(XCB_UTIL_CURSOR_CFLAGS) \
-	$(XCB_UTIL_KEYSYM_CFLAGS) \
-	$(XCB_UTIL_WM_CFLAGS) \
-	$(XCB_UTIL_XRM_CFLAGS) \
-	$(XKBCOMMON_CFLAGS) \
-	$(YAJL_CFLAGS) \
-	$(LIBPCRE_CFLAGS) \
-	$(PTHREAD_CFLAGS) \
-	$(CODE_COVERAGE_CFLAGS)
-
-i3_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	$(CODE_COVERAGE_CPPFLAGS)
-
-i3_LDADD = \
-	$(libi3_LIBS) \
-	$(LIBSN_LIBS) \
-	$(XCB_LIBS) \
-	$(XCB_UTIL_CURSOR_LIBS) \
-	$(XCB_UTIL_KEYSYMS_LIBS) \
-	$(XCB_UTIL_WM_LIBS) \
-	$(XCB_UTIL_XRM_LIBS) \
-	$(XKBCOMMON_LIBS) \
-	$(YAJL_LIBS) \
-	$(LIBPCRE_LIBS) \
-	$(PANGOCAIRO_LIBS) \
-	$(PTHREAD_LIBS) \
-	$(CODE_COVERAGE_LDFLAGS)
-
-libi3_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(GLIBGOBJECT_CFLAGS) \
-	$(XCB_CFLAGS) \
-	$(XCB_UTIL_CFLAGS) \
-	$(XCB_UTIL_XRM_CFLAGS) \
-	$(YAJL_CFLAGS) \
-	$(PANGOCAIRO_CFLAGS)
-
-libi3_LIBS = \
-	$(top_builddir)/libi3.a \
-	$(GLIBGOBJECT_LIBS) \
-	$(XCB_LIBS) \
-	$(XCB_UTIL_LIBS) \
-	$(XCB_UTIL_XRM_LIBS) \
-	$(YAJL_LIBS) \
-	$(PANGOCAIRO_LIBS)
-
-libi3_a_CFLAGS = \
-	$(libi3_CFLAGS)
-
-libi3_a_SOURCES = \
-	include/libi3.h \
-	libi3/dpi.c \
-	libi3/draw_util.c \
-	libi3/fake_configure_notify.c \
-	libi3/font.c \
-	libi3/format_placeholders.c \
-	libi3/g_utf8_make_valid.c \
-	libi3/get_colorpixel.c \
-	libi3/get_config_path.c \
-	libi3/get_exe_path.c \
-	libi3/get_mod_mask.c \
-	libi3/get_process_filename.c \
-	libi3/get_visualtype.c \
-	libi3/ipc_connect.c \
-	libi3/ipc_recv_message.c \
-	libi3/ipc_send_message.c \
-	libi3/is_debug_build.c \
-	libi3/mkdirp.c \
-	libi3/resolve_tilde.c \
-	libi3/root_atom_contents.c \
-	libi3/safewrappers.c \
-	libi3/string.c \
-	libi3/strndup.c \
-	libi3/ucs2_conversion.c
-
-i3_dump_log_i3_dump_log_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(PTHREAD_CFLAGS) \
-	$(libi3_CFLAGS)
-
-i3_dump_log_i3_dump_log_LDADD = \
-	$(PTHREAD_LIBS) \
-	$(libi3_LIBS)
-
-i3_dump_log_i3_dump_log_SOURCES = \
-	i3-dump-log/main.c
-
-i3_input_i3_input_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(libi3_CFLAGS)
-
-i3_input_i3_input_LDADD = \
-	$(libi3_LIBS) \
-	$(XCB_UTIL_KEYSYMS_LIBS)
-
-i3_input_i3_input_SOURCES = \
-	i3-input/i3-input.h \
-	i3-input/keysym2ucs.c \
-	i3-input/keysym2ucs.h \
-	i3-input/main.c
-
-i3_msg_i3_msg_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(libi3_CFLAGS)
-
-i3_msg_i3_msg_LDADD = \
-	$(libi3_LIBS)
-
-i3_msg_i3_msg_SOURCES = \
-	i3-msg/main.c
-
-i3_nagbar_i3_nagbar_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(LIBSN_CFLAGS) \
-	$(libi3_CFLAGS)
-
-i3_nagbar_i3_nagbar_LDADD = \
-	$(libi3_LIBS) \
-	$(LIBSN_LIBS) \
-	$(XCB_UTIL_CURSOR_LIBS)
-
-i3_nagbar_i3_nagbar_SOURCES = \
-	i3-nagbar/atoms.xmacro \
-	i3-nagbar/i3-nagbar.h \
-	i3-nagbar/main.c
-
-i3bar_i3bar_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	-I$(top_srcdir)/i3bar/include
-
-i3bar_i3bar_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(libi3_CFLAGS) \
-	$(XCB_CFLAGS) \
-	$(XKBCOMMON_CFLAGS) \
-	$(PANGOCAIRO_CFLAGS) \
-	$(YAJL_CFLAGS)
-
-i3bar_i3bar_LDADD = \
-	$(libi3_LIBS) \
-	$(XCB_LIBS) \
-	$(XCB_UTIL_CURSOR_LIBS) \
-	$(XKBCOMMON_LIBS) \
-	$(PANGOCAIRO_LIBS) \
-	$(YAJL_LIBS)
-
-i3bar_i3bar_SOURCES = \
-	i3bar/include/child.h \
-	i3bar/include/common.h \
-	i3bar/include/configuration.h \
-	i3bar/include/ipc.h \
-	i3bar/include/mode.h \
-	i3bar/include/outputs.h \
-	i3bar/include/parse_json_header.h \
-	i3bar/include/trayclients.h \
-	i3bar/include/util.h \
-	i3bar/include/workspaces.h \
-	i3bar/include/xcb_atoms.def \
-	i3bar/include/xcb.h \
-	i3bar/src/child.c \
-	i3bar/src/config.c \
-	i3bar/src/ipc.c \
-	i3bar/src/main.c \
-	i3bar/src/mode.c \
-	i3bar/src/outputs.c \
-	i3bar/src/parse_json_header.c \
-	i3bar/src/workspaces.c \
-	i3bar/src/xcb.c
-
-i3_config_wizard_i3_config_wizard_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(libi3_CFLAGS) \
-	$(LIBSN_CFLAGS) \
-	$(XKBCOMMON_CFLAGS)
-
-i3_config_wizard_i3_config_wizard_LDADD = \
-	$(libi3_LIBS) \
-	$(LIBSN_LIBS) \
-	$(XCB_UTIL_KEYSYMS_LIBS) \
-	$(XKBCOMMON_LIBS)
-
-i3_config_wizard_i3_config_wizard_SOURCES = \
-	i3-config-wizard/atoms.xmacro \
-	i3-config-wizard/main.c \
-	i3-config-wizard/xcb.h
-
-i3_config_wizard_i3_config_wizard_DEPENDENCIES = \
-	$(config_parser_SOURCES)
-
-test_inject_randr15_CPPFLAGS = \
-	$(AM_CPPFLAGS)
-
-test_inject_randr15_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(i3_CFLAGS)
-
-test_inject_randr15_SOURCES = \
-	testcases/inject_randr1.5.c
-
-test_inject_randr15_LDADD = \
-	$(i3_LDADD)
-
-test_commands_parser_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	-DTEST_PARSER
-
-test_commands_parser_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(i3_CFLAGS)
-
-test_commands_parser_SOURCES = \
-	src/commands_parser.c
-
-test_commands_parser_LDADD = \
-	$(i3_LDADD)
-
-test_config_parser_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	-DTEST_PARSER
-
-test_config_parser_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(i3_CFLAGS)
-
-test_config_parser_SOURCES = \
-	src/config_parser.c
-
-test_config_parser_LDADD = \
-	$(i3_LDADD)
-
-command_parser_SOURCES = \
-	parser/GENERATED_command_enums.h \
-	parser/GENERATED_command_tokens.h \
-	parser/GENERATED_command_call.h
-
-config_parser_SOURCES = \
-	parser/GENERATED_config_enums.h \
-	parser/GENERATED_config_tokens.h \
-	parser/GENERATED_config_call.h
-
-i3_SOURCES = \
-	$(command_parser_SOURCES) \
-	$(config_parser_SOURCES) \
-	include/all.h \
-	include/assignments.h \
-	include/atoms_NET_SUPPORTED.xmacro \
-	include/atoms_rest.xmacro \
-	include/atoms.xmacro \
-	include/bindings.h \
-	include/click.h \
-	include/cmdparse.h \
-	include/commands.h \
-	include/commands_parser.h \
-	include/config_directives.h \
-	include/configuration.h \
-	include/config_parser.h \
-	include/con.h \
-	include/data.h \
-	include/display_version.h \
-	include/drag.h \
-	include/ewmh.h \
-	include/fake_outputs.h \
-	include/floating.h \
-	include/handlers.h \
-	include/i3.h \
-	include/ipc.h \
-	include/key_press.h \
-	include/load_layout.h \
-	include/log.h \
-	include/main.h \
-	include/manage.h \
-	include/match.h \
-	include/move.h \
-	include/output.h \
-	include/queue.h \
-	include/randr.h \
-	include/regex.h \
-	include/render.h \
-	include/resize.h \
-	include/restore_layout.h \
-	include/scratchpad.h \
-	include/sd-daemon.h \
-	include/shmlog.h \
-	include/sighandler.h \
-	include/startup.h \
-	include/sync.h \
-	include/tree.h \
-	include/util.h \
-	include/window.h \
-	include/workspace.h \
-	include/xcb.h \
-	include/xcursor.h \
-	include/x.h \
-	include/xinerama.h \
-	include/yajl_utils.h \
-	src/assignments.c \
-	src/bindings.c \
-	src/click.c \
-	src/commands.c \
-	src/commands_parser.c \
-	src/con.c \
-	src/config.c \
-	src/config_directives.c \
-	src/config_parser.c \
-	src/display_version.c \
-	src/drag.c \
-	src/ewmh.c \
-	src/fake_outputs.c \
-	src/floating.c \
-	src/handlers.c \
-	src/ipc.c \
-	src/key_press.c \
-	src/load_layout.c \
-	src/log.c \
-	src/main.c \
-	src/manage.c \
-	src/match.c \
-	src/move.c \
-	src/output.c \
-	src/randr.c \
-	src/regex.c \
-	src/render.c \
-	src/resize.c \
-	src/restore_layout.c \
-	src/scratchpad.c \
-	src/sd-daemon.c \
-	src/sighandler.c \
-	src/startup.c \
-	src/sync.c \
-	src/tree.c \
-	src/util.c \
-	src/version.c \
-	src/window.c \
-	src/workspace.c \
-	src/x.c \
-	src/xcb.c \
-	src/xcursor.c \
-	src/xinerama.c
-
-################################################################################
-# parser generation
-################################################################################
-
-$(command_parser_SOURCES): %.h: i3-command-parser.stamp
-
-$(config_parser_SOURCES): %.h: i3-config-parser.stamp
-
-src/i3-commands_parser.$(OBJEXT): i3-command-parser.stamp
-
-src/i3-config_parser.$(OBJEXT): i3-config-parser.stamp
-
-i3-command-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/commands.spec
-	$(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/commands.spec --prefix=command
-	$(AM_V_at) mv GENERATED_command_* $(top_builddir)/parser
-	$(AM_V_at) touch $@
-
-i3-config-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/config.spec
-	$(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/config.spec --prefix=config
-	$(AM_V_at) mv GENERATED_config_* $(top_builddir)/parser
-	$(AM_V_at) touch $@
-
-################################################################################
-# AnyEvent-I3 build process
-################################################################################
-
-anyevent-i3.stamp: AnyEvent-I3/lib/AnyEvent/I3.pm
-	$(AM_V_BUILD) (cd $(top_srcdir)/AnyEvent-I3 && perl Makefile.PL && make)
-	$(AM_V_at) touch $@
-
-CLEANFILES = \
-	i3-command-parser.stamp \
-	i3-config-parser.stamp \
-	anyevent-i3.stamp
-
-################################################################################
-# Language Server support
-################################################################################
-
-# Recursively run make through https://github.com/rizsotto/Bear,
-# which generates a compile_commands.json file in the source directory.
-# This is useful for running e.g. the clangd or ccls language servers:
-# https://clang.llvm.org/extra/clangd/
-# https://github.com/MaskRay/ccls/wiki
-.PHONY: bear
-bear: clean
-	bear -o $(top_srcdir)/compile_commands.json $(MAKE) $(MAKEFLAGS)
diff --git a/PACKAGE-MAINTAINER b/PACKAGE-MAINTAINER
index 953fac53..c5a7bea6 100644
--- a/PACKAGE-MAINTAINER
+++ b/PACKAGE-MAINTAINER
@@ -21,12 +21,14 @@ i3-sensible-terminal is used in the default configuration.
 If your distribution has a mechanism to get the preferred terminal, such as the
 x-terminal-emulator symlink in Debian, please use it in i3-sensible-terminal.
 
-On debian, compilation and installing the manpages looks like this:
+You can build i3 like you build any other software package which uses
+https://mesonbuild.com/; see
+https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project
+In case you’re unfamiliar:
 
-	autoreconf -fi
-	mkdir -p build && cd build
-	../configure
-	make -j8 install
+    $ mkdir -p build && cd build
+    $ meson ..
+    $ ninja
 
 Please make sure that i3-migrate-config-to-v4 and i3-config-wizard are
 installed with i3. The Perl script is necessary to (automatically) convert v3
@@ -35,10 +37,8 @@ config with the user’s preferred modifier and should be started on the first
 start of i3 (it will automatically exit if it finds a config file).
 
 If you have any questions, ideas, hints, problems or whatever, please do not
-hesitate to contact me. I will help you out. Just drop me an E-Mail (find the
-address at https://michael.stapelberg.de/Impressum/, scroll down to bottom),
-contact me using the same address in jabber or ask on our IRC channel:
-(#i3 on irc.freenode.net).
+hesitate to contact me. I will help you out. Please see
+https://i3wm.org/contact/
 
 Thanks again for your efforts,
 Michael
diff --git a/README.md b/README.md
index f8b3f06d..d94d62c6 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ i3-gaps is a fork of [i3wm](https://www.i3wm.org), a tiling window manager for X
 
 ## How do I install i3-gaps?
 
-Please refer to the [wiki](https://github.com/Airblader/i3/wiki).
+Please refer to the [wiki](https://github.com/Airblader/i3/wiki/installation).
 
 ## Where can I get help?
 
diff --git a/RELEASE-NOTES-4.18.1 b/RELEASE-NOTES-4.18.1
deleted file mode 100644
index c05180f8..00000000
--- a/RELEASE-NOTES-4.18.1
+++ /dev/null
@@ -1,32 +0,0 @@
-
- ┌──────────────────────────────┐
- │ Release notes for i3 v4.18.1 │
- └──────────────────────────────┘
-
-This is i3 v4.18.1. This version is considered stable. All users of i3 are
-strongly encouraged to upgrade.
-
-This is a bugfix release for v4.18.
-
- ┌────────────────────────────┐
- │ Bugfixes                   │
- └────────────────────────────┘
-
-  • Move parent nodes in scratchpad correctly
-  • i3bar: Call cont_child() more liberally
-  • Fix load_layout crash when floating node doesn't have CT_FLOATING_CON parent
-  • Fix SEGFAULT when i3bar receives invalid input
-  • Revert "floating_reposition: avoid extra tree_render"
-  • Call tree_render if floating move changes workspace
-  • Update EWMH properties on workspace move
-  • cmd_focus_sibling: Fix crash on workspace level
-
- ┌────────────────────────────┐
- │ Thanks!                    │
- └────────────────────────────┘
-
-Thanks for testing, bugfixes, discussions and everything I forgot go out to:
-
-  Heman Gandhi, Orestis Floros
-
--- Michael Stapelberg, 2020-04-22
diff --git a/RELEASE-NOTES-4.19 b/RELEASE-NOTES-4.19
new file mode 100644
index 00000000..cd5bf2c6
--- /dev/null
+++ b/RELEASE-NOTES-4.19
@@ -0,0 +1,81 @@
+
+ ┌──────────────────────────────┐
+ │ Release notes for i3 v4.19   │
+ └──────────────────────────────┘
+
+This is i3 v4.19. This version is considered stable. All users of i3 are
+strongly encouraged to upgrade.
+
+In this release, we switched from the autotools build system to the meson build
+system (https://mesonbuild.com/). Check https://github.com/i3/i3/issues/4086 for
+details. If this causes problems for you, you can revert the commit which
+removed autotools from the tree: we tried our best to keep both build systems
+working. Please reach out to us in that case!
+
+ ┌────────────────────────────┐
+ │ Changes in i3 v4.19        │
+ └────────────────────────────┘
+
+  • userguide: explain button6 and button7 (scroll wheel right/left)
+  • ipc: always include the marks property (even if empty)
+  • ipc: introduce GET_BINDING_STATE command
+  • ipc: clarify workspace name field semantics
+  • ipc: document parse_error COMMAND reply field
+  • i3bar: launch using exec to avoid leaving useless shell process
+  • i3bar: make dock client order deterministic (sorted by class/instance) as a
+    side effect, i3bars without an explicit bar-id will be sorted according
+    to their definition order in the config file
+  • i3bar: update config when necessary (reduces redraws on bar mode changes)
+  • i3bar: add coordinates relative to the current output in i3bar click events
+  • i3bar: add “nonprimary” output option
+  • i3bar: set WM_CLASS instance to bar id
+  • i3-input: add different exit codes for when i3-input fails
+  • i3-dmenu-desktop: Support symlinks in search path
+  • pod2html: render without stylesheet by default
+  • introduce “tiling_from” and ”floating_from” criteria
+  • mention rofi in default config file
+  • allow ppt values in move direction and move position commands
+  • allow matching on empty properties like class, title, etc.
+
+ ┌────────────────────────────┐
+ │ Bugfixes                   │
+ └────────────────────────────┘
+
+  • i3-nagbar: Use _PATH_BSHELL to ensure using a bourne shell
+  • i3bar: fix Xorg memory leak
+  • i3bar: fix hang when pausing/resuming bar program
+  • i3bar: fix crash on invalid JSON input
+  • i3bar: kick tray clients before destroying the bar
+  • ensure client windows have a size of at least 1px after resize
+  • correctly handle overlapping decorations
+  • limit workspace numbers within 0..INT32_MAX
+  • fix a bug with tiling resize inside floating container
+  • correctly handle mouse resize in fullscreen containers by
+    not propagating $mod+right click to fullscreen clients
+  • do not try to resize fullscreen and non-fullscreen windows
+  • do not focus floating windows changing workspace with ConfigureNotify
+  • set _NET_DESKTOP_VIEWPORT after randr changes
+  • fix a bug with i3-nagbar not starting after it has already started once
+  • fix conflict when moving parent of fullscreen window to workspace
+  • fix named workspace assignments on output changes
+  • fix named workspace assignment precedence on workspace renames
+  • fix windows getting swallowed more than once
+  • erase i3 --moreversion progress line before overwriting
+  • fix test case 180-fd-leaks when running on Fedora
+  • fix crash in `focus next sibling`
+  • fix moving tiling windows out of the scratchpad
+  • floating_maybe_reassign_ws: only re-focus if previously focused
+    (fixes a focus issue with KDE notifications)
+  • fix crash on invalid JSON input in stored layouts
+  • fix monitor change during/with i3 restart by moving
+    content for non-existing output containers
+
+Thanks for testing, bugfixes, discussions and everything I forgot go out to:
+
+  6144, acheronfail, Albert Safin, Alessandro Vinciguerra, Andrey Burov,
+  Francesc Hervada-Sala, Heman Gandhi, Ian Fan, Ingo Bürk, izzel, Jason, Jason
+  Nader, Jorg Heymans, Joseph, Konstantin Kharlamov, Lukas Kern, Mark Guptill,
+  Martin T. H. Sandsmark, Matthew Martin, Maxim Schuwalow, Mike Sharov, Orestis
+  Floros, Vasily Fomin, Wilhelm Schuster, xzfc, zero77
+
+-- Michael Stapelberg, 2020-11-15
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index fe34d6b2..00000000
--- a/configure.ac
+++ /dev/null
@@ -1,202 +0,0 @@
-#                                               -*- Autoconf -*-
-# Run autoreconf -fi to generate a configure script from this file.
-
-AC_PREREQ([2.69])
-AC_INIT([i3], [4.18.1], [https://github.com/i3/i3/issues])
-# For AX_EXTEND_SRCDIR
-AX_ENABLE_BUILDDIR
-AM_INIT_AUTOMAKE([foreign subdir-objects -Wall no-dist-gzip dist-bzip2])
-# Default to silent rules, use V=1 to get verbose compilation output.
-AM_SILENT_RULES([yes])
-# Make it possible to disable maintainer mode to disable re-generation of build
-# system files.
-AM_MAINTAINER_MODE([enable])
-AC_CONFIG_SRCDIR([libi3/ipc_recv_message.c])
-AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_MACRO_DIR([m4])
-
-dnl Verify macros defined in m4/ such as AX_SANITIZERS are not present in the
-dnl output, i.e. are replaced as expected. This line results in a better error
-dnl message when using aclocal < 1.13 (which does not understand
-dnl AC_CONFIG_MACRO_DIR) without passing the -I m4 parameter.
-m4_pattern_forbid([AX_SANITIZERS])
-
-# Verify we are using GNU make because we use '%'-style pattern rules in
-# Makefile.am, which are a GNU make extension. Pull requests to replace
-# '%'-style pattern rules with a more portable alternative are welcome.
-AX_CHECK_GNU_MAKE
-AS_VAR_IF([_cv_gnu_make_command], [""], [AC_MSG_ERROR([the i3 Makefile.am requires GNU make])])
-
-AX_EXTEND_SRCDIR
-
-AS_IF([test -e ${srcdir}/.git],
-      [
-        VERSION="$(git -C ${srcdir} describe --tags --abbrev=0)"
-        I3_VERSION="$(git -C ${srcdir} describe --tags --always) ($(git -C ${srcdir} rev-list --format=%cd --date=short -n1 $(git rev-parse HEAD) | tail -n1), branch \\\"$(git -C ${srcdir} describe --tags --always --all | sed s:heads/::)\\\")"
-        # Mirrors what libi3/is_debug_build.c does:
-        is_release=$(test $(echo "${I3_VERSION}" | cut -d '(' -f 1 | wc -m) -lt 10 && echo yes || echo no)
-      ],
-      [
-        VERSION="$(cut -d '-' -f 1 ${srcdir}/I3_VERSION | cut -d ' ' -f 1)"
-        I3_VERSION="$(sed -e 's/@<:@\"?\\@:>@/\\&/g' ${srcdir}/I3_VERSION)"
-        is_release="$(grep -q non-git ${srcdir}/I3_VERSION && echo no || echo yes)"
-      ])
-AC_SUBST([I3_VERSION], [$I3_VERSION])
-MAJOR_VERSION="$(echo ${VERSION} | cut -d '.' -f 1)"
-MINOR_VERSION="$(echo ${VERSION} | cut -d '.' -f 2)"
-PATCH_VERSION="$(echo ${VERSION} | cut -d '.' -f 3)"
-AS_IF([test "x${PATCH_VERSION}" = x], [PATCH_VERSION=0])
-AC_DEFINE_UNQUOTED([I3_VERSION], ["${I3_VERSION}"], [i3 version])
-AC_DEFINE_UNQUOTED([MAJOR_VERSION], [${MAJOR_VERSION}], [i3 major version])
-AC_DEFINE_UNQUOTED([MINOR_VERSION], [${MINOR_VERSION}], [i3 minor version])
-AC_DEFINE_UNQUOTED([PATCH_VERSION], [${PATCH_VERSION}], [i3 patch version])
-
-AX_CODE_COVERAGE
-
-dnl is_release must be lowercase because AX_CHECK_ENABLE_DEBUG calls m4_tolower
-dnl on its fourth argument.
-AX_CHECK_ENABLE_DEBUG([yes], , [UNUSED_NDEBUG], [$is_release])
-
-AC_PROG_CC_C99
-
-# For strnlen() and vasprintf().
-AC_USE_SYSTEM_EXTENSIONS
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_CHECK_HEADER_STDBOOL
-dnl The error message should include the specific type which could not be
-dnl found, but I do not see a way to achieve that.
-AC_CHECK_TYPES([mode_t, off_t, pid_t, size_t, ssize_t], , [AC_MSG_FAILURE([cannot find required type])])
-
-# Checks for library functions.
-AC_FUNC_FORK
-AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
-AC_FUNC_STRNLEN
-AC_CHECK_FUNCS([atexit dup2 ftruncate getcwd gettimeofday localtime_r memchr memset mkdir rmdir setlocale socket strcasecmp strchr strdup strerror strncasecmp strrchr strspn strstr strtol strtoul], , [AC_MSG_FAILURE([cannot find the $ac_func function, which i3 requires])])
-AC_REPLACE_FUNCS([mkdirp strndup])
-
-# Checks for libraries.
-
-AC_SEARCH_LIBS([floor], [m], , [AC_MSG_FAILURE([cannot find the required floor() function despite trying to link with -lm])])
-
-# libev does not ship with a pkg-config file :(.
-AC_SEARCH_LIBS([ev_run], [ev], , [AC_MSG_FAILURE([cannot find the required ev_run() function despite trying to link with -lev])])
-
-AC_SEARCH_LIBS([shm_open], [rt], [], [], [-pthread])
-
-AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <iconv.h>], [iconv_open(0, 0)])], ,
-  [LIBS="-liconv $LIBS"
-   AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <iconv.h>], [iconv_open(0, 0)])], ,
-     [AC_MSG_FAILURE([cannot find the required iconv_open() function despite trying to link with -liconv])])]
-)
-
-AX_PTHREAD
-
-dnl Each prefix corresponds to a source tarball which users might have
-dnl downloaded in a newer version and would like to overwrite.
-PKG_CHECK_MODULES([LIBSN], [libstartup-notification-1.0])
-PKG_CHECK_MODULES([XCB], [xcb xcb-xkb xcb-xinerama xcb-randr xcb-shape])
-PKG_CHECK_MODULES([XCB_UTIL], [xcb-event xcb-util])
-PKG_CHECK_MODULES([XCB_UTIL_CURSOR], [xcb-cursor])
-PKG_CHECK_MODULES([XCB_UTIL_KEYSYMS], [xcb-keysyms])
-PKG_CHECK_MODULES([XCB_UTIL_WM], [xcb-icccm])
-PKG_CHECK_MODULES([XCB_UTIL_XRM], [xcb-xrm])
-PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon xkbcommon-x11])
-PKG_CHECK_MODULES([YAJL], [yajl])
-PKG_CHECK_MODULES([LIBPCRE], [libpcre >= 8.10])
-PKG_CHECK_MODULES([PANGOCAIRO], [cairo >= 1.14.4 pangocairo])
-PKG_CHECK_MODULES([GLIBGOBJECT], [glib-2.0 gobject-2.0])
-
-# Checks for programs.
-AC_PROG_AWK
-AC_PROG_CPP
-AC_PROG_INSTALL
-AC_PROG_MAKE_SET
-AC_PROG_RANLIB
-AC_PROG_LN_S
-
-AC_ARG_ENABLE(docs,
-  AS_HELP_STRING(
-    [--disable-docs],
-    [disable building documentation]),
-  [ax_docs=$enableval],
-  [ax_docs=yes])
-AC_ARG_ENABLE(mans,
-  AS_HELP_STRING(
-    [--disable-mans],
-    [disable building manual pages]),
-  [ax_mans=$enableval],
-  [ax_mans=yes])
-AS_IF([test x$ax_docs = xyes || test x$ax_mans = xyes], [
-  AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc])
-])
-AS_IF([test x$ax_mans = xyes], [
-  AC_PATH_PROG([PATH_XMLTO], [xmlto])
-  AC_PATH_PROG([PATH_POD2MAN], [pod2man])
-])
-AM_CONDITIONAL([BUILD_MANS], [test x$ax_mans = xyes && test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x])
-AM_CONDITIONAL([BUILD_DOCS], [test x$ax_docs = xyes && test x$PATH_ASCIIDOC != x])
-
-AM_PROG_AR
-
-AX_FLAGS_WARN_ALL
-AX_CHECK_COMPILE_FLAG([-Wunused-value], [AX_APPEND_FLAG([-Wunused-value], [AM_CFLAGS])])
-AC_SUBST(AM_CFLAGS)
-
-# Checks for header files.
-AC_CHECK_HEADERS([fcntl.h float.h inttypes.h limits.h locale.h netinet/in.h paths.h stddef.h stdint.h stdlib.h string.h sys/param.h sys/socket.h sys/time.h unistd.h], , [AC_MSG_FAILURE([cannot find the $ac_header header, which i3 requires])])
-
-AC_CONFIG_FILES([Makefile testcases/lib/i3test.pm man/asciidoc.conf])
-AC_CONFIG_FILES([testcases/complete-run.pl], [chmod +x testcases/complete-run.pl])
-
-# Enable address sanitizer for non-release builds. The performance hit is a
-# 50% increase of wallclock time for the testsuite on my machine.
-if test x$is_release = xyes; then
-	default_sanitizers=
-else
-	default_sanitizers=address
-fi
-AX_SANITIZERS(, [$default_sanitizers], [AC_DEFINE([I3_ASAN_ENABLED], [], [Enable ASAN])])
-
-AC_OUTPUT
-
-if test -z "${BUILD_DOCS_TRUE}"; then
-	print_BUILD_DOCS=yes
-else
-	print_BUILD_DOCS=no
-fi
-
-
-if test -z "${BUILD_MANS_TRUE}"; then
-	print_BUILD_MANS=yes
-else
-	print_BUILD_MANS=no
-fi
-
-in_git_worktree=`git rev-parse --is-inside-work-tree 2>/dev/null`
-if [[ "$in_git_worktree" = "true" ]]; then
-	git_dir=`git rev-parse --git-dir 2>/dev/null`
-	srcdir=`dirname "$git_dir"`
-	exclude_dir=`pwd | sed "s,^$srcdir,,g"`
-	if ! grep -q "^$exclude_dir" "$git_dir/info/exclude"; then
-		echo "$exclude_dir" >> "$git_dir/info/exclude"
-	fi
-fi
-
-echo \
-"--------------------------------------------------------------------------------
-build configured:
-
-AS_HELP_STRING([i3 version:], [`echo ${I3_VERSION} | sed 's,\\\\,,g'`])
-AS_HELP_STRING([is release version:], [${is_release}])
-
-AS_HELP_STRING([build manpages:], [${print_BUILD_MANS}])
-AS_HELP_STRING([build docs:], [${print_BUILD_DOCS}])
-AS_HELP_STRING([enable debug flags:], [${ax_enable_debug}])
-AS_HELP_STRING([code coverage:], [${CODE_COVERAGE_ENABLED}])
-AS_HELP_STRING([enabled sanitizers:], [${ax_enabled_sanitizers}])
-
-To compile, run:
-
-  cd `pwd` && make -j8
---------------------------------------------------------------------------------"
diff --git a/debian/changelog b/debian/changelog
index 059fa327..c9df8676 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,27 @@
+i3-wm (4.19.1-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Mon, 19 Oct 2020 22:48:30 +0200
+
+i3-wm (4.19-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Sun, 15 Nov 2020 18:28:11 +0100
+
+i3-wm (4.18.3-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Mon, 19 Oct 2020 22:48:30 +0200
+
+i3-wm (4.18.2-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Sun, 26 Jul 2020 10:24:46 +0200
+
 i3-wm (4.18.1-1) unstable; urgency=medium
 
   * New upstream release.
diff --git a/debian/compat b/debian/compat
index ec635144..f599e28b 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-9
+10
diff --git a/debian/control b/debian/control
index 843e6557..5cd27cd6 100644
--- a/debian/control
+++ b/debian/control
@@ -2,8 +2,8 @@ Source: i3-wm
 Section: x11
 Priority: extra
 Maintainer: Michael Stapelberg <stapelberg@debian.org>
-Build-Depends: debhelper (>= 9),
-               dh-autoreconf,
+Build-Depends: debhelper (>= 10),
+               meson,
                libx11-dev,
                libxcb-util0-dev (>= 0.3.8),
                libxcb-keysyms1-dev,
diff --git a/debian/i3-wm.docs b/debian/i3-wm.docs
deleted file mode 100644
index 437175b3..00000000
--- a/debian/i3-wm.docs
+++ /dev/null
@@ -1,32 +0,0 @@
-docs/debugging.html
-docs/hacking-howto.html
-docs/i3bar-protocol.html
-docs/userguide.html
-docs/bigpicture.png
-docs/single_terminal.png
-docs/snapping.png
-docs/two_columns.png
-docs/two_terminals.png
-docs/modes.png
-docs/ipc.html
-docs/multi-monitor.html
-docs/wsbar.html
-docs/wsbar.png
-docs/keyboard-layer1.png
-docs/keyboard-layer2.png
-docs/testsuite.html
-docs/i3-sync-working.png
-docs/i3-sync.png
-docs/tree-layout1.png
-docs/tree-layout2.png
-docs/tree-shot1.png
-docs/tree-shot2.png
-docs/tree-shot3.png
-docs/tree-shot4.png
-docs/refcard.html
-docs/refcard_style.css
-docs/logo-30.png
-docs/lib-i3test.html
-docs/lib-i3test-test.html
-docs/layout-saving.html
-docs/layout-saving-1.png
diff --git a/debian/i3-wm.manpages b/debian/i3-wm.manpages
deleted file mode 100644
index 1953b6a8..00000000
--- a/debian/i3-wm.manpages
+++ /dev/null
@@ -1,13 +0,0 @@
-man/i3.1
-man/i3-msg.1
-man/i3-input.1
-man/i3-nagbar.1
-man/i3-config-wizard.1
-man/i3-dump-log.1
-man/i3-migrate-config-to-v4.1
-man/i3-sensible-pager.1
-man/i3-sensible-editor.1
-man/i3-sensible-terminal.1
-man/i3-dmenu-desktop.1
-man/i3-save-tree.1
-man/i3bar.1
diff --git a/debian/rules b/debian/rules
index d5c30686..26e303e6 100755
--- a/debian/rules
+++ b/debian/rules
@@ -14,12 +14,12 @@ override_dh_auto_test:
 	# TODO: enable tests
 
 override_dh_auto_configure:
-	# The default is /usr/share/doc/i3
-	dh_auto_configure -- --docdir=/usr/share/doc/i3-wm
+	# Set -Ddocdir; the default is /usr/share/doc/i3
+	dh_auto_configure -- -Ddocdir=/usr/share/doc/i3-wm -Dmans=true
 
 override_dh_builddeb:
 	# bintray does not support xz currently.
 	dh_builddeb -- -Zgzip
 
 %:
-	dh $@ --parallel --builddirectory=build --with=autoreconf
+	dh $@ --buildsystem=meson
diff --git a/docs/bigpicture.asy b/docs/bigpicture.asy
new file mode 100644
index 00000000..4b3656c6
--- /dev/null
+++ b/docs/bigpicture.asy
@@ -0,0 +1,19 @@
+import drawtree;
+treeLevelStep = 2cm;
+TreeNode n94457831379296 = makeNode("``root'' (splith) []");
+TreeNode n94457831380944 = makeNode(n94457831379296, "``\_\_i3'' (output) []");
+TreeNode n94457831384048 = makeNode(n94457831380944, "``content'' (splith) []");
+TreeNode n94457831387184 = makeNode(n94457831384048, "``\_\_i3\_scratch'' (splith) []");
+TreeNode n94457831390576 = makeNode(n94457831379296, "``eDP-1'' (output) []");
+TreeNode n94457831393744 = makeNode(n94457831390576, "``topdock'' (dockarea) []");
+TreeNode n94457831396992 = makeNode(n94457831390576, "``content'' (splith) []");
+TreeNode n94457831628304 = makeNode(n94457831396992, "``1'' (splith) []");
+TreeNode n94457831571040 = makeNode(n94457831628304, "``Hacking i3: How To - Mozilla Firefox'' (leaf) []");
+TreeNode n94457831246384 = makeNode(n94457831628304, "``vim'' (leaf) []");
+TreeNode n94457831461088 = makeNode(n94457831396992, "``Named workspace'' (splith) []");
+TreeNode n94457831471424 = makeNode(n94457831461088, "``[Empty]'' (tabbed) []");
+TreeNode n94457831570576 = makeNode(n94457831471424, "``contrib/dump-asy.pl --no-gv'' (leaf) [Marks go here]");
+TreeNode n94457831645488 = makeNode(n94457831471424, "``ipython'' (leaf) []");
+TreeNode n94457831400192 = makeNode(n94457831390576, "``bottomdock'' (dockarea) []");
+TreeNode n94457831424848 = makeNode(n94457831400192, "``i3bar for output eDP-1'' (leaf) []");
+draw(n94457831379296, (0, 0));
diff --git a/docs/bigpicture.png b/docs/bigpicture.png
index fc3c8db7..031673b8 100644
Binary files a/docs/bigpicture.png and b/docs/bigpicture.png differ
diff --git a/docs/bigpicture.xcf b/docs/bigpicture.xcf
deleted file mode 100644
index ead00719..00000000
Binary files a/docs/bigpicture.xcf and /dev/null differ
diff --git a/docs/hacking-howto b/docs/hacking-howto
index 2ca44a5f..cc08cd1f 100644
--- a/docs/hacking-howto
+++ b/docs/hacking-howto
@@ -8,83 +8,82 @@ touching i3’s source code. It should contain all important information to help
 you understand why things are like they are. If it does not mention something
 you find necessary, please do not hesitate to contact me.
 
+++++
+<div style="background-color:red; color:white; padding:20px;">
+<strong style="color:white;">WARNING!</strong>
+<p>
+++++
+This document is not 100% up to date. Specifically, everything up to and
+including <<data_structures>> has been updated recently. The rest might contain
+outdated information.
+++++
+</p>
+</div>
+++++
+
 == Building i3
 
-You can build i3 like you build any other software package which uses autotools.
-Here’s a memory refresher:
+You can build i3 like you build any other software package which uses
+https://mesonbuild.com/[The Meson Build system]; see
+https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project[Quickstart
+Guide → Compiling a Meson project]. In case you’re unfamiliar:
 
-    $ autoreconf -fi
     $ mkdir -p build && cd build
-    $ ../configure
-    $ make -j8
-
-(The autoreconf -fi step is unnecessary if you are building from a release tarball,
- but shouldn’t hurt either.)
+    $ meson ..
+    $ ninja
 
 === Build system features
 
-* We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate
-  directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building
+* We use the +AX_ENABLE_BUILDDIR+ macro to enforce builds happening in a separate
+  directory. This is a prerequisite for the +AX_EXTEND_SRCDIR+ macro and building
   in a separate directory is common practice anyway. In case this causes any
   trouble when packaging i3 for your distribution, please open an issue.
 
-* “make check” runs the i3 testsuite. See docs/testsuite for details.
+* +make check+ runs the i3 testsuite. See docs/testsuite for details.
 
-* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
+* +make distcheck+ (runs testsuite on +make dist+ result, tiny bit quicker
   feedback cycle than waiting for the travis build to catch the issue).
 
-* “make uninstall” (occasionally requested by users who compile from source)
+* +make uninstall+ (occasionally requested by users who compile from source)
 
-* “make” will build manpages/docs by default if the tools are installed.
+* +make+ will build manpages/docs by default if the tools are installed.
   Conversely, manpages/docs are not tried to be built for users who don’t want
-  to install all these dependencies to get started hacking on i3.
+  to install all these dependencies to get started hacking on i3. Manpages and
+  docs can be disabled with the +--disable-mans++ and ++--disable-docs++
+  configure options respectively.
 
 * non-release builds will enable address sanitizer by default. Use the
-  --disable-sanitizers configure option to turn off all sanitizers, and see
-  --help for available sanitizers.
-
-* Support for pre-compiled headers (PCH) has been dropped for now in the
-  interest of simplicity. If you need support for PCH, please open an issue.
+  +--disable-sanitizers+ configure option to turn off all sanitizers, and see
+  +--help+ for available sanitizers.
 
-* Coverage reports are now generated using “make check-code-coverage”, which
-  requires specifying --enable-code-coverage when calling configure.
+* Coverage reports are now generated using +make check-code-coverage+, which
+  requires specifying +--enable-code-coverage+ when calling configure.
 
-== Using git / sending patches
-
-For a short introduction into using git, see
-https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
-or, for more documentation, see https://git-scm.com/documentation
+== Pull requests
 
 Please talk to us before working on new features to see whether they will be
 accepted. A good way for this is to open an issue and asking for opinions on it.
-Even for accepted features, this can be a good way to refine an idea upfront. However,
-we don't want to see certain features in i3, e.g., switching window focus in an
-Alt+Tab like way.
+Even for accepted features, this can be a good way to refine an idea upfront.
+However, we don't want to see certain features in i3, e.g., switching window
+focus in an Alt+Tab like way.
 
-When working on bugfixes, please make sure you mention that you are working on
-it in the corresponding bug report at https://github.com/i3/i3/issues. In case
-there is no bug report yet, please create one.
+When working on bugfixes, please make sure you mention that you are working on it
+in the corresponding bug report at https://github.com/i3/i3/issues. In case there
+is no bug report yet, please create one.
 
 After you are done, please submit your work for review as a pull request at
-https://github.com/i3/i3.
-
-Do not send emails to the mailing list or any author directly, and don’t submit
-them in the bugtracker, since all reviews should be done in public at
-https://github.com/i3/i3. In order to make your review go as fast as possible, you
-could have a look at previous reviews and see what the common mistakes are.
+https://github.com/i3/i3. In order to make your review go as fast as possible,
+you could have a look at previous reviews and see what the common mistakes are.
 
 === Which branch to use?
 
-Work on i3 generally happens in two branches: “master” and “next” (the latter
-being the default branch, the one that people get when they check out the git
-repository).
+Work on i3 generally happens in two branches: “next” (default) and “stable”.
 
-The contents of “master” are always stable. That is, it contains the source code
+The contents of “stable” are always stable. That is, it contains the source code
 of the latest release, plus any bugfixes that were applied since that release.
 
-New features are only found in the “next” branch. Therefore, if you are working
-on a new feature, use the “next” branch. If you are working on a bugfix, use the
-“next” branch, too, but make sure your code also works on “master”.
+New features are only found in the “next” branch. Always use this branch when
+writing new code (both bugfixes and features).
 
 == Window Managers
 
@@ -106,9 +105,9 @@ In the case of i3, the tasks (and order of them) are the following:
   the first client of X) and manage them (reparent them, create window
   decorations, etc.)
 . When new windows are created, manage them
-. Handle the client’s `_WM_STATE` property, but only `_WM_STATE_FULLSCREEN` and
-  `_NET_WM_STATE_DEMANDS_ATTENTION`
-. Handle the client’s `WM_NAME` property
+. Handle the client’s +_WM_STATE+ property, but only +_WM_STATE_FULLSCREEN+ and
+  +_NET_WM_STATE_DEMANDS_ATTENTION+
+. Handle the client’s +WM_NAME+ property
 . Handle the client’s size hints to display them proportionally
 . Handle the client’s urgency hint
 . Handle enter notifications (focus follows mouse)
@@ -123,11 +122,11 @@ will be discussed.
 
 === Tiling window managers
 
-Traditionally, there are two approaches to managing windows: The most common
-one nowadays is floating, which means the user can freely move/resize the
-windows. The other approach is called tiling, which means that your window
-manager distributes windows to use as much space as possible while not
-overlapping each other.
+Traditionally, there are two approaches to managing windows: The most common one
+nowadays is stacking (or floating, using i3's terminology), which means the user
+can freely move/resize the windows, potentially overlapping them. The other
+approach is called tiling, which means that the window manager distributes
+windows to use as much space as possible while not overlapping each other.
 
 The idea behind tiling is that you should not need to waste your time
 moving/resizing windows while you usually want to get some work done. After
@@ -161,63 +160,67 @@ example.
 
 == Files
 
-include/atoms.xmacro::
-A file containing all X11 atoms which i3 uses. This file will be included
-various times (for defining, requesting and receiving the atoms), each time
-with a different definition of xmacro().
+i3's source code is in the +src+ folder while header files reside in +include+.
+Other tools such as i3bar and i3-nagbar have their own folders. i3 and its tools
+share an internal library called ``libi3'' which also has its own folder.
+
+The following list gives an overview of the codebase, explaining the
+functionality of the most important, core source code files. Other files in the
+tree that are not mentioned here implement specific functionalities: for example,
++src/scratchpad.c+ is obviously about the scratchpad functionality.
 
 include/data.h::
-Contains data definitions used by nearly all files. You really need to read
-this first.
+Contains data definitions used by nearly all files.
 
 include/*.h::
 Contains forward definitions for all public functions, as well as
 doxygen-compatible comments (so if you want to get a bit more of the big
 picture, either browse all header files or use doxygen if you prefer that).
 
-src/config_parser.c::
-Contains a custom configuration parser. See src/command_parser.c for rationale
- on why we use a custom parser.
-
-src/click.c::
-Contains all functions which handle mouse button clicks (right mouse button
-clicks initiate resizing and thus are relatively complex).
-
-src/command_parser.c::
-Contains a hand-written parser to parse commands (commands are what
-you bind on keys and what you can send to i3 using the IPC interface, like
-'move left' or 'workspace 4').
+src/config_directives.c::
+src/commands.c::
+Contain the definitions for all high-level config and command directives. These
+are excellent places to start with a top-to-bottom approach to understand
+specific i3 behavior. For example, if you want to investigate a bug that happens
+for the +move to mark+ command, you can use gdb to pause in
++cmd_move_con_to_mark+ and then work your way from there, stepping into
+lower-level functions.
 
 src/con.c::
-Contains all functions which deal with containers directly (creating
-containers, searching containers, getting specific properties from containers,
-…).
-
-src/config.c::
-Contains all functions handling the configuration file (calling the parser
-src/config_parser.c) with the correct path, switching key bindings mode).
+Contains all functions which deal with containers directly (creating containers,
+searching containers, getting specific properties from containers, …). Contains
+abstractions and auxiliary functions necessary to work with the container
+structure which is used in almost all parts of the codebase.
 
-src/ewmh.c::
-Functions to get/set certain EWMH properties easily.
+src/tree.c::
+Contains functions which deal with the tree abstraction. However, be aware that
++src/con.c+ also contains functions that heavily interact with the tree
+structure. Some functions that are included in +str/tree.c+ are those that handle
+opening and closing containers in the tree, finding the container that should be
+focused next and flattening the tree. See also +src/move.c+ for other
+move-specific functions that interact with the tree, which were moved into their
+own file because they are so long.
 
-src/floating.c::
-Contains functions for floating mode (mostly resizing/dragging).
+src/workspace.c::
+Contains functions which deal with workspaces. Includes code that creates new
+workspaces, shows existing ones and deals with workspace assignments.
 
 src/handlers.c::
 Contains all handlers for all kinds of X events (new window title, new hints,
-unmapping, key presses, button presses, …).
-
-src/ipc.c::
-Contains code for the IPC interface.
-
-src/load_layout.c::
-Contains code for loading layouts from JSON files.
+unmapping, key presses, button presses, …). This is a very important file to
+understand how i3 interacts with changes to its environment.
 
-src/log.c::
-Contains the logging functions.
+src/command_parser.c::
+src/config_parser.c::
+Contain a hand-written parser to parse commands and configuration (commands are what
+you bind on keys and what you can send to i3 using the IPC interface, like
++move left+ or +workspace 4+). +src/config.c+ is responsible for calling the
+configuration parser.
 
-src/main.c::
-Initializes the window manager.
+src/click.c::
+src/resize.c::
+Contain functions which handle mouse button clicks (right mouse button
+clicks initiate resizing and thus are relatively complex).
 
 src/manage.c::
 Looks at existing or new windows and decides whether to manage them. If so, it
@@ -226,93 +229,66 @@ reparents the window and inserts it into our data structures.
 src/match.c::
 A "match" is a data structure which acts like a mask or expression to match
 certain windows or not. For example, when using commands, you can specify a
-command like this: [title="*Firefox*"] kill. The title member of the match
+command like this: +[title="*Firefox*"] kill+. The title member of the match
 data structure will then be filled and i3 will check each window using
-match_matches_window() to find the windows affected by this command.
-
-src/move.c::
-Contains code to move a container in a specific direction.
-
-src/output.c::
-Functions to handle CT_OUTPUT cons.
++match_matches_window()+ to find the windows affected by this command.
 
 src/randr.c::
 The RandR API is used to get (and re-query) the configured outputs (monitors,
-…).
+…). Legacy Xinerama support resides in +src/xinerama.c+.
 
 src/render.c::
 Renders the tree data structure by assigning coordinates to every node. These
 values will later be pushed to X11 in +src/x.c+.
 
-src/resize.c::
-Contains the functions to resize containers.
-
-src/restore_layout.c::
-Everything for restored containers that is not pure state parsing (which can be
-found in load_layout.c).
-
 src/sighandler.c::
 Handles +SIGSEGV+, +SIGABRT+ and +SIGFPE+ by showing a dialog that i3 crashed.
-You can chose to let it dump core, to restart it in-place or to restart it
-in-place but forget about the layout.
-
-src/tree.c::
-Contains functions which open or close containers in the tree, change focus or
-cleanup ("flatten") the tree. See also +src/move.c+ for another similar
-function, which was moved into its own file because it is so long.
-
-src/util.c::
-Contains useful functions which are not really dependent on anything.
+You can choose to let it dump core and restart i3 in-place (either trying to
+preserve layout or forget about it).
 
 src/window.c::
 Handlers to update X11 window properties like +WM_CLASS+, +_NET_WM_NAME+,
 +CLIENT_LEADER+, etc.
 
-src/workspace.c::
-Contains all functions related to workspaces (displaying, hiding, renaming…)
-
-src/x.c::
-Transfers our in-memory tree (see +src/render.c+) to X11.
-
-src/xcb.c::
-Contains wrappers to use xcb more easily.
-
-src/xcursor.c::
-XCursor functions (for cursor themes).
-
-src/xinerama.c::
-Legacy support for Xinerama. See +src/randr.c+ for the preferred API.
+include/*.xmacro.*::
+A file containing all X11 atoms which i3 uses. This file will be included
+various times (for defining, requesting and receiving the atoms), each time
+with a different definition of xmacro().
 
+[[data_structures]]
 == Data structures
 
+See +include/data.h+ for documented data structures. The most important ones are
+explained here.
 
-See include/data.h for documented data structures. The most important ones are
-explained right here.
-
-/////////////////////////////////////////////////////////////////////////////////
-// TODO: update image
-
-image:bigpicture.png[The Big Picture]
+The following picture is generated by the +contrib/dump-asy.pl+ script.
 
-/////////////////////////////////////////////////////////////////////////////////
+image:bigpicture.png["The Big Picture",width=1000,link="bigpicture.png"]
 
-So, the hierarchy is:
+The hierarchy is:
 
-. *X11 root window*, the root container
-. *Output container* (LVDS1 in this example)
-. *Content container* (there are also containers for dock windows)
-. *Workspaces* (Workspace 1 in this example, with horizontal orientation)
-. *Split container* (vertically split)
-. *X11 window containers*
+. *Root container*
+. *Output containers*: +eDP-1+ in this example and the internal +__i3++ output
+. *Content and 2 dockarea containers*
+. *Workspaces*: Numbered workspace ``1'' and a ``Named workspace''
+. *Split containers*: One horizontal in the first workspace and a tabbed one in
+  the named one.
+. *Leaf containers*: Windows like vim and an i3bar dock.
 
 The data type is +Con+, in all cases.
 
-=== X11 root window
+=== Root container
+
+The root container (global variable +croot+) is the up-most ascendant of every i3
+container. It can be used to iterate over the whole tree structure. E.g., it is
+used to reply to the +GET_WORKSPACES+ request, iterating over it's children to
+find all workspaces. This is different from the X11 root window.
 
-The X11 root window is a single window per X11 display (a display is identified
-by +:0+ or +:1+ etc.). The root window is what you draw your background image
-on. It spans all the available outputs, e.g. +VGA1+ is a specific part of the
-root window and +LVDS1+ is a specific part of the root window.
+The X11 root window (global variable +root+) is a single window per X11 display
+(a display is identified by +:0+ or +:1+ etc.). The root window is what you draw
+your background image on. It spans all the available outputs, e.g. +VGA1+ is a
+specific part of the root window and +LVDS1+ is a specific part of the root
+window.
 
 === Output container
 
@@ -334,8 +310,8 @@ currently on.
 === Content container
 
 Each output has multiple children. Two of them are dock containers which hold
-dock clients. The other one is the content container, which holds the actual
-content (workspaces) of this output.
+the top and bottom dock clients. The other one is the content container, which
+holds the actual content (workspaces) of this output.
 
 === Workspace
 
@@ -354,21 +330,20 @@ vertical) and a layout.
 Split containers (and X11 window containers, which are a subtype of split
 containers) can have different border styles.
 
-=== X11 window container
+=== Leaf containers
 
-An X11 window container holds exactly one X11 window. These are the leaf nodes
-of the layout tree, they cannot have any children.
+A leaf container holds exactly one X11 window. They can't have any children.
 
 == List/queue macros
 
 i3 makes heavy use of the list macros defined in BSD operating systems. To
 ensure that the operating system on which i3 is compiled has all the expected
-features, i3 comes with `include/queue.h`. On BSD systems, you can use man
-`queue(3)`. On Linux, you have to use google (or read the source).
+features, i3 comes with +include/queue.h+. On BSD systems, you can use +man
+queue(3)+. On Linux, you have to use google (or read the source).
 
 The lists used are +SLIST+ (single linked lists), +CIRCLEQ+ (circular
 queues) and +TAILQ+ (tail queues). Usually, only forward traversal is necessary,
-so an `SLIST` works fine. If inserting elements at arbitrary positions or at
+so an +SLIST+ works fine. If inserting elements at arbitrary positions or at
 the end of a list is necessary, a +TAILQ+ is used instead. However, for the
 windows inside a container, a +CIRCLEQ+ is necessary to go from the currently
 selected window to the window above/below.
@@ -378,10 +353,10 @@ selected window to the window above/below.
 There is a row of standard variables used in many events. The following names
 should be chosen for those:
 
- * ``conn'' is the xcb_connection_t
- * ``event'' is the event of the particular type
- * ``con'' names a container
- * ``current'' is a loop variable when using +TAILQ_FOREACH+ etc.
+ * +conn+ is the xcb_connection_t
+ * +event+ is the event of the particular type
+ * +con+ names a container
+ * +current+ is a loop variable when using +TAILQ_FOREACH+ etc.
 
 == Startup (src/mainx.c, main())
 
@@ -430,7 +405,7 @@ The bound command is parsed by the cmdparse lexer/parser, see +parse_cmd+ in
 
 == Manage windows (src/main.c, manage_window() and reparent_window())
 
-`manage_window()` does some checks to decide whether the window should be
++manage_window()+ does some checks to decide whether the window should be
 managed at all:
 
  * Windows have to be mapped, that is, visible on screen
@@ -438,18 +413,18 @@ managed at all:
    not be managed by a window manager
 
 Afterwards, i3 gets the initial geometry and reparents the window (see
-`reparent_window()`) if it wasn’t already managed.
++reparent_window()+) if it wasn’t already managed.
 
 Reparenting means that for each window which is reparented, a new window,
 slightly larger than the original one, is created. The original window is then
 reparented to the bigger one (called "frame").
 
-After reparenting, the window type (`_NET_WM_WINDOW_TYPE`) is checked to see
-whether this window is a dock (`_NET_WM_WINDOW_TYPE_DOCK`), like dzen2 for
+After reparenting, the window type (+_NET_WM_WINDOW_TYPE+) is checked to see
+whether this window is a dock (+_NET_WM_WINDOW_TYPE_DOCK+), like dzen2 for
 example. Docks are handled differently, they don’t have decorations and are not
 assigned to a specific container. Instead, they are positioned at the bottom
 or top of the screen (in the appropriate dock area containers). To get the
-height which needs to be reserved for the window, the `_NET_WM_STRUT_PARTIAL`
+height which needs to be reserved for the window, the +_NET_WM_STRUT_PARTIAL+
 property is used.
 
 Furthermore, the list of assignments (to other workspaces, which may be on
@@ -460,10 +435,10 @@ target workspace is not visible, the window will not be mapped.
 == What happens when an application is started?
 
 i3 does not care about applications. All it notices is when new windows are
-mapped (see `src/handlers.c`, `handle_map_request()`). The window is then
+mapped (see +src/handlers.c+, +handle_map_request()+). The window is then
 reparented (see section "Manage windows").
 
-After reparenting the window, `render_tree()` is called which renders the
+After reparenting the window, +render_tree()+ is called which renders the
 internal layout table. The new window has been placed in the currently focused
 container and therefore the new window and the old windows (if any) need to be
 moved/resized so that the currently active layout (default/stacking/tabbed mode)
@@ -482,7 +457,7 @@ can reconfigure themselves).
 Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
 are handled.
 
-The former calls ``toggle_fullscreen()'' for the specific client which just
+The former calls +toggle_fullscreen()+ for the specific client which just
 configures the client to use the whole screen on which it currently is.
 Also, it is set as fullscreen_client for the i3Screen.
 
@@ -539,7 +514,7 @@ container) to the bottom.
 
 === Rendering the root container
 
-The i3 root container (`con->type == CT_ROOT`) represents the X11 root window.
+The i3 root container (+con->type == CT_ROOT+) represents the X11 root window.
 It contains one child container for every output (like LVDS1, VGA1, …), which
 is available on your computer.
 
@@ -558,7 +533,7 @@ only called for the global fullscreen window.
 
 === Rendering an output
 
-Output containers (`con->layout == L_OUTPUT`) represent a hardware output like
+Output containers (+con->layout == L_OUTPUT+) represent a hardware output like
 LVDS1, VGA1, etc. An output container has three children (at the moment): One
 content container (having workspaces as children) and the top/bottom dock area
 containers.
@@ -566,7 +541,7 @@ containers.
 The rendering happens in the function +render_l_output()+ in the following
 steps:
 
-1. Find the content container (`con->type == CT_CON`)
+1. Find the content container (+con->type == CT_CON+)
 2. Get the currently visible workspace (+con_get_fullscreen_con(content,
    CF_OUTPUT)+).
 3. If there is a fullscreened window on that workspace, directly render it and
@@ -574,22 +549,22 @@ steps:
 4. Sum up the space used by all the dock windows (they have a variable height
    only).
 5. Set the workspace rects (x/y/width/height) based on the position of the
-   output (stored in `con->rect`) and the usable space
-   (`con->rect.{width,height}` without the space used for dock windows).
+   output (stored in +con->rect+) and the usable space
+   (+con->rect.{width,height}+ without the space used for dock windows).
 6. Recursively raise and render the output’s child containers (meaning dock
    area containers and the content container).
 
 === Rendering a workspace or split container
 
 From here on, there really is no difference anymore. All containers are of
-`con->type == CT_CON` (whether workspace or split container) and some of them
-have a `con->window`, meaning they represent an actual window instead of a
++con->type == CT_CON+ (whether workspace or split container) and some of them
+have a +con->window+, meaning they represent an actual window instead of a
 split container.
 
 ==== Default layout
 
 In default layout, containers are placed horizontally or vertically next to
-each other (depending on the `con->orientation`). If a child is a leaf node (as
+each other (depending on the +con->orientation+). If a child is a leaf node (as
 opposed to a split container) and has border style "normal", appropriate space
 will be reserved for its window decoration.
 
@@ -835,8 +810,8 @@ workspace <direction>::
 	the beginning. +
 
 NOTE: Note that you can specify multiple literals in the same line. This has
-	exactly the same effect as if you specified `direction =
-	'next_on_output' -> call cmd_workspace($direction)` and so forth. +
+	exactly the same effect as if you specified +direction =
+	'next_on_output' -> call cmd_workspace($direction)+ and so forth. +
 
 NOTE: Also note that the order of literals is important here: If 'next' were
 	ordered before 'next_on_output', then 'next_on_output' would never
@@ -1020,11 +995,11 @@ Without much ado, here is the list of cases which need to be considered:
 
 == Gotchas
 
-* Forgetting to call `xcb_flush(conn);` after sending a request. This usually
+* Forgetting to call +xcb_flush(conn);+ after sending a request. This usually
   leads to code which looks like it works fine but which does not work under
   certain conditions.
 
-* Forgetting to call `floating_fix_coordinates(con, old_rect, new_rect)` after
+* Forgetting to call +floating_fix_coordinates(con, old_rect, new_rect)+ after
   moving workspaces across outputs. Coordinates for floating containers are
   not relative to workspace boundaries, so you must correct their coordinates
   or those containers will show up in the wrong workspace or not at all.
diff --git a/docs/i3bar-protocol b/docs/i3bar-protocol
index cadcd8a8..c1bf8f80 100644
--- a/docs/i3bar-protocol
+++ b/docs/i3bar-protocol
@@ -103,9 +103,11 @@ https://github.com/i3/i3/blob/next/contrib/trivial-bar-script.sh
 version::
 	The version number (as an integer) of the i3bar protocol you will use.
 stop_signal::
-	Specify to i3bar the signal (as an integer) to send to stop your
-	processing.
-	The default value (if none is specified) is SIGSTOP.
+	Specify the signal (as an integer) that i3bar should send to request that you
+	pause your output. This is used to conserve battery power when the bar is
+	hidden by not unnecessarily computing bar updates. The default value is SIGSTOP,
+	which will unconditionally stop your process. If this is an issue, this feature
+	can be disabled by setting the value to 0.
 cont_signal::
 	Specify to i3bar the signal (as an integer) to send to continue your
 	processing.
@@ -258,6 +260,8 @@ button::
 relative_x, relative_y::
     Coordinates where the click occurred, with respect to the top left corner
     of the block
+output_x, output_y::
+    Coordinates relative to the current output where the click occurred
 width, height::
     Width and height (in px) of the block
 modifiers::
@@ -271,10 +275,12 @@ modifiers::
  "instance": "eth0",
  "button": 1,
  "modifiers": ["Shift", "Mod1"],
- "x": 1320,
+ "x": 1925,
  "y": 1400,
  "relative_x": 12,
  "relative_y": 8,
+ "output_x": 5,
+ "output_y": 1400,
  "width": 50,
  "height": 22
 }
diff --git a/docs/ipc b/docs/ipc
index 5bc40e26..bb8719cb 100644
--- a/docs/ipc
+++ b/docs/ipc
@@ -1,7 +1,7 @@
 IPC interface (interprocess communication)
 ==========================================
 Michael Stapelberg <michael@i3wm.org>
-September 2017
+June 2020
 
 This document describes how to interface with i3 from a separate process. This
 is useful for example to remote-control i3 (to write test cases for example) or
@@ -66,6 +66,7 @@ to do that).
 | 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config.
 | 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload.
 | 11 | +SYNC+ | <<_sync_reply,SYNC>> | Sends an i3 sync event with the specified random value to the specified window.
+| 12 | +GET_BINDING_STATE+ | <<_binding_state_reply,BINDING_STATE>> | Request the current binding state, i.e. the currently active binding mode name.
 |======================================================
 
 So, a typical message could look like this:
@@ -131,6 +132,8 @@ GET_CONFIG (9)::
 	Reply to the GET_CONFIG message.
 TICK (10)::
 	Reply to the SEND_TICK message.
+GET_BINDING_STATE (12)::
+	Reply to the GET_BINDING_STATE message.
 
 [[_command_reply]]
 === COMMAND reply
@@ -158,6 +161,14 @@ sending a reply. Expect the socket to be shut down.
 [{ "success": true }]
 -------------------
 
+When the specified command cannot be parsed, `success` will be false and
+`parse_error` will be true:
+
+*Example:*
+-------------------
+[{ "success": false, "parse_error": true }]
+-------------------
+
 [[_workspaces_reply]]
 === WORKSPACES reply
 
@@ -172,8 +183,8 @@ num (integer)::
 	The logical number of the workspace. Corresponds to the command
 	to switch to this workspace. For named workspaces, this will be -1.
 name (string)::
-	The name of this workspace (by default num+1), as changed by the
-	user. Encoded in UTF-8.
+	The name of this workspace if changed by the user, otherwise defaults
+	to the string representation of the +num+ field). Encoded in UTF-8.
 visible (boolean)::
 	Whether this workspace is currently visible on an output (multiple
 	workspaces can be visible at the same time).
@@ -709,6 +720,17 @@ responded to.
 { "success": true }
 -------------------
 
+[[_binding_state_reply]]
+=== GET_BINDING_STATE reply
+
+The binding_state reply is a map which currently only contains the "name"
+member, which is the name of the currently active binding mode as a string.
+
+*Example:*
+-------------------
+{ "name": "default" }
+-------------------
+
 == Events
 
 [[events]]
diff --git a/docs/refcard.html b/docs/refcard.html
index 6d71c98d..0e3a4121 100644
--- a/docs/refcard.html
+++ b/docs/refcard.html
@@ -15,7 +15,7 @@
 		h1 { font-size: 1.1em; }
 		header a { font-size: 0.7em; }
 		header p { margin: 5px 0; font-size: 0.8em; text-align: left; }
-		kbd { font-family: LinuxBiolinumKeyboard, Linux Biolinum Keyboard O, Linux Biolinum Keyboard, DejaVu Sans Mono, monospace; font-size: 0.9em; }
+		kbd { font-family: LinuxBiolinumKeyboard, Linux Biolinum Keyboard O, Linux Biolinum Keyboard, DejaVu Sans Mono, monospace; font-size: 1.2em; }
 		code { font-family: DejaVu Sans Mono, monospace; font-size: 0.8em; }
 		section { break-inside: avoid-column; -moz-break-inside: -moz-avoid-column; -webkit-break-inside: avoid-column; }
 		h2 { margin: 7px 0 2px; padding: 2px 4px; font-size: 1.1em; font-family: LinuxBiolinum, Linux Biolinum O, Linux Biolinum, sans; background-color: #b3b3b3; }
diff --git a/docs/testsuite b/docs/testsuite
index b535e7c1..145da158 100644
--- a/docs/testsuite
+++ b/docs/testsuite
@@ -120,13 +120,11 @@ tests are run under Xvfb.
 ---------------------------------------
 $ cd ~/i3
 
-$ autoreconf -fi
-
 $ mkdir -p build && cd build
 
-$ ../configure
+$ meson ..
 
-$ make -j8
+$ ninja
 # output omitted because it is very long
 
 $ cd testcases
@@ -183,13 +181,11 @@ You can still use ./testcases/complete-run.pl to get the interactive progress ou
 ---------------------------------------
 $ cd ~/i3
 
-$ autoreconf -fi
-
 $ mkdir -p build && cd build
 
-$ ../configure
+$ meson ..
 
-$ make -j8
+$ ninja
 # output omitted because it is very long
 
 $ make check
diff --git a/docs/userguide b/docs/userguide
index 02130cd9..2ad34184 100644
--- a/docs/userguide
+++ b/docs/userguide
@@ -54,7 +54,7 @@ existing window (rotated displays).
 image:two_terminals.png[Two terminals]
 
 To move the focus between the two terminals, you can use the direction keys
-which you may know from the editor +vi+. However, in i3, your homerow is used
+which you might know from the editor +vi+. However, in i3, your homerow is used
 for these keys (in +vi+, the keys are shifted to the left by one for
 compatibility with most keyboard layouts). Therefore, +$mod+j+ is left, +$mod+k+
 is down, +$mod+l+ is up and `$mod+;` is right. So, to switch between the
@@ -245,7 +245,7 @@ you open a new terminal, it will open below the current one.
 
 So, how can you open a new terminal window to the *right* of the current one?
 The solution is to use +focus parent+, which will focus the +Parent Container+ of
-the current +Container+. In default configuration, use +$mod+a+ to navigate one 
+the current +Container+. In default configuration, use +$mod+a+ to navigate one
 +Container+ up the tree (you can repeat this multiple times until you get to the
 +Workspace Container+). In this case, you would focus the +Vertical Split Container+
 which is *inside* the horizontally oriented workspace. Thus, now new windows will be
@@ -263,7 +263,7 @@ single workspace on which you open three terminal windows. All these terminal
 windows are directly attached to one node inside i3’s layout tree, the
 workspace node. By default, the workspace node’s orientation is +horizontal+.
 
-Now you move one of these terminals down (+$mod+Shift+k+ by default). The
+Now you move one of these terminals down (+$mod+Shift+j+ by default). The
 workspace node’s orientation will be changed to +vertical+. The terminal window
 you moved down is directly attached to the workspace and appears on the bottom
 of the screen. A new (horizontal) container was created to accommodate the
@@ -924,7 +924,7 @@ available outputs by running +xrandr --current+.
 If your X server supports RandR 1.5 or newer, i3 will use RandR monitor objects
 instead of output objects. Run +xrandr --listmonitors+ to see a list. Usually,
 a monitor object contains exactly one output, and has the same name as the
-output; but should that not be the case, you may specify the name of either the
+output; but should that not be the case, you can specify the name of either the
 monitor or the output in i3's configuration. For example, the Dell UP2414Q uses
 two scalers internally, so its output names might be “DP1” and “DP2”, but the
 monitor name is “Dell UP2414Q”.
@@ -1066,7 +1066,7 @@ mouse_warping none
 === Popups during fullscreen mode
 
 When you are in fullscreen mode, some applications still open popup windows
-(take Xpdf for example). This is because these applications may not be aware
+(take Xpdf for example). This is because these applications might not be aware
 that they are in fullscreen mode (they do not check the corresponding hint).
 There are three things which are possible to do in this situation:
 
@@ -1176,9 +1176,9 @@ workspace_auto_back_and_forth yes
 === Delaying urgency hint reset on workspace change
 
 If an application on another workspace sets an urgency hint, switching to this
-workspace may lead to immediate focus of the application, which also means the
+workspace might lead to immediate focus of the application, which also means the
 window decoration color would be immediately reset to +client.focused+. This
-may make it unnecessarily hard to tell which window originally raised the
+might make it unnecessarily hard to tell which window originally raised the
 event.
 
 In order to prevent this, you can tell i3 to delay resetting the urgency state
@@ -1201,9 +1201,9 @@ force_display_urgency_hint 500 ms
 === Focus on window activation
 
 If a window is activated, e.g., via +google-chrome www.google.com+, it may request
-to take focus. Since this may not be preferable, different reactions can be configured.
+to take focus. Since this might not be preferable, different reactions can be configured.
 
-Note that this may not affect windows that are being opened. To prevent new windows
+Note that this might not affect windows that are being opened. To prevent new windows
 from being focused, see <<no_focus>>.
 
 *Syntax*:
@@ -1350,9 +1350,11 @@ mode). The modifier key can be configured using the +modifier+ option.
 The mode option can be changed during runtime through the +bar mode+ command.
 On reload the mode will be reverted to its configured value.
 
-The hide mode maximizes screen space that can be used for actual windows. Also,
-i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to
-save battery power.
+The hide mode maximizes screen space that can be used for actual windows. When
+the bar is hidden, i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the
++status_command+ process in order to conserve battery power. This feature can
+be disabled by the +status_command+ process by setting the appropriate values
+in its JSON header message.
 
 Invisible mode allows to permanently maximize screen space, as the bar is never
 shown. Thus, you can configure i3bar to not disturb you by popping up because
@@ -1413,7 +1415,7 @@ button6::
     Scroll wheel right.
 button7::
     Scroll wheel left.
-    
+
 
 Please note that the old +wheel_up_cmd+ and +wheel_down_cmd+ commands are deprecated
 and will be removed in a future release. We strongly recommend using the more general
@@ -1482,9 +1484,16 @@ options for different outputs by using multiple 'bar' blocks.
 To make a particular i3bar instance handle multiple outputs, specify the output
 directive multiple times.
 
+These output names have a special meaning:
+
+primary::
+    Selects the output that is configured as primary in the X server.
+nonprimary::
+    Selects every output that is not configured as primary in the X server.
+
 *Syntax*:
 ---------------
-output primary|<output>
+output primary|nonprimary|<output>
 ---------------
 
 *Example*:
@@ -1640,7 +1649,7 @@ bar {
 
 By default, the width a workspace button is determined by the width of the text
 showing the workspace name. If the name is too short (say, one letter), then the
-workspace button may look too small.
+workspace button might look too small.
 
 This option specifies the minimum width for workspace buttons. If the name of
 a workspace is too short to cover the button, an additional padding is added on
@@ -1650,7 +1659,7 @@ The default value of zero means that no additional padding is added.
 
 The setting also applies to the current binding mode indicator.
 
-Note that the specified pixels refer to logical pixels, which may translate
+Note that the specified pixels refer to logical pixels, which might translate
 into more pixels on HiDPI displays.
 
 *Syntax*:
@@ -1680,8 +1689,8 @@ When +strip_workspace_name+ is set to +yes+, any workspace that has a name of
 the form "[n][:][NAME]" will display only the number.
 
 The default is to display the full name within the workspace button. Be aware
-that the colon in the workspace name is optional, so `[n][NAME]` will also 
-have the the workspace name and number stripped correctly. 
+that the colon in the workspace name is optional, so `[n][NAME]` will also
+have the workspace name and number stripped correctly.
 
 *Syntax*:
 ------------------------------
@@ -2155,12 +2164,15 @@ Use the +move+ command to move a container.
 # Moves the container into the given direction.
 # The optional pixel argument specifies how far the
 # container should be moved if it is floating and
-# defaults to 10 pixels.
-move <left|right|down|up> [<px> px]
+# defaults to 10 pixels. The optional ppt argument
+# means "percentage points", and if specified it indicates
+# how many points the container should be moved if it is
+# floating rather than by a pixel value.
+move <left|right|down|up> [<amount> [px|ppt]]
 
 # Moves the container to the specified pos_x and pos_y
 # coordinates on the screen.
-move position <pos_x> [px] <pos_y> [px]
+move position <pos_x> [px|ppt] <pos_y> [px|ppt]
 
 # Moves the container to the center of the screen.
 # If 'absolute' is used, it is moved to the center of
@@ -2476,7 +2488,7 @@ for_window [class="urxvt"] resize set 640 480
 === Jumping to specific windows
 
 Often when in a multi-monitor environment, you want to quickly jump to a
-specific window. For example, while working on workspace 3 you may want to
+specific window. For example, while working on workspace 3 you might want to
 jump to your mail client to email your boss that you’ve achieved some
 important goal. Instead of figuring out how to navigate to your mail client,
 it would be more convenient to have a shortcut. You can use the +focus+ command
@@ -2511,7 +2523,7 @@ for this purpose: It lets you input a command and sends the command to i3. It
 can also prefix this command and display a custom prompt for the input dialog.
 
 The additional +--toggle+ option will remove the mark if the window already has
-this mark or add it otherwise. Note that you may need to use this in
+this mark or add it otherwise. Note that you might need to use this in
 combination with +--add+ (see below) as any other marks will otherwise be
 removed.
 
@@ -2610,7 +2622,7 @@ optional pixel argument can be used to specify the border width when switching
 to the normal and pixel styles.
 
 Note that "pixel" refers to logical pixel. On HiDPI displays, a logical pixel
-may be represented by multiple physical pixels, so +pixel 1+ might not
+is represented by multiple physical pixels, so +pixel 1+ might not
 necessarily translate into a single pixel row wide border.
 
 *Syntax*:
@@ -2873,7 +2885,7 @@ See also <<presentations>> for more examples of multi-monitor setups.
 
 === Interesting configuration for multi-monitor environments
 
-There are several things to configure in i3 which may be interesting if you
+There are several things to configure in i3 which might be interesting if you
 have more than one monitor:
 
 1. You can specify which workspace should be put on which screen. This
diff --git a/etc/config b/etc/config
index 2591c187..19cb8c48 100644
--- a/etc/config
+++ b/etc/config
@@ -52,10 +52,11 @@ bindsym Mod1+Return exec i3-sensible-terminal
 bindsym Mod1+Shift+q kill
 
 # start dmenu (a program launcher)
-bindsym Mod1+d exec dmenu_run
-# There also is the (new) i3-dmenu-desktop which only displays applications
-# shipping a .desktop file. It is a wrapper around dmenu, so you need that
-# installed.
+bindsym Mod1+d exec --no-startup-id dmenu_run
+# A more modern dmenu replacement is rofi:
+# bindsym Mod1+d exec rofi -modi drun,run -show drun
+# There also is i3-dmenu-desktop which only displays applications shipping a
+# .desktop file. It is a wrapper around dmenu, so you need that installed.
 # bindsym Mod1+d exec --no-startup-id i3-dmenu-desktop
 
 # change focus
diff --git a/etc/config.keycodes b/etc/config.keycodes
index 951c0a23..aa79901b 100644
--- a/etc/config.keycodes
+++ b/etc/config.keycodes
@@ -46,11 +46,12 @@ bindcode $mod+36 exec i3-sensible-terminal
 bindcode $mod+Shift+24 kill
 
 # start dmenu (a program launcher)
-bindcode $mod+40 exec dmenu_run
-# There also is the (new) i3-dmenu-desktop which only displays applications
-# shipping a .desktop file. It is a wrapper around dmenu, so you need that
-# installed.
-# bindsym $mod+d exec --no-startup-id i3-dmenu-desktop
+bindcode $mod+40 exec --no-startup-id dmenu_run
+# A more modern dmenu replacement is rofi:
+# bindcode $mod+40 exec rofi -modi drun,run -show drun
+# There also is i3-dmenu-desktop which only displays applications shipping a
+# .desktop file. It is a wrapper around dmenu, so you need that installed.
+bindcode $mod+40 exec --no-startup-id i3-dmenu-desktop
 
 # change focus
 bindcode $mod+44 focus left
diff --git a/generate-command-parser.pl b/generate-command-parser.pl
index a017edab..77502db7 100755
--- a/generate-command-parser.pl
+++ b/generate-command-parser.pl
@@ -117,6 +117,7 @@ my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states;
 open(my $enumfh, '>', "GENERATED_${prefix}_enums.h");
 
 my %statenum;
+say $enumfh '#pragma once';
 say $enumfh 'typedef enum {';
 my $cnt = 0;
 for my $state (@keys, '__CALL') {
@@ -131,6 +132,7 @@ close($enumfh);
 # Third step: Generate the call function.
 open(my $callfh, '>', "GENERATED_${prefix}_call.h");
 my $resultname = uc(substr($prefix, 0, 1)) . substr($prefix, 1) . 'ResultIR';
+say $callfh '#pragma once';
 say $callfh "static void GENERATED_call(const int call_identifier, struct $resultname *result) {";
 say $callfh '    switch (call_identifier) {';
 my $call_id = 0;
@@ -206,6 +208,7 @@ close($callfh);
 # Fourth step: Generate the token datastructures.
 
 open(my $tokfh, '>', "GENERATED_${prefix}_tokens.h");
+say $tokfh '#pragma once';
 
 for my $state (@keys) {
     my $tokens = $states{$state};
diff --git a/i3-config-wizard/atoms.xmacro b/i3-config-wizard/atoms.xmacro
deleted file mode 100644
index 8f94ceaa..00000000
--- a/i3-config-wizard/atoms.xmacro
+++ /dev/null
@@ -1,6 +0,0 @@
-xmacro(_NET_WM_NAME)
-xmacro(_NET_WM_WINDOW_TYPE)
-xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
-xmacro(ATOM)
-xmacro(CARDINAL)
-xmacro(UTF8_STRING)
diff --git a/i3-config-wizard/i3-config-wizard-atoms.xmacro.h b/i3-config-wizard/i3-config-wizard-atoms.xmacro.h
new file mode 100644
index 00000000..2d504678
--- /dev/null
+++ b/i3-config-wizard/i3-config-wizard-atoms.xmacro.h
@@ -0,0 +1,8 @@
+// clang-format off
+#define CONFIG_WIZARD_ATOMS_XMACRO		\
+xmacro(_NET_WM_NAME) \
+xmacro(_NET_WM_WINDOW_TYPE) \
+xmacro(_NET_WM_WINDOW_TYPE_DIALOG) \
+xmacro(ATOM) \
+xmacro(CARDINAL) \
+xmacro(UTF8_STRING)
diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c
index f999de30..25117f2b 100644
--- a/i3-config-wizard/main.c
+++ b/i3-config-wizard/main.c
@@ -10,6 +10,8 @@
  */
 #include <config.h>
 
+#include "libi3.h"
+
 #if defined(__FreeBSD__)
 #include <sys/param.h>
 #endif
@@ -23,37 +25,33 @@
 #define _WITH_GETLINE
 #endif
 
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <string.h>
 #include <ctype.h>
-#include <errno.h>
 #include <err.h>
-#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <limits.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
-#include <fcntl.h>
-#include <glob.h>
-#include <assert.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
 #include <xcb/xcb_event.h>
 #include <xcb/xcb_keysyms.h>
-
-#include <xkbcommon/xkbcommon.h>
 #include <xkbcommon/xkbcommon-x11.h>
+#include <xkbcommon/xkbcommon.h>
 
 #define SN_API_NOT_YET_FROZEN 1
 #include <libsn/sn-launchee.h>
 
+#include <X11/XKBlib.h>
 #include <X11/Xlib.h>
 #include <X11/keysym.h>
-#include <X11/XKBlib.h>
+
+#include "i3-config-wizard-atoms.xmacro.h"
 
 /* We need SYSCONFDIR for the path to the keycode config template, so raise an
  * error if it’s not defined for whatever reason */
@@ -69,7 +67,6 @@
 
 #include "xcb.h"
 xcb_visualtype_t *visual_type = NULL;
-#include "libi3.h"
 
 #define TEXT_PADDING logical_px(4)
 #define WIN_POS_X logical_px(490)
@@ -848,7 +845,7 @@ int main(int argc, char *argv[]) {
 /* Place requests for the atoms we need as soon as possible */
 #define xmacro(atom) \
     xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
-#include "atoms.xmacro"
+    CONFIG_WIZARD_ATOMS_XMACRO
 #undef xmacro
 
     /* Init startup notification. */
@@ -905,7 +902,7 @@ int main(int argc, char *argv[]) {
         A_##name = reply->atom;                                                            \
         free(reply);                                                                       \
     } while (0);
-#include "atoms.xmacro"
+    CONFIG_WIZARD_ATOMS_XMACRO
 #undef xmacro
 
     /* Set dock mode */
diff --git a/i3-config-wizard/xcb.h b/i3-config-wizard/xcb.h
index f3e204e8..dc8568a7 100644
--- a/i3-config-wizard/xcb.h
+++ b/i3-config-wizard/xcb.h
@@ -3,6 +3,8 @@
 /* from X11/keysymdef.h */
 #define XCB_NUM_LOCK 0xff7f
 
+#include "i3-config-wizard-atoms.xmacro.h"
+
 #define xmacro(atom) xcb_atom_t A_##atom;
-#include "atoms.xmacro"
+CONFIG_WIZARD_ATOMS_XMACRO
 #undef xmacro
diff --git a/i3-dmenu-desktop b/i3-dmenu-desktop
index 5c34b2bb..07fe8336 100755
--- a/i3-dmenu-desktop
+++ b/i3-dmenu-desktop
@@ -13,6 +13,7 @@ use POSIX qw(locale_h);
 use File::Find;
 use File::Basename qw(basename);
 use File::Temp qw(tempfile);
+use List::Util 'first';
 use Getopt::Long;
 use Pod::Usage;
 use v5.10;
@@ -123,6 +124,8 @@ for my $dir (split(':', $xdg_data_dirs)) {
 # Cleanup the paths, maybe some application does not cope with double slashes
 # (the field code %k is replaced with the .desktop file location).
 @searchdirs = map { s,//,/,g; $_ } @searchdirs;
+# Also remove any trailing slashes.
+@searchdirs = map { s,/*$,,g; $_ } @searchdirs;
 
 # To avoid errors by File::Find’s find(), only pass existing directories.
 @searchdirs = grep { -d $_ } @searchdirs;
@@ -133,8 +136,15 @@ find(
             return unless substr($_, -1 * length('.desktop')) eq '.desktop';
             my $relative = $File::Find::name;
 
-            # + 1 for the trailing /, which is missing in ::topdir.
-            substr($relative, 0, length($File::Find::topdir) + 1) = '';
+            # Find and then replace the directory in @searchdirs in which the
+            # current file is located. We used to do this with
+            # $File::Find::topdir but it is not supported when using the
+            # 'follow' or 'follow_fast' options.
+            # See #3973, #4031.
+            my $topdir = first { substr($relative, 0, length($_)) eq $_ } @searchdirs;
+
+            # + 1 for the trailing /, which is missing in $topdir.
+            substr($relative, 0, length($topdir) + 1) = '';
 
             # Don’t overwrite files with the same relative path, we search in
             # descending order of importance.
diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c
index ba60d396..e58b0c37 100644
--- a/i3-dump-log/main.c
+++ b/i3-dump-log/main.c
@@ -9,27 +9,22 @@
  */
 #include <config.h>
 
-#include <stdio.h>
+#include "libi3.h"
+#include "shmlog.h"
+
+#include <err.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <i3/ipc.h>
+#include <signal.h>
 #include <stdbool.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <errno.h>
-#include <err.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <limits.h>
-#include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <signal.h>
-
-#include "libi3.h"
-#include "shmlog.h"
-#include <i3/ipc.h>
+#include <unistd.h>
 
 #if !defined(__OpenBSD__)
 static uint32_t offset_next_write;
diff --git a/i3-input/keysym2ucs.c b/i3-input/keysym2ucs.c
index 52bdc044..80375099 100644
--- a/i3-input/keysym2ucs.c
+++ b/i3-input/keysym2ucs.c
@@ -31,9 +31,10 @@
  * This software is in the public domain. Share and enjoy!
  */
 
-#include <xcb/xcb.h>
 #include "keysym2ucs.h"
 
+#include <xcb/xcb.h>
+
 struct codepair {
     unsigned short keysym;
     unsigned short ucs;
diff --git a/i3-input/main.c b/i3-input/main.c
index e495fe10..ef9e0701 100644
--- a/i3-input/main.c
+++ b/i3-input/main.c
@@ -8,36 +8,38 @@
  *                  to i3.
  *
  */
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
-#include <sys/types.h>
 #include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
 #include <string.h>
-#include <errno.h>
-#include <err.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <limits.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
-#include <xcb/xcb_event.h>
 #include <xcb/xcb_keysyms.h>
 
 xcb_visualtype_t *visual_type = NULL;
+#include "i3-input.h"
+#include "keysym2ucs.h"
 #include "libi3.h"
 
 #include <X11/keysym.h>
 
-#include "keysym2ucs.h"
-
-#include "i3-input.h"
-
 #define MAX_WIDTH logical_px(500)
 #define BORDER logical_px(2)
 #define PADDING logical_px(2)
 
+/* Exit codes for i3-input:
+ * 0 if i3-input exited successfully and the command was run
+ * 1 if the user canceled input
+ * 2 if i3-input fails for any other reason */
+const int EXIT_OK = 0;
+const int EXIT_CANCEL = 1;
+const int EXIT_ERROR = 2;
+
 /* IPC format string. %s will be replaced with what the user entered, then
  * the command will be sent to i3 */
 static char *format;
@@ -192,11 +194,11 @@ static void finish_input(void) {
     /* prefix the command if a prefix was specified on commandline */
     printf("command = %s\n", full);
 
-    ipc_send_message(sockfd, strlen(full), 0, (uint8_t *)full);
+    int ret = ipc_send_message(sockfd, strlen(full), 0, (uint8_t *)full);
 
     free(full);
 
-    exit(0);
+    exit(ret == 0 ? EXIT_OK : EXIT_ERROR);
 }
 
 /*
@@ -245,7 +247,7 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
         return 1;
     }
     if (sym == XK_Escape) {
-        exit(0);
+        exit(EXIT_CANCEL);
     }
 
     /* TODO: handle all of these? */
@@ -303,7 +305,7 @@ static xcb_rectangle_t get_window_position(void) {
     xcb_intern_atom_reply_t *nswc_reply = xcb_intern_atom_reply(conn, nswc_cookie, NULL);
     if (nswc_reply == NULL) {
         ELOG("Could not intern atom _NET_SUPPORTING_WM_CHECK\n");
-        exit(-1);
+        exit(EXIT_ERROR);
     }
     A__NET_SUPPORTING_WM_CHECK = nswc_reply->atom;
     free(nswc_reply);
@@ -373,9 +375,8 @@ free_resources:
 }
 
 int main(int argc, char *argv[]) {
-    format = sstrdup("%s");
     char *socket_path = NULL;
-    char *pattern = sstrdup("pango:monospace 8");
+    char *pattern = NULL;
     int o, option_index = 0;
 
     static struct option long_options[] = {
@@ -399,7 +400,7 @@ int main(int argc, char *argv[]) {
                 break;
             case 'v':
                 printf("i3-input " I3_VERSION);
-                return 0;
+                return EXIT_OK;
             case 'p':
                 /* This option is deprecated, but will still work in i3 v4.1, 4.2 and 4.3 */
                 fprintf(stderr, "i3-input: WARNING: the -p option is DEPRECATED in favor of the -F (format) option\n");
@@ -427,9 +428,12 @@ int main(int argc, char *argv[]) {
                 printf("\n");
                 printf("Example:\n");
                 printf("    i3-input -F 'workspace \"%%s\"' -P 'Switch to workspace: '\n");
-                return 0;
+                return EXIT_OK;
         }
     }
+    if (!format) {
+        format = "%s";
+    }
 
     printf("using format \"%s\"\n", format);
 
@@ -446,7 +450,7 @@ int main(int argc, char *argv[]) {
     symbols = xcb_key_symbols_alloc(conn);
 
     init_dpi();
-    font = load_font(pattern, true);
+    font = load_font(pattern ? pattern : "pango:monospace 8", true);
     set_font(&font);
 
     if (prompt != NULL)
@@ -495,7 +499,7 @@ int main(int argc, char *argv[]) {
 
     if (reply->status != XCB_GRAB_STATUS_SUCCESS) {
         fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status);
-        exit(-1);
+        exit(EXIT_ERROR);
     }
 
     xcb_flush(conn);
@@ -531,5 +535,5 @@ int main(int argc, char *argv[]) {
     }
 
     draw_util_surface_free(conn, &surface);
-    return 0;
+    return EXIT_OK;
 }
diff --git a/i3-msg/main.c b/i3-msg/main.c
index 3a897416..c1c8bb81 100644
--- a/i3-msg/main.c
+++ b/i3-msg/main.c
@@ -16,27 +16,17 @@
  */
 #include "libi3.h"
 
-#include <stdio.h>
+#include <err.h>
+#include <getopt.h>
+#include <i3/ipc.h>
 #include <stdbool.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <errno.h>
-#include <err.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <limits.h>
+#include <unistd.h>
 
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
-
-#include <xcb/xcb.h>
-#include <xcb/xcb_aux.h>
-
-#include <i3/ipc.h>
 
 /*
  * Having verboselog() and errorlog() is necessary when using libi3.
@@ -199,6 +189,8 @@ int main(int argc, char *argv[]) {
                 message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
             } else if (strcasecmp(optarg, "get_binding_modes") == 0) {
                 message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES;
+            } else if (strcasecmp(optarg, "get_binding_state") == 0) {
+                message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_STATE;
             } else if (strcasecmp(optarg, "get_version") == 0) {
                 message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
             } else if (strcasecmp(optarg, "get_config") == 0) {
@@ -209,7 +201,7 @@ int main(int argc, char *argv[]) {
                 message_type = I3_IPC_MESSAGE_TYPE_SUBSCRIBE;
             } else {
                 printf("Unknown message type\n");
-                printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick, subscribe\n");
+                printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_binding_state, get_version, get_config, send_tick, subscribe\n");
                 exit(EXIT_FAILURE);
             }
         } else if (o == 'q') {
diff --git a/i3-nagbar/atoms.xmacro b/i3-nagbar/atoms.xmacro
deleted file mode 100644
index 333ba2d6..00000000
--- a/i3-nagbar/atoms.xmacro
+++ /dev/null
@@ -1,6 +0,0 @@
-xmacro(_NET_WM_WINDOW_TYPE)
-xmacro(_NET_WM_WINDOW_TYPE_DOCK)
-xmacro(_NET_WM_STRUT_PARTIAL)
-xmacro(I3_SOCKET_PATH)
-xmacro(ATOM)
-xmacro(CARDINAL)
diff --git a/i3-nagbar/i3-nagbar-atoms.xmacro.h b/i3-nagbar/i3-nagbar-atoms.xmacro.h
new file mode 100644
index 00000000..c430b59a
--- /dev/null
+++ b/i3-nagbar/i3-nagbar-atoms.xmacro.h
@@ -0,0 +1,8 @@
+// clang-format off
+#define NAGBAR_ATOMS_XMACRO \
+xmacro(_NET_WM_WINDOW_TYPE) \
+xmacro(_NET_WM_WINDOW_TYPE_DOCK) \
+xmacro(_NET_WM_STRUT_PARTIAL) \
+xmacro(I3_SOCKET_PATH) \
+xmacro(ATOM) \
+xmacro(CARDINAL)
diff --git a/i3-nagbar/i3-nagbar.h b/i3-nagbar/i3-nagbar.h
deleted file mode 100644
index cb672bea..00000000
--- a/i3-nagbar/i3-nagbar.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <config.h>
-
-#include <err.h>
-
-#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
-#define FREE(pointer)   \
-    do {                \
-        free(pointer);  \
-        pointer = NULL; \
-    } while (0)
-
-#define xmacro(atom) xcb_atom_t A_##atom;
-#include "atoms.xmacro"
-#undef xmacro
-
-extern xcb_window_t root;
diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c
index e59f5a6a..b13ee134 100644
--- a/i3-nagbar/main.c
+++ b/i3-nagbar/main.c
@@ -8,35 +8,34 @@
  * when the user has an error in their configuration file.
  *
  */
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
+#include <config.h>
+
+#include "libi3.h"
+
 #include <err.h>
-#include <stdint.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <limits.h>
-#include <fcntl.h>
 #include <paths.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
 
+#include <xcb/randr.h>
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
-#include <xcb/xcb_event.h>
-#include <xcb/randr.h>
 #include <xcb/xcb_cursor.h>
 
 xcb_visualtype_t *visual_type = NULL;
-#include "libi3.h"
 
 #define SN_API_NOT_YET_FROZEN 1
 #include <libsn/sn-launchee.h>
 
-#include "i3-nagbar.h"
+#include "i3-nagbar-atoms.xmacro.h"
 
 #define MSG_PADDING logical_px(8)
 #define BTN_PADDING logical_px(3)
@@ -45,6 +44,12 @@ xcb_visualtype_t *visual_type = NULL;
 #define CLOSE_BTN_GAP logical_px(15)
 #define BAR_BORDER logical_px(2)
 
+#define xmacro(atom) xcb_atom_t A_##atom;
+NAGBAR_ATOMS_XMACRO
+#undef xmacro
+
+#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
+
 static char *argv0 = NULL;
 
 typedef struct {
@@ -314,8 +319,8 @@ static xcb_rectangle_t get_window_position(void) {
     goto free_resources;
 
 free_resources:
-    FREE(res);
-    FREE(primary);
+    free(res);
+    free(primary);
     return result;
 }
 
@@ -423,7 +428,7 @@ int main(int argc, char *argv[]) {
 /* Place requests for the atoms we need as soon as possible */
 #define xmacro(atom) \
     xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
-#include "atoms.xmacro"
+    NAGBAR_ATOMS_XMACRO
 #undef xmacro
 
     /* Init startup notification. */
@@ -505,7 +510,7 @@ int main(int argc, char *argv[]) {
         A_##name = reply->atom;                                                            \
         free(reply);                                                                       \
     } while (0);
-#include "atoms.xmacro"
+    NAGBAR_ATOMS_XMACRO
 #undef xmacro
 
     /* Set dock mode */
@@ -598,7 +603,7 @@ int main(int argc, char *argv[]) {
         free(event);
     }
 
-    FREE(pattern);
+    free(pattern);
     draw_util_surface_free(conn, &bar);
 
     return 0;
diff --git a/i3bar/include/child.h b/i3bar/include/child.h
index 3afed819..adc638be 100644
--- a/i3bar/include/child.h
+++ b/i3bar/include/child.h
@@ -85,4 +85,4 @@ bool child_want_click_events(void);
  * Generates a click event, if enabled.
  *
  */
-void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height, int mods);
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int out_x, int out_y, int width, int height, int mods);
diff --git a/i3bar/include/common.h b/i3bar/include/common.h
index 222f42dd..52f77b0e 100644
--- a/i3bar/include/common.h
+++ b/i3bar/include/common.h
@@ -9,7 +9,6 @@
 
 #include <config.h>
 
-#include <stdbool.h>
 #include <xcb/xcb.h>
 #include <xcb/xproto.h>
 #include "libi3.h"
@@ -78,12 +77,10 @@ struct status_block {
     char *name;
     char *instance;
 
-    TAILQ_ENTRY(status_block)
-    blocks;
+    TAILQ_ENTRY(status_block) blocks;
 };
 
-extern TAILQ_HEAD(statusline_head, status_block)
-    statusline_head;
+extern TAILQ_HEAD(statusline_head, status_block) statusline_head;
 
 #include "child.h"
 #include "ipc.h"
@@ -94,5 +91,4 @@ extern TAILQ_HEAD(statusline_head, status_block)
 #include "trayclients.h"
 #include "xcb.h"
 #include "configuration.h"
-#include "libi3.h"
 #include "parse_json_header.h"
diff --git a/i3bar/include/configuration.h b/i3bar/include/configuration.h
index f1fb9a50..589c6396 100644
--- a/i3bar/include/configuration.h
+++ b/i3bar/include/configuration.h
@@ -29,23 +29,18 @@ typedef struct binding_t {
     char *command;
     bool release;
 
-    TAILQ_ENTRY(binding_t)
-    bindings;
+    TAILQ_ENTRY(binding_t) bindings;
 } binding_t;
 
 typedef struct tray_output_t {
     char *output;
 
-    TAILQ_ENTRY(tray_output_t)
-    tray_outputs;
+    TAILQ_ENTRY(tray_output_t) tray_outputs;
 } tray_output_t;
 
 typedef struct config_t {
     uint32_t modifier;
-
-    TAILQ_HEAD(bindings_head, binding_t)
-    bindings;
-
+    TAILQ_HEAD(bindings_head, binding_t) bindings;
     position_t position;
     bool verbose;
     uint32_t bar_height;
@@ -60,10 +55,7 @@ typedef struct config_t {
     char *command;
     char *fontname;
     i3String *separator_symbol;
-
-    TAILQ_HEAD(tray_outputs_head, tray_output_t)
-    tray_outputs;
-
+    TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
     int tray_padding;
     int num_outputs;
     char **outputs;
diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h
index 5bb19c40..4685e51e 100644
--- a/i3bar/include/outputs.h
+++ b/i3bar/include/outputs.h
@@ -73,6 +73,5 @@ struct i3_output {
     struct ws_head* workspaces;  /* The workspaces on this output */
     struct tc_head* trayclients; /* The tray clients on this output */
 
-    SLIST_ENTRY(i3_output)
-    slist; /* Pointer for the SLIST-Macro */
+    SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
 };
diff --git a/i3bar/include/trayclients.h b/i3bar/include/trayclients.h
index 3f215ce4..bcf55959 100644
--- a/i3bar/include/trayclients.h
+++ b/i3bar/include/trayclients.h
@@ -21,6 +21,5 @@ struct trayclient {
     char *class_class;
     char *class_instance;
 
-    TAILQ_ENTRY(trayclient)
-    tailq; /* Pointer for the TAILQ-Macro */
+    TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */
 };
diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h
index 0ef5c0a9..ff61450c 100644
--- a/i3bar/include/workspaces.h
+++ b/i3bar/include/workspaces.h
@@ -41,6 +41,5 @@ struct i3_ws {
     rect rect;                /* The rect of the ws (not used (yet)) */
     struct i3_output *output; /* The current output of the ws */
 
-    TAILQ_ENTRY(i3_ws)
-    tailq; /* Pointer for the TAILQ-Macro */
+    TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */
 };
diff --git a/i3bar/src/child.c b/i3bar/src/child.c
index e56145fa..bece314f 100644
--- a/i3bar/src/child.c
+++ b/i3bar/src/child.c
@@ -10,25 +10,21 @@
 #include "common.h"
 #include "yajl_utils.h"
 
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
 #include <err.h>
+#include <errno.h>
 #include <ev.h>
-#include <yajl/yajl_common.h>
-#include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
-#include <yajl/yajl_gen.h>
+#include <fcntl.h>
 #include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
 
-#include <xcb/xcb_keysyms.h>
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
 
 /* Global variables for child_*() */
 i3bar_child child;
@@ -638,7 +634,7 @@ static void child_click_events_initialize(void) {
  * Generates a click event, if enabled.
  *
  */
-void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height, int mods) {
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int out_x, int out_y, int width, int height, int mods) {
     if (!child.click_events) {
         return;
     }
@@ -690,6 +686,12 @@ void send_block_clicked(int button, const char *name, const char *instance, int
     ystr("relative_y");
     yajl_gen_integer(gen, y_rel);
 
+    ystr("output_x");
+    yajl_gen_integer(gen, out_x);
+
+    ystr("output_y");
+    yajl_gen_integer(gen, out_y);
+
     ystr("width");
     yajl_gen_integer(gen, width);
 
diff --git a/i3bar/src/config.c b/i3bar/src/config.c
index 11d8e0a2..c9627790 100644
--- a/i3bar/src/config.c
+++ b/i3bar/src/config.c
@@ -9,15 +9,12 @@
  */
 #include "common.h"
 
-#include <string.h>
-#include <stdlib.h>
 #include <stdio.h>
-#include <errno.h>
-#include <i3/ipc.h>
-#include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <X11/Xlib.h>
+#include <yajl/yajl_parse.h>
 
 config_t config;
 static char *cur_key;
diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c
index 37bdbac2..f2c105ac 100644
--- a/i3bar/src/ipc.c
+++ b/i3bar/src/ipc.c
@@ -9,16 +9,13 @@
  */
 #include "common.h"
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <string.h>
 #include <errno.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <i3/ipc.h>
 #include <ev.h>
+#include <i3/ipc.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #ifdef I3_ASAN_ENABLED
 #include <sanitizer/lsan_interface.h>
 #endif
diff --git a/i3bar/src/main.c b/i3bar/src/main.c
index 65cb00ff..4e729341 100644
--- a/i3bar/src/main.c
+++ b/i3bar/src/main.c
@@ -7,15 +7,13 @@
  */
 #include "common.h"
 
-#include <stdio.h>
-#include <i3/ipc.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
 #include <ev.h>
 #include <getopt.h>
 #include <glob.h>
+#include <i3/ipc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 struct ev_loop *main_loop;
 
diff --git a/i3bar/src/mode.c b/i3bar/src/mode.c
index 97087ce0..13d02110 100644
--- a/i3bar/src/mode.c
+++ b/i3bar/src/mode.c
@@ -9,12 +9,10 @@
  */
 #include "common.h"
 
-#include <string.h>
 #include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
+#include <string.h>
+
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
 
 /* A datatype to pass through the callbacks to save the state */
 struct mode_json_params {
diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c
index 6ebb7cc6..168f3eef 100644
--- a/i3bar/src/outputs.c
+++ b/i3bar/src/outputs.c
@@ -9,13 +9,12 @@
  */
 #include "common.h"
 
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
 #include <errno.h>
-#include <i3/ipc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
 
 /* A datatype to pass through the callbacks to save the state */
 struct outputs_json_params {
@@ -193,11 +192,12 @@ static int outputs_end_map_cb(void *params_) {
 
     /* See if we actually handle that output */
     if (config.num_outputs > 0) {
+        const bool is_primary = params->outputs_walk->primary;
         bool handle_output = false;
         for (int c = 0; c < config.num_outputs; c++) {
-            if (strcasecmp(params->outputs_walk->name, config.outputs[c]) == 0 ||
-                (strcasecmp(config.outputs[c], "primary") == 0 &&
-                 params->outputs_walk->primary)) {
+            if ((strcasecmp(params->outputs_walk->name, config.outputs[c]) == 0) ||
+                (strcasecmp(config.outputs[c], "primary") == 0 && is_primary) ||
+                (strcasecmp(config.outputs[c], "nonprimary") == 0 && !is_primary)) {
                 handle_output = true;
                 break;
             }
diff --git a/i3bar/src/parse_json_header.c b/i3bar/src/parse_json_header.c
index 3d4c2a67..c74a62fe 100644
--- a/i3bar/src/parse_json_header.c
+++ b/i3bar/src/parse_json_header.c
@@ -10,22 +10,10 @@
  */
 #include "common.h"
 
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
 #include <signal.h>
-#include <stdio.h>
-#include <fcntl.h>
 #include <string.h>
-#include <errno.h>
-#include <err.h>
-#include <ev.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <yajl/yajl_common.h>
+
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
 
 static enum {
     KEY_VERSION,
diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c
index 68686611..bd56f5d0 100644
--- a/i3bar/src/workspaces.c
+++ b/i3bar/src/workspaces.c
@@ -9,12 +9,10 @@
  */
 #include "common.h"
 
-#include <string.h>
 #include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
+#include <string.h>
+
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
 
 /* A datatype to pass through the callbacks to save the state */
 struct workspaces_json_params {
diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c
index 15c6f82f..89b34102 100644
--- a/i3bar/src/xcb.c
+++ b/i3bar/src/xcb.c
@@ -9,26 +9,15 @@
  */
 #include "common.h"
 
-#include <xcb/xcb.h>
-#include <xcb/xkb.h>
-#include <xcb/xproto.h>
-#include <xcb/xcb_aux.h>
-#include <xcb/xcb_cursor.h>
-
-#include <stdio.h>
+#include <err.h>
+#include <ev.h>
+#include <i3/ipc.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <string.h>
-#include <i3/ipc.h>
-#include <ev.h>
-#include <errno.h>
-#include <limits.h>
-#include <err.h>
 
-#include <X11/Xlib.h>
-#include <X11/XKBlib.h>
-#include <X11/extensions/XKB.h>
+#include <xcb/xcb_aux.h>
+#include <xcb/xcb_cursor.h>
+#include <xcb/xkb.h>
 
 #ifdef I3_ASAN_ENABLED
 #include <sanitizer/lsan_interface.h>
@@ -454,7 +443,7 @@ static bool execute_custom_command(xcb_keycode_t input_code, bool event_is_relea
     return false;
 }
 
-static void child_handle_button(xcb_button_press_event_t *event, i3_output *output, uint32_t statusline_x) {
+static void child_handle_button(xcb_button_press_event_t *event, i3_output *output, uint32_t statusline_x, uint32_t statusline_y) {
     if (statusline_x > (uint32_t)output->statusline_width) {
         return;
     }
@@ -483,9 +472,13 @@ static void child_handle_button(xcb_button_press_event_t *event, i3_output *outp
         /* x of the click event relative to the current block. */
         const uint32_t relative_x = statusline_x - last_block_x;
         if (relative_x <= full_render_width) {
+            const uint32_t output_x = event->event_x;
+            const uint32_t output_y = statusline_y + event->event_y;
+
             send_block_clicked(event->detail, block->name, block->instance,
                                event->root_x, event->root_y, relative_x,
-                               event->event_y, full_render_width, bar_height,
+                               event->event_y, output_x, output_y,
+                               full_render_width, bar_height,
                                event->state);
             return;
         }
@@ -553,9 +546,9 @@ static void handle_button(xcb_button_press_event_t *event) {
         /* Calculate the horizontal coordinate (x) of the start of the
          * statusline by subtracting its width and the width of the tray from
          * the bar width. */
-        const int offset = walk->rect.w - walk->statusline_width -
-                           tray_width - logical_px((tray_width > 0) * sb_hoff_px);
-        if (x >= offset) {
+        const int offset_x = walk->rect.w - walk->statusline_width -
+                             tray_width - logical_px((tray_width > 0) * sb_hoff_px);
+        if (x >= offset_x) {
             /* Click was after the start of the statusline, return to avoid
              * executing any other actions even if a click event is not
              * produced eventually. */
@@ -563,8 +556,10 @@ static void handle_button(xcb_button_press_event_t *event) {
             if (!event_is_release) {
                 /* x of the click event relative to the start of the
                  * statusline. */
-                const uint32_t statusline_x = x - offset;
-                child_handle_button(event, walk, statusline_x);
+                const uint32_t statusline_x = x - offset_x;
+                const uint32_t statusline_y = event->root_y - walk->rect.y - event->event_y;
+
+                child_handle_button(event, walk, statusline_x, statusline_y);
             }
 
             return;
@@ -1628,15 +1623,18 @@ void kick_tray_clients(i3_output *output) {
  *
  */
 void destroy_window(i3_output *output) {
-    if (output == NULL) {
-        return;
-    }
-    if (output->bar.id == XCB_NONE) {
+    if (output == NULL || output->bar.id == XCB_NONE) {
         return;
     }
 
     kick_tray_clients(output);
+
+    draw_util_surface_free(xcb_connection, &(output->bar));
+    draw_util_surface_free(xcb_connection, &(output->buffer));
+    draw_util_surface_free(xcb_connection, &(output->statusline_buffer));
     xcb_destroy_window(xcb_connection, output->bar.id);
+    xcb_free_pixmap(xcb_connection, output->buffer.id);
+    xcb_free_pixmap(xcb_connection, output->statusline_buffer.id);
     output->bar.id = XCB_NONE;
 }
 
diff --git a/include/all.h b/include/all.h
index aa2b5b25..55111944 100644
--- a/include/all.h
+++ b/include/all.h
@@ -15,31 +15,24 @@
 #include <config.h>
 
 #include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
 #include <stdbool.h>
-#include <stdlib.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <locale.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <glob.h>
-#include <errno.h>
-#include <err.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <math.h>
-#include <limits.h>
 
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
-#include <xcb/xcb_keysyms.h>
 #include <xcb/xcb_icccm.h>
+#include <xcb/xcb_keysyms.h>
 
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_version.h>
-
+#include "libi3.h"
 #include "data.h"
 #include "util.h"
 #include "ipc.h"
@@ -72,7 +65,6 @@
 #include "ewmh.h"
 #include "assignments.h"
 #include "regex.h"
-#include "libi3.h"
 #include "startup.h"
 #include "scratchpad.h"
 #include "commands.h"
diff --git a/include/atoms.xmacro b/include/atoms.xmacro
deleted file mode 100644
index 730e569a..00000000
--- a/include/atoms.xmacro
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "atoms_NET_SUPPORTED.xmacro"
-#include "atoms_rest.xmacro"
diff --git a/include/atoms_NET_SUPPORTED.xmacro b/include/atoms_NET_SUPPORTED.xmacro
deleted file mode 100644
index a81948a9..00000000
--- a/include/atoms_NET_SUPPORTED.xmacro
+++ /dev/null
@@ -1,35 +0,0 @@
-xmacro(_NET_SUPPORTED)
-xmacro(_NET_SUPPORTING_WM_CHECK)
-xmacro(_NET_WM_NAME)
-xmacro(_NET_WM_VISIBLE_NAME)
-xmacro(_NET_WM_MOVERESIZE)
-xmacro(_NET_WM_STATE_STICKY)
-xmacro(_NET_WM_STATE_FULLSCREEN)
-xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
-xmacro(_NET_WM_STATE_MODAL)
-xmacro(_NET_WM_STATE_HIDDEN)
-xmacro(_NET_WM_STATE_FOCUSED)
-xmacro(_NET_WM_STATE)
-xmacro(_NET_WM_WINDOW_TYPE)
-xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
-xmacro(_NET_WM_WINDOW_TYPE_DOCK)
-xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
-xmacro(_NET_WM_WINDOW_TYPE_UTILITY)
-xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
-xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
-xmacro(_NET_WM_WINDOW_TYPE_MENU)
-xmacro(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
-xmacro(_NET_WM_WINDOW_TYPE_POPUP_MENU)
-xmacro(_NET_WM_WINDOW_TYPE_TOOLTIP)
-xmacro(_NET_WM_WINDOW_TYPE_NOTIFICATION)
-xmacro(_NET_WM_DESKTOP)
-xmacro(_NET_WM_STRUT_PARTIAL)
-xmacro(_NET_CLIENT_LIST)
-xmacro(_NET_CLIENT_LIST_STACKING)
-xmacro(_NET_CURRENT_DESKTOP)
-xmacro(_NET_NUMBER_OF_DESKTOPS)
-xmacro(_NET_DESKTOP_NAMES)
-xmacro(_NET_DESKTOP_VIEWPORT)
-xmacro(_NET_ACTIVE_WINDOW)
-xmacro(_NET_CLOSE_WINDOW)
-xmacro(_NET_MOVERESIZE_WINDOW)
diff --git a/include/atoms_rest.xmacro b/include/atoms_rest.xmacro
deleted file mode 100644
index b65a81d8..00000000
--- a/include/atoms_rest.xmacro
+++ /dev/null
@@ -1,20 +0,0 @@
-xmacro(_NET_WM_USER_TIME)
-xmacro(_NET_STARTUP_ID)
-xmacro(_NET_WORKAREA)
-xmacro(WM_PROTOCOLS)
-xmacro(WM_DELETE_WINDOW)
-xmacro(UTF8_STRING)
-xmacro(WM_STATE)
-xmacro(WM_CLIENT_LEADER)
-xmacro(WM_TAKE_FOCUS)
-xmacro(WM_WINDOW_ROLE)
-xmacro(I3_SOCKET_PATH)
-xmacro(I3_CONFIG_PATH)
-xmacro(I3_SYNC)
-xmacro(I3_SHMLOG_PATH)
-xmacro(I3_PID)
-xmacro(I3_FLOATING_WINDOW)
-xmacro(_NET_REQUEST_FRAME_EXTENTS)
-xmacro(_NET_FRAME_EXTENTS)
-xmacro(_MOTIF_WM_HINTS)
-xmacro(WM_CHANGE_STATE)
diff --git a/include/commands.h b/include/commands.h
index de066e45..4b2f99bc 100644
--- a/include/commands.h
+++ b/include/commands.h
@@ -219,10 +219,10 @@ void cmd_fullscreen(I3_CMD, const char *action, const char *fullscreen_mode);
 void cmd_sticky(I3_CMD, const char *action);
 
 /**
- * Implementation of 'move <direction> [<pixels> [px]]'.
+ * Implementation of 'move <direction> [<amount> [px|ppt]]'.
  *
  */
-void cmd_move_direction(I3_CMD, const char *direction_str, long move_px);
+void cmd_move_direction(I3_CMD, const char *direction_str, long amount, const char *mode);
 
 /**
  * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
@@ -267,10 +267,10 @@ void cmd_open(I3_CMD);
 void cmd_focus_output(I3_CMD, const char *name);
 
 /**
- * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
+ * Implementation of 'move [window|container] [to] [absolute] position [<pos_x> [px|ppt] <pos_y> [px|ppt]]
  *
  */
-void cmd_move_window_to_position(I3_CMD, long x, long y);
+void cmd_move_window_to_position(I3_CMD, long x, const char *mode_x, long y, const char *mode_y);
 
 /**
  * Implementation of 'move [window|container] [to] [absolute] position center
@@ -315,10 +315,16 @@ void cmd_title_format(I3_CMD, const char *format);
 void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name);
 
 /**
- * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
+ * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
  *
  */
-void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id);
+void cmd_bar_mode(I3_CMD, const char *bar_mode, const char *bar_id);
+
+/**
+ * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
+ *
+ */
+void cmd_bar_hidden_state(I3_CMD, const char *bar_hidden_state, const char *bar_id);
 
 /**
  * Implementation of 'shmlog <size>|toggle|on|off'
diff --git a/include/configuration.h b/include/configuration.h
index cd5daa8e..eff28e6b 100644
--- a/include/configuration.h
+++ b/include/configuration.h
@@ -12,9 +12,6 @@
  */
 #pragma once
 
-#include "libi3.h"
-
-#include <stdbool.h>
 #include "queue.h"
 #include "i3.h"
 
@@ -69,8 +66,7 @@ struct Variable {
     char *value;
     char *next_match;
 
-    SLIST_ENTRY(Variable)
-    variables;
+    SLIST_ENTRY(Variable) variables;
 };
 
 /**
@@ -84,8 +80,7 @@ struct Mode {
     bool pango_markup;
     struct bindings_head *bindings;
 
-    SLIST_ENTRY(Mode)
-    modes;
+    SLIST_ENTRY(Mode) modes;
 };
 
 /**
@@ -284,8 +279,7 @@ struct Barconfig {
     /* List of outputs on which the tray is allowed to be shown, in order.
      * The special value "none" disables it (per default, it will be shown) and
      * the special value "primary" enabled it on the primary output. */
-    TAILQ_HEAD(tray_outputs_head, tray_output_t)
-    tray_outputs;
+    TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
 
     /* Padding around the tray icons. */
     int tray_padding;
@@ -307,8 +301,7 @@ struct Barconfig {
     /** Bar modifier (to show bar when in hide mode). */
     uint32_t modifier;
 
-    TAILQ_HEAD(bar_bindings_head, Barbinding)
-    bar_bindings;
+    TAILQ_HEAD(bar_bindings_head, Barbinding) bar_bindings;
 
     /** Bar position (bottom by default). */
     enum { P_BOTTOM = 0,
@@ -385,8 +378,7 @@ struct Barconfig {
         char *binding_mode_text;
     } colors;
 
-    TAILQ_ENTRY(Barconfig)
-    configs;
+    TAILQ_ENTRY(Barconfig) configs;
 };
 
 /**
@@ -404,15 +396,13 @@ struct Barbinding {
     /** If true, the command will be executed after the button is released. */
     bool release;
 
-    TAILQ_ENTRY(Barbinding)
-    bindings;
+    TAILQ_ENTRY(Barbinding) bindings;
 };
 
 struct tray_output_t {
     char *output;
 
-    TAILQ_ENTRY(tray_output_t)
-    tray_outputs;
+    TAILQ_ENTRY(tray_output_t) tray_outputs;
 };
 
 typedef enum {
@@ -440,9 +430,3 @@ bool load_configuration(const char *override_configfile, config_load_t load_type
  *
  */
 void ungrab_all_keys(xcb_connection_t *conn);
-
-/**
- * Sends the current bar configuration as an event to all barconfig_update listeners.
- *
- */
-void update_barconfig(void);
diff --git a/include/data.h b/include/data.h
index 2dfb86cd..9ea09a8c 100644
--- a/include/data.h
+++ b/include/data.h
@@ -9,13 +9,10 @@
  */
 #pragma once
 
-#include "libi3.h"
-
 #define SN_API_NOT_YET_FROZEN 1
 #include <libsn/sn-launcher.h>
 
 #include <xcb/randr.h>
-#include <stdbool.h>
 #include <pcre.h>
 #include <sys/time.h>
 
@@ -228,8 +225,7 @@ struct Workspace_Assignment {
     char *output;
     gaps_t gaps;
 
-    TAILQ_ENTRY(Workspace_Assignment)
-    ws_assignments;
+    TAILQ_ENTRY(Workspace_Assignment) ws_assignments;
 };
 
 struct Ignore_Event {
@@ -237,8 +233,7 @@ struct Ignore_Event {
     int response_type;
     time_t added;
 
-    SLIST_ENTRY(Ignore_Event)
-    ignore_events;
+    SLIST_ENTRY(Ignore_Event) ignore_events;
 };
 
 /**
@@ -257,8 +252,7 @@ struct Startup_Sequence {
      * completed) */
     time_t delete_at;
 
-    TAILQ_ENTRY(Startup_Sequence)
-    sequences;
+    TAILQ_ENTRY(Startup_Sequence) sequences;
 };
 
 /**
@@ -284,9 +278,7 @@ struct regex {
 struct Binding_Keycode {
     xcb_keycode_t keycode;
     i3_event_state_mask_t modifiers;
-
-    TAILQ_ENTRY(Binding_Keycode)
-    keycodes;
+    TAILQ_ENTRY(Binding_Keycode) keycodes;
 };
 
 /******************************************************************************
@@ -347,14 +339,12 @@ struct Binding {
     /** Only in use if symbol != NULL. Contains keycodes which generate the
      * specified symbol. Useful for unbinding and checking which binding was
      * used when a key press event comes in. */
-    TAILQ_HEAD(keycodes_head, Binding_Keycode)
-    keycodes_head;
+    TAILQ_HEAD(keycodes_head, Binding_Keycode) keycodes_head;
 
     /** Command, like in command mode */
     char *command;
 
-    TAILQ_ENTRY(Binding)
-    bindings;
+    TAILQ_ENTRY(Binding) bindings;
 };
 
 /**
@@ -370,19 +360,13 @@ struct Autostart {
     /** no_startup_id flag for start_application(). Determines whether a
      * startup notification context/ID should be created. */
     bool no_startup_id;
-
-    TAILQ_ENTRY(Autostart)
-    autostarts;
-
-    TAILQ_ENTRY(Autostart)
-    autostarts_always;
+    TAILQ_ENTRY(Autostart) autostarts;
+    TAILQ_ENTRY(Autostart) autostarts_always;
 };
 
 struct output_name {
     char *name;
-
-    SLIST_ENTRY(output_name)
-    names;
+    SLIST_ENTRY(output_name) names;
 };
 
 /**
@@ -409,8 +393,7 @@ struct xoutput {
     /** List of names for the output.
      * An output always has at least one name; the first name is
      * considered the primary one. */
-    SLIST_HEAD(names_head, output_name)
-    names_head;
+    SLIST_HEAD(names_head, output_name) names_head;
 
     /** Pointer to the Con which represents this output */
     Con *con;
@@ -418,8 +401,7 @@ struct xoutput {
     /** x, y, width, height */
     Rect rect;
 
-    TAILQ_ENTRY(xoutput)
-    outputs;
+    TAILQ_ENTRY(xoutput) outputs;
 };
 
 /**
@@ -513,6 +495,9 @@ struct Window {
     /* Time when the window became managed. Used to determine whether a window
      * should be swallowed after initial management. */
     time_t managed_since;
+
+    /* The window has been swallowed. */
+    bool swallowed;
 };
 
 /**
@@ -570,8 +555,7 @@ struct Match {
            M_ASSIGN_WS,
            M_BELOW } insert_where;
 
-    TAILQ_ENTRY(Match)
-    matches;
+    TAILQ_ENTRY(Match) matches;
 
     /* Whether this match was generated when restarting i3 inplace.
      * Leads to not setting focus when managing a new window, because the old
@@ -618,8 +602,7 @@ struct Assignment {
         char *output;
     } dest;
 
-    TAILQ_ENTRY(Assignment)
-    assignments;
+    TAILQ_ENTRY(Assignment) assignments;
 };
 
 /** Fullscreen modes. Used by Con.fullscreen_mode. */
@@ -630,8 +613,7 @@ typedef enum { CF_NONE = 0,
 struct mark_t {
     char *name;
 
-    TAILQ_ENTRY(mark_t)
-    marks;
+    TAILQ_ENTRY(mark_t) marks;
 };
 
 /**
@@ -698,8 +680,7 @@ struct Con {
     char *sticky_group;
 
     /* user-definable marks to jump to this container later */
-    TAILQ_HEAD(marks_head, mark_t)
-    marks_head;
+    TAILQ_HEAD(marks_head, mark_t) marks_head;
     /* cached to decide whether a redraw is needed */
     bool mark_changed;
 
@@ -718,17 +699,12 @@ struct Con {
     struct deco_render_params *deco_render_params;
 
     /* Only workspace-containers can have floating clients */
-    TAILQ_HEAD(floating_head, Con)
-    floating_head;
-
-    TAILQ_HEAD(nodes_head, Con)
-    nodes_head;
+    TAILQ_HEAD(floating_head, Con) floating_head;
 
-    TAILQ_HEAD(focus_head, Con)
-    focus_head;
+    TAILQ_HEAD(nodes_head, Con) nodes_head;
+    TAILQ_HEAD(focus_head, Con) focus_head;
 
-    TAILQ_HEAD(swallow_head, Match)
-    swallow_head;
+    TAILQ_HEAD(swallow_head, Match) swallow_head;
 
     fullscreen_mode_t fullscreen_mode;
 
@@ -766,17 +742,10 @@ struct Con {
         FLOATING_USER_ON = 3
     } floating;
 
-    TAILQ_ENTRY(Con)
-    nodes;
-
-    TAILQ_ENTRY(Con)
-    focused;
-
-    TAILQ_ENTRY(Con)
-    all_cons;
-
-    TAILQ_ENTRY(Con)
-    floating_windows;
+    TAILQ_ENTRY(Con) nodes;
+    TAILQ_ENTRY(Con) focused;
+    TAILQ_ENTRY(Con) all_cons;
+    TAILQ_ENTRY(Con) floating_windows;
 
     /** callbacks */
     void (*on_remove_child)(Con *);
diff --git a/include/floating.h b/include/floating.h
index 8e60f78c..612874fc 100644
--- a/include/floating.h
+++ b/include/floating.h
@@ -25,7 +25,7 @@ typedef enum { BORDER_LEFT = (1 << 0),
  * floating_windows list of the workspace.
  *
  */
-void floating_enable(Con *con, bool automatic);
+bool floating_enable(Con *con, bool automatic);
 
 /**
  * Disables floating mode for the given container by re-attaching the container
diff --git a/include/handlers.h b/include/handlers.h
index d2c79c59..81012e7b 100644
--- a/include/handlers.h
+++ b/include/handlers.h
@@ -47,22 +47,3 @@ void handle_event(int type, xcb_generic_event_t *event);
  *
  */
 void property_handlers_init(void);
-
-#if 0
-/**
- * Configuration notifies are only handled because we need to set up ignore
- * for the following enter notify events
- *
- */
-int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event);
-#endif
-
-#if 0
-/**
- * Handles _NET_WM_WINDOW_TYPE changes
- *
- */
-int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state,
-                       xcb_window_t window, xcb_atom_t atom,
-                       xcb_get_property_reply_t *property);
-#endif
diff --git a/include/i3-atoms_NET_SUPPORTED.xmacro.h b/include/i3-atoms_NET_SUPPORTED.xmacro.h
new file mode 100644
index 00000000..b491da98
--- /dev/null
+++ b/include/i3-atoms_NET_SUPPORTED.xmacro.h
@@ -0,0 +1,37 @@
+// clang-format off
+#define I3_NET_SUPPORTED_ATOMS_XMACRO \
+xmacro(_NET_SUPPORTED) \
+xmacro(_NET_SUPPORTING_WM_CHECK) \
+xmacro(_NET_WM_NAME) \
+xmacro(_NET_WM_VISIBLE_NAME) \
+xmacro(_NET_WM_MOVERESIZE) \
+xmacro(_NET_WM_STATE_STICKY) \
+xmacro(_NET_WM_STATE_FULLSCREEN) \
+xmacro(_NET_WM_STATE_DEMANDS_ATTENTION) \
+xmacro(_NET_WM_STATE_MODAL) \
+xmacro(_NET_WM_STATE_HIDDEN) \
+xmacro(_NET_WM_STATE_FOCUSED) \
+xmacro(_NET_WM_STATE) \
+xmacro(_NET_WM_WINDOW_TYPE) \
+xmacro(_NET_WM_WINDOW_TYPE_NORMAL) \
+xmacro(_NET_WM_WINDOW_TYPE_DOCK) \
+xmacro(_NET_WM_WINDOW_TYPE_DIALOG) \
+xmacro(_NET_WM_WINDOW_TYPE_UTILITY) \
+xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR) \
+xmacro(_NET_WM_WINDOW_TYPE_SPLASH) \
+xmacro(_NET_WM_WINDOW_TYPE_MENU) \
+xmacro(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU) \
+xmacro(_NET_WM_WINDOW_TYPE_POPUP_MENU) \
+xmacro(_NET_WM_WINDOW_TYPE_TOOLTIP) \
+xmacro(_NET_WM_WINDOW_TYPE_NOTIFICATION) \
+xmacro(_NET_WM_DESKTOP) \
+xmacro(_NET_WM_STRUT_PARTIAL) \
+xmacro(_NET_CLIENT_LIST) \
+xmacro(_NET_CLIENT_LIST_STACKING) \
+xmacro(_NET_CURRENT_DESKTOP) \
+xmacro(_NET_NUMBER_OF_DESKTOPS) \
+xmacro(_NET_DESKTOP_NAMES) \
+xmacro(_NET_DESKTOP_VIEWPORT) \
+xmacro(_NET_ACTIVE_WINDOW) \
+xmacro(_NET_CLOSE_WINDOW) \
+xmacro(_NET_MOVERESIZE_WINDOW)
diff --git a/include/i3-atoms_rest.xmacro.h b/include/i3-atoms_rest.xmacro.h
new file mode 100644
index 00000000..75a5f230
--- /dev/null
+++ b/include/i3-atoms_rest.xmacro.h
@@ -0,0 +1,22 @@
+// clang-format off
+#define I3_REST_ATOMS_XMACRO \
+xmacro(_NET_WM_USER_TIME) \
+xmacro(_NET_STARTUP_ID) \
+xmacro(_NET_WORKAREA) \
+xmacro(WM_PROTOCOLS) \
+xmacro(WM_DELETE_WINDOW) \
+xmacro(UTF8_STRING) \
+xmacro(WM_STATE) \
+xmacro(WM_CLIENT_LEADER) \
+xmacro(WM_TAKE_FOCUS) \
+xmacro(WM_WINDOW_ROLE) \
+xmacro(I3_SOCKET_PATH) \
+xmacro(I3_CONFIG_PATH) \
+xmacro(I3_SYNC) \
+xmacro(I3_SHMLOG_PATH) \
+xmacro(I3_PID) \
+xmacro(I3_FLOATING_WINDOW) \
+xmacro(_NET_REQUEST_FRAME_EXTENTS) \
+xmacro(_NET_FRAME_EXTENTS) \
+xmacro(_MOTIF_WM_HINTS) \
+xmacro(WM_CHANGE_STATE)
diff --git a/include/i3.h b/include/i3.h
index aef34715..2c550fa9 100644
--- a/include/i3.h
+++ b/include/i3.h
@@ -57,7 +57,8 @@ extern xcb_key_symbols_t *keysyms;
 extern char **start_argv;
 extern Display *xlibdpy, *xkbdpy;
 extern int xkb_current_group;
-extern TAILQ_HEAD(bindings_head, Binding) * bindings;
+extern TAILQ_HEAD(bindings_head, Binding) *bindings;
+extern const char *current_binding_mode;
 extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
 extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
 extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;
diff --git a/include/i3/ipc.h b/include/i3/ipc.h
index 884a0cf6..187640cd 100644
--- a/include/i3/ipc.h
+++ b/include/i3/ipc.h
@@ -66,6 +66,9 @@ typedef struct i3_ipc_header {
 /** Trigger an i3 sync protocol message via IPC. */
 #define I3_IPC_MESSAGE_TYPE_SYNC 11
 
+/** Request the current binding state. */
+#define I3_IPC_MESSAGE_TYPE_GET_BINDING_STATE 12
+
 /*
  * Messages from i3 to clients
  *
@@ -82,6 +85,7 @@ typedef struct i3_ipc_header {
 #define I3_IPC_REPLY_TYPE_CONFIG 9
 #define I3_IPC_REPLY_TYPE_TICK 10
 #define I3_IPC_REPLY_TYPE_SYNC 11
+#define I3_IPC_REPLY_TYPE_GET_BINDING_STATE 12
 
 /*
  * Events from i3 to clients. Events have the first bit set high.
diff --git a/include/ipc.h b/include/ipc.h
index 8ea9fd2e..0d19daf4 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -12,7 +12,6 @@
 #include <config.h>
 
 #include <ev.h>
-#include <stdbool.h>
 #include <yajl/yajl_gen.h>
 #include <yajl/yajl_parse.h>
 
@@ -41,8 +40,7 @@ typedef struct ipc_client {
     uint8_t *buffer;
     size_t buffer_size;
 
-    TAILQ_ENTRY(ipc_client)
-    clients;
+    TAILQ_ENTRY(ipc_client) clients;
 } ipc_client;
 
 /*
diff --git a/include/libi3.h b/include/libi3.h
index d9c89ddd..c0b9ddbd 100644
--- a/include/libi3.h
+++ b/include/libi3.h
@@ -101,7 +101,7 @@ void errorlog(char *fmt, ...)
 #if !defined(DLOG)
 void debuglog(char *fmt, ...)
     __attribute__((format(printf, 1, 2)));
-#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, STRIPPED__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
 #endif
 
 /**
@@ -347,7 +347,7 @@ gchar *g_utf8_make_valid(const gchar *str, gssize len);
  */
 uint32_t get_colorpixel(const char *hex) __attribute__((const));
 
-#ifndef HAVE_strndup
+#ifndef HAVE_STRNDUP
 /**
  * Taken from FreeBSD
  * Returns a pointer to a new string which is a duplicate of the
@@ -532,7 +532,7 @@ char *resolve_tilde(const char *path);
  */
 char *get_config_path(const char *override_configpath, bool use_system_paths);
 
-#ifndef HAVE_mkdirp
+#ifndef HAVE_MKDIRP
 /**
  * Emulates mkdir -p (creates any missing folders)
  *
@@ -543,9 +543,9 @@ int mkdirp(const char *path, mode_t mode);
 /** Helper structure for usage in format_placeholders(). */
 typedef struct placeholder_t {
     /* The placeholder to be replaced, e.g., "%title". */
-    char *name;
+    const char *name;
     /* The value this placeholder should be replaced with. */
-    char *value;
+    const char *value;
 } placeholder_t;
 
 /**
diff --git a/include/log.h b/include/log.h
index 5c530231..c2758443 100644
--- a/include/log.h
+++ b/include/log.h
@@ -11,9 +11,6 @@
 
 #include <config.h>
 
-#include <stdarg.h>
-#include <stdbool.h>
-
 /* We will include libi3.h which define its own version of LOG, ELOG.
  * We want *our* version, so we undef the libi3 one. */
 #if defined(LOG)
@@ -29,7 +26,7 @@
    is, delete the preceding comma */
 #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__)
 #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__)
-#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, STRIPPED__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
 
 extern char *errorfilename;
 extern char *shmlogname;
diff --git a/include/shmlog.h b/include/shmlog.h
index dc8081f1..7b7e133a 100644
--- a/include/shmlog.h
+++ b/include/shmlog.h
@@ -12,7 +12,6 @@
 
 #include <config.h>
 
-#include <stdint.h>
 #if !defined(__OpenBSD__)
 #include <pthread.h>
 #endif
diff --git a/include/util.h b/include/util.h
index f0db03a5..09ad941f 100644
--- a/include/util.h
+++ b/include/util.h
@@ -143,13 +143,13 @@ char *pango_escape_markup(char *input);
 void start_nagbar(pid_t *nagbar_pid, char *argv[]);
 
 /**
- * Kills the i3-nagbar process, if *nagbar_pid != -1.
+ * Kills the i3-nagbar process, if nagbar_pid != -1.
  *
  * If wait_for_it is set (restarting i3), this function will waitpid(),
  * otherwise, ev is assumed to handle it (reloading).
  *
  */
-void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it);
+void kill_nagbar(pid_t nagbar_pid, bool wait_for_it);
 
 /**
  * Converts a string into a long using strtol().
diff --git a/include/workspace.h b/include/workspace.h
index 69974a2e..2193ed0b 100644
--- a/include/workspace.h
+++ b/include/workspace.h
@@ -45,6 +45,19 @@ Con *get_existing_workspace_by_name(const char *name);
  */
 Con *get_existing_workspace_by_num(int num);
 
+/**
+ * Returns the first output that is assigned to a workspace specified by the
+ * given name or number. Returns NULL if no such output exists.
+ *
+ * If an assignment matches by number but there is an assignment later that
+ * matches by name, the second one is preferred.
+ * The order of the 'ws_assignments' queue is respected: if multiple
+ * assignments match the criteria, the first one is returned.
+ * 'name' is ignored when NULL, 'parsed_num' is ignored when it is -1.
+ *
+ */
+Con *get_assigned_output(const char *name, long parsed_num);
+
 /**
  * Returns true if the first output assigned to a workspace with the given
  * workspace assignment is the same as the given output.
@@ -57,11 +70,8 @@ bool output_triggers_assignment(Output *output, struct Workspace_Assignment *ass
  * creating the workspace if necessary (by allocating the necessary amount of
  * memory and initializing the data structures correctly).
  *
- * If created is not NULL, *created will be set to whether or not the
- * workspace has just been created.
- *
  */
-Con *workspace_get(const char *num, bool *created);
+Con *workspace_get(const char *num);
 
 /**
  * Extracts workspace names from keybindings (e.g. “web” from “bindsym $mod+1
@@ -136,51 +146,6 @@ void workspace_back_and_forth(void);
  */
 Con *workspace_back_and_forth_get(void);
 
-#if 0
-/**
- * Assigns the given workspace to the given screen by correctly updating its
- * state and reconfiguring all the clients on this workspace.
- *
- * This is called when initializing a screen and when re-assigning it to a
- * different screen which just got available (if you configured it to be on
- * screen 1 and you just plugged in screen 1).
- *
- */
-void workspace_assign_to(Workspace *ws, Output *screen, bool hide_it);
-
-/**
- * Initializes the given workspace if it is not already initialized. The given
- * screen is to be understood as a fallback, if the workspace itself either
- * was not assigned to a particular screen or cannot be placed there because
- * the screen is not attached at the moment.
- *
- */
-void workspace_initialize(Workspace *ws, Output *screen, bool recheck);
-
-/**
- * Gets the first unused workspace for the given screen, taking into account
- * the preferred_screen setting of every workspace (workspace assignments).
- *
- */
-Workspace *get_first_workspace_for_output(Output *screen);
-
-/**
- * Unmaps all clients (and stack windows) of the given workspace.
- *
- * This needs to be called separately when temporarily rendering a workspace
- * which is not the active workspace to force reconfiguration of all clients,
- * like in src/xinerama.c when re-assigning a workspace to another screen.
- *
- */
-void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws);
-
-/**
- * Maps all clients (and stack windows) of the given workspace.
- *
- */
-void workspace_map_clients(xcb_connection_t *conn, Workspace *ws);
-#endif
-
 /**
  * Goes through all clients on the given workspace and updates the workspace’s
  * urgent flag accordingly.
diff --git a/include/xcb.h b/include/xcb.h
index 15132609..ba4ff2f3 100644
--- a/include/xcb.h
+++ b/include/xcb.h
@@ -49,8 +49,12 @@
                          XCB_EVENT_MASK_FOCUS_CHANGE |                                                \
                          XCB_EVENT_MASK_ENTER_WINDOW)
 
+#include "i3-atoms_rest.xmacro.h"
+#include "i3-atoms_NET_SUPPORTED.xmacro.h"
+
 #define xmacro(atom) extern xcb_atom_t A_##atom;
-#include "atoms.xmacro"
+I3_NET_SUPPORTED_ATOMS_XMACRO
+I3_REST_ATOMS_XMACRO
 #undef xmacro
 
 extern unsigned int xcb_numlock_mask;
diff --git a/include/xinerama.h b/include/xinerama.h
index f01ee568..52a5db33 100644
--- a/include/xinerama.h
+++ b/include/xinerama.h
@@ -13,8 +13,6 @@
 
 #include <config.h>
 
-#include "data.h"
-
 /**
  * We have just established a connection to the X server and need the initial
  * Xinerama information to setup workspaces for each screen.
diff --git a/libi3/dpi.c b/libi3/dpi.c
index d15e35be..dec38bc8 100644
--- a/libi3/dpi.c
+++ b/libi3/dpi.c
@@ -9,6 +9,7 @@
 
 #include <math.h>
 #include <stdlib.h>
+
 #include <xcb/xcb_xrm.h>
 
 static long dpi;
diff --git a/libi3/draw_util.c b/libi3/draw_util.c
index 092ac967..313dc29a 100644
--- a/libi3/draw_util.c
+++ b/libi3/draw_util.c
@@ -9,11 +9,10 @@
 #include "libi3.h"
 
 #include <stdlib.h>
-#include <err.h>
 #include <string.h>
+
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
-#include <cairo/cairo-xcb.h>
 
 /* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */
 extern xcb_visualtype_t *visual_type;
diff --git a/libi3/fake_configure_notify.c b/libi3/fake_configure_notify.c
index 145f4512..5d87c3da 100644
--- a/libi3/fake_configure_notify.c
+++ b/libi3/fake_configure_notify.c
@@ -8,7 +8,6 @@
 #include "libi3.h"
 
 #include <stdlib.h>
-#include <stdbool.h>
 
 #include <xcb/xcb.h>
 #include <xcb/xproto.h>
diff --git a/libi3/font.c b/libi3/font.c
index e01edb53..477509d0 100644
--- a/libi3/font.c
+++ b/libi3/font.c
@@ -8,14 +8,12 @@
 #include "libi3.h"
 
 #include <assert.h>
+#include <cairo/cairo-xcb.h>
+#include <err.h>
+#include <pango/pangocairo.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdbool.h>
-#include <err.h>
-
-#include <cairo/cairo-xcb.h>
-#include <pango/pangocairo.h>
 
 static const i3Font *savedFont = NULL;
 
diff --git a/libi3/format_placeholders.c b/libi3/format_placeholders.c
index 770e383d..71870a7b 100644
--- a/libi3/format_placeholders.c
+++ b/libi3/format_placeholders.c
@@ -8,7 +8,6 @@
 #include "libi3.h"
 
 #include <stdlib.h>
-#include <stdint.h>
 #include <string.h>
 
 #ifndef CS_STARTS_WITH
diff --git a/libi3/g_utf8_make_valid.c b/libi3/g_utf8_make_valid.c
index b15873b3..71beafd3 100644
--- a/libi3/g_utf8_make_valid.c
+++ b/libi3/g_utf8_make_valid.c
@@ -19,8 +19,8 @@
 
 #include "libi3.h"
 
-#include <string.h>
 #include <glib.h>
+#include <string.h>
 
 /* Copied from:
  * https://gitlab.gnome.org/GNOME/glib/blob/f928dfdf57bf92c883b53b16d7a9d49add504f52/glib/gutf8.c#L1752-1815 */
diff --git a/libi3/get_colorpixel.c b/libi3/get_colorpixel.c
index 09ce70b9..45e47725 100644
--- a/libi3/get_colorpixel.c
+++ b/libi3/get_colorpixel.c
@@ -6,22 +6,19 @@
  *
  */
 #include "libi3.h"
+#include "queue.h"
 
-#include <stdlib.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
-
-#include "queue.h"
 struct Colorpixel {
     char hex[8];
     uint32_t pixel;
 
-    SLIST_ENTRY(Colorpixel)
-    colorpixels;
+    SLIST_ENTRY(Colorpixel) colorpixels;
 };
 
-SLIST_HEAD(colorpixel_head, Colorpixel)
-colorpixels;
+SLIST_HEAD(colorpixel_head, Colorpixel) colorpixels;
 
 /*
  * Returns the colorpixel to use for the given hex color (think of HTML).
diff --git a/libi3/get_exe_path.c b/libi3/get_exe_path.c
index 430fb2e0..3b46ef82 100644
--- a/libi3/get_exe_path.c
+++ b/libi3/get_exe_path.c
@@ -7,12 +7,10 @@
  */
 #include "libi3.h"
 
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
 #include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 /*
  * This function returns the absolute path to the executable it is running in.
diff --git a/libi3/get_mod_mask.c b/libi3/get_mod_mask.c
index 98031d4c..92af456d 100644
--- a/libi3/get_mod_mask.c
+++ b/libi3/get_mod_mask.c
@@ -9,6 +9,7 @@
 
 #include <stdint.h>
 #include <stdlib.h>
+
 #include <xcb/xcb.h>
 #include <xcb/xcb_keysyms.h>
 
diff --git a/libi3/get_process_filename.c b/libi3/get_process_filename.c
index 7e2ecbd2..21429ec3 100644
--- a/libi3/get_process_filename.c
+++ b/libi3/get_process_filename.c
@@ -7,17 +7,11 @@
  */
 #include "libi3.h"
 
-#include <assert.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
 #include <err.h>
-#include <sys/stat.h>
-#include <sys/types.h>
 #include <pwd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
-#include <err.h>
 
 /*
  * Returns the name of a temporary file with the specified prefix.
diff --git a/libi3/ipc_connect.c b/libi3/ipc_connect.c
index f659a1a4..871fe083 100644
--- a/libi3/ipc_connect.c
+++ b/libi3/ipc_connect.c
@@ -7,14 +7,12 @@
  */
 #include "libi3.h"
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <string.h>
 #include <err.h>
-#include <stdlib.h>
-#include <unistd.h>
 #include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 /*
  * Connects to the i3 IPC socket and returns the file descriptor for the
diff --git a/libi3/ipc_recv_message.c b/libi3/ipc_recv_message.c
index 84da5aa3..516405b0 100644
--- a/libi3/ipc_recv_message.c
+++ b/libi3/ipc_recv_message.c
@@ -7,15 +7,12 @@
  */
 #include "libi3.h"
 
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
 #include <errno.h>
-#include <inttypes.h>
-
 #include <i3/ipc.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
 
 /*
  * Reads a message from the given socket file descriptor and stores its length
diff --git a/libi3/ipc_send_message.c b/libi3/ipc_send_message.c
index c2cc0127..4faeea7f 100644
--- a/libi3/ipc_send_message.c
+++ b/libi3/ipc_send_message.c
@@ -7,14 +7,8 @@
  */
 #include "libi3.h"
 
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <err.h>
-#include <errno.h>
-
 #include <i3/ipc.h>
+#include <stdint.h>
 
 /*
  * Formats a message (payload) of the given size and type and sends it to i3 via
diff --git a/libi3/is_debug_build.c b/libi3/is_debug_build.c
index 52187bda..9458f75b 100644
--- a/libi3/is_debug_build.c
+++ b/libi3/is_debug_build.c
@@ -8,7 +8,6 @@
 #include "libi3.h"
 
 #include <string.h>
-#include <stdbool.h>
 
 /*
  * Returns true if this version of i3 is a debug build (anything which is not a
@@ -17,10 +16,16 @@
  */
 bool is_debug_build(void) {
     /* i3_version contains either something like this:
-     *     "4.0.2 (2011-11-11, branch "release")".
-     * or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".
+     *     "4.0.2 (2011-11-11)" (release version)
+     * or: "4.0.2-123-gC0FFEE"  (debug version)
      *
      * So we check for the offset of the first opening round bracket to
      * determine whether this is a git version or a release version. */
+    if (strchr(I3_VERSION, '(') == NULL) {
+        return true;  // e.g. 4.0.2-123-gC0FFEE
+    }
+    /* In practice, debug versions do not contain parentheses at all,
+     * but leave the logic as it was before so that we can re-add
+     * parentheses if we chose to. */
     return ((strchr(I3_VERSION, '(') - I3_VERSION) > 10);
 }
diff --git a/libi3/mkdirp.c b/libi3/mkdirp.c
index 35a30475..d29bb95b 100644
--- a/libi3/mkdirp.c
+++ b/libi3/mkdirp.c
@@ -12,7 +12,7 @@
 #include <string.h>
 #include <sys/stat.h>
 
-#ifndef HAVE_mkdirp
+#ifndef HAVE_MKDIRP
 /*
  * Emulates mkdir -p (creates any missing folders)
  *
diff --git a/libi3/root_atom_contents.c b/libi3/root_atom_contents.c
index d6394d4b..6feb31bc 100644
--- a/libi3/root_atom_contents.c
+++ b/libi3/root_atom_contents.c
@@ -7,12 +7,9 @@
  */
 #include "libi3.h"
 
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <stdlib.h>
 #include <math.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
diff --git a/libi3/safewrappers.c b/libi3/safewrappers.c
index fdea636e..767a0f05 100644
--- a/libi3/safewrappers.c
+++ b/libi3/safewrappers.c
@@ -7,13 +7,11 @@
  */
 #include "libi3.h"
 
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <stdio.h>
 #include <err.h>
 #include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 /*
  * The s* functions (safe) are wrappers around malloc, strdup, …, which exits if one of
diff --git a/libi3/string.c b/libi3/string.c
index 9efa3690..da18c550 100644
--- a/libi3/string.c
+++ b/libi3/string.c
@@ -11,11 +11,10 @@
  */
 #include "libi3.h"
 
+#include <glib.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <glib.h>
-
 struct _i3String {
     char *utf8;
     xcb_char2b_t *ucs2;
diff --git a/libi3/strndup.c b/libi3/strndup.c
index e215a76f..8911732c 100644
--- a/libi3/strndup.c
+++ b/libi3/strndup.c
@@ -7,10 +7,9 @@
  */
 #include "libi3.h"
 
-#include <sys/types.h>
 #include <string.h>
 
-#ifndef HAVE_strndup
+#ifndef HAVE_STRNDUP
 /*
  * Taken from FreeBSD
  * Returns a pointer to a new string which is a duplicate of the
diff --git a/libi3/ucs2_conversion.c b/libi3/ucs2_conversion.c
index c651cdb3..c7467239 100644
--- a/libi3/ucs2_conversion.c
+++ b/libi3/ucs2_conversion.c
@@ -8,7 +8,6 @@
 #include "libi3.h"
 
 #include <err.h>
-#include <errno.h>
 #include <iconv.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4
deleted file mode 100644
index 08f2e07e..00000000
--- a/m4/ax_append_flag.m4
+++ /dev/null
@@ -1,71 +0,0 @@
-# ===========================================================================
-#      http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
-#
-# DESCRIPTION
-#
-#   FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
-#   added in between.
-#
-#   If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
-#   CFLAGS) is used.  FLAGS-VARIABLE is not changed if it already contains
-#   FLAG.  If FLAGS-VARIABLE is unset in the shell, it is set to exactly
-#   FLAG.
-#
-#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
-#
-#   This program is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation, either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 6
-
-AC_DEFUN([AX_APPEND_FLAG],
-[dnl
-AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
-AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
-AS_VAR_SET_IF(FLAGS,[
-  AS_CASE([" AS_VAR_GET(FLAGS) "],
-    [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
-    [
-     AS_VAR_APPEND(FLAGS,[" $1"])
-     AC_RUN_LOG([: FLAGS="$FLAGS"])
-    ])
-  ],
-  [
-  AS_VAR_SET(FLAGS,[$1])
-  AC_RUN_LOG([: FLAGS="$FLAGS"])
-  ])
-AS_VAR_POPDEF([FLAGS])dnl
-])dnl AX_APPEND_FLAG
diff --git a/m4/ax_cflags_warn_all.m4 b/m4/ax_cflags_warn_all.m4
deleted file mode 100644
index 1f077992..00000000
--- a/m4/ax_cflags_warn_all.m4
+++ /dev/null
@@ -1,122 +0,0 @@
-# ===========================================================================
-#    http://www.gnu.org/software/autoconf-archive/ax_cflags_warn_all.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CFLAGS_WARN_ALL   [(shellvar [,default, [A/NA]])]
-#   AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])]
-#   AX_FCFLAGS_WARN_ALL  [(shellvar [,default, [A/NA]])]
-#
-# DESCRIPTION
-#
-#   Try to find a compiler option that enables most reasonable warnings.
-#
-#   For the GNU compiler it will be -Wall (and -ansi -pedantic) The result
-#   is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default.
-#
-#   Currently this macro knows about the GCC, Solaris, Digital Unix, AIX,
-#   HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and
-#   Intel compilers.  For a given compiler, the Fortran flags are much more
-#   experimental than their C equivalents.
-#
-#    - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS
-#    - $2 add-value-if-not-found : nothing
-#    - $3 action-if-found : add value to shellvariable
-#    - $4 action-if-not-found : nothing
-#
-#   NOTE: These macros depend on AX_APPEND_FLAG.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-#   Copyright (c) 2010 Rhys Ulerich <rhys.ulerich@gmail.com>
-#
-#   This program is free software; you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation; either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 15
-
-AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl
-AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl
-AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl
-AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings],
-VAR,[VAR="no, unknown"
-ac_save_[]FLAGS="$[]FLAGS"
-for ac_arg dnl
-in "-warn all  % -warn all"   dnl Intel
-   "-pedantic  % -Wall"       dnl GCC
-   "-xstrconst % -v"          dnl Solaris C
-   "-std1      % -verbose -w0 -warnprotos" dnl Digital Unix
-   "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX
-   "-ansi -ansiE % -fullwarn" dnl IRIX
-   "+ESlit     % +w1"         dnl HP-UX C
-   "-Xc        % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10)
-   "-h conform % -h msglevel 2" dnl Cray C (Unicos)
-   #
-do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
-   AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
-                     [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
-done
-FLAGS="$ac_save_[]FLAGS"
-])
-AS_VAR_POPDEF([FLAGS])dnl
-AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
-case ".$VAR" in
-     .ok|.ok,*) m4_ifvaln($3,$3) ;;
-   .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;;
-   *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;;
-esac
-AS_VAR_POPDEF([VAR])dnl
-])dnl AX_FLAGS_WARN_ALL
-dnl  implementation tactics:
-dnl   the for-argument contains a list of options. The first part of
-dnl   these does only exist to detect the compiler - usually it is
-dnl   a global option to enable -ansi or -extrawarnings. All other
-dnl   compilers will fail about it. That was needed since a lot of
-dnl   compilers will give false positives for some option-syntax
-dnl   like -Woption or -Xoption as they think of it is a pass-through
-dnl   to later compile stages or something. The "%" is used as a
-dnl   delimiter. A non-option comment can be given after "%%" marks
-dnl   which will be shown but not added to the respective C/CXXFLAGS.
-
-AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl
-AC_LANG_PUSH([C])
-AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
-AC_LANG_POP([C])
-])
-
-AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl
-AC_LANG_PUSH([C++])
-AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
-AC_LANG_POP([C++])
-])
-
-AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl
-AC_LANG_PUSH([Fortran])
-AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
-AC_LANG_POP([Fortran])
-])
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
deleted file mode 100644
index ca363971..00000000
--- a/m4/ax_check_compile_flag.m4
+++ /dev/null
@@ -1,74 +0,0 @@
-# ===========================================================================
-#   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
-#
-# DESCRIPTION
-#
-#   Check whether the given FLAG works with the current language's compiler
-#   or gives an error.  (Warnings, however, are ignored)
-#
-#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
-#   success/failure.
-#
-#   If EXTRA-FLAGS is defined, it is added to the current language's default
-#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
-#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
-#   force the compiler to issue an error when a bad flag is given.
-#
-#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
-#
-#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
-#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
-#
-#   This program is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation, either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 4
-
-AC_DEFUN([AX_CHECK_COMPILE_FLAG],
-[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
-AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
-AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
-  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
-  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
-  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
-    [AS_VAR_SET(CACHEVAR,[yes])],
-    [AS_VAR_SET(CACHEVAR,[no])])
-  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
-AS_VAR_IF(CACHEVAR,yes,
-  [m4_default([$2], :)],
-  [m4_default([$3], :)])
-AS_VAR_POPDEF([CACHEVAR])dnl
-])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_check_enable_debug.m4 b/m4/ax_check_enable_debug.m4
deleted file mode 100644
index f99d75fe..00000000
--- a/m4/ax_check_enable_debug.m4
+++ /dev/null
@@ -1,124 +0,0 @@
-# ===========================================================================
-#   http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE])
-#
-# DESCRIPTION
-#
-#   Check for the presence of an --enable-debug option to configure, with
-#   the specified default value used when the option is not present.  Return
-#   the value in the variable $ax_enable_debug.
-#
-#   Specifying 'yes' adds '-g -O0' to the compilation flags for all
-#   languages. Specifying 'info' adds '-g' to the compilation flags.
-#   Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to
-#   the linking flags. Otherwise, nothing is added.
-#
-#   Define the variables listed in the second argument if debug is enabled,
-#   defaulting to no variables.  Defines the variables listed in the third
-#   argument if debug is disabled, defaulting to NDEBUG.  All lists of
-#   variables should be space-separated.
-#
-#   If debug is not enabled, ensure AC_PROG_* will not add debugging flags.
-#   Should be invoked prior to any AC_PROG_* compiler checks.
-#
-#   IS-RELEASE can be used to change the default to 'no' when making a
-#   release.  Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it
-#   uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE
-#   macro, there is no need to pass this parameter.
-#
-#     AX_IS_RELEASE([git-directory])
-#     AX_CHECK_ENABLE_DEBUG()
-#
-# LICENSE
-#
-#   Copyright (c) 2011 Rhys Ulerich <rhys.ulerich@gmail.com>
-#   Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
-#
-#   Copying and distribution of this file, with or without modification, are
-#   permitted in any medium without royalty provided the copyright notice
-#   and this notice are preserved.
-
-#serial 5
-
-AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[
-    AC_BEFORE([$0],[AC_PROG_CC])dnl
-    AC_BEFORE([$0],[AC_PROG_CXX])dnl
-    AC_BEFORE([$0],[AC_PROG_F77])dnl
-    AC_BEFORE([$0],[AC_PROG_FC])dnl
-
-    AC_MSG_CHECKING(whether to enable debugging)
-
-    ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1])))
-    ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],,
-                                                              [$ax_is_release],
-                                                              [$4])))
-
-    # If this is a release, override the default.
-    AS_IF([test "$ax_enable_debug_is_release" = "yes"],
-      [ax_enable_debug_default="no"])
-
-    m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))])
-    m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))])
-
-    AC_ARG_ENABLE(debug,
-	[AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])],
-	[],enable_debug=$ax_enable_debug_default)
-
-    # empty mean debug yes
-    AS_IF([test "x$enable_debug" = "x"],
-      [enable_debug="yes"])
-
-    # case of debug
-    AS_CASE([$enable_debug],
-      [yes],[
-	AC_MSG_RESULT(yes)
-	CFLAGS="${CFLAGS} -g -O0"
-	CXXFLAGS="${CXXFLAGS} -g -O0"
-	FFLAGS="${FFLAGS} -g -O0"
-	FCFLAGS="${FCFLAGS} -g -O0"
-	OBJCFLAGS="${OBJCFLAGS} -g -O0"
-      ],
-      [info],[
-	AC_MSG_RESULT(info)
-	CFLAGS="${CFLAGS} -g"
-	CXXFLAGS="${CXXFLAGS} -g"
-	FFLAGS="${FFLAGS} -g"
-	FCFLAGS="${FCFLAGS} -g"
-	OBJCFLAGS="${OBJCFLAGS} -g"
-      ],
-      [profile],[
-	AC_MSG_RESULT(profile)
-	CFLAGS="${CFLAGS} -g -pg"
-	CXXFLAGS="${CXXFLAGS} -g -pg"
-	FFLAGS="${FFLAGS} -g -pg"
-	FCFLAGS="${FCFLAGS} -g -pg"
-	OBJCFLAGS="${OBJCFLAGS} -g -pg"
-	LDFLAGS="${LDFLAGS} -pg"
-      ],
-      [
-	AC_MSG_RESULT(no)
-	dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags
-	dnl by setting any unset environment flag variables
-	AS_IF([test "x${CFLAGS+set}" != "xset"],
-	  [CFLAGS=""])
-	AS_IF([test "x${CXXFLAGS+set}" != "xset"],
-	  [CXXFLAGS=""])
-	AS_IF([test "x${FFLAGS+set}" != "xset"],
-	  [FFLAGS=""])
-	AS_IF([test "x${FCFLAGS+set}" != "xset"],
-	  [FCFLAGS=""])
-	AS_IF([test "x${OBJCFLAGS+set}" != "xset"],
-	  [OBJCFLAGS=""])
-      ])
-
-    dnl Define various variables if debugging is disabled.
-    dnl assert.h is a NOP if NDEBUG is defined, so define it by default.
-    AS_IF([test "x$enable_debug" = "xyes"],
-      [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])],
-      [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])])
-    ax_enable_debug=$enable_debug
-])
diff --git a/m4/ax_check_gnu_make.m4 b/m4/ax_check_gnu_make.m4
deleted file mode 100644
index 6762e9ed..00000000
--- a/m4/ax_check_gnu_make.m4
+++ /dev/null
@@ -1,84 +0,0 @@
-# ===========================================================================
-#     http://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CHECK_GNU_MAKE()
-#
-# DESCRIPTION
-#
-#   This macro searches for a GNU version of make. If a match is found:
-#
-#     * The makefile variable `ifGNUmake' is set to the empty string, otherwise
-#       it is set to "#". This is useful for including a special features in a
-#       Makefile, which cannot be handled by other versions of make.
-#     * The variable `_cv_gnu_make_command` is set to the command to invoke
-#       GNU make if it exists, the empty string otherwise.
-#     * The variable `ax_cv_gnu_make_command` is set to the command to invoke
-#       GNU make by copying `_cv_gnu_make_command`, otherwise it is unset.
-#     * If GNU Make is found, its version is extracted from the output of
-#       `make --version` as the last field of a record of space-separated
-#       columns and saved into the variable `ax_check_gnu_make_version`.
-#
-#   Here is an example of its use:
-#
-#   Makefile.in might contain:
-#
-#     # A failsafe way of putting a dependency rule into a makefile
-#     $(DEPEND):
-#             $(CC) -MM $(srcdir)/*.c > $(DEPEND)
-#
-#     @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND)))
-#     @ifGNUmake@ include $(DEPEND)
-#     @ifGNUmake@ endif
-#
-#   Then configure.in would normally contain:
-#
-#     AX_CHECK_GNU_MAKE()
-#     AC_OUTPUT(Makefile)
-#
-#   Then perhaps to cause gnu make to override any other make, we could do
-#   something like this (note that GNU make always looks for GNUmakefile
-#   first):
-#
-#     if  ! test x$_cv_gnu_make_command = x ; then
-#             mv Makefile GNUmakefile
-#             echo .DEFAULT: > Makefile ;
-#             echo \  $_cv_gnu_make_command \$@ >> Makefile;
-#     fi
-#
-#   Then, if any (well almost any) other make is called, and GNU make also
-#   exists, then the other make wraps the GNU make.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 John Darrington <j.darrington@elvis.murdoch.edu.au>
-#   Copyright (c) 2015 Enrico M. Crisostomo <enrico.m.crisostomo@gmail.com>
-#
-#   Copying and distribution of this file, with or without modification, are
-#   permitted in any medium without royalty provided the copyright notice
-#   and this notice are preserved. This file is offered as-is, without any
-#   warranty.
-
-#serial 8
-
-AC_DEFUN([AX_CHECK_GNU_MAKE],dnl
-  [AC_PROG_AWK
-  AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl
-    _cv_gnu_make_command="" ;
-dnl Search all the common names for GNU make
-    for a in "$MAKE" make gmake gnumake ; do
-      if test -z "$a" ; then continue ; fi ;
-      if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then
-        _cv_gnu_make_command=$a ;
-        AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make")
-        ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }')
-        break ;
-      fi
-    done ;])
-dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise
-  AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])],   [AS_VAR_SET([ifGNUmake], [""])])
-  AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])])
-  AC_SUBST([ifGNUmake])
-])
diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4
deleted file mode 100644
index eb01a6ce..00000000
--- a/m4/ax_check_link_flag.m4
+++ /dev/null
@@ -1,74 +0,0 @@
-# ===========================================================================
-#    http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
-#
-# DESCRIPTION
-#
-#   Check whether the given FLAG works with the linker or gives an error.
-#   (Warnings, however, are ignored)
-#
-#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
-#   success/failure.
-#
-#   If EXTRA-FLAGS is defined, it is added to the linker's default flags
-#   when the check is done.  The check is thus made with the flags: "LDFLAGS
-#   EXTRA-FLAGS FLAG".  This can for example be used to force the linker to
-#   issue an error when a bad flag is given.
-#
-#   INPUT gives an alternative input source to AC_LINK_IFELSE.
-#
-#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
-#   macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
-#
-#   This program is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation, either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 4
-
-AC_DEFUN([AX_CHECK_LINK_FLAG],
-[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
-AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
-AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
-  ax_check_save_flags=$LDFLAGS
-  LDFLAGS="$LDFLAGS $4 $1"
-  AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
-    [AS_VAR_SET(CACHEVAR,[yes])],
-    [AS_VAR_SET(CACHEVAR,[no])])
-  LDFLAGS=$ax_check_save_flags])
-AS_VAR_IF(CACHEVAR,yes,
-  [m4_default([$2], :)],
-  [m4_default([$3], :)])
-AS_VAR_POPDEF([CACHEVAR])dnl
-])dnl AX_CHECK_LINK_FLAGS
diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4
deleted file mode 100644
index 1f1bc702..00000000
--- a/m4/ax_code_coverage.m4
+++ /dev/null
@@ -1,273 +0,0 @@
-# ===========================================================================
-#     http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CODE_COVERAGE()
-#
-# DESCRIPTION
-#
-#   Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS,
-#   CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LDFLAGS which should be
-#   included in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LDFLAGS variables of
-#   every build target (program or library) which should be built with code
-#   coverage support. Also defines CODE_COVERAGE_RULES which should be
-#   substituted in your Makefile; and $enable_code_coverage which can be
-#   used in subsequent configure output. CODE_COVERAGE_ENABLED is defined
-#   and substituted, and corresponds to the value of the
-#   --enable-code-coverage option, which defaults to being disabled.
-#
-#   Test also for gcov program and create GCOV variable that could be
-#   substituted.
-#
-#   Note that all optimisation flags in CFLAGS must be disabled when code
-#   coverage is enabled.
-#
-#   Usage example:
-#
-#   configure.ac:
-#
-#     AX_CODE_COVERAGE
-#
-#   Makefile.am:
-#
-#     @CODE_COVERAGE_RULES@
-#     my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ...
-#     my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ...
-#     my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
-#     my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ...
-#
-#   This results in a "check-code-coverage" rule being added to any
-#   Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
-#   has been configured with --enable-code-coverage). Running `make
-#   check-code-coverage` in that directory will run the module's test suite
-#   (`make check`) and build a code coverage report detailing the code which
-#   was touched, then print the URI for the report.
-#
-#   This code was derived from Makefile.decl in GLib, originally licenced
-#   under LGPLv2.1+.
-#
-# LICENSE
-#
-#   Copyright (c) 2012, 2016 Philip Withnall
-#   Copyright (c) 2012 Xan Lopez
-#   Copyright (c) 2012 Christian Persch
-#   Copyright (c) 2012 Paolo Borelli
-#   Copyright (c) 2012 Dan Winship
-#   Copyright (c) 2015 Bastien ROUCARIES
-#
-#   This library is free software; you can redistribute it and/or modify it
-#   under the terms of the GNU Lesser General Public License as published by
-#   the Free Software Foundation; either version 2.1 of the License, or (at
-#   your option) any later version.
-#
-#   This library is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
-#   General Public License for more details.
-#
-#   You should have received a copy of the GNU Lesser General Public License
-#   along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#serial 15
-
-AC_DEFUN([AX_CODE_COVERAGE],[
-	dnl Check for --enable-code-coverage
-	AC_REQUIRE([AC_PROG_SED])
-
-	# allow to override gcov location
-	AC_ARG_WITH([gcov],
-	  [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
-	  [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
-	  [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
-
-	AC_MSG_CHECKING([whether to build with code coverage support])
-	AC_ARG_ENABLE([code-coverage],
-	  AS_HELP_STRING([--enable-code-coverage],
-	  [Whether to enable code coverage support]),,
-	  enable_code_coverage=no)
-
-	AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
-	AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
-	AC_MSG_RESULT($enable_code_coverage)
-
-	AS_IF([ test "$enable_code_coverage" = "yes" ], [
-		# check for gcov
-		AC_CHECK_TOOL([GCOV],
-		  [$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
-		  [:])
-		AS_IF([test "X$GCOV" = "X:"],
-		  [AC_MSG_ERROR([gcov is needed to do coverage])])
-		AC_SUBST([GCOV])
-
-		dnl Check if gcc is being used
-		AS_IF([ test "$GCC" = "no" ], [
-			AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
-		])
-
-		# List of supported lcov versions.
-		lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11 1.12 1.13"
-
-		AC_CHECK_PROG([LCOV], [lcov], [lcov])
-		AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
-
-		AS_IF([ test "$LCOV" ], [
-			AC_CACHE_CHECK([for lcov version], ax_cv_lcov_version, [
-				ax_cv_lcov_version=invalid
-				lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
-				for lcov_check_version in $lcov_version_list; do
-					if test "$lcov_version" = "$lcov_check_version"; then
-						ax_cv_lcov_version="$lcov_check_version (ok)"
-					fi
-				done
-			])
-		], [
-			lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
-			AC_MSG_ERROR([$lcov_msg])
-		])
-
-		case $ax_cv_lcov_version in
-			""|invalid[)]
-				lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
-				AC_MSG_ERROR([$lcov_msg])
-				LCOV="exit 0;"
-			;;
-		esac
-
-		AS_IF([ test -z "$GENHTML" ], [
-			AC_MSG_ERROR([Could not find genhtml from the lcov package])
-		])
-
-		dnl Build the code coverage flags
-		CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
-		CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
-		CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
-		CODE_COVERAGE_LDFLAGS="-lgcov"
-
-		AC_SUBST([CODE_COVERAGE_CPPFLAGS])
-		AC_SUBST([CODE_COVERAGE_CFLAGS])
-		AC_SUBST([CODE_COVERAGE_CXXFLAGS])
-		AC_SUBST([CODE_COVERAGE_LDFLAGS])
-	])
-
-[CODE_COVERAGE_RULES='
-# Code coverage
-#
-# Optional:
-#  - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
-#    Multiple directories may be specified, separated by whitespace.
-#    (Default: $(top_builddir))
-#  - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
-#    by lcov for code coverage. (Default:
-#    $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
-#  - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
-#    reports to be created. (Default:
-#    $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
-#  - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
-#    set to 0 to disable it and leave empty to stay with the default.
-#    (Default: empty)
-#  - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
-#    instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
-#  - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
-#    instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
-#  - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
-#  - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
-#    collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
-#  - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
-#    instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
-#  - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
-#    lcov instance. (Default: empty)
-#  - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
-#    instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
-#  - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
-#    genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
-#  - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
-#    instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
-#  - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
-#
-# The generated report will be titled using the $(PACKAGE_NAME) and
-# $(PACKAGE_VERSION). In order to add the current git hash to the title,
-# use the git-version-gen script, available online.
-
-# Optional variables
-CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
-CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
-CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
-CODE_COVERAGE_BRANCH_COVERAGE ?=
-CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
---rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
-CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
-CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
-CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
-CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
-CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
-CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
-CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
-$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
---rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
-CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS)
-CODE_COVERAGE_IGNORE_PATTERN ?=
-
-code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
-code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
-code_coverage_v_lcov_cap_0 = @echo "  LCOV   --capture"\
- $(CODE_COVERAGE_OUTPUT_FILE);
-code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
-code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
-code_coverage_v_lcov_ign_0 = @echo "  LCOV   --remove /tmp/*"\
- $(CODE_COVERAGE_IGNORE_PATTERN);
-code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
-code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
-code_coverage_v_genhtml_0 = @echo "  GEN   " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
-code_coverage_quiet = $(code_coverage_quiet_$(V))
-code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
-code_coverage_quiet_0 = --quiet
-
-# sanitizes the test-name: replaces with underscores: dashes and dots
-code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
-
-# Use recursive makes in order to ignore errors during check
-check-code-coverage:
-ifeq ($(CODE_COVERAGE_ENABLED),yes)
-	-$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check
-	$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
-else
-	@echo "Need to reconfigure with --enable-code-coverage"
-endif
-
-# Capture code coverage data
-code-coverage-capture: code-coverage-capture-hook
-ifeq ($(CODE_COVERAGE_ENABLED),yes)
-	$(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS)
-	$(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
-	-@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
-	$(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
-	@echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
-else
-	@echo "Need to reconfigure with --enable-code-coverage"
-endif
-
-# Hook rule executed before code-coverage-capture, overridable by the user
-code-coverage-capture-hook:
-
-ifeq ($(CODE_COVERAGE_ENABLED),yes)
-clean: code-coverage-clean
-code-coverage-clean:
-	-$(LCOV) --directory $(top_builddir) -z
-	-rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
-	-find . -name "*.gcda" -o -name "*.gcov" -delete
-endif
-
-GITIGNOREFILES ?=
-GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
-
-A''M_DISTCHECK_CONFIGURE_FLAGS ?=
-A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
-
-.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
-']
-
-	AC_SUBST([CODE_COVERAGE_RULES])
-	m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
-])
diff --git a/m4/ax_configure_args.m4 b/m4/ax_configure_args.m4
deleted file mode 100644
index 0726b1bc..00000000
--- a/m4/ax_configure_args.m4
+++ /dev/null
@@ -1,70 +0,0 @@
-# ===========================================================================
-#     http://www.gnu.org/software/autoconf-archive/ax_configure_args.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_CONFIGURE_ARGS
-#
-# DESCRIPTION
-#
-#   Helper macro for AX_ENABLE_BUILDDIR.
-#
-#   The traditional way of starting a subdir-configure is running the script
-#   with ${1+"$@"} but since autoconf 2.60 this is broken. Instead we have
-#   to rely on eval'ing $ac_configure_args however some old autoconf
-#   versions do not provide that. To ensure maximum portability of autoconf
-#   extension macros this helper can be AC_REQUIRE'd so that
-#   $ac_configure_args will alsways be present.
-#
-#   Sadly, the traditional "exec $SHELL" of the enable_builddir macros is
-#   spoiled now and must be replaced by "eval + exit $?".
-#
-#   Example:
-#
-#     AC_DEFUN([AX_ENABLE_SUBDIR],[dnl
-#       AC_REQUIRE([AX_CONFIGURE_ARGS])dnl
-#       eval $SHELL $ac_configure_args || exit $?
-#       ...])
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
-#
-#   This program is free software; you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation; either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 9
-
-AC_DEFUN([AX_CONFIGURE_ARGS],[
-   # [$]@ is unsable in 2.60+ but earlier autoconf had no ac_configure_args
-   if test "${ac_configure_args+set}" != "set" ; then
-      ac_configure_args=
-      for ac_arg in ${1+"[$]@"}; do
-         ac_configure_args="$ac_configure_args '$ac_arg'"
-      done
-   fi
-])
diff --git a/m4/ax_enable_builddir.m4 b/m4/ax_enable_builddir.m4
deleted file mode 100644
index 5f4ba327..00000000
--- a/m4/ax_enable_builddir.m4
+++ /dev/null
@@ -1,302 +0,0 @@
-# ===========================================================================
-#    http://www.gnu.org/software/autoconf-archive/ax_enable_builddir.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_ENABLE_BUILDDIR [(dirstring-or-command [,Makefile.mk [,-all]])]
-#
-# DESCRIPTION
-#
-#   If the current configure was run within the srcdir then we move all
-#   configure-files into a subdir and let the configure steps continue
-#   there. We provide an option --disable-builddir to suppress the move into
-#   a separate builddir.
-#
-#   Defaults:
-#
-#     $1 = $host (overridden with $HOST)
-#     $2 = Makefile.mk
-#     $3 = -all
-#
-#   This macro must be called before AM_INIT_AUTOMAKE. It creates a default
-#   toplevel srcdir Makefile from the information found in the created
-#   toplevel builddir Makefile. It just copies the variables and
-#   rule-targets, each extended with a default rule-execution that recurses
-#   into the build directory of the current "HOST". You can override the
-#   auto-dection through `config.guess` and build-time of course, as in
-#
-#     make HOST=i386-mingw-cross
-#
-#   which can of course set at configure time as well using
-#
-#     configure --host=i386-mingw-cross
-#
-#   After the default has been created, additional rules can be appended
-#   that will not just recurse into the subdirectories and only ever exist
-#   in the srcdir toplevel makefile - these parts are read from the $2 =
-#   Makefile.mk file
-#
-#   The automatic rules are usually scanning the toplevel Makefile for lines
-#   like '#### $host |$builddir' to recognize the place where to recurse
-#   into. Usually, the last one is the only one used. However, almost all
-#   targets have an additional "*-all" rule which makes the script to
-#   recurse into _all_ variants of the current HOST (!!) setting. The "-all"
-#   suffix can be overriden for the macro as well.
-#
-#   a special rule is only given for things like "dist" that will copy the
-#   tarball from the builddir to the sourcedir (or $(PUB)) for reason of
-#   convenience.
-#
-# LICENSE
-#
-#   Copyright (c) 2009 Guido U. Draheim <guidod@gmx.de>
-#   Copyright (c) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
-#
-#   This program is free software; you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation; either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 25
-
-AC_DEFUN([AX_ENABLE_BUILDDIR],[
-AC_REQUIRE([AC_CANONICAL_HOST])[]dnl
-AC_REQUIRE([AC_CANONICAL_TARGET])[]dnl
-AC_REQUIRE([AX_CONFIGURE_ARGS])[]dnl
-AC_REQUIRE([AM_AUX_DIR_EXPAND])[]dnl
-AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl
-AS_VAR_PUSHDEF([SUB],[ax_enable_builddir])dnl
-AS_VAR_PUSHDEF([AUX],[ax_enable_builddir_auxdir])dnl
-AS_VAR_PUSHDEF([SED],[ax_enable_builddir_sed])dnl
-SUB="."
-AC_ARG_ENABLE([builddir], AS_HELP_STRING(
-  [--disable-builddir],[disable automatic build in subdir of sources])
-  ,[SUB="$enableval"], [SUB="auto"])
-if test ".$ac_srcdir_defaulted" != ".no" ; then
-if test ".$srcdir" = ".." ; then
-  if test -f config.status ; then
-    AC_MSG_NOTICE(toplevel srcdir already configured... skipping subdir build)
-  else
-    test ".$SUB" = "."  && SUB="."
-    test ".$SUB" = ".no"  && SUB="."
-    test ".$TARGET" = "." && TARGET="$target"
-    test ".$SUB" = ".auto" && SUB="m4_ifval([$1], [$1],[$TARGET])"
-    if test ".$SUB" != ".." ; then    # we know where to go and
-      AS_MKDIR_P([$SUB])
-      echo __.$SUB.__ > $SUB/conftest.tmp
-      cd $SUB
-      if grep __.$SUB.__ conftest.tmp >/dev/null 2>/dev/null ; then
-        rm conftest.tmp
-        AC_MSG_RESULT([continue configure in default builddir "./$SUB"])
-      else
-        AC_MSG_ERROR([could not change to default builddir "./$SUB"])
-      fi
-      srcdir=`echo "$SUB" |
-              sed -e 's,^\./,,;s,[[^/]]$,&/,;s,[[^/]]*/,../,g;s,[[/]]$,,;'`
-      # going to restart from subdirectory location
-      test -f $srcdir/config.log   && mv $srcdir/config.log   .
-      test -f $srcdir/confdefs.h   && mv $srcdir/confdefs.h   .
-      test -f $srcdir/conftest.log && mv $srcdir/conftest.log .
-      test -f $srcdir/$cache_file  && mv $srcdir/$cache_file  .
-      AC_MSG_RESULT(....exec $SHELL $srcdir/[$]0 "--srcdir=$srcdir" "--enable-builddir=$SUB" ${1+"[$]@"})
-      case "[$]0" in # restart
-       [[\\/]]* | ?:[[\\/]]*) # Asbolute name
-         eval $SHELL "'[$]0'" "'--srcdir=$srcdir'" "'--enable-builddir=$SUB'" $ac_configure_args ;;
-       *) eval $SHELL "'$srcdir/[$]0'" "'--srcdir=$srcdir'" "'--enable-builddir=$SUB'" $ac_configure_args ;;
-      esac ; exit $?
-    fi
-  fi
-fi fi
-test ".$SUB" = ".auto" && SUB="."
-dnl ac_path_prog uses "set dummy" to override $@ which would defeat the "exec"
-AC_PATH_PROG(SED,gsed sed, sed)
-AUX="$am_aux_dir"
-AS_VAR_POPDEF([SED])dnl
-AS_VAR_POPDEF([AUX])dnl
-AS_VAR_POPDEF([SUB])dnl
-AC_CONFIG_COMMANDS([buildir],[dnl .............. config.status ..............
-AS_VAR_PUSHDEF([SUB],[ax_enable_builddir])dnl
-AS_VAR_PUSHDEF([TOP],[top_srcdir])dnl
-AS_VAR_PUSHDEF([SRC],[ac_top_srcdir])dnl
-AS_VAR_PUSHDEF([AUX],[ax_enable_builddir_auxdir])dnl
-AS_VAR_PUSHDEF([SED],[ax_enable_builddir_sed])dnl
-pushdef([END],[Makefile.mk])dnl
-pushdef([_ALL],[ifelse([$3],,[-all],[$3])])dnl
-  SRC="$ax_enable_builddir_srcdir"
-  if test ".$SUB" = ".." ; then
-    if test -f "$TOP/Makefile" ; then
-      AC_MSG_NOTICE([skipping TOP/Makefile - left untouched])
-    else
-      AC_MSG_NOTICE([skipping TOP/Makefile - not created])
-    fi
-  else
-    if test -f "$SRC/Makefile" ; then
-      a=`grep "^VERSION " "$SRC/Makefile"` ; b=`grep "^VERSION " Makefile`
-      test "$a" != "$b" && rm "$SRC/Makefile"
-    fi
-    if test -f "$SRC/Makefile" ; then
-	echo "$SRC/Makefile : $SRC/Makefile.in" > $tmp/conftemp.mk
-	echo "	[]@ echo 'REMOVED,,,' >\$[]@" >> $tmp/conftemp.mk
-      eval "${MAKE-make} -f $tmp/conftemp.mk 2>/dev/null >/dev/null"
-      if grep '^REMOVED,,,' "$SRC/Makefile" >/dev/null
-      then rm $SRC/Makefile ; fi
-      cp $tmp/conftemp.mk $SRC/makefiles.mk~      ## DEBUGGING
-    fi
-    if test ! -f "$SRC/Makefile" ; then
-      AC_MSG_NOTICE([create TOP/Makefile guessed from local Makefile])
-      x='`' ; cat >$tmp/conftemp.sed <<_EOF
-/^\$/n
-x
-/^\$/bS
-x
-/\\\\\$/{H;d;}
-{H;s/.*//;x;}
-bM
-:S
-x
-/\\\\\$/{h;d;}
-{h;s/.*//;x;}
-:M
-s/\\(\\n\\)	/\\1 /g
-/^	/d
-/^[[	 ]]*[[\\#]]/d
-/^VPATH *=/d
-s/^srcdir *=.*/srcdir = ./
-s/^top_srcdir *=.*/top_srcdir = ./
-/[[:=]]/!d
-/^\\./d
-dnl Now handle rules (i.e. lines containing ":" but not " = ").
-/ = /b
-/ .= /b
-/:/!b
-s/:.*/:/
-s/ /  /g
-s/ \\([[a-z]][[a-z-]]*[[a-zA-Z0-9]]\\)\\([[ :]]\\)/ \\1 \\1[]_ALL\\2/g
-s/^\\([[a-z]][[a-z-]]*[[a-zA-Z0-9]]\\)\\([[ :]]\\)/\\1 \\1[]_ALL\\2/
-s/  / /g
-/^all all[]_ALL[[ :]]/i\\
-all-configured : all[]_ALL
-dnl dist-all exists... and would make for dist-all-all
-s/ [[a-zA-Z0-9-]]*[]_ALL [[a-zA-Z0-9-]]*[]_ALL[]_ALL//g
-/[]_ALL[]_ALL/d
-a\\
-	@ HOST="\$(HOST)\" \\\\\\
-	; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\
-	; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
-	; use=$x basename "\$\@" _ALL $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\
-	; echo "MAKE \$\$HOST : \$\$n * \$\@"; if test "\$\$n" -eq "0" ; then : \\\\\\
-	; BUILD=$x grep "^####.*|" Makefile |tail -1| sed -e 's/.*|//' $x ; fi \\\\\\
-	; test ".\$\$BUILD" = "." && BUILD="." \\\\\\
-	; test "\$\$use" = "\$\@" && BUILD=$x echo "\$\$BUILD" | tail -1 $x \\\\\\
-	; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
-	; (cd "\$\$i" && test ! -f configure && \$(MAKE) \$\$use) || exit; done
-dnl special rule add-on: "dist" copies the tarball to $(PUB). (source tree)
-/dist[]_ALL *:/a\\
-	@ HOST="\$(HOST)\" \\\\\\
-	; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\
-	; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
-	; found=$x echo \$\$BUILD | wc -w $x \\\\\\
-	; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).tar.*" \\\\\\
-	; if test "\$\$found" -eq "0" ; then : \\\\\\
-	; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\
-	; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
-	; for f in \$\$i/\$(PACKAGE)-\$(VERSION).tar.* \\\\\\
-	; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done
-dnl special rule add-on: "dist-foo" copies all the archives to $(PUB). (source tree)
-/dist-[[a-zA-Z0-9]]*[]_ALL *:/a\\
-	@ HOST="\$(HOST)\" \\\\\\
-	; test ".\$\$HOST" = "." && HOST=$x sh ./config.guess $x \\\\\\
-	; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
-	; found=$x echo \$\$BUILD | wc -w $x \\\\\\
-	; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).*" \\\\\\
-	; if test "\$\$found" -eq "0" ; then : \\\\\\
-	; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\
-	; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
-	; for f in \$\$i/\$(PACKAGE)-\$(VERSION).* \\\\\\
-	; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done
-dnl special rule add-on: "distclean" removes all local builddirs completely
-/distclean[]_ALL *:/a\\
-	@ HOST="\$(HOST)\" \\\\\\
-	; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\
-	; BUILD=$x grep "^#### .*|" Makefile | sed -e 's/.*|//' $x \\\\\\
-	; use=$x basename "\$\@" _ALL $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\
-	; echo "MAKE \$\$HOST : \$\$n * \$\@ (all local builds)" \\\\\\
-	; test ".\$\$BUILD" = "." && BUILD="." \\\\\\
-	; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
-	; echo "# rm -r \$\$i"; done ; echo "# (sleep 3)" ; sleep 3 \\\\\\
-	; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
-	; echo "\$\$i" | grep "^/" > /dev/null && continue \\\\\\
-	; echo "\$\$i" | grep "^../" > /dev/null && continue \\\\\\
-	; echo "rm -r \$\$i"; (rm -r "\$\$i") ; done ; rm Makefile
-_EOF
-      cp "$tmp/conftemp.sed" "$SRC/makefile.sed~"            ## DEBUGGING
-      $SED -f $tmp/conftemp.sed Makefile >$SRC/Makefile
-      if test -f "$SRC/m4_ifval([$2],[$2],[END])" ; then
-        AC_MSG_NOTICE([extend TOP/Makefile with TOP/m4_ifval([$2],[$2],[END])])
-        cat $SRC/END >>$SRC/Makefile
-      fi ; xxxx="####"
-      echo "$xxxx CONFIGURATIONS FOR TOPLEVEL MAKEFILE: " >>$SRC/Makefile
-      # sanity check
-      if grep '^; echo "MAKE ' $SRC/Makefile >/dev/null ; then
-        AC_MSG_NOTICE([buggy sed found - it deletes tab in "a" text parts])
-        $SED -e '/^@ HOST=/s/^/	/' -e '/^; /s/^/	/' $SRC/Makefile \
-          >$SRC/Makefile~
-        (test -s $SRC/Makefile~ && mv $SRC/Makefile~ $SRC/Makefile) 2>/dev/null
-      fi
-    else
-      xxxx="\\#\\#\\#\\#"
-      # echo "/^$xxxx *$ax_enable_builddir_host /d" >$tmp/conftemp.sed
-      echo "s!^$xxxx [[^|]]* | *$SUB *\$!$xxxx ...... $SUB!" >$tmp/conftemp.sed
-      $SED -f "$tmp/conftemp.sed" "$SRC/Makefile" >$tmp/mkfile.tmp
-        cp "$tmp/conftemp.sed" "$SRC/makefiles.sed~"         ## DEBUGGING
-        cp "$tmp/mkfile.tmp"   "$SRC/makefiles.out~"         ## DEBUGGING
-      if cmp -s "$SRC/Makefile" "$tmp/mkfile.tmp" 2>/dev/null ; then
-        AC_MSG_NOTICE([keeping TOP/Makefile from earlier configure])
-        rm "$tmp/mkfile.tmp"
-      else
-        AC_MSG_NOTICE([reusing TOP/Makefile from earlier configure])
-        mv "$tmp/mkfile.tmp" "$SRC/Makefile"
-      fi
-    fi
-    AC_MSG_NOTICE([build in $SUB (HOST=$ax_enable_builddir_host)])
-    xxxx="####"
-    echo "$xxxx" "$ax_enable_builddir_host" "|$SUB" >>$SRC/Makefile
-  fi
-popdef([END])dnl
-AS_VAR_POPDEF([SED])dnl
-AS_VAR_POPDEF([AUX])dnl
-AS_VAR_POPDEF([SRC])dnl
-AS_VAR_POPDEF([TOP])dnl
-AS_VAR_POPDEF([SUB])dnl
-],[dnl
-ax_enable_builddir_srcdir="$srcdir"                    # $srcdir
-ax_enable_builddir_host="$HOST"                        # $HOST / $host
-ax_enable_builddir_version="$VERSION"                  # $VERSION
-ax_enable_builddir_package="$PACKAGE"                  # $PACKAGE
-ax_enable_builddir_auxdir="$ax_enable_builddir_auxdir" # $AUX
-ax_enable_builddir_sed="$ax_enable_builddir_sed"       # $SED
-ax_enable_builddir="$ax_enable_builddir"               # $SUB
-])dnl
-])
diff --git a/m4/ax_extend_srcdir.m4 b/m4/ax_extend_srcdir.m4
deleted file mode 100644
index 40f37878..00000000
--- a/m4/ax_extend_srcdir.m4
+++ /dev/null
@@ -1,86 +0,0 @@
-# ===========================================================================
-#     http://www.gnu.org/software/autoconf-archive/ax_extend_srcdir.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_EXTEND_SRCDIR
-#
-# DESCRIPTION
-#
-#   The AX_EXTEND_SRCDIR macro extends $srcdir by one path component.
-#
-#   As an example, when working in /home/michael/i3-4.12/build and calling
-#   ../configure, your $srcdir is "..". After calling AX_EXTEND_SRCDIR,
-#   $srcdir will be set to "../../i3-4.12".
-#
-#   The result of extending $srcdir is that filenames (e.g. in the output of
-#   the "backtrace" gdb command) will include one more path component of the
-#   absolute source path. The additional path component makes it easy for
-#   users to recognize which files belong to the PACKAGE, and -- provided a
-#   dist tarball was unpacked -- which version of PACKAGE was used.
-#
-#   As an example, in "backtrace", you will see:
-#
-#     #0  main (argc=1, argv=0x7fffffff1fc8) at ../../i3-4.12/src/main.c:187
-#
-#   instead of:
-#
-#     #0  main (argc=1, argv=0x7fffffff1fc8) at ../src/main.c:187
-#
-#   In case your code uses the __FILE__ preprocessor directive to refer to
-#   the filename of the current source file (e.g. in debug messages), using
-#   the extended path might be undesirable. For this purpose,
-#   AX_EXTEND_SRCDIR defines the output variable AX_EXTEND_SRCDIR_CPPFLAGS,
-#   which can be added to AM_CPPFLAGS in Makefile.am in order to define the
-#   preprocessor directive STRIPPED__FILE__. As an example, when compiling
-#   the file "../../i3-4.12/src/main.c", STRIPPED__FILE__ evaluates to
-#   "main.c".
-#
-#   There are some caveats: When $srcdir is "." (i.e. when ./configure was
-#   called instead of ../configure in a separate build directory),
-#   AX_EXTEND_SRCDIR will still extend $srcdir, but the intended effect will
-#   not be achieved because of the way automake specifies file paths:
-#   automake defines COMPILE to use "`test -f '$source' || echo
-#   '\$(srcdir)/'`$source" in order to prefer files in the current directory
-#   over specifying $srcdir explicitly.
-#
-#   The AX_EXTEND_SRCDIR author is not aware of any way to influence this
-#   automake behavior. Patches very welcome.
-#
-#   To work around this issue, you can use AX_ENABLE_BUILDDIR i.e. by adding
-#   the following code to configure.ac:
-#
-#     AX_ENABLE_BUILDDIR
-#     dnl ...
-#     AX_EXTEND_SRCDIR
-#
-#   Then also add this bit to Makefile.am (if you wish to use
-#   STRIPPED__FILE__ in your code):
-#
-#     AM_CPPFLAGS = @AX_EXTEND_SRCDIR_CPPFLAGS@
-#
-# LICENSE
-#
-#   Copyright (c) 2016 Michael Stapelberg <michael@i3wm.org>
-#
-#   Copying and distribution of this file, with or without modification, are
-#   permitted in any medium without royalty provided the copyright notice
-#   and this notice are preserved.  This file is offered as-is, without any
-#   warranty.
-
-#serial 3
-
-AC_DEFUN([AX_EXTEND_SRCDIR],
-[dnl
-AS_CASE([$srcdir],
-  [.|.*|/*],
-  [
-    # pwd -P is specified in IEEE 1003.1 from 2004
-    as_dir=`cd "$srcdir" && pwd -P`
-    as_base=`AS_BASENAME([$as_dir])`
-    srcdir=${srcdir}/../${as_base}
-
-    AC_SUBST([AX_EXTEND_SRCDIR_CPPFLAGS], ["-DSTRIPPED__FILE__=AS_ESCAPE([\"$$(basename $<)\"])"])
-  ])
-])dnl AX_EXTEND_SRCDIR
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
deleted file mode 100644
index 4c4051ea..00000000
--- a/m4/ax_pthread.m4
+++ /dev/null
@@ -1,485 +0,0 @@
-# ===========================================================================
-#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
-#
-# DESCRIPTION
-#
-#   This macro figures out how to build C programs using POSIX threads. It
-#   sets the PTHREAD_LIBS output variable to the threads library and linker
-#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
-#   flags that are needed. (The user can also force certain compiler
-#   flags/libs to be tested by setting these environment variables.)
-#
-#   Also sets PTHREAD_CC to any special C compiler that is needed for
-#   multi-threaded programs (defaults to the value of CC otherwise). (This
-#   is necessary on AIX to use the special cc_r compiler alias.)
-#
-#   NOTE: You are assumed to not only compile your program with these flags,
-#   but also to link with them as well. For example, you might link with
-#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
-#
-#   If you are only building threaded programs, you may wish to use these
-#   variables in your default LIBS, CFLAGS, and CC:
-#
-#     LIBS="$PTHREAD_LIBS $LIBS"
-#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-#     CC="$PTHREAD_CC"
-#
-#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
-#   has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
-#   that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
-#
-#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
-#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
-#   PTHREAD_CFLAGS.
-#
-#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
-#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
-#   is not found. If ACTION-IF-FOUND is not specified, the default action
-#   will define HAVE_PTHREAD.
-#
-#   Please let the authors know if this macro fails on any platform, or if
-#   you have any other suggestions or comments. This macro was based on work
-#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
-#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
-#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
-#   grateful for the helpful feedback of numerous users.
-#
-#   Updated for Autoconf 2.68 by Daniel Richard G.
-#
-# LICENSE
-#
-#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
-#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
-#
-#   This program is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by the
-#   Free Software Foundation, either version 3 of the License, or (at your
-#   option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-#   Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#   As a special exception, the respective Autoconf Macro's copyright owner
-#   gives unlimited permission to copy, distribute and modify the configure
-#   scripts that are the output of Autoconf when processing the Macro. You
-#   need not follow the terms of the GNU General Public License when using
-#   or distributing such scripts, even though portions of the text of the
-#   Macro appear in them. The GNU General Public License (GPL) does govern
-#   all other use of the material that constitutes the Autoconf Macro.
-#
-#   This special exception to the GPL applies to versions of the Autoconf
-#   Macro released by the Autoconf Archive. When you make and distribute a
-#   modified version of the Autoconf Macro, you may extend this special
-#   exception to the GPL to apply to your modified version as well.
-
-#serial 23
-
-AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
-AC_DEFUN([AX_PTHREAD], [
-AC_REQUIRE([AC_CANONICAL_HOST])
-AC_REQUIRE([AC_PROG_CC])
-AC_REQUIRE([AC_PROG_SED])
-AC_LANG_PUSH([C])
-ax_pthread_ok=no
-
-# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on Tru64 or Sequent).
-# It gets checked for in the link test anyway.
-
-# First of all, check if the user has set any of the PTHREAD_LIBS,
-# etcetera environment variables, and if threads linking works using
-# them:
-if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
-        ax_pthread_save_CC="$CC"
-        ax_pthread_save_CFLAGS="$CFLAGS"
-        ax_pthread_save_LIBS="$LIBS"
-        AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
-        AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
-        AC_MSG_RESULT([$ax_pthread_ok])
-        if test "x$ax_pthread_ok" = "xno"; then
-                PTHREAD_LIBS=""
-                PTHREAD_CFLAGS=""
-        fi
-        CC="$ax_pthread_save_CC"
-        CFLAGS="$ax_pthread_save_CFLAGS"
-        LIBS="$ax_pthread_save_LIBS"
-fi
-
-# We must check for the threads library under a number of different
-# names; the ordering is very important because some systems
-# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
-# libraries is broken (non-POSIX).
-
-# Create a list of thread flags to try.  Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
-
-ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
-
-# The ordering *is* (sometimes) important.  Some notes on the
-# individual items follow:
-
-# pthreads: AIX (must check this before -lpthread)
-# none: in case threads are in libc; should be tried before -Kthread and
-#       other compiler flags to prevent continual compiler warnings
-# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
-#           (Note: HP C rejects this with "bad form for `-t' option")
-# -pthreads: Solaris/gcc (Note: HP C also rejects)
-# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-#      doesn't hurt to check since this sometimes defines pthreads and
-#      -D_REENTRANT too), HP C (must be checked before -lpthread, which
-#      is present but should not be used directly; and before -mthreads,
-#      because the compiler interprets this as "-mt" + "-hreads")
-# -mthreads: Mingw32/gcc, Lynx/gcc
-# pthread: Linux, etcetera
-# --thread-safe: KAI C++
-# pthread-config: use pthread-config program (for GNU Pth library)
-
-case $host_os in
-
-        freebsd*)
-
-        # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-        # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-
-        ax_pthread_flags="-kthread lthread $ax_pthread_flags"
-        ;;
-
-        hpux*)
-
-        # From the cc(1) man page: "[-mt] Sets various -D flags to enable
-        # multi-threading and also sets -lpthread."
-
-        ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
-        ;;
-
-        openedition*)
-
-        # IBM z/OS requires a feature-test macro to be defined in order to
-        # enable POSIX threads at all, so give the user a hint if this is
-        # not set. (We don't define these ourselves, as they can affect
-        # other portions of the system API in unpredictable ways.)
-
-        AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
-            [
-#            if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
-             AX_PTHREAD_ZOS_MISSING
-#            endif
-            ],
-            [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
-        ;;
-
-        solaris*)
-
-        # On Solaris (at least, for some versions), libc contains stubbed
-        # (non-functional) versions of the pthreads routines, so link-based
-        # tests will erroneously succeed. (N.B.: The stubs are missing
-        # pthread_cleanup_push, or rather a function called by this macro,
-        # so we could check for that, but who knows whether they'll stub
-        # that too in a future libc.)  So we'll check first for the
-        # standard Solaris way of linking pthreads (-mt -lpthread).
-
-        ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
-        ;;
-esac
-
-# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
-
-AS_IF([test "x$GCC" = "xyes"],
-      [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
-
-# The presence of a feature test macro requesting re-entrant function
-# definitions is, on some systems, a strong hint that pthreads support is
-# correctly enabled
-
-case $host_os in
-        darwin* | hpux* | linux* | osf* | solaris*)
-        ax_pthread_check_macro="_REENTRANT"
-        ;;
-
-        aix*)
-        ax_pthread_check_macro="_THREAD_SAFE"
-        ;;
-
-        *)
-        ax_pthread_check_macro="--"
-        ;;
-esac
-AS_IF([test "x$ax_pthread_check_macro" = "x--"],
-      [ax_pthread_check_cond=0],
-      [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
-
-# Are we compiling with Clang?
-
-AC_CACHE_CHECK([whether $CC is Clang],
-    [ax_cv_PTHREAD_CLANG],
-    [ax_cv_PTHREAD_CLANG=no
-     # Note that Autoconf sets GCC=yes for Clang as well as GCC
-     if test "x$GCC" = "xyes"; then
-        AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
-            [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
-#            if defined(__clang__) && defined(__llvm__)
-             AX_PTHREAD_CC_IS_CLANG
-#            endif
-            ],
-            [ax_cv_PTHREAD_CLANG=yes])
-     fi
-    ])
-ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
-
-ax_pthread_clang_warning=no
-
-# Clang needs special handling, because older versions handle the -pthread
-# option in a rather... idiosyncratic way
-
-if test "x$ax_pthread_clang" = "xyes"; then
-
-        # Clang takes -pthread; it has never supported any other flag
-
-        # (Note 1: This will need to be revisited if a system that Clang
-        # supports has POSIX threads in a separate library.  This tends not
-        # to be the way of modern systems, but it's conceivable.)
-
-        # (Note 2: On some systems, notably Darwin, -pthread is not needed
-        # to get POSIX threads support; the API is always present and
-        # active.  We could reasonably leave PTHREAD_CFLAGS empty.  But
-        # -pthread does define _REENTRANT, and while the Darwin headers
-        # ignore this macro, third-party headers might not.)
-
-        PTHREAD_CFLAGS="-pthread"
-        PTHREAD_LIBS=
-
-        ax_pthread_ok=yes
-
-        # However, older versions of Clang make a point of warning the user
-        # that, in an invocation where only linking and no compilation is
-        # taking place, the -pthread option has no effect ("argument unused
-        # during compilation").  They expect -pthread to be passed in only
-        # when source code is being compiled.
-        #
-        # Problem is, this is at odds with the way Automake and most other
-        # C build frameworks function, which is that the same flags used in
-        # compilation (CFLAGS) are also used in linking.  Many systems
-        # supported by AX_PTHREAD require exactly this for POSIX threads
-        # support, and in fact it is often not straightforward to specify a
-        # flag that is used only in the compilation phase and not in
-        # linking.  Such a scenario is extremely rare in practice.
-        #
-        # Even though use of the -pthread flag in linking would only print
-        # a warning, this can be a nuisance for well-run software projects
-        # that build with -Werror.  So if the active version of Clang has
-        # this misfeature, we search for an option to squash it.
-
-        AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
-            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
-            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
-             # Create an alternate version of $ac_link that compiles and
-             # links in two steps (.c -> .o, .o -> exe) instead of one
-             # (.c -> exe), because the warning occurs only in the second
-             # step
-             ax_pthread_save_ac_link="$ac_link"
-             ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
-             ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
-             ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
-             ax_pthread_save_CFLAGS="$CFLAGS"
-             for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
-                AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
-                CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
-                ac_link="$ax_pthread_save_ac_link"
-                AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
-                    [ac_link="$ax_pthread_2step_ac_link"
-                     AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
-                         [break])
-                    ])
-             done
-             ac_link="$ax_pthread_save_ac_link"
-             CFLAGS="$ax_pthread_save_CFLAGS"
-             AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
-             ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
-            ])
-
-        case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
-                no | unknown) ;;
-                *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
-        esac
-
-fi # $ax_pthread_clang = yes
-
-if test "x$ax_pthread_ok" = "xno"; then
-for ax_pthread_try_flag in $ax_pthread_flags; do
-
-        case $ax_pthread_try_flag in
-                none)
-                AC_MSG_CHECKING([whether pthreads work without any flags])
-                ;;
-
-                -mt,pthread)
-                AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
-                PTHREAD_CFLAGS="-mt"
-                PTHREAD_LIBS="-lpthread"
-                ;;
-
-                -*)
-                AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
-                PTHREAD_CFLAGS="$ax_pthread_try_flag"
-                ;;
-
-                pthread-config)
-                AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
-                AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
-                PTHREAD_CFLAGS="`pthread-config --cflags`"
-                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
-                ;;
-
-                *)
-                AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
-                PTHREAD_LIBS="-l$ax_pthread_try_flag"
-                ;;
-        esac
-
-        ax_pthread_save_CFLAGS="$CFLAGS"
-        ax_pthread_save_LIBS="$LIBS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-
-        # Check for various functions.  We must include pthread.h,
-        # since some functions may be macros.  (On the Sequent, we
-        # need a special flag -Kthread to make this header compile.)
-        # We check for pthread_join because it is in -lpthread on IRIX
-        # while pthread_create is in libc.  We check for pthread_attr_init
-        # due to DEC craziness with -lpthreads.  We check for
-        # pthread_cleanup_push because it is one of the few pthread
-        # functions on Solaris that doesn't have a non-functional libc stub.
-        # We try pthread_create on general principles.
-
-        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
-#                       if $ax_pthread_check_cond
-#                        error "$ax_pthread_check_macro must be defined"
-#                       endif
-                        static void routine(void *a) { a = 0; }
-                        static void *start_routine(void *a) { return a; }],
-                       [pthread_t th; pthread_attr_t attr;
-                        pthread_create(&th, 0, start_routine, 0);
-                        pthread_join(th, 0);
-                        pthread_attr_init(&attr);
-                        pthread_cleanup_push(routine, 0);
-                        pthread_cleanup_pop(0) /* ; */])],
-            [ax_pthread_ok=yes],
-            [])
-
-        CFLAGS="$ax_pthread_save_CFLAGS"
-        LIBS="$ax_pthread_save_LIBS"
-
-        AC_MSG_RESULT([$ax_pthread_ok])
-        AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
-
-        PTHREAD_LIBS=""
-        PTHREAD_CFLAGS=""
-done
-fi
-
-# Various other checks:
-if test "x$ax_pthread_ok" = "xyes"; then
-        ax_pthread_save_CFLAGS="$CFLAGS"
-        ax_pthread_save_LIBS="$LIBS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-
-        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
-        AC_CACHE_CHECK([for joinable pthread attribute],
-            [ax_cv_PTHREAD_JOINABLE_ATTR],
-            [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
-             for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
-                 AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
-                                                 [int attr = $ax_pthread_attr; return attr /* ; */])],
-                                [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
-                                [])
-             done
-            ])
-        AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
-               test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
-               test "x$ax_pthread_joinable_attr_defined" != "xyes"],
-              [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
-                                  [$ax_cv_PTHREAD_JOINABLE_ATTR],
-                                  [Define to necessary symbol if this constant
-                                   uses a non-standard name on your system.])
-               ax_pthread_joinable_attr_defined=yes
-              ])
-
-        AC_CACHE_CHECK([whether more special flags are required for pthreads],
-            [ax_cv_PTHREAD_SPECIAL_FLAGS],
-            [ax_cv_PTHREAD_SPECIAL_FLAGS=no
-             case $host_os in
-             solaris*)
-             ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
-             ;;
-             esac
-            ])
-        AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
-               test "x$ax_pthread_special_flags_added" != "xyes"],
-              [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
-               ax_pthread_special_flags_added=yes])
-
-        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
-            [ax_cv_PTHREAD_PRIO_INHERIT],
-            [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
-                                             [[int i = PTHREAD_PRIO_INHERIT;]])],
-                            [ax_cv_PTHREAD_PRIO_INHERIT=yes],
-                            [ax_cv_PTHREAD_PRIO_INHERIT=no])
-            ])
-        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
-               test "x$ax_pthread_prio_inherit_defined" != "xyes"],
-              [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
-               ax_pthread_prio_inherit_defined=yes
-              ])
-
-        CFLAGS="$ax_pthread_save_CFLAGS"
-        LIBS="$ax_pthread_save_LIBS"
-
-        # More AIX lossage: compile with *_r variant
-        if test "x$GCC" != "xyes"; then
-            case $host_os in
-                aix*)
-                AS_CASE(["x/$CC"],
-                    [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
-                    [#handle absolute path differently from PATH based program lookup
-                     AS_CASE(["x$CC"],
-                         [x/*],
-                         [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
-                         [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
-                ;;
-            esac
-        fi
-fi
-
-test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
-
-AC_SUBST([PTHREAD_LIBS])
-AC_SUBST([PTHREAD_CFLAGS])
-AC_SUBST([PTHREAD_CC])
-
-# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test "x$ax_pthread_ok" = "xyes"; then
-        ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
-        :
-else
-        ax_pthread_ok=no
-        $2
-fi
-AC_LANG_POP
-])dnl AX_PTHREAD
diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4
deleted file mode 100644
index cae11112..00000000
--- a/m4/ax_require_defined.m4
+++ /dev/null
@@ -1,37 +0,0 @@
-# ===========================================================================
-#    http://www.gnu.org/software/autoconf-archive/ax_require_defined.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_REQUIRE_DEFINED(MACRO)
-#
-# DESCRIPTION
-#
-#   AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
-#   been defined and thus are available for use.  This avoids random issues
-#   where a macro isn't expanded.  Instead the configure script emits a
-#   non-fatal:
-#
-#     ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
-#
-#   It's like AC_REQUIRE except it doesn't expand the required macro.
-#
-#   Here's an example:
-#
-#     AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
-#
-# LICENSE
-#
-#   Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
-#
-#   Copying and distribution of this file, with or without modification, are
-#   permitted in any medium without royalty provided the copyright notice
-#   and this notice are preserved. This file is offered as-is, without any
-#   warranty.
-
-#serial 1
-
-AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
-  m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
-])dnl AX_REQUIRE_DEFINED
diff --git a/m4/ax_sanitizers.m4 b/m4/ax_sanitizers.m4
deleted file mode 100644
index 836d4afd..00000000
--- a/m4/ax_sanitizers.m4
+++ /dev/null
@@ -1,130 +0,0 @@
-# ===========================================================================
-#      http://www.gnu.org/software/autoconf-archive/ax_sanitizers.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-#   AX_SANITIZERS([SANITIZERS], [ENABLED-BY-DEFAULT], [ACTION-SUCCESS])
-#
-# DESCRIPTION
-#
-#   Offers users to enable one or more sanitizers (see
-#   https://github.com/google/sanitizers) with the corresponding
-#   --enable-<sanitizer>-sanitizer option.
-#
-#   SANITIZERS is a whitespace-separated list of sanitizers to offer via
-#   --enable-<sanitizer>-sanitizer options, e.g. "address memory" for the
-#   address sanitizer and the memory sanitizer. If SANITIZERS is not specified,
-#   all known sanitizers to AX_SANITIZERS will be offered, which at the time of
-#   writing are "address memory undefined".
-#   NOTE that SANITIZERS is expanded at autoconf time, not at configure time,
-#   i.e. you cannot use shell variables in SANITIZERS.
-#
-#   ENABLED-BY-DEFAULT is a whitespace-separated list of sanitizers which
-#   should be enabled by default, e.g. "memory undefined". Note that not all
-#   sanitizers can be combined, e.g. memory sanitizer cannot be enabled when
-#   address sanitizer is already enabled.
-#   Set ENABLED-BY-DEFAULT to a single whitespace in order to disable all
-#   sanitizers by default.
-#   ENABLED-BY-DEFAULT is expanded at configure time, so you can use shell
-#   variables.
-#
-#   ACTION-SUCCESS allows to specify shell commands to execute on success, i.e.
-#   when one of the sanitizers was successfully enabled. This is a good place
-#   to call AC_DEFINE for any precompiler constants you might need to make your
-#   code play nice with sanitizers.
-#
-#   The variable ax_enabled_sanitizers contains a whitespace-separated list of
-#   all enabled sanitizers, so that you can print them at the end of configure,
-#   if you wish.
-#
-#   The additional --enable-sanitizers option allows users to enable/disable
-#   all sanitizers, effectively overriding ENABLED-BY-DEFAULT.
-#
-# EXAMPLES
-#
-#   AX_SANITIZERS([address])
-#     dnl offer users to enable address sanitizer via --enable-address-sanitizer
-#
-#   is_debug_build=…
-#   if test "x$is_debug_build" = "xyes"; then
-#     default_sanitizers="address memory"
-#   else
-#     default_sanitizers=
-#   fi
-#   AX_SANITIZERS([address memory], [$default_sanitizers])
-#     dnl enable address sanitizer and memory sanitizer by default for debug
-#     dnl builds, e.g. when building from git instead of a dist tarball.
-#
-#   AX_SANITIZERS(, , [
-#     AC_DEFINE([SANITIZERS_ENABLED],
-#               [],
-#               [At least one sanitizer was enabled])])
-#     dnl enable all sanitizers known to AX_SANITIZERS by default and set the
-#     dnl SANITIZERS_ENABLED precompiler constant.
-#
-#   AX_SANITIZERS(, [ ])
-#     dnl provide all sanitizers, but enable none by default.
-#
-# LICENSE
-#
-#   Copyright (c) 2016 Michael Stapelberg <michael@i3wm.org>
-#
-#   Copying and distribution of this file, with or without modification,
-#   are permitted in any medium without royalty provided the copyright
-#   notice and this notice are preserved.  This file is offered as-is,
-#   without any warranty.
-
-AC_DEFUN([AX_SANITIZERS],
-[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
-AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
-AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
-AC_ARG_ENABLE(sanitizers,
-  AS_HELP_STRING(
-    [--enable-sanitizers],
-    [enable all known sanitizers]),
-  [ax_sanitizers_default=$enableval],
-  [ax_sanitizers_default=])
-ax_enabled_sanitizers=
-m4_foreach_w([mysan], m4_default($1, [address memory undefined]), [
-  dnl If ax_sanitizers_default is unset, i.e. the user neither explicitly
-  dnl enabled nor explicitly disabled all sanitizers, we get the default value
-  dnl for this sanitizer based on whether it is listed in ENABLED-BY-DEFAULT.
-  AS_IF([test "x$ax_sanitizers_default" = "x"], [dnl
-          ax_sanitizer_default=
-          for mycheck in m4_default([$2], [address memory undefined]); do
-            AS_IF([test "x$mycheck" = "x[]mysan"], [ax_sanitizer_default=yes])
-          done
-          AS_IF([test "x$ax_sanitizer_default" = "x"], [ax_sanitizer_default=no])
-        ],
-        [ax_sanitizer_default=$ax_sanitizers_default])
-  AC_ARG_ENABLE(mysan[]-sanitizer,
-    AS_HELP_STRING(
-      [--enable-[]mysan[]-sanitizer],
-      [enable -fsanitize=mysan]),
-    [ax_sanitizer_enabled=$enableval],
-    [ax_sanitizer_enabled=$ax_sanitizer_default])
-
-AS_IF([test "x$ax_sanitizer_enabled" = "xyes"], [
-dnl Not using AX_APPEND_COMPILE_FLAGS and AX_APPEND_LINK_FLAGS because they
-dnl lack the ability to specify ACTION-SUCCESS.
-  AX_CHECK_COMPILE_FLAG([-fsanitize=[]mysan], [
-    AX_CHECK_LINK_FLAG([-fsanitize=[]mysan], [
-      AX_APPEND_FLAG([-fsanitize=[]mysan], [])
-dnl If and only if libtool is being used, LDFLAGS needs to contain -Wc,-fsanitize=….
-dnl See e.g. https://sources.debian.net/src/systemd/231-7/configure.ac/?hl=128#L135
-dnl TODO: how can recognize that situation and add -Wc,?
-      AX_APPEND_FLAG([-fsanitize=[]mysan], [LDFLAGS])
-dnl TODO: add -fPIE -pie for memory
-      # -fno-omit-frame-pointer results in nicer stack traces in error
-      # messages, see http://clang.llvm.org/docs/AddressSanitizer.html#usage
-      AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], [
-        AX_APPEND_FLAG([-fno-omit-frame-pointer], [])])
-dnl TODO: at least for clang, we should specify exactly -O1, not -O2 or -O0, so that performance is reasonable but stacktraces are not tampered with (due to inlining), see http://clang.llvm.org/docs/AddressSanitizer.html#usage
-      m4_default([$3], :)
-      ax_enabled_sanitizers="[]mysan $ax_enabled_sanitizers"
-    ])
-  ])
-])
-])dnl
-])dnl AX_SANITIZERS
diff --git a/meson.build b/meson.build
new file mode 100644
index 00000000..11541e21
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,719 @@
+# -*- mode: meson -*-
+
+# Style objective: be consistent with what mesonbuild.com documents/uses, and/or
+# the meson book: https://meson-manual.com/
+
+project(
+  'i3',
+  'c',
+  version: '4.19',
+  default_options: [
+    'c_std=c11',
+    'warning_level=1',  # enable all warnings (-Wall)
+    # TODO(https://github.com/i3/i3/issues/4087): switch to
+    # 'buildtype=debugoptimized',
+  ],
+  # Ubuntu 18.04 (supported until 2023) has meson 0.45.
+  # We can revisit our minimum supported meson version
+  # if it turns out to be too hard to maintain.
+  meson_version: '>=0.45.0',
+)
+
+cc = meson.get_compiler('c')
+add_project_arguments(cc.get_supported_arguments(['-Wunused-value']), language: 'c')
+
+if meson.version().version_compare('>=0.48.0')
+  # https://github.com/mesonbuild/meson/issues/2166#issuecomment-629696911
+  meson.add_dist_script('meson/meson-dist-script')
+else
+  message('meson <0.48.0 detected, dist tarballs will not be filtered')
+endif
+
+################################################################################
+# Version handling
+################################################################################
+
+cdata = configuration_data()
+
+version_array = meson.project_version().split('.')
+cdata.set('MAJOR_VERSION', version_array[0].to_int())
+cdata.set('MINOR_VERSION', version_array[1].to_int())
+if version_array.length() > 2
+  cdata.set('PATCH_VERSION', version_array[2].to_int())
+else
+  cdata.set('PATCH_VERSION', 0)
+endif
+cdata.set_quoted('I3_VERSION', '@VCS_TAG@')
+cdata.set_quoted('SYSCONFDIR', join_paths(get_option('prefix'), get_option('sysconfdir')))
+
+if get_option('b_sanitize').split(',').contains('address')
+  cdata.set('I3_ASAN_ENABLED', 1)
+endif
+
+cdata.set('HAVE_STRNDUP', cc.has_function('strndup'))
+cdata.set('HAVE_MKDIRP', cc.has_function('mkdirp'))
+
+# Instead of generating config.h directly, make vcs_tag generate it so that
+# @VCS_TAG@ is replaced.
+config_h_in = configure_file(
+  output: 'config.h.in',
+  configuration: cdata,
+)
+config_h = declare_dependency(
+  sources: vcs_tag(
+    input: config_h_in,
+    output: 'config.h',
+    fallback: meson.project_version() + ' (2020-11-15)',
+  )
+)
+
+################################################################################
+# docs generation
+################################################################################
+
+docdir = get_option('docdir')
+if docdir == ''
+  docdir = join_paths(get_option('datadir'), 'doc', 'i3')
+endif
+
+if get_option('docs')
+  asciidoc = find_program('asciidoc')
+  doc_toc_inputs = [
+    'docs/hacking-howto',
+    'docs/userguide',
+    'docs/ipc',
+    'docs/multi-monitor',
+    'docs/wsbar',
+    'docs/testsuite',
+    'docs/i3bar-protocol',
+    'docs/layout-saving',
+  ]
+  foreach m : doc_toc_inputs
+    custom_target(
+      m.underscorify()+'_asciidoc',
+      input: m,
+      output: '@BASENAME@.html',
+      command: [
+        asciidoc,
+        '-a', 'toc',
+        '-n',
+        '-o', '@OUTPUT@',
+        '@INPUT@',
+      ],
+      install: true,
+      install_dir: docdir,
+    )
+  endforeach
+
+  doc_notoc_inputs = [
+    'docs/debugging',
+  ]
+  foreach m : doc_notoc_inputs
+    custom_target(
+      m.underscorify()+'_asciidoc',
+      input: m,
+      output: '@BASENAME@.html',
+      command: [
+        asciidoc,
+        '-n',
+        '-o', '@OUTPUT@',
+        '@INPUT@',
+      ],
+      install: true,
+      install_dir: docdir,
+    )
+  endforeach
+
+else
+  if run_command('[', '-f', 'docs/hacking-howto.html', ']').returncode() == 0
+    install_data(
+      [
+	'docs/hacking-howto.html',
+	'docs/userguide.html',
+	'docs/ipc.html',
+	'docs/multi-monitor.html',
+	'docs/wsbar.html',
+	'docs/testsuite.html',
+	'docs/i3bar-protocol.html',
+	'docs/layout-saving.html',
+	'docs/debugging.html',
+      ],
+      install_dir: docdir,
+    )
+  endif
+endif
+
+install_data(
+  [
+    'docs/bigpicture.png',
+    'docs/single_terminal.png',
+    'docs/snapping.png',
+    'docs/two_columns.png',
+    'docs/two_terminals.png',
+    'docs/modes.png',
+    'docs/wsbar.png',
+    'docs/keyboard-layer1.png',
+    'docs/keyboard-layer2.png',
+    'docs/i3-sync-working.png',
+    'docs/i3-sync.png',
+    'docs/tree-layout1.png',
+    'docs/tree-layout2.png',
+    'docs/tree-shot1.png',
+    'docs/tree-shot2.png',
+    'docs/tree-shot3.png',
+    'docs/tree-shot4.png',
+    'docs/refcard.html',
+    'docs/refcard_style.css',
+    'docs/logo-30.png',
+    'docs/layout-saving-1.png',
+  ],
+  install_dir: docdir,
+)
+
+if meson.version().version_compare('>=0.53')
+  summary('build docs (-Ddocs)', get_option('docs'))
+endif
+
+################################################################################
+# manpages
+################################################################################
+
+man1 = join_paths(get_option('mandir'), 'man1')
+
+if get_option('mans')
+  asciidoc = find_program('asciidoc')
+  asciidoc_cdata = configuration_data()
+  asciidoc_cdata.set('PACKAGE_VERSION', meson.project_version())
+  asciidoc_conf = configure_file(
+    input: 'man/asciidoc.conf.in',
+    output: 'asciidoc.conf',
+    configuration: asciidoc_cdata,
+  )
+
+  xmlto = find_program('xmlto')
+
+  pod2man = find_program('pod2man')
+
+  man_inputs = [
+    'man/i3.man',
+    'man/i3bar.man',
+    'man/i3-msg.man',
+    'man/i3-input.man',
+    'man/i3-nagbar.man',
+    'man/i3-config-wizard.man',
+    'man/i3-migrate-config-to-v4.man',
+    'man/i3-sensible-editor.man',
+    'man/i3-sensible-pager.man',
+    'man/i3-sensible-terminal.man',
+    'man/i3-dump-log.man',
+  ]
+
+  foreach m : man_inputs
+    xml = custom_target(
+      m.underscorify()+'_asciidoc',
+      input: m,
+      output: '@BASENAME@.xml',
+      command: [
+        asciidoc,
+        '-d', 'manpage',
+        '-b', 'docbook',
+        '-f', asciidoc_conf,
+        '-o', '@OUTPUT@',
+        '@INPUT@',
+      ],
+    )
+
+    custom_target(
+      m.underscorify()+'_xmlto',
+      input: xml,
+      output: '@BASENAME@.1',
+      command: [
+        xmlto,
+        'man',
+        '-o',
+        '@OUTDIR@',
+        '@INPUT@',
+      ],
+      # We should use install and install_dir instead of install_man as per:
+      # https://github.com/mesonbuild/meson/issues/4981#issuecomment-467084867
+      # https://github.com/mesonbuild/meson/issues/1550#issuecomment-370164307
+      install: true,
+      install_dir: man1,
+    )
+  endforeach
+
+  pod2man_inputs = [
+    'i3-dmenu-desktop',
+    'i3-save-tree',
+  ]
+  foreach m : pod2man_inputs
+    custom_target(
+      m.underscorify()+'_pod2man',
+      input: m,
+      output: '@BASENAME@.1',
+      command: [
+        pod2man,
+        '--utf8',
+        '@INPUT@',
+        '@OUTPUT@',
+      ],
+      # We should use install and install_dir instead of install_man as per:
+      # https://github.com/mesonbuild/meson/issues/4981#issuecomment-467084867
+      # https://github.com/mesonbuild/meson/issues/1550#issuecomment-370164307
+      install: true,
+      install_dir: man1,
+    )
+  endforeach
+
+else
+  if run_command('[', '-f', 'man/i3.1', ']').returncode() == 0
+    install_data(
+      [
+	'man/i3.1',
+	'man/i3bar.1',
+	'man/i3-msg.1',
+	'man/i3-input.1',
+	'man/i3-nagbar.1',
+	'man/i3-config-wizard.1',
+	'man/i3-migrate-config-to-v4.1',
+	'man/i3-sensible-editor.1',
+	'man/i3-sensible-pager.1',
+	'man/i3-sensible-terminal.1',
+	'man/i3-dump-log.1',
+	'man/i3-dmenu-desktop.1',
+	'man/i3-save-tree.1',
+      ],
+      install_dir: man1,
+    )
+  endif
+endif
+
+if meson.version().version_compare('>=0.53')
+  summary('build manpages (-Dmans)', get_option('mans'))
+endif
+
+# Required for e.g. struct ucred to be defined as per unix(7).
+add_project_arguments('-D_GNU_SOURCE', language: 'c')
+
+# https://mesonbuild.com/howtox.html#add-math-library-lm-portably
+m_dep = cc.find_library('m', required: false)
+rt_dep = cc.find_library('rt', required: false)
+iconv_dep = cc.find_library('iconv', required: false)
+
+libsn_dep = dependency('libstartup-notification-1.0', method: 'pkg-config')
+xcb_dep = dependency('xcb', method: 'pkg-config')
+xcb_xkb_dep = dependency('xcb-xkb', method: 'pkg-config')
+xcb_xinerama_dep = dependency('xcb-xinerama', method: 'pkg-config')
+xcb_randr_dep = dependency('xcb-randr', method: 'pkg-config')
+xcb_shape_dep = dependency('xcb-shape', method: 'pkg-config')
+xcb_util_dep = dependency('xcb-util', method: 'pkg-config')
+xcb_util_cursor_dep = dependency('xcb-cursor', method: 'pkg-config')
+xcb_util_keysyms_dep = dependency('xcb-keysyms', method: 'pkg-config')
+xcb_util_wm_dep = dependency('xcb-icccm', method: 'pkg-config')
+xcb_util_xrm_dep = dependency('xcb-xrm', method: 'pkg-config')
+xkbcommon_dep = dependency('xkbcommon', method: 'pkg-config')
+xkbcommon_x11_dep = dependency('xkbcommon-x11', method: 'pkg-config')
+yajl_dep = dependency('yajl', method: 'pkg-config')
+libpcre_dep = dependency('libpcre', version: '>=8.10', method: 'pkg-config')
+cairo_dep = dependency('cairo', version: '>=1.14.4', method: 'pkg-config')
+pangocairo_dep = dependency('pangocairo', method: 'pkg-config')
+glib_dep = dependency('glib-2.0', method: 'pkg-config')
+gobject_dep = dependency('gobject-2.0', method: 'pkg-config')
+
+ev_dep = cc.find_library('ev')
+
+inc = include_directories('include')
+
+libi3srcs = [
+  'libi3/dpi.c',
+  'libi3/draw_util.c',
+  'libi3/fake_configure_notify.c',
+  'libi3/font.c',
+  'libi3/format_placeholders.c',
+  'libi3/get_colorpixel.c',
+  'libi3/get_config_path.c',
+  'libi3/get_exe_path.c',
+  'libi3/get_mod_mask.c',
+  'libi3/get_process_filename.c',
+  'libi3/get_visualtype.c',
+  'libi3/g_utf8_make_valid.c',
+  'libi3/ipc_connect.c',
+  'libi3/ipc_recv_message.c',
+  'libi3/ipc_send_message.c',
+  'libi3/is_debug_build.c',
+  'libi3/resolve_tilde.c',
+  'libi3/root_atom_contents.c',
+  'libi3/safewrappers.c',
+  'libi3/string.c',
+  'libi3/ucs2_conversion.c',
+]
+
+if not cdata.get('HAVE_STRNDUP')
+  libi3srcs += 'libi3/strndup.c'
+endif
+
+if not cdata.get('HAVE_MKDIRP')
+  libi3srcs += 'libi3/mkdirp.c'
+endif
+
+libi3 = static_library(
+  'i3',
+  libi3srcs,
+  include_directories: inc,
+  dependencies: [
+    pangocairo_dep,
+    config_h,
+  ],
+)
+
+i3srcs = [
+  'src/assignments.c',
+  'src/bindings.c',
+  'src/click.c',
+  'src/commands.c',
+  'src/commands_parser.c',
+  'src/con.c',
+  'src/config.c',
+  'src/config_directives.c',
+  'src/config_parser.c',
+  'src/display_version.c',
+  'src/drag.c',
+  'src/ewmh.c',
+  'src/fake_outputs.c',
+  'src/floating.c',
+  'src/handlers.c',
+  'src/ipc.c',
+  'src/key_press.c',
+  'src/load_layout.c',
+  'src/log.c',
+  'src/main.c',
+  'src/manage.c',
+  'src/match.c',
+  'src/move.c',
+  'src/output.c',
+  'src/randr.c',
+  'src/regex.c',
+  'src/render.c',
+  'src/resize.c',
+  'src/restore_layout.c',
+  'src/scratchpad.c',
+  'src/sd-daemon.c',
+  'src/sighandler.c',
+  'src/startup.c',
+  'src/sync.c',
+  'src/tree.c',
+  'src/util.c',
+  'src/version.c',
+  'src/window.c',
+  'src/workspace.c',
+  'src/x.c',
+  'src/xcb.c',
+  'src/xcursor.c',
+  'src/xinerama.c',
+]
+
+# Verify the perl interpreter is present for running parser_gen,
+# ensuring a good error message when it isn’t:
+perl = find_program('perl')
+parser_gen = find_program('generate-command-parser.pl')
+
+command_parser = custom_target(
+  'command_parser',
+  input: 'parser-specs/commands.spec',
+  output: [
+    'GENERATED_command_enums.h',
+    'GENERATED_command_tokens.h',
+    'GENERATED_command_call.h',
+  ],
+  command: [perl, parser_gen, '--input=@INPUT@', '--prefix=command'],
+)
+
+i3srcs += command_parser
+
+config_parser = custom_target(
+  'config_parser',
+  input: 'parser-specs/config.spec',
+  output: [
+    'GENERATED_config_enums.h',
+    'GENERATED_config_tokens.h',
+    'GENERATED_config_call.h',
+  ],
+  command: [parser_gen, '--input=@INPUT@', '--prefix=config'],
+)
+
+i3srcs += config_parser
+
+# src/log.c uses threading primitives for synchronization
+thread_dep = dependency('threads')
+
+common_deps = [
+  thread_dep,
+  m_dep,
+  iconv_dep,
+  rt_dep,
+  libsn_dep,
+  xcb_dep,
+  xcb_xkb_dep,
+  xcb_xinerama_dep,
+  xcb_randr_dep,
+  xcb_shape_dep,
+  xcb_util_dep,
+  xcb_util_cursor_dep,
+  xcb_util_keysyms_dep,
+  xcb_util_wm_dep,
+  xcb_util_xrm_dep,
+  xkbcommon_dep,
+  xkbcommon_x11_dep,
+  yajl_dep,
+  libpcre_dep,
+  cairo_dep,
+  pangocairo_dep,
+  glib_dep,
+  gobject_dep,
+  ev_dep,
+  config_h,
+]
+
+executable(
+  'i3',
+  i3srcs,
+  install: true,
+  include_directories: inc,
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+# This is the only currently working way of installing a symbolic link:
+meson.add_install_script(
+  'meson/meson-install-i3-with-shmlog',
+  get_option('bindir'),
+)
+
+executable(
+  'i3bar',
+  [
+    'i3bar/src/child.c',
+    'i3bar/src/config.c',
+    'i3bar/src/ipc.c',
+    'i3bar/src/main.c',
+    'i3bar/src/mode.c',
+    'i3bar/src/outputs.c',
+    'i3bar/src/parse_json_header.c',
+    'i3bar/src/workspaces.c',
+    'i3bar/src/xcb.c',
+  ],
+  install: true,
+  include_directories: include_directories('include', 'i3bar/include'),
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'i3-config-wizard',
+  [
+    'i3-config-wizard/i3-config-wizard-atoms.xmacro.h',
+    'i3-config-wizard/main.c',
+    'i3-config-wizard/xcb.h',
+  ],
+  install: true,
+  include_directories: include_directories('include', 'i3-config-wizard'),
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'i3-dump-log',
+  'i3-dump-log/main.c',
+  install: true,
+  include_directories: inc,
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'i3-input',
+  [
+    'i3-input/i3-input.h',
+    'i3-input/keysym2ucs.h',
+    'i3-input/keysym2ucs.c',
+    'i3-input/main.c',
+  ],
+  install: true,
+  include_directories: inc,
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'i3-msg',
+  'i3-msg/main.c',
+  install: true,
+  include_directories: inc,
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'i3-nagbar',
+  [
+    'i3-nagbar/i3-nagbar-atoms.xmacro.h',
+    'i3-nagbar/main.c',
+  ],
+  install: true,
+  include_directories: include_directories('include', 'i3-nagbar'),
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+install_data(
+  [
+    'i3-dmenu-desktop',
+    'i3-migrate-config-to-v4',
+    'i3-save-tree',
+    'i3-sensible-editor',
+    'i3-sensible-pager',
+    'i3-sensible-terminal',
+  ],
+  install_dir: 'bin',
+)
+
+install_subdir(
+  'etc',
+  strip_directory: true,
+  install_dir: join_paths(get_option('sysconfdir'), 'i3'),
+)
+
+install_subdir(
+  'share/',
+  strip_directory: true,
+  install_dir: get_option('datadir'),
+)
+
+install_headers(
+  'include/i3/ipc.h',
+  subdir: 'i3',
+)
+
+# We cannot use configure_file for complete-run.pl.in and i3test.pm.in
+# because configure_file strips the backslash in e.g. \@display,
+# resulting in @display, breaking our Perl code:
+# https://github.com/mesonbuild/meson/issues/7165
+bash = find_program('bash')
+replace_dirs = [
+  bash, '-c',  # Use bash to capture output and mark as executable
+  'sed -e \'s,@abs_top_builddir@,'
+  + meson.current_build_dir()
+  + ',g;s,@abs_top_srcdir@,'
+  + meson.current_source_dir()+',g\''
+  # Only mark files ending in .pl as executables
+  + ' "$0" > "$1" && { [[ "${1##*.}" == pl ]] && chmod +x "$1" || true; }',
+  '@INPUT0@',   # $0
+  '@OUTPUT0@',  # $1
+]
+complete_run = custom_target(
+  'complete-run',
+  input: ['testcases/complete-run.pl.in'],
+  output: ['complete-run.pl'],
+  command: replace_dirs,
+  # build this target when running e.g. ninja or ninja test.
+  # This is required for older meson versions (< 0.46.0).
+  build_by_default: true,
+)
+i3test_pm = custom_target(
+  'i3test-pm',
+  input: ['testcases/lib/i3test.pm.in'],
+  output: ['i3test.pm'],
+  command: replace_dirs,
+  # build this target when running e.g. ninja or ninja test.
+  # This is required for older meson versions (< 0.46.0).
+  build_by_default: true,
+)
+
+if get_option('docs')
+  i3_pod2html = find_program('docs/i3-pod2html')
+
+  custom_target(
+    'lib-i3test.html',
+    input: i3test_pm,
+    output: 'lib-i3test.html',
+    command: [
+      i3_pod2html,
+      '@INPUT@',
+      '@OUTPUT@',
+    ],
+    install: true,
+    install_dir: join_paths(get_option('datadir'), 'doc', 'i3'),
+  )
+
+  custom_target(
+    'lib-i3test-test.html',
+    input: 'testcases/lib/i3test/Test.pm',
+    output: 'lib-i3test-test.html',
+    command: [
+      i3_pod2html,
+      '@INPUT@',
+      '@OUTPUT@',
+    ],
+    install: true,
+    install_dir: join_paths(get_option('datadir'), 'doc', 'i3'),
+  )
+endif
+
+executable(
+  'test.inject_randr15',
+  'testcases/inject_randr1.5.c',
+  include_directories: inc,
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'test.commands_parser',
+  'src/commands_parser.c',
+  include_directories: inc,
+  c_args: '-DTEST_PARSER',
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+executable(
+  'test.config_parser',
+  'src/config_parser.c',
+  include_directories: inc,
+  c_args: '-DTEST_PARSER',
+  dependencies: common_deps,
+  link_with: libi3,
+)
+
+anyevent_i3 = custom_target(
+  'anyevent-i3',
+  # Should be AnyEvent-I3/blib/lib/AnyEvent/I3.pm,
+  # but see https://github.com/mesonbuild/meson/issues/2320
+  output: 'AnyEvent-I3.stamp',
+  command: [
+    'sh',
+    '-c',
+    'cp -r @0@/AnyEvent-I3 . && cd AnyEvent-I3 && perl Makefile.PL && make && touch ../AnyEvent-I3.stamp'.format(meson.current_source_dir()),
+  ],
+)
+
+if meson.version().version_compare('>=0.46.0')
+  test(
+    'complete-run',
+    perl,
+    args: [complete_run],
+    depends: [
+      anyevent_i3,
+      i3test_pm,
+    ],
+  )
+else
+  # meson < 0.46.0 does not support the depends arg in test targets.
+  # Just hope for the best.
+  test(
+    'complete-run',
+    perl,
+    args: [complete_run],
+  )
+  message('meson < 0.46 detected, you might need to run ninja test twice')
+endif
diff --git a/meson/meson-dist-script b/meson/meson-dist-script
new file mode 100755
index 00000000..47d9ce36
--- /dev/null
+++ b/meson/meson-dist-script
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+set -eu
+
+cd "${MESON_DIST_ROOT}"
+
+# Delete everything we do not want to have in the release tarballs:
+rm -rf \
+   contrib/banner.svg \
+   contrib/show-download-count.sh \
+   contrib/sticker-7x5cm-stickma.tif.lzma \
+   contrib/sticker_stickma_black.svg \
+   debian/ \
+   docs/GPN-2009-06-27/ \
+   docs/NoName-2009-03-12/ \
+   docs/slides-2012-01-25/ \
+   docs/slides-2012-03-16/ \
+   testcases/.gitignore \
+   travis/ \
+   .clang-format \
+   .editorconfig \
+   i3bar/.gitignore \
+   .travis.yml \
+   logo.svg \
+   README.md \
+   RELEASE-NOTES-next \
+   release.sh
+
+mkdir build
+cd build
+meson .. -Dprefix=/usr -Ddocs=true -Dmans=true
+ninja
+cp *.1 ../man/
+cp *.html ../docs/
+cd ..
+rm -rf build
diff --git a/meson/meson-install-i3-with-shmlog b/meson/meson-install-i3-with-shmlog
new file mode 100755
index 00000000..2290a47a
--- /dev/null
+++ b/meson/meson-install-i3-with-shmlog
@@ -0,0 +1,2 @@
+#!/bin/sh
+ln -sf "i3" "${MESON_INSTALL_DESTDIR_PREFIX}/$1/i3-with-shmlog"
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 00000000..0c4d4cfb
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,10 @@
+# -*- mode: meson -*-
+
+option('docs', type: 'boolean', value: false,
+       description: 'Build documentation from source (release tarballs contain a generated copy)')
+
+option('mans', type: 'boolean', value: false,
+       description: 'Build manpages from source (release tarballs contain a generated copy)')
+
+option('docdir', type: 'string', value: '',
+       description: 'documentation directory (default: $datadir/docs/i3)')
diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec
index f6e541e9..20de3e44 100644
--- a/parser-specs/commands.spec
+++ b/parser-specs/commands.spec
@@ -149,7 +149,7 @@ state WORKSPACE:
       -> call cmd_workspace_back_and_forth()
   'number'
       -> WORKSPACE_NUMBER
-  workspace = string 
+  workspace = string
       -> call cmd_workspace_name($workspace, $no_auto_back_and_forth)
 
 state WORKSPACE_NUMBER:
@@ -339,14 +339,15 @@ state RENAME_WORKSPACE_TO_NEW_NAME:
   new_name = string
       -> call cmd_rename_workspace($old_name, $new_name)
 
-# move <direction> [<pixels> [px]]
+
+# move <direction> [<amount> [px|ppt]]
 # move [window|container] [to] workspace [<str>|next|prev|next_on_output|prev_on_output|current]
 # move [window|container] [to] output <str>
 # move [window|container] [to] mark <str>
 # move [window|container] [to] scratchpad
 # move workspace to [output] <str>
 # move scratchpad
-# move [window|container] [to] [absolute] position [ [<pixels> [px] <pixels> [px]] | center ]
+# move [window|container] [to] [absolute] position [ [<pos_x> [px|ppt] <pos_y> [px|ppt] ] | center ]
 # move [window|container] [to] position mouse|cursor|pointer
 state MOVE:
   'window'
@@ -373,16 +374,16 @@ state MOVE:
       -> MOVE_TO_ABSOLUTE_POSITION
 
 state MOVE_DIRECTION:
-  pixels = number
-      -> MOVE_DIRECTION_PX
+  amount = number
+      -> MOVE_DIRECTION_NUMBER
   end
-      -> call cmd_move_direction($direction, 10)
+      -> call cmd_move_direction($direction, 10, "px")
 
-state MOVE_DIRECTION_PX:
-  'px'
-      -> call cmd_move_direction($direction, &pixels)
+state MOVE_DIRECTION_NUMBER:
+  mode = 'px', 'ppt'
+      -> call cmd_move_direction($direction, &amount, $mode)
   end
-      -> call cmd_move_direction($direction, &pixels)
+      -> call cmd_move_direction($direction, &amount, "px")
 
 state MOVE_WORKSPACE:
   'to '
@@ -427,14 +428,16 @@ state MOVE_TO_POSITION:
       -> MOVE_TO_POSITION_X
 
 state MOVE_TO_POSITION_X:
-  'px'
+  mode_x = 'px', 'ppt'
       ->
   coord_y = number
       -> MOVE_TO_POSITION_Y
 
 state MOVE_TO_POSITION_Y:
-  'px', end
-      -> call cmd_move_window_to_position(&coord_x, &coord_y)
+  mode_y = 'px', 'ppt'
+      -> call cmd_move_window_to_position(&coord_x, $mode_x, &coord_y, $mode_y)
+  end
+      -> call cmd_move_window_to_position(&coord_x, $mode_x, &coord_y, 0)
 
 # mode <string>
 state MODE:
@@ -472,21 +475,27 @@ state TITLE_FORMAT:
 
 # bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]
 state BAR:
-  bar_type = 'hidden_state'
+  'hidden_state'
       -> BAR_HIDDEN_STATE
-  bar_type = 'mode'
+  'mode'
       -> BAR_MODE
 
 state BAR_HIDDEN_STATE:
   bar_value = 'hide', 'show', 'toggle'
-      -> BAR_W_ID
+      -> BAR_HIDDEN_STATE_ID
+
+state BAR_HIDDEN_STATE_ID:
+  bar_id = word
+      ->
+  end
+      -> call cmd_bar_hidden_state($bar_value, $bar_id)
 
 state BAR_MODE:
   bar_value = 'dock', 'hide', 'invisible', 'toggle'
-      -> BAR_W_ID
+      -> BAR_MODE_ID
 
-state BAR_W_ID:
+state BAR_MODE_ID:
   bar_id = word
       ->
   end
-      -> call cmd_bar($bar_type, $bar_value, $bar_id)
+      -> call cmd_bar_mode($bar_value, $bar_id)
diff --git a/release.sh b/release.sh
index 095bc50b..c6b045c0 100755
--- a/release.sh
+++ b/release.sh
@@ -1,8 +1,8 @@
 #!/bin/zsh
 # This script is used to prepare a new release of i3.
 
-export RELEASE_VERSION="4.17"
-export PREVIOUS_VERSION="4.16"
+export RELEASE_VERSION="4.19"
+export PREVIOUS_VERSION="4.18"
 export RELEASE_BRANCH="next"
 
 if [ ! -e "../i3.github.io" ]
@@ -55,49 +55,43 @@ git checkout -b release-${RELEASE_VERSION}
 cp "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" "RELEASE-NOTES-${RELEASE_VERSION}"
 git add RELEASE-NOTES-${RELEASE_VERSION}
 git rm RELEASE-NOTES-${PREVIOUS_VERSION}
-sed -i "s,RELEASE-NOTES-${PREVIOUS_VERSION},RELEASE-NOTES-${RELEASE_VERSION},g" Makefile.am
-sed -i "s/AC_INIT(\[i3\], \[${PREVIOUS_VERSION}\]/AC_INIT([i3], [${RELEASE_VERSION}]/" configure.ac
-echo "${RELEASE_VERSION} ($(date +%F))" > I3_VERSION
-git add I3_VERSION
+sed -i "s/^\s*version: '${PREVIOUS_VERSION}'/    version: '${RELEASE_VERSION}'/" meson.build
 git commit -a -m "release i3 ${RELEASE_VERSION}"
 git tag "${RELEASE_VERSION}" -m "release i3 ${RELEASE_VERSION}" --sign --local-user=0x4AC8EE1D
 
-autoreconf -fi
 mkdir build
-(cd build && ../configure && make dist-bzip2 -j8)
-cp build/i3-${RELEASE_VERSION}.tar.bz2 .
+(cd build && meson .. && ninja dist)
+cp build/meson-build/i3-${RELEASE_VERSION}.tar.xz .
 
 echo "Differences in the release tarball file lists:"
 
-diff -u \
-	<(tar tf ../i3-${PREVIOUS_VERSION}.tar.bz2 | sed "s,i3-${PREVIOUS_VERSION}/,,g" | sort) \
-	<(tar tf    i3-${RELEASE_VERSION}.tar.bz2  | sed "s,i3-${RELEASE_VERSION}/,,g"  | sort) \
-	| colordiff
-
+diff --color -u \
+	<(tar tf ../i3-${PREVIOUS_VERSION}.tar.xz | sed "s,i3-${PREVIOUS_VERSION}/,,g" | sort) \
+	<(tar tf    i3-${RELEASE_VERSION}.tar.xz  | sed "s,i3-${RELEASE_VERSION}/,,g"  | sort)
 
-gpg --armor -b i3-${RELEASE_VERSION}.tar.bz2
+gpg --armor -b i3-${RELEASE_VERSION}.tar.xz
 
 echo "${RELEASE_VERSION}-non-git" > I3_VERSION
 git add I3_VERSION
 git commit -a -m "Set non-git version to ${RELEASE_VERSION}-non-git."
 
-if [ "${RELEASE_BRANCH}" = "master" ]; then
-	git checkout master
+if [ "${RELEASE_BRANCH}" = "stable" ]; then
+	git checkout stable
 	git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
 	git checkout next
-	git merge --no-ff -s recursive -X ours -X no-renames master -m "Merge branch 'master' into next"
+	git merge --no-ff -s recursive -X ours -X no-renames stable -m "Merge branch 'stable' into next"
 else
 	git checkout next
 	git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
-	git checkout master
-	git merge --no-ff -s recursive -X theirs -X no-renames next -m "Merge branch 'next' into master"
+	git checkout stable
+	git merge --no-ff -s recursive -X theirs -X no-renames next -m "Merge branch 'next' into stable"
 fi
 
 git remote remove origin
 git remote add origin git@github.com:i3/i3.git
 git config --add remote.origin.push "+refs/tags/*:refs/tags/*"
 git config --add remote.origin.push "+refs/heads/next:refs/heads/next"
-git config --add remote.origin.push "+refs/heads/master:refs/heads/master"
+git config --add remote.origin.push "+refs/heads/stable:refs/heads/stable"
 
 ################################################################################
 # Section 2: Debian packaging
@@ -115,9 +109,9 @@ cat > ${TMPDIR}/Dockerfile <<EOT
 FROM debian:sid
 RUN sed -i 's,^deb \(.*\),deb \1\ndeb-src \1,g' /etc/apt/sources.list
 RUN apt-get update && apt-get install -y dpkg-dev devscripts
-COPY i3/i3-${RELEASE_VERSION}.tar.bz2 /usr/src/i3-wm_${RELEASE_VERSION}.orig.tar.bz2
+COPY i3/i3-${RELEASE_VERSION}.tar.xz /usr/src/i3-wm_${RELEASE_VERSION}.orig.tar.xz
 WORKDIR /usr/src/
-RUN tar xf i3-wm_${RELEASE_VERSION}.orig.tar.bz2
+RUN tar xf i3-wm_${RELEASE_VERSION}.orig.tar.xz
 WORKDIR /usr/src/i3-${RELEASE_VERSION}
 COPY i3/debian /usr/src/i3-${RELEASE_VERSION}/debian/
 RUN mkdir debian/source
@@ -130,7 +124,7 @@ RUN dpkg-buildpackage -S -sa -j8
 EOT
 
 CONTAINER_NAME=$(echo "i3-${TMPDIR}" | sed 's,/,,g')
-docker build -t i3 .
+docker build --no-cache -t i3 .
 for file in $(docker run --name "${CONTAINER_NAME}" i3 /bin/sh -c "ls /usr/src/i3*_${RELEASE_VERSION}*")
 do
 	docker cp "${CONTAINER_NAME}:${file}" ${TMPDIR}/debian/
@@ -161,14 +155,14 @@ tar cf - '--exclude=[0-9]\.[0-9e]*' docs | tar xf - --strip-components=1 -C docs
 git add docs/${PREVIOUS_VERSION}
 git commit -a -m "save docs for ${PREVIOUS_VERSION}"
 
-cp ${TMPDIR}/i3/i3-${RELEASE_VERSION}.tar.bz2* downloads/
-git add downloads/i3-${RELEASE_VERSION}.tar.bz2*
+cp ${TMPDIR}/i3/i3-${RELEASE_VERSION}.tar.xz* downloads/
+git add downloads/i3-${RELEASE_VERSION}.tar.xz*
 cp ${TMPDIR}/i3/RELEASE-NOTES-${RELEASE_VERSION} downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt
 git add downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt
 sed -i "s,<h2>Documentation for i3 v[^<]*</h2>,<h2>Documentation for i3 v${RELEASE_VERSION}</h2>,g" docs/index.html
 sed -i "s,<span style=\"margin-left: 2em; color: #c0c0c0\">[^<]*</span>,<span style=\"margin-left: 2em; color: #c0c0c0\">${RELEASE_VERSION}</span>,g" index.html
 sed -i "s,The current stable version is .*$,The current stable version is ${RELEASE_VERSION}.,g" downloads/index.html
-sed -i "s,<tbody>,<tbody>\n  <tr>\n    <td>${RELEASE_VERSION}</td>\n    <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.bz2\">i3-${RELEASE_VERSION}.tar.bz2</a></td>\n    <td>$(LC_ALL=en_US.UTF-8 ls -lh ../i3/i3-${RELEASE_VERSION}.tar.bz2 | awk -F " " {'print $5'} | sed 's/K$/ KiB/g' | sed 's/M$/ MiB/g')</td>\n    <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.bz2.asc\">signature</a></td>\n    <td>$(date +'%Y-%m-%d')</td>\n    <td><a href=\"/downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt\">release notes</a></td>\n  </tr>\n,g" downloads/index.html
+sed -i "s,<tbody>,<tbody>\n  <tr>\n    <td>${RELEASE_VERSION}</td>\n    <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.xz\">i3-${RELEASE_VERSION}.tar.xz</a></td>\n    <td>$(LC_ALL=en_US.UTF-8 ls -lh ../i3/i3-${RELEASE_VERSION}.tar.xz | awk -F " " {'print $5'} | sed 's/K$/ KiB/g' | sed 's/M$/ MiB/g')</td>\n    <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.xz.asc\">signature</a></td>\n    <td>$(date +'%Y-%m-%d')</td>\n    <td><a href=\"/downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt\">release notes</a></td>\n  </tr>\n,g" downloads/index.html
 
 git commit -a -m "add ${RELEASE_VERSION} release"
 
diff --git a/src/bindings.c b/src/bindings.c
index 03dc2306..d6255e73 100644
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -8,8 +8,10 @@
  */
 #include "all.h"
 
-#include <xkbcommon/xkbcommon.h>
+#include <math.h>
+
 #include <xkbcommon/xkbcommon-x11.h>
+#include <xkbcommon/xkbcommon.h>
 
 static struct xkb_context *xkb_context;
 static struct xkb_keymap *xkb_keymap;
@@ -626,6 +628,7 @@ void switch_mode(const char *new_mode) {
 
         ungrab_all_keys(conn);
         bindings = mode->bindings;
+        current_binding_mode = mode->name;
         translate_keysyms();
         grab_all_keys(conn);
 
diff --git a/src/click.c b/src/click.c
index 12e391b5..7a2c0329 100644
--- a/src/click.c
+++ b/src/click.c
@@ -10,11 +10,6 @@
 #include "all.h"
 
 #include <time.h>
-#include <math.h>
-
-#include <xcb/xcb_icccm.h>
-
-#include <X11/XKBlib.h>
 
 typedef enum { CLICK_BORDER = 0,
                CLICK_DECORATION = 1,
@@ -277,14 +272,18 @@ static void route_click(Con *con, xcb_button_press_event_t *event, const bool mo
 
     /* 7: floating modifier pressed, initiate a resize */
     if (dest == CLICK_INSIDE && mod_pressed && is_right_click) {
-        floating_mod_on_tiled_client(con, event);
+        if (floating_mod_on_tiled_client(con, event)) {
+            return;
+        }
         /* Avoid propagating events to clients, since the user expects
-         * $mod + click to be handled by i3. */
+         * $mod+click to be handled by i3. */
+        xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
+        xcb_flush(conn);
         return;
     }
     /* 8: otherwise, check for border/decoration clicks and resize */
-    else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) &&
-             is_left_or_right_click) {
+    if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) &&
+        is_left_or_right_click) {
         DLOG("Trying to resize (tiling)\n");
         tiling_resize(con, event, dest, dest == CLICK_DECORATION && !was_focused);
     }
diff --git a/src/commands.c b/src/commands.c
index 69bb426d..26ea876b 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -8,14 +8,11 @@
  *
  */
 #include "all.h"
+#include "shmlog.h"
 
+#include <fcntl.h>
 #include <stdint.h>
-#include <float.h>
-#include <stdarg.h>
 #include <unistd.h>
-#include <fcntl.h>
-
-#include "shmlog.h"
 
 // Macros to make the YAJL API a bit easier to use.
 #define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_##x(cmd_output->json_gen, ##__VA_ARGS__) : 0)
@@ -132,9 +129,7 @@ static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
  */
 typedef struct owindow {
     Con *con;
-
-    TAILQ_ENTRY(owindow)
-    owindows;
+    TAILQ_ENTRY(owindow) owindows;
 } owindow;
 
 typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
@@ -362,7 +357,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_aut
 
     LOG("should move window to workspace %s\n", name);
     /* get the workspace */
-    Con *ws = workspace_get(name, NULL);
+    Con *ws = workspace_get(name);
 
     if (no_auto_back_and_forth == NULL) {
         ws = maybe_auto_back_and_forth_workspace(ws);
@@ -393,7 +388,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_
 
     Con *ws = get_existing_workspace_by_num(parsed_num);
     if (!ws) {
-        ws = workspace_get(which, NULL);
+        ws = workspace_get(which);
     }
 
     if (no_auto_back_and_forth == NULL) {
@@ -1402,7 +1397,7 @@ void cmd_focus(I3_CMD) {
 
     CMD_FOCUS_WARN_CHILDREN;
 
-    Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
+    Con *__i3_scratch = workspace_get("__i3_scratch");
     owindow *current;
     TAILQ_FOREACH (current, &owindows, owindows) {
         Con *ws = con_get_workspace(current->con);
@@ -1495,34 +1490,37 @@ void cmd_sticky(I3_CMD, const char *action) {
 }
 
 /*
- * Implementation of 'move <direction> [<pixels> [px]]'.
+ * Implementation of 'move <direction> [<amount> [px|ppt]]'.
  *
  */
-void cmd_move_direction(I3_CMD, const char *direction_str, long move_px) {
+void cmd_move_direction(I3_CMD, const char *direction_str, long amount, const char *mode) {
     owindow *current;
     HANDLE_EMPTY_MATCH;
 
     Con *initially_focused = focused;
     direction_t direction = parse_direction(direction_str);
 
+    const bool is_ppt = mode && strcmp(mode, "ppt") == 0;
+
+    DLOG("moving in direction %s, %ld %s\n", direction_str, amount, mode);
     TAILQ_FOREACH (current, &owindows, owindows) {
-        DLOG("moving in direction %s, px %ld\n", direction_str, move_px);
         if (con_is_floating(current->con)) {
-            DLOG("floating move with %ld pixels\n", move_px);
+            DLOG("floating move with %ld %s\n", amount, mode);
             Rect newrect = current->con->parent->rect;
+            Con *output = con_get_output(current->con);
 
             switch (direction) {
                 case D_LEFT:
-                    newrect.x -= move_px;
+                    newrect.x -= is_ppt ? output->rect.width * ((double)amount / 100.0) : amount;
                     break;
                 case D_RIGHT:
-                    newrect.x += move_px;
+                    newrect.x += is_ppt ? output->rect.width * ((double)amount / 100.0) : amount;
                     break;
                 case D_UP:
-                    newrect.y -= move_px;
+                    newrect.y -= is_ppt ? output->rect.height * ((double)amount / 100.0) : amount;
                     break;
                 case D_DOWN:
-                    newrect.y += move_px;
+                    newrect.y += is_ppt ? output->rect.height * ((double)amount / 100.0) : amount;
                     break;
             }
 
@@ -1618,14 +1616,25 @@ void cmd_exit(I3_CMD) {
  */
 void cmd_reload(I3_CMD) {
     LOG("reloading\n");
-    kill_nagbar(&config_error_nagbar_pid, false);
-    kill_nagbar(&command_error_nagbar_pid, false);
+
+    kill_nagbar(config_error_nagbar_pid, false);
+    kill_nagbar(command_error_nagbar_pid, false);
+    /* start_nagbar() will refuse to start a new process if the passed pid is
+     * set. This will happen when our child watcher is triggered by libev when
+     * the loop re-starts. However, config errors might be detected before
+     * that since we will read the config right now with load_configuration.
+     * See #4104. */
+    config_error_nagbar_pid = command_error_nagbar_pid = -1;
+
     load_configuration(NULL, C_RELOAD);
     x_set_i3_atoms();
     /* Send an IPC event just in case the ws names have changed */
     ipc_send_workspace_event("reload", NULL, NULL);
-    /* Send an update event for the barconfig just in case it has changed */
-    update_barconfig();
+    /* Send an update event for each barconfig just in case it has changed */
+    Barconfig *current;
+    TAILQ_FOREACH (current, &barconfigs, configs) {
+        ipc_send_barconfig_update_event(current);
+    }
 
     // XXX: default reply for now, make this a better reply
     ysuccess(true);
@@ -1715,10 +1724,10 @@ void cmd_focus_output(I3_CMD, const char *name) {
 }
 
 /*
- * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
+ * Implementation of 'move [window|container] [to] [absolute] position [<pos_x> [px|ppt] <pos_y> [px|ppt]]
  *
  */
-void cmd_move_window_to_position(I3_CMD, long x, long y) {
+void cmd_move_window_to_position(I3_CMD, long x, const char *mode_x, long y, const char *mode_y) {
     bool has_error = false;
 
     owindow *current;
@@ -1737,10 +1746,11 @@ void cmd_move_window_to_position(I3_CMD, long x, long y) {
         }
 
         Rect newrect = current->con->parent->rect;
+        Con *output = con_get_output(current->con);
 
-        DLOG("moving to position %ld %ld\n", x, y);
-        newrect.x = x;
-        newrect.y = y;
+        newrect.x = mode_x && strcmp(mode_x, "ppt") == 0 ? output->rect.width * ((double)x / 100.0) : x;
+        newrect.y = mode_y && strcmp(mode_y, "ppt") == 0 ? output->rect.height * ((double)y / 100.0) : y;
+        DLOG("moving to position %d %s %d %s\n", newrect.x, mode_x, newrect.y, mode_y);
 
         if (!floating_reposition(current->con->parent, newrect)) {
             yerror("Cannot move window/container out of bounds.");
@@ -2018,26 +2028,9 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
     con_attach(workspace, parent, false);
     ipc_send_workspace_event("rename", workspace, NULL);
 
-    /* Move the workspace to the correct output if it has an assignment */
-    struct Workspace_Assignment *assignment = NULL;
-    TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
-        if (assignment->output == NULL)
-            continue;
-        if (strcmp(assignment->name, workspace->name) != 0 && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) {
-            continue;
-        }
-
-        Output *target_output = get_output_by_name(assignment->output, true);
-        if (!target_output) {
-            LOG("Could not get output named \"%s\"\n", assignment->output);
-            continue;
-        }
-        if (!output_triggers_assignment(target_output, assignment)) {
-            continue;
-        }
-        workspace_move_to_output(workspace, target_output);
-
-        break;
+    Con *assigned = get_assigned_output(workspace->name, workspace->num);
+    if (assigned) {
+        workspace_move_to_output(workspace, get_output_for_con(assigned));
     }
 
     bool can_restore_focus = previously_focused != NULL;
@@ -2078,7 +2071,7 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
  * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
  *
  */
-static bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
+void cmd_bar_mode(I3_CMD, const char *bar_mode, const char *bar_id) {
     int mode = M_DOCK;
     bool toggle = false;
     if (strcmp(bar_mode, "dock") == 0)
@@ -2091,39 +2084,53 @@ static bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
         toggle = true;
     else {
         ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
-        return false;
+        assert(false);
+    }
+
+    if (TAILQ_EMPTY(&barconfigs)) {
+        yerror("No bars found\n");
+        return;
     }
 
-    bool changed_sth = false;
     Barconfig *current = NULL;
     TAILQ_FOREACH (current, &barconfigs, configs) {
-        if (bar_id && strcmp(current->id, bar_id) != 0)
+        if (bar_id && strcmp(current->id, bar_id) != 0) {
             continue;
+        }
 
-        if (toggle)
+        if (toggle) {
             mode = (current->mode + 1) % 2;
+        }
 
-        DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
-        current->mode = mode;
-        changed_sth = true;
+        DLOG("Changing bar mode of bar_id '%s' from '%d' to '%s (%d)'\n",
+             current->id, current->mode, bar_mode, mode);
+        if ((int)current->mode != mode) {
+            current->mode = mode;
+            ipc_send_barconfig_update_event(current);
+        }
 
-        if (bar_id)
-            break;
+        if (bar_id) {
+            /* We are looking for a specific bar and we found it */
+            ysuccess(true);
+            return;
+        }
     }
 
-    if (bar_id && !changed_sth) {
-        DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
-        return false;
+    if (bar_id) {
+        /* We are looking for a specific bar and we did not find it */
+        yerror("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
+    } else {
+        /* We have already handled the case of no bars, so we must have
+         * updated all active bars now. */
+        ysuccess(true);
     }
-
-    return true;
 }
 
 /*
  * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
  *
  */
-static bool cmd_bar_hidden_state(const char *bar_hidden_state, const char *bar_id) {
+void cmd_bar_hidden_state(I3_CMD, const char *bar_hidden_state, const char *bar_id) {
     int hidden_state = S_SHOW;
     bool toggle = false;
     if (strcmp(bar_hidden_state, "hide") == 0)
@@ -2134,54 +2141,46 @@ static bool cmd_bar_hidden_state(const char *bar_hidden_state, const char *bar_i
         toggle = true;
     else {
         ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
-        return false;
+        assert(false);
+    }
+
+    if (TAILQ_EMPTY(&barconfigs)) {
+        yerror("No bars found\n");
+        return;
     }
 
-    bool changed_sth = false;
     Barconfig *current = NULL;
     TAILQ_FOREACH (current, &barconfigs, configs) {
-        if (bar_id && strcmp(current->id, bar_id) != 0)
+        if (bar_id && strcmp(current->id, bar_id) != 0) {
             continue;
+        }
 
-        if (toggle)
+        if (toggle) {
             hidden_state = (current->hidden_state + 1) % 2;
+        }
 
-        DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
-        current->hidden_state = hidden_state;
-        changed_sth = true;
-
-        if (bar_id)
-            break;
-    }
+        DLOG("Changing bar hidden_state of bar_id '%s' from '%d' to '%s (%d)'\n",
+             current->id, current->hidden_state, bar_hidden_state, hidden_state);
+        if ((int)current->hidden_state != hidden_state) {
+            current->hidden_state = hidden_state;
+            ipc_send_barconfig_update_event(current);
+        }
 
-    if (bar_id && !changed_sth) {
-        DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
-        return false;
+        if (bar_id) {
+            /* We are looking for a specific bar and we found it */
+            ysuccess(true);
+            return;
+        }
     }
 
-    return true;
-}
-
-/*
- * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
- *
- */
-void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id) {
-    bool ret;
-    if (strcmp(bar_type, "mode") == 0)
-        ret = cmd_bar_mode(bar_value, bar_id);
-    else if (strcmp(bar_type, "hidden_state") == 0)
-        ret = cmd_bar_hidden_state(bar_value, bar_id);
-    else {
-        ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
-        ret = false;
+    if (bar_id) {
+        /* We are looking for a specific bar and we did not find it */
+        yerror("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
+    } else {
+        /* We have already handled the case of no bars, so we must have
+         * updated all active bars now. */
+        ysuccess(true);
     }
-
-    ysuccess(ret);
-    if (!ret)
-        return;
-
-    update_barconfig();
 }
 
 /*
@@ -2244,32 +2243,8 @@ void cmd_gaps(I3_CMD, const char *type, const char *scope, const char *mode, con
     int pixels = logical_px(atoi(value));
     Con *workspace = con_get_workspace(focused);
 
-#define CMD_GAPS(type)                                                  \
+#define CMD_SET_GAPS_VALUE(type, value, reset)                          \
     do {                                                                \
-        int current_value = config.gaps.type;                           \
-        if (strcmp(scope, "current") == 0)                              \
-            current_value += workspace->gaps.type;                      \
-                                                                        \
-        bool reset = false;                                             \
-        if (!strcmp(mode, "plus"))                                      \
-            current_value += pixels;                                    \
-        else if (!strcmp(mode, "minus"))                                \
-            current_value -= pixels;                                    \
-        else if (!strcmp(mode, "set")) {                                \
-            current_value = pixels;                                     \
-            reset = true;                                               \
-        } else if (!strcmp(mode, "toggle")) {                           \
-            current_value = !current_value * pixels;                    \
-            reset = true;                                               \
-        } else {                                                        \
-            ELOG("Invalid mode %s when changing gaps", mode);           \
-            ysuccess(false);                                            \
-            return;                                                     \
-        }                                                               \
-                                                                        \
-        if (current_value < 0)                                          \
-            current_value = 0;                                          \
-                                                                        \
         if (!strcmp(scope, "all")) {                                    \
             Con *output, *cur_ws = NULL;                                \
             TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {       \
@@ -2277,19 +2252,71 @@ void cmd_gaps(I3_CMD, const char *type, const char *scope, const char *mode, con
                 TAILQ_FOREACH (cur_ws, &(content->nodes_head), nodes) { \
                     if (reset)                                          \
                         cur_ws->gaps.type = 0;                          \
-                    else if (current_value + cur_ws->gaps.type < 0)     \
-                        cur_ws->gaps.type = -current_value;             \
+                    else if (value + cur_ws->gaps.type < 0)             \
+                        cur_ws->gaps.type = -value;                     \
                 }                                                       \
             }                                                           \
                                                                         \
-            config.gaps.type = current_value;                           \
+            config.gaps.type = value;                                   \
         } else {                                                        \
-            workspace->gaps.type = current_value - config.gaps.type;    \
+            workspace->gaps.type = value - config.gaps.type;            \
         }                                                               \
     } while (0)
 
+#define CMD_GAPS(type)                                                                                          \
+    do {                                                                                                        \
+        int current_value = config.gaps.type;                                                                   \
+        if (strcmp(scope, "current") == 0)                                                                      \
+            current_value += workspace->gaps.type;                                                              \
+                                                                                                                \
+        bool reset = false;                                                                                     \
+        if (!strcmp(mode, "plus"))                                                                              \
+            current_value += pixels;                                                                            \
+        else if (!strcmp(mode, "minus"))                                                                        \
+            current_value -= pixels;                                                                            \
+        else if (!strcmp(mode, "set")) {                                                                        \
+            current_value = pixels;                                                                             \
+            reset = true;                                                                                       \
+        } else if (!strcmp(mode, "toggle")) {                                                                   \
+            current_value = !current_value * pixels;                                                            \
+            reset = true;                                                                                       \
+        } else {                                                                                                \
+            ELOG("Invalid mode %s when changing gaps", mode);                                                   \
+            ysuccess(false);                                                                                    \
+            return;                                                                                             \
+        }                                                                                                       \
+                                                                                                                \
+        /* see issue 262 */                                                                                     \
+        int min_value = 0;                                                                                      \
+        if (strcmp(#type, "inner") != 0) {                                                                      \
+            min_value = strcmp(scope, "all") ? -config.gaps.inner - workspace->gaps.inner : -config.gaps.inner; \
+        }                                                                                                       \
+                                                                                                                \
+        if (current_value < min_value)                                                                          \
+            current_value = min_value;                                                                          \
+                                                                                                                \
+        CMD_SET_GAPS_VALUE(type, current_value, reset);                                                         \
+    } while (0)
+
+#define CMD_UPDATE_GAPS(type)                                                                              \
+    do {                                                                                                   \
+        if (!strcmp(scope, "all")) {                                                                       \
+            if (config.gaps.type + config.gaps.inner < 0)                                                  \
+                CMD_SET_GAPS_VALUE(type, -config.gaps.inner, true);                                        \
+        } else {                                                                                           \
+            if (config.gaps.type + workspace->gaps.type + config.gaps.inner + workspace->gaps.inner < 0) { \
+                CMD_SET_GAPS_VALUE(type, -config.gaps.inner - workspace->gaps.inner, true);                \
+            }                                                                                              \
+        }                                                                                                  \
+    } while (0)
+
     if (!strcmp(type, "inner")) {
         CMD_GAPS(inner);
+        // update inconsistent values
+        CMD_UPDATE_GAPS(top);
+        CMD_UPDATE_GAPS(bottom);
+        CMD_UPDATE_GAPS(right);
+        CMD_UPDATE_GAPS(left);
     } else if (!strcmp(type, "outer")) {
         CMD_GAPS(top);
         CMD_GAPS(bottom);
diff --git a/src/commands_parser.c b/src/commands_parser.c
index 2fd76309..6c791415 100644
--- a/src/commands_parser.c
+++ b/src/commands_parser.c
@@ -25,13 +25,6 @@
  */
 #include "all.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdint.h>
-
 // Macros to make the YAJL API a bit easier to use.
 #define y(x, ...) (command_output.json_gen != NULL ? yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) : 0)
 #define ystr(str) (command_output.json_gen != NULL ? yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) : 0)
diff --git a/src/con.c b/src/con.c
index d9aaa5e5..22f87c15 100644
--- a/src/con.c
+++ b/src/con.c
@@ -10,7 +10,6 @@
  *
  */
 #include "all.h"
-
 #include "yajl_utils.h"
 
 static void con_on_remove_child(Con *con);
@@ -92,8 +91,8 @@ void con_free(Con *con) {
         FREE(mark->name);
         FREE(mark);
     }
-    free(con);
     DLOG("con %p freed\n", con);
+    free(con);
 }
 
 static void _con_attach(Con *con, Con *parent, Con *previous, bool ignore_focus) {
@@ -515,8 +514,7 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
 struct bfs_entry {
     Con *con;
 
-    TAILQ_ENTRY(bfs_entry)
-    entries;
+    TAILQ_ENTRY(bfs_entry) entries;
 };
 
 /*
@@ -528,9 +526,7 @@ Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) {
 
     /* TODO: is breadth-first-search really appropriate? (check as soon as
      * fullscreen levels and fullscreen for containers is implemented) */
-    TAILQ_HEAD(bfs_head, bfs_entry)
-    bfs_head = TAILQ_HEAD_INITIALIZER(bfs_head);
-
+    TAILQ_HEAD(bfs_head, bfs_entry) bfs_head = TAILQ_HEAD_INITIALIZER(bfs_head);
     struct bfs_entry *entry = smalloc(sizeof(struct bfs_entry));
     entry->con = con;
     TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
@@ -1262,9 +1258,17 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
     }
 
     /* If moving a fullscreen container and the destination already has a
-     * fullscreen window on it, un-fullscreen the target's fullscreen con. */
+     * fullscreen window on it, un-fullscreen the target's fullscreen con.
+     * con->fullscreen_mode is not enough in some edge cases:
+     * 1. con is CT_FLOATING_CON, child is fullscreen.
+     * 2. con is the parent of a fullscreen container, can be triggered by
+     * moving the parent with command criteria.
+     */
     Con *fullscreen = con_get_fullscreen_con(target_ws, CF_OUTPUT);
-    if (con->fullscreen_mode != CF_NONE && fullscreen != NULL) {
+    const bool con_has_fullscreen = con->fullscreen_mode != CF_NONE ||
+                                    con_get_fullscreen_con(con, CF_GLOBAL) ||
+                                    con_get_fullscreen_con(con, CF_OUTPUT);
+    if (con_has_fullscreen && fullscreen != NULL) {
         con_toggle_fullscreen(fullscreen, CF_OUTPUT);
         fullscreen = NULL;
     }
@@ -1363,7 +1367,7 @@ bool con_move_to_mark(Con *con, const char *mark) {
     }
 
     /* For target containers in the scratchpad, we just send the window to the scratchpad. */
-    if (con_get_workspace(target) == workspace_get("__i3_scratch", NULL)) {
+    if (con_get_workspace(target) == workspace_get("__i3_scratch")) {
         DLOG("target container is in the scratchpad, moving container to scratchpad.\n");
         scratchpad_move(con);
         return true;
@@ -2373,11 +2377,11 @@ i3String *con_parse_title_format(Con *con) {
     char *formatted_str = format_placeholders(con->title_format, &placeholders[0], num);
     i3String *formatted = i3string_from_utf8(formatted_str);
     i3string_set_markup(formatted, pango_markup);
-    FREE(formatted_str);
 
-    for (size_t i = 0; i < num; i++) {
-        FREE(placeholders[i].value);
-    }
+    free(formatted_str);
+    free(title);
+    free(class);
+    free(instance);
 
     return formatted;
 }
diff --git a/src/config.c b/src/config.c
index 112f2baa..77ba38cb 100644
--- a/src/config.c
+++ b/src/config.c
@@ -28,17 +28,6 @@ void ungrab_all_keys(xcb_connection_t *conn) {
     xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
 }
 
-/*
- * Sends the current bar configuration as an event to all barconfig_update listeners.
- *
- */
-void update_barconfig(void) {
-    Barconfig *current;
-    TAILQ_FOREACH (current, &barconfigs, configs) {
-        ipc_send_barconfig_update_event(current);
-    }
-}
-
 static void free_configuration(void) {
     assert(conn != NULL);
 
@@ -185,6 +174,7 @@ bool load_configuration(const char *override_configpath, config_load_t load_type
     SLIST_INSERT_HEAD(&modes, default_mode, modes);
 
     bindings = default_mode->bindings;
+    current_binding_mode = default_mode->name;
 
     /* Clear the old config or initialize the data structure */
     memset(&config, 0, sizeof(config));
diff --git a/src/config_directives.c b/src/config_directives.c
index cf81cc16..52362547 100644
--- a/src/config_directives.c
+++ b/src/config_directives.c
@@ -9,9 +9,6 @@
  */
 #include "all.h"
 
-#include <float.h>
-#include <stdarg.h>
-
 /*******************************************************************************
  * Criteria functions.
  ******************************************************************************/
diff --git a/src/config_parser.c b/src/config_parser.c
index f120cfa3..f78e75f8 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -25,16 +25,17 @@
  */
 #include "all.h"
 
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdint.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <sys/stat.h>
-#include <fcntl.h>
+#include <unistd.h>
+
 #include <xcb/xcb_xrm.h>
 
 // Macros to make the YAJL API a bit easier to use.
diff --git a/src/display_version.c b/src/display_version.c
index e44540e0..32250c15 100644
--- a/src/display_version.c
+++ b/src/display_version.c
@@ -10,13 +10,9 @@
  */
 #include "all.h"
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
 #include <fcntl.h>
 #include <time.h>
+#include <unistd.h>
 
 static bool human_readable_key, loaded_config_file_name_key;
 static char *human_readable_version, *loaded_config_file_name;
diff --git a/src/ewmh.c b/src/ewmh.c
index 1cdf8947..c61fb5f3 100644
--- a/src/ewmh.c
+++ b/src/ewmh.c
@@ -9,6 +9,8 @@
  */
 #include "all.h"
 
+#include "i3-atoms_NET_SUPPORTED.xmacro.h"
+
 xcb_window_t ewmh_window;
 
 #define FOREACH_NONINTERNAL                                                  \
@@ -305,7 +307,7 @@ void ewmh_update_focused(xcb_window_t window, bool is_focused) {
 void ewmh_setup_hints(void) {
     xcb_atom_t supported_atoms[] = {
 #define xmacro(atom) A_##atom,
-#include "atoms_NET_SUPPORTED.xmacro"
+        I3_NET_SUPPORTED_ATOMS_XMACRO
 #undef xmacro
     };
 
diff --git a/src/floating.c b/src/floating.c
index fd313a52..02e6ac3b 100644
--- a/src/floating.c
+++ b/src/floating.c
@@ -9,6 +9,8 @@
  */
 #include "all.h"
 
+#include <math.h>
+
 #ifndef MAX
 #define MAX(x, y) ((x) > (y) ? (x) : (y))
 #endif
@@ -221,22 +223,22 @@ void floating_check_size(Con *floating_con, bool prefer_height) {
     }
 }
 
-void floating_enable(Con *con, bool automatic) {
+bool floating_enable(Con *con, bool automatic) {
     bool set_focus = (con == focused);
 
     if (con_is_docked(con)) {
         LOG("Container is a dock window, not enabling floating mode.\n");
-        return;
+        return false;
     }
 
     if (con_is_floating(con)) {
         LOG("Container is already in floating mode, not doing anything.\n");
-        return;
+        return false;
     }
 
     if (con->type == CT_WORKSPACE) {
         LOG("Container is a workspace, not enabling floating mode.\n");
-        return;
+        return false;
     }
 
     Con *focus_head_placeholder = NULL;
@@ -419,6 +421,7 @@ void floating_enable(Con *con, bool automatic) {
 
     floating_set_hint_atom(nc, true);
     ipc_send_window_event("floating", con);
+    return true;
 }
 
 void floating_disable(Con *con) {
diff --git a/src/handlers.c b/src/handlers.c
index d6d6e20b..eba5fe29 100644
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -10,9 +10,9 @@
  */
 #include "all.h"
 
-#include <time.h>
-#include <float.h>
 #include <sys/time.h>
+#include <time.h>
+
 #include <xcb/randr.h>
 #define SN_API_NOT_YET_FROZEN 1
 #include <libsn/sn-monitor.h>
@@ -727,7 +727,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             return;
         }
 
-        if (con_is_internal(ws) && ws != workspace_get("__i3_scratch", NULL)) {
+        if (con_is_internal(ws) && ws != workspace_get("__i3_scratch")) {
             DLOG("Workspace is internal but not scratchpad, ignoring _NET_ACTIVE_WINDOW\n");
             return;
         }
@@ -841,12 +841,13 @@ static void handle_client_message(xcb_client_message_event_t *event) {
              * let's float it and make it sticky. */
             DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n");
 
-            floating_enable(con, false);
-            con->floating = FLOATING_AUTO_ON;
+            if (floating_enable(con, false)) {
+                con->floating = FLOATING_AUTO_ON;
 
-            con->sticky = true;
-            ewmh_update_sticky(con->window->id, true);
-            output_push_sticky_windows(focused);
+                con->sticky = true;
+                ewmh_update_sticky(con->window->id, true);
+                output_push_sticky_windows(focused);
+            }
         } else {
             Con *ws = ewmh_get_workspace_by_index(index);
             if (ws == NULL) {
diff --git a/src/ipc.c b/src/ipc.c
index e2b97255..eac4d9b3 100644
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -7,23 +7,25 @@
  * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
  *
  */
-#include "all.h"
 
+#include "all.h"
 #include "yajl_utils.h"
 
+#include <ev.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <locale.h>
 #include <stdint.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <ev.h>
+#include <unistd.h>
+
 #include <yajl/yajl_gen.h>
 #include <yajl/yajl_parse.h>
 
 char *current_socketpath = NULL;
 
-TAILQ_HEAD(ipc_client_head, ipc_client)
-all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
+TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
 
 /*
  * Puts the given socket file descriptor into non-blocking mode or dies if
@@ -1363,9 +1365,27 @@ IPC_HANDLER(sync) {
     ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
 }
 
+IPC_HANDLER(get_binding_state) {
+    yajl_gen gen = ygenalloc();
+
+    y(map_open);
+
+    ystr("name");
+    ystr(current_binding_mode);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_GET_BINDING_STATE, payload);
+    y(free);
+}
+
 /* The index of each callback function corresponds to the numeric
  * value of the message type (see include/i3/ipc.h) */
-handler_t handlers[12] = {
+handler_t handlers[13] = {
     handle_run_command,
     handle_get_workspaces,
     handle_subscribe,
@@ -1378,6 +1398,7 @@ handler_t handlers[12] = {
     handle_get_config,
     handle_send_tick,
     handle_sync,
+    handle_get_binding_state,
 };
 
 /*
diff --git a/src/load_layout.c b/src/load_layout.c
index acc83fb5..c117f9c6 100644
--- a/src/load_layout.c
+++ b/src/load_layout.c
@@ -10,10 +10,9 @@
  */
 #include "all.h"
 
-#include <yajl/yajl_common.h>
-#include <yajl/yajl_gen.h>
+#include <locale.h>
+
 #include <yajl/yajl_parse.h>
-#include <yajl/yajl_version.h>
 
 /* TODO: refactor the whole parsing thing */
 
@@ -46,9 +45,7 @@ struct pending_marks {
  * array. */
 struct focus_mapping {
     int old_id;
-
-    TAILQ_ENTRY(focus_mapping)
-    focus_mappings;
+    TAILQ_ENTRY(focus_mapping) focus_mappings;
 };
 
 static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
diff --git a/src/log.c b/src/log.c
index 31db8b33..326f82b8 100644
--- a/src/log.c
+++ b/src/log.c
@@ -9,27 +9,24 @@
  */
 #include <config.h>
 
+#include "all.h"
+#include "shmlog.h"
+
+#include <errno.h>
+#include <fcntl.h>
 #include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <fcntl.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
 #if !defined(__OpenBSD__)
 #include <pthread.h>
 #endif
 
-#include "util.h"
-#include "log.h"
-#include "i3.h"
-#include "libi3.h"
-#include "shmlog.h"
-
 #if defined(__APPLE__)
 #include <sys/sysctl.h>
 #endif
diff --git a/src/main.c b/src/main.c
index 369f2f66..22019559 100644
--- a/src/main.c
+++ b/src/main.c
@@ -8,18 +8,22 @@
  *
  */
 #include "all.h"
+#include "shmlog.h"
 
 #include <ev.h>
 #include <fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/time.h>
-#include <sys/resource.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <locale.h>
+#include <signal.h>
 #include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
-#include <libgen.h>
-#include "shmlog.h"
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 #ifdef I3_ASAN_ENABLED
 #include <sanitizer/lsan_interface.h>
@@ -27,6 +31,9 @@
 
 #include "sd-daemon.h"
 
+#include "i3-atoms_NET_SUPPORTED.xmacro.h"
+#include "i3-atoms_rest.xmacro.h"
+
 /* The original value of RLIMIT_CORE when i3 was started. We need to restore
  * this before starting any other process, since we set RLIMIT_CORE to
  * RLIM_INFINITY for i3 debugging versions. */
@@ -72,6 +79,7 @@ const int default_shmlog_size = 25 * 1024 * 1024;
 
 /* The list of key bindings */
 struct bindings_head *bindings;
+const char *current_binding_mode = NULL;
 
 /* The list of exec-lines */
 struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
@@ -94,7 +102,8 @@ bool force_xinerama = false;
 
 /* Define all atoms as global variables */
 #define xmacro(atom) xcb_atom_t A_##atom;
-#include "atoms.xmacro"
+I3_NET_SUPPORTED_ATOMS_XMACRO
+I3_REST_ATOMS_XMACRO
 #undef xmacro
 
 /*
@@ -174,6 +183,10 @@ static void i3_exit(void) {
     unlink(config.ipc_socket_path);
     xcb_disconnect(conn);
 
+    /* If a nagbar is active, kill it */
+    kill_nagbar(config_error_nagbar_pid, false);
+    kill_nagbar(command_error_nagbar_pid, false);
+
 /* We need ev >= 4 for the following code. Since it is not *that* important (it
  * only makes sure that there are no i3-nagbar instances left behind) we still
  * support old systems with libev 3. */
@@ -372,6 +385,11 @@ int main(int argc, char *argv[]) {
                     char *socket_path = root_atom_contents("I3_SOCKET_PATH", NULL, 0);
                     if (socket_path) {
                         printf("%s\n", socket_path);
+                        /* With -O2 (i.e. the buildtype=debugoptimized meson
+                         * option, which we set by default), gcc 9.2.1 optimizes
+                         * away socket_path at this point, resulting in a Leak
+                         * Sanitizer report. An explicit free helps: */
+                        free(socket_path);
                         exit(EXIT_SUCCESS);
                     }
 
@@ -555,7 +573,8 @@ int main(int argc, char *argv[]) {
     /* Place requests for the atoms we need as soon as possible */
 #define xmacro(atom) \
     xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
-#include "atoms.xmacro"
+    I3_NET_SUPPORTED_ATOMS_XMACRO
+    I3_REST_ATOMS_XMACRO
 #undef xmacro
 
     root_depth = root_screen->root_depth;
@@ -601,7 +620,8 @@ int main(int argc, char *argv[]) {
         A_##name = reply->atom;                                                            \
         free(reply);                                                                       \
     } while (0);
-#include "atoms.xmacro"
+    I3_NET_SUPPORTED_ATOMS_XMACRO
+    I3_REST_ATOMS_XMACRO
 #undef xmacro
 
     load_configuration(override_configpath, C_LOAD);
diff --git a/src/manage.c b/src/manage.c
index 4e2803a5..82c0ff85 100644
--- a/src/manage.c
+++ b/src/manage.c
@@ -9,10 +9,6 @@
  */
 #include "all.h"
 
-#include "yajl_utils.h"
-
-#include <yajl/yajl_gen.h>
-
 /*
  * Match frame and window depth. This is needed because X will refuse to reparent a
  * window whose background is ParentRelative under a window with a different depth.
@@ -274,6 +270,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height);
 
     /* See if any container swallows this new window */
+    cwindow->swallowed = false;
     Match *match = NULL;
     Con *nc = con_for_window(search_at, cwindow, &match);
     const bool match_from_restart_mode = (match && match->restart_mode);
@@ -295,7 +292,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
             /* A_TO_WORKSPACE type assignment or fallback from A_TO_WORKSPACE_NUMBER
              * when the target workspace number does not exist yet. */
             if (!assigned_ws) {
-                assigned_ws = workspace_get(assignment->dest.workspace, NULL);
+                assigned_ws = workspace_get(assignment->dest.workspace);
             }
 
             nc = con_descend_tiling_focused(assigned_ws);
@@ -326,7 +323,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
         } else if (startup_ws) {
             /* If it was started on a specific workspace, we want to open it there. */
             DLOG("Using workspace on which this application was started (%s)\n", startup_ws);
-            nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL));
+            nc = con_descend_tiling_focused(workspace_get(startup_ws));
             DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name);
             if (nc->type == CT_WORKSPACE)
                 nc = tree_open_con(nc, cwindow);
@@ -362,6 +359,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
             match_free(match);
             FREE(match);
         }
+
+        cwindow->swallowed = true;
     }
 
     DLOG("new container = %p\n", nc);
@@ -537,8 +536,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
          * was not specified */
         bool automatic_border = (motif_border_style == BS_NORMAL);
 
-        floating_enable(nc, automatic_border);
-        nc->floating = FLOATING_AUTO_ON;
+        if (floating_enable(nc, automatic_border)) {
+            nc->floating = FLOATING_AUTO_ON;
+        }
     }
 
     /* explicitly set the border width to the default */
@@ -698,6 +698,11 @@ out:
  *
  */
 Con *remanage_window(Con *con) {
+    /* Make sure this windows hasn't already been swallowed. */
+    if (con->window->swallowed) {
+        run_assignments(con->window);
+        return con;
+    }
     Match *match;
     Con *nc = con_for_window(croot, con->window, &match);
     if (nc == NULL || nc->window == NULL || nc->window == con->window) {
@@ -743,5 +748,6 @@ Con *remanage_window(Con *con) {
         ewmh_update_wm_desktop();
     }
 
+    nc->window->swallowed = true;
     return nc;
 }
diff --git a/src/randr.c b/src/randr.c
index b163eca7..8e000ca6 100644
--- a/src/randr.c
+++ b/src/randr.c
@@ -12,6 +12,7 @@
 #include "all.h"
 
 #include <time.h>
+
 #include <xcb/randr.h>
 
 /* Pointer to the result of the query for primary output */
@@ -50,12 +51,12 @@ Output *get_output_by_name(const char *name, const bool require_active) {
     Output *output;
     bool get_primary = (strcasecmp("primary", name) == 0);
     TAILQ_FOREACH (output, &outputs, outputs) {
-        if (output->primary && get_primary) {
-            return output;
-        }
         if (require_active && !output->active) {
             continue;
         }
+        if (output->primary && get_primary) {
+            return output;
+        }
         struct output_name *output_name;
         SLIST_FOREACH (output_name, &output->names_head, names) {
             if (strcasecmp(output_name->name, name) == 0) {
@@ -437,34 +438,29 @@ void init_ws_for_output(Output *output) {
     Con *content = output_get_content(output->con);
     Con *previous_focus = con_get_workspace(focused);
 
-    /* go through all assignments and move the existing workspaces to this output */
-    struct Workspace_Assignment *assignment;
-    TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
-        if (!output_triggers_assignment(output, assignment)) {
+    /* Iterate over all workspaces and check if any of them should be assigned
+     * to this output. */
+    Con *output_con;
+    TAILQ_FOREACH (output_con, &(croot->nodes_head), nodes) {
+        if (con_is_internal(output_con)) {
             continue;
         }
-        Con *workspace = get_existing_workspace_by_name(assignment->name);
-        if (workspace == NULL)
-            continue;
 
-        /* check that this workspace is not already attached (that means the
-         * user configured this assignment twice) */
-        Con *workspace_out = con_get_output(workspace);
-        if (workspace_out == output->con) {
-            LOG("Workspace \"%s\" assigned to output \"%s\", but it is already "
-                "there. Do you have two assignment directives for the same "
-                "workspace in your configuration file?\n",
-                workspace->name, output_primary_name(output));
-            continue;
-        }
+        Con *workspace;
+        TAILQ_FOREACH (workspace, &(output_get_content(output_con)->nodes_head), nodes) {
+            Con *workspace_out = get_assigned_output(workspace->name, workspace->num);
+            if (output->con != workspace_out) {
+                continue;
+            }
 
-        DLOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
-             workspace->name, workspace_out->name, output_primary_name(output));
-        /* Need to copy output's rect since content is not yet rendered. We
-         * can't call render_con here because render_output only proceeds if a
-         * workspace exists. */
-        content->rect = output->con->rect;
-        workspace_move_to_output(workspace, output);
+            DLOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
+                 workspace->name, workspace_out->name, output_primary_name(output));
+            /* Need to copy output's rect since content is not yet rendered. We
+             * can't call render_con here because render_output only proceeds
+             * if a workspace exists. */
+            content->rect = output->con->rect;
+            workspace_move_to_output(workspace, output);
+        }
     }
 
     /* Temporarily set the focused container, might not be initialized yet. */
@@ -484,6 +480,7 @@ void init_ws_for_output(Output *output) {
     }
 
     /* otherwise, we create the first assigned ws for this output */
+    struct Workspace_Assignment *assignment;
     TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
         if (!output_triggers_assignment(output, assignment)) {
             continue;
@@ -1022,6 +1019,7 @@ void randr_query_outputs(void) {
     }
 
     /* render_layout flushes */
+    ewmh_update_desktop_properties();
     tree_render();
 
     FREE(primary);
diff --git a/src/render.c b/src/render.c
index 79bf7eb4..072daeb3 100644
--- a/src/render.c
+++ b/src/render.c
@@ -10,6 +10,8 @@
  */
 #include "all.h"
 
+#include <math.h>
+
 /* Forward declarations */
 static int *precalculate_sizes(Con *con, render_params *p);
 static void render_root(Con *con, Con *fullscreen);
@@ -453,7 +455,7 @@ static void render_con_tabbed(Con *con, Con *child, render_params *p, int i) {
     /* Since the tab width may be something like 31,6 px per tab, we
      * let the last tab have all the extra space (0,6 * children). */
     if (i == (p->children - 1)) {
-        child->deco_rect.width += (child->rect.width - (child->deco_rect.x + child->deco_rect.width));
+        child->deco_rect.width = child->rect.width - child->deco_rect.x;
     }
 
     if (p->children > 1 || (child->border_style != BS_PIXEL && child->border_style != BS_NONE)) {
diff --git a/src/restore_layout.c b/src/restore_layout.c
index 25f631b7..c51bfcbe 100644
--- a/src/restore_layout.c
+++ b/src/restore_layout.c
@@ -29,8 +29,7 @@ typedef struct placeholder_state {
     /** The drawable surface */
     surface_t surface;
 
-    TAILQ_ENTRY(placeholder_state)
-    state;
+    TAILQ_ENTRY(placeholder_state) state;
 } placeholder_state;
 
 static TAILQ_HEAD(state_head, placeholder_state) state_head =
diff --git a/src/scratchpad.c b/src/scratchpad.c
index e8d70be7..24cde612 100644
--- a/src/scratchpad.c
+++ b/src/scratchpad.c
@@ -32,7 +32,7 @@ void scratchpad_move(Con *con) {
     }
     DLOG("should move con %p to __i3_scratch\n", con);
 
-    Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
+    Con *__i3_scratch = workspace_get("__i3_scratch");
     if (con_get_workspace(con) == __i3_scratch) {
         DLOG("This window is already on __i3_scratch.\n");
         return;
@@ -84,7 +84,7 @@ void scratchpad_move(Con *con) {
  */
 bool scratchpad_show(Con *con) {
     DLOG("should show scratchpad window %p\n", con);
-    Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
+    Con *__i3_scratch = workspace_get("__i3_scratch");
     Con *floating;
 
     /* If this was 'scratchpad show' without criteria, we check if the
@@ -245,7 +245,7 @@ static int _lcm(const int m, const int n) {
  *
  */
 void scratchpad_fix_resolution(void) {
-    Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
+    Con *__i3_scratch = workspace_get("__i3_scratch");
     Con *__i3_output = con_get_output(__i3_scratch);
     DLOG("Current resolution: (%d, %d) %d x %d\n",
          __i3_output->rect.x, __i3_output->rect.y,
diff --git a/src/sd-daemon.c b/src/sd-daemon.c
index 28a88bfd..84761025 100644
--- a/src/sd-daemon.c
+++ b/src/sd-daemon.c
@@ -28,21 +28,21 @@
 #define _GNU_SOURCE
 #endif
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/fcntl.h>
-#include <netinet/in.h>
-#include <stdlib.h>
+#include "sd-daemon.h"
+
 #include <errno.h>
-#include <unistd.h>
-#include <string.h>
+#include <netinet/in.h>
 #include <stdarg.h>
-#include <stdio.h>
 #include <stddef.h>
-
-#include "sd-daemon.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 int sd_listen_fds(int unset_environment) {
     int r, fd;
diff --git a/src/sighandler.c b/src/sighandler.c
index 86921b40..2be69c31 100644
--- a/src/sighandler.c
+++ b/src/sighandler.c
@@ -7,23 +7,16 @@
  */
 #include "all.h"
 
-#include <ev.h>
-#include <iconv.h>
 #include <signal.h>
 #include <sys/wait.h>
-
-#include <xcb/xcb_event.h>
-
-#include <X11/keysym.h>
+#include <unistd.h>
 
 typedef struct dialog_t {
     xcb_window_t id;
     xcb_colormap_t colormap;
     Rect dims;
     surface_t surface;
-
-    TAILQ_ENTRY(dialog_t)
-    dialogs;
+    TAILQ_ENTRY(dialog_t) dialogs;
 } dialog_t;
 
 static TAILQ_HEAD(dialogs_head, dialog_t) dialogs = TAILQ_HEAD_INITIALIZER(dialogs);
diff --git a/src/startup.c b/src/startup.c
index e994e0a8..9b0576e8 100644
--- a/src/startup.c
+++ b/src/startup.c
@@ -11,12 +11,12 @@
  *
  */
 #include "all.h"
-
 #include "sd-daemon.h"
 
+#include <paths.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <paths.h>
+#include <unistd.h>
 
 #define SN_API_NOT_YET_FROZEN 1
 #include <libsn/sn-launcher.h>
diff --git a/src/util.c b/src/util.c
index 3544297c..cf7f41c8 100644
--- a/src/util.c
+++ b/src/util.c
@@ -10,19 +10,16 @@
  */
 #include "all.h"
 
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <locale.h>
 #include <sys/wait.h>
-#include <stdarg.h>
+#include <unistd.h>
 #if defined(__OpenBSD__)
 #include <sys/cdefs.h>
 #endif
-#include <fcntl.h>
-#include <pwd.h>
-#include <yajl/yajl_version.h>
-#include <libgen.h>
-#include <ctype.h>
-
-#define SN_API_NOT_YET_FROZEN 1
-#include <libsn/sn-launcher.h>
 
 int min(int a, int b) {
     return (a < b ? a : b);
@@ -290,8 +287,8 @@ static char *store_restart_layout(void) {
 void i3_restart(bool forget_layout) {
     char *restart_filename = forget_layout ? NULL : store_restart_layout();
 
-    kill_nagbar(&config_error_nagbar_pid, true);
-    kill_nagbar(&command_error_nagbar_pid, true);
+    kill_nagbar(config_error_nagbar_pid, true);
+    kill_nagbar(command_error_nagbar_pid, true);
 
     restore_geometry();
 
@@ -339,30 +336,19 @@ char *pango_escape_markup(char *input) {
 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
     ev_child_stop(EV_A_ watcher);
 
-    if (!WIFEXITED(watcher->rstatus)) {
-        ELOG("ERROR: i3-nagbar did not exit normally.\n");
-        return;
-    }
-
     int exitcode = WEXITSTATUS(watcher->rstatus);
-    DLOG("i3-nagbar process exited with status %d\n", exitcode);
-    if (exitcode == 2) {
-        ELOG("ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
+    if (!WIFEXITED(watcher->rstatus)) {
+        ELOG("i3-nagbar (%d) did not exit normally. This is not an error if the config was reloaded while a nagbar was active.\n", watcher->pid);
+    } else if (exitcode != 0) {
+        ELOG("i3-nagbar (%d) process exited with status %d\n", watcher->pid, exitcode);
+    } else {
+        DLOG("i3-nagbar (%d) process exited with status %d\n", watcher->pid, exitcode);
     }
 
-    *((pid_t *)watcher->data) = -1;
-}
-
-/*
- * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
- * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
- *
- */
-static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
-    pid_t *nagbar_pid = (pid_t *)watcher->data;
-    if (*nagbar_pid != -1) {
-        LOG("Sending SIGKILL (%d) to i3-nagbar with PID %d\n", SIGKILL, *nagbar_pid);
-        kill(*nagbar_pid, SIGKILL);
+    pid_t *nagbar_pid = watcher->data;
+    if (*nagbar_pid == watcher->pid) {
+        /* Only reset if the watched nagbar is the active nagbar */
+        *nagbar_pid = -1;
     }
 }
 
@@ -398,27 +384,20 @@ void start_nagbar(pid_t *nagbar_pid, char *argv[]) {
     ev_child_init(child, &nagbar_exited, *nagbar_pid, 0);
     child->data = nagbar_pid;
     ev_child_start(main_loop, child);
-
-    /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
-     * still running) */
-    ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
-    ev_cleanup_init(cleanup, nagbar_cleanup);
-    cleanup->data = nagbar_pid;
-    ev_cleanup_start(main_loop, cleanup);
 }
 
 /*
- * Kills the i3-nagbar process, if *nagbar_pid != -1.
+ * Kills the i3-nagbar process, if nagbar_pid != -1.
  *
  * If wait_for_it is set (restarting i3), this function will waitpid(),
  * otherwise, ev is assumed to handle it (reloading).
  *
  */
-void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it) {
-    if (*nagbar_pid == -1)
+void kill_nagbar(pid_t nagbar_pid, bool wait_for_it) {
+    if (nagbar_pid == -1)
         return;
 
-    if (kill(*nagbar_pid, SIGTERM) == -1)
+    if (kill(nagbar_pid, SIGTERM) == -1)
         warn("kill(configerror_nagbar) failed");
 
     if (!wait_for_it)
@@ -428,7 +407,7 @@ void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it) {
      * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD
      * for us and we would end up with a <defunct> process. Therefore we
      * waitpid() here. */
-    waitpid(*nagbar_pid, NULL, 0);
+    waitpid(nagbar_pid, NULL, 0);
 }
 
 /*
diff --git a/src/window.c b/src/window.c
index d84f8869..bee3fa66 100644
--- a/src/window.c
+++ b/src/window.c
@@ -9,6 +9,8 @@
  */
 #include "all.h"
 
+#include <math.h>
+
 /*
  * Frees an i3Window and all its members.
  *
diff --git a/src/workspace.c b/src/workspace.c
index 569d2e9a..e77d544b 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -72,16 +72,16 @@ static void _workspace_apply_default_orientation(Con *ws) {
 
 /*
  * Returns the first output that is assigned to a workspace specified by the
- * given name or number or NULL if no such output exists. If there is a
- * workspace with a matching name and another workspace with a matching number,
- * the output assigned to the first one is returned.
- * The order of the 'ws_assignments' queue is respected: if multiple assignments
- * match the specified workspace, the first one is returned.
- * If 'name' is NULL it will be ignored.
- * If 'parsed_num' is -1 it will be ignored.
+ * given name or number. Returns NULL if no such output exists.
+ *
+ * If an assignment matches by number but there is an assignment later that
+ * matches by name, the second one is preferred.
+ * The order of the 'ws_assignments' queue is respected: if multiple
+ * assignments match the criteria, the first one is returned.
+ * 'name' is ignored when NULL, 'parsed_num' is ignored when it is -1.
  *
  */
-static Con *get_assigned_output(const char *name, long parsed_num) {
+Con *get_assigned_output(const char *name, long parsed_num) {
     Con *output = NULL;
     struct Workspace_Assignment *assignment;
     TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
@@ -90,7 +90,8 @@ static Con *get_assigned_output(const char *name, long parsed_num) {
         }
 
         if (name && strcmp(assignment->name, name) == 0) {
-            DLOG("Found workspace name assignment to output \"%s\"\n", assignment->output);
+            DLOG("Found workspace name=\"%s\" assignment to output \"%s\"\n",
+                 name, assignment->output);
             Output *assigned_by_name = get_output_by_name(assignment->output, true);
             if (assigned_by_name) {
                 /* When the name matches exactly, skip numbered assignments. */
@@ -100,7 +101,8 @@ static Con *get_assigned_output(const char *name, long parsed_num) {
                    parsed_num != -1 &&
                    name_is_digits(assignment->name) &&
                    ws_name_to_number(assignment->name) == parsed_num) {
-            DLOG("Found workspace number assignment to output \"%s\"\n", assignment->output);
+            DLOG("Found workspace number=%ld assignment to output \"%s\"\n",
+                 parsed_num, assignment->output);
             Output *assigned_by_num = get_output_by_name(assignment->output, true);
             if (assigned_by_num) {
                 output = assigned_by_num->con;
@@ -126,64 +128,57 @@ bool output_triggers_assignment(Output *output, struct Workspace_Assignment *ass
  * memory and initializing the data structures correctly).
  *
  */
-Con *workspace_get(const char *num, bool *created) {
+Con *workspace_get(const char *num) {
     Con *workspace = get_existing_workspace_by_name(num);
+    if (workspace) {
+        return workspace;
+    }
 
-    if (workspace == NULL) {
-        LOG("Creating new workspace \"%s\"\n", num);
-        gaps_t gaps = (gaps_t){0, 0, 0, 0, 0};
+    LOG("Creating new workspace \"%s\"\n", num);
+    gaps_t gaps = (gaps_t){0, 0, 0, 0, 0};
 
-        /* We set workspace->num to the number if this workspace’s name begins
-         * with a positive number. Otherwise it’s a named ws and num will be
-         * -1. */
-        long parsed_num = ws_name_to_number(num);
+    /* We set workspace->num to the number if this workspace’s name begins with
+     * a positive number. Otherwise it’s a named ws and num will be 1. */
+    const long parsed_num = ws_name_to_number(num);
 
-        struct Workspace_Assignment *assignment;
-        TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
-            if (strcmp(assignment->name, num) == 0) {
-                gaps = assignment->gaps;
-                break;
-            } else if (parsed_num != -1 && name_is_digits(assignment->name) && ws_name_to_number(assignment->name) == parsed_num) {
-                gaps = assignment->gaps;
-            }
-        }
-
-        Con *output = get_assigned_output(num, parsed_num);
-        /* if an assignment is not found, we create this workspace on the current output */
-        if (!output) {
-            output = con_get_output(focused);
+    struct Workspace_Assignment *assignment;
+    TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+        if (strcmp(assignment->name, num) == 0) {
+            gaps = assignment->gaps;
+            break;
+        } else if (parsed_num != -1 && name_is_digits(assignment->name) && ws_name_to_number(assignment->name) == parsed_num) {
+            gaps = assignment->gaps;
         }
+    }
 
-        Con *content = output_get_content(output);
-        LOG("got output %p with content %p\n", output, content);
-        /* We need to attach this container after setting its type. con_attach
-         * will handle CT_WORKSPACEs differently */
-        workspace = con_new(NULL, NULL);
-        char *name;
-        sasprintf(&name, "[i3 con] workspace %s", num);
-        x_set_name(workspace, name);
-        free(name);
-        workspace->type = CT_WORKSPACE;
-        FREE(workspace->name);
-        workspace->name = sstrdup(num);
-        workspace->workspace_layout = config.default_layout;
-        workspace->num = parsed_num;
-        LOG("num = %d\n", workspace->num);
-        workspace->gaps = gaps;
-
-        workspace->parent = content;
-        _workspace_apply_default_orientation(workspace);
-
-        con_attach(workspace, content, false);
-
-        ipc_send_workspace_event("init", workspace, NULL);
-        ewmh_update_desktop_properties();
-        if (created != NULL)
-            *created = true;
-    } else if (created != NULL) {
-        *created = false;
+    Con *output = get_assigned_output(num, parsed_num);
+    /* if an assignment is not found, we create this workspace on the current output */
+    if (!output) {
+        output = con_get_output(focused);
     }
 
+    /* No parent because we need to attach this container after setting its
+     * type. con_attach will handle CT_WORKSPACEs differently. */
+    workspace = con_new(NULL, NULL);
+
+    char *name;
+    sasprintf(&name, "[i3 con] workspace %s", num);
+    x_set_name(workspace, name);
+    free(name);
+
+    FREE(workspace->name);
+    workspace->name = sstrdup(num);
+    workspace->workspace_layout = config.default_layout;
+    workspace->num = parsed_num;
+    workspace->type = CT_WORKSPACE;
+    workspace->gaps = gaps;
+
+    con_attach(workspace, output_get_content(output), false);
+    _workspace_apply_default_orientation(workspace);
+
+    ipc_send_workspace_event("init", workspace, NULL);
+    ewmh_update_desktop_properties();
+
     return workspace;
 }
 
@@ -577,9 +572,7 @@ void workspace_show(Con *workspace) {
  *
  */
 void workspace_show_by_name(const char *num) {
-    Con *workspace;
-    workspace = workspace_get(num, NULL);
-    workspace_show(workspace);
+    workspace_show(workspace_get(num));
 }
 
 /*
@@ -846,10 +839,7 @@ Con *workspace_back_and_forth_get(void) {
         return NULL;
     }
 
-    Con *workspace;
-    workspace = workspace_get(previous_workspace_name, NULL);
-
-    return workspace;
+    return workspace_get(previous_workspace_name);
 }
 
 static bool get_urgency_flag(Con *con) {
@@ -1036,7 +1026,7 @@ void workspace_move_to_output(Con *ws, Output *output) {
 
             /* so create the workspace referenced to by this assignment */
             DLOG("Creating workspace from assignment %s.\n", assignment->name);
-            workspace_get(assignment->name, NULL);
+            workspace_get(assignment->name);
             used_assignment = true;
             break;
         }
diff --git a/src/x.c b/src/x.c
index 712cf89c..48af5f37 100644
--- a/src/x.c
+++ b/src/x.c
@@ -10,6 +10,8 @@
  */
 #include "all.h"
 
+#include <unistd.h>
+
 #ifndef MAX
 #define MAX(x, y) ((x) > (y) ? (x) : (y))
 #endif
@@ -61,26 +63,18 @@ typedef struct con_state {
 
     char *name;
 
-    CIRCLEQ_ENTRY(con_state)
-    state;
-
-    CIRCLEQ_ENTRY(con_state)
-    old_state;
-
-    TAILQ_ENTRY(con_state)
-    initial_mapping_order;
+    CIRCLEQ_ENTRY(con_state) state;
+    CIRCLEQ_ENTRY(con_state) old_state;
+    TAILQ_ENTRY(con_state) initial_mapping_order;
 } con_state;
 
-CIRCLEQ_HEAD(state_head, con_state)
-state_head =
+CIRCLEQ_HEAD(state_head, con_state) state_head =
     CIRCLEQ_HEAD_INITIALIZER(state_head);
 
-CIRCLEQ_HEAD(old_state_head, con_state)
-old_state_head =
+CIRCLEQ_HEAD(old_state_head, con_state) old_state_head =
     CIRCLEQ_HEAD_INITIALIZER(old_state_head);
 
-TAILQ_HEAD(initial_mapping_head, con_state)
-initial_mapping_head =
+TAILQ_HEAD(initial_mapping_head, con_state) initial_mapping_head =
     TAILQ_HEAD_INITIALIZER(initial_mapping_head);
 
 /*
@@ -590,11 +584,6 @@ void x_draw_decoration(Con *con) {
         }
     }
 
-    /* if this is a borderless/1pixel window, we don’t need to render the
-     * decoration. */
-    if (p->border_style != BS_NORMAL)
-        goto copy_pixmaps;
-
     /* If the parent hasn't been set up yet, skip the decoration rendering
      * for now. */
     if (parent->frame_buffer.id == XCB_NONE)
@@ -608,6 +597,11 @@ void x_draw_decoration(Con *con) {
         FREE(con->parent->deco_render_params);
     }
 
+    /* if this is a borderless/1pixel window, we don’t need to render the
+     * decoration. */
+    if (p->border_style != BS_NORMAL)
+        goto copy_pixmaps;
+
     /* 4: paint the bar */
     draw_util_rectangle(&(parent->frame_buffer), p->color->background,
                         con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height);
diff --git a/src/xcursor.c b/src/xcursor.c
index cffb094b..cb98399f 100644
--- a/src/xcursor.c
+++ b/src/xcursor.c
@@ -9,13 +9,12 @@
  */
 #include <config.h>
 
+#include "all.h"
+
 #include <assert.h>
 #include <err.h>
-#include <xcb/xcb_cursor.h>
 
-#include "i3.h"
-#include "xcb.h"
-#include "xcursor.h"
+#include <xcb/xcb_cursor.h>
 
 static xcb_cursor_context_t *ctx;
 static xcb_cursor_t cursors[XCURSOR_CURSOR_MAX];
diff --git a/testcases/complete-run.pl.in b/testcases/complete-run.pl.in
index 96b93bed..00f8e609 100755
--- a/testcases/complete-run.pl.in
+++ b/testcases/complete-run.pl.in
@@ -18,7 +18,7 @@ use Time::HiRes qw(time);
 use IO::Handle;
 
 # these are shipped with the testsuite
-use lib qw(@abs_top_builddir@/testcases/lib @abs_top_srcdir@/testcases/lib @abs_top_srcdir@/AnyEvent-I3/blib/lib);
+use lib qw(@abs_top_builddir@ @abs_top_builddir@/testcases/lib @abs_top_srcdir@/testcases/lib @abs_top_builddir@/AnyEvent-I3/blib/lib);
 use i3test::Util qw(slurp);
 use StartXServer;
 use StatusLine;
@@ -78,12 +78,12 @@ pod2usage(-verbose => 2, -exitcode => 0) if $help;
 # Check for missing executables
 my @binaries = qw(
                    @abs_top_builddir@/i3
-                   @abs_top_builddir@/i3bar/i3bar
-                   @abs_top_builddir@/i3-config-wizard/i3-config-wizard
-                   @abs_top_builddir@/i3-dump-log/i3-dump-log
-                   @abs_top_builddir@/i3-input/i3-input
-                   @abs_top_builddir@/i3-msg/i3-msg
-                   @abs_top_builddir@/i3-nagbar/i3-nagbar
+                   @abs_top_builddir@/i3bar
+                   @abs_top_builddir@/i3-config-wizard
+                   @abs_top_builddir@/i3-dump-log
+                   @abs_top_builddir@/i3-input
+                   @abs_top_builddir@/i3-msg
+                   @abs_top_builddir@/i3-nagbar
                );
 
 foreach my $binary (@binaries) {
@@ -103,12 +103,6 @@ foreach my $binary (@test_binaries) {
 }
 
 $ENV{PATH} = join(':',
-    '@abs_top_builddir@/i3-nagbar',
-    '@abs_top_builddir@/i3-msg',
-    '@abs_top_builddir@/i3-input',
-    '@abs_top_builddir@/i3-dump-log',
-    '@abs_top_builddir@/i3-config-wizard',
-    '@abs_top_builddir@/i3bar',
     '@abs_top_builddir@',
     '@abs_top_srcdir@',
     $ENV{PATH});
diff --git a/testcases/inject_randr1.5.c b/testcases/inject_randr1.5.c
index 819c029b..9c0b73d4 100644
--- a/testcases/inject_randr1.5.c
+++ b/testcases/inject_randr1.5.c
@@ -16,15 +16,17 @@
 
 #include <ev.h>
 #include <fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/time.h>
-#include <sys/resource.h>
+#include <getopt.h>
+#include <libgen.h>
 #include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
 #include <sys/wait.h>
-#include <libgen.h>
+#include <unistd.h>
 
 static void uds_connection_cb(EV_P_ ev_io *w, int revents);
 static void read_client_setup_request_cb(EV_P_ ev_io *w, int revents);
diff --git a/testcases/lib/i3test.pm.in b/testcases/lib/i3test.pm.in
index 161ddf79..aedaa601 100644
--- a/testcases/lib/i3test.pm.in
+++ b/testcases/lib/i3test.pm.in
@@ -7,7 +7,7 @@ use Test::Builder;
 use X11::XCB::Rect;
 use X11::XCB::Window;
 use X11::XCB qw(:all);
-use lib qw(@abs_top_srcdir@/AnyEvent-I3/blib/lib);
+use lib qw(@abs_top_builddir@/AnyEvent-I3/blib/lib);
 use AnyEvent::I3;
 use List::Util qw(first);
 use Time::HiRes qw(sleep);
diff --git a/testcases/lib/i3test/XTEST.pm b/testcases/lib/i3test/XTEST.pm
index 4c464c5e..89b8facb 100644
--- a/testcases/lib/i3test/XTEST.pm
+++ b/testcases/lib/i3test/XTEST.pm
@@ -7,7 +7,7 @@ use v5.10;
 
 use Test::More;
 use i3test::Util qw(get_socket_path);
-use lib qw(@abs_top_srcdir@/AnyEvent-I3/blib/lib);
+use lib qw(@abs_top_builddir@/AnyEvent-I3/blib/lib);
 use AnyEvent::I3;
 use ExtUtils::PkgConfig;
 
diff --git a/testcases/t/124-move.t b/testcases/t/124-move.t
index b7ef0b2c..b4a8ca3e 100644
--- a/testcases/t/124-move.t
+++ b/testcases/t/124-move.t
@@ -220,21 +220,49 @@ is($absolute->y, $absolute_before->y, 'y not changed');
 is($absolute->width, $absolute_before->width, 'width not changed');
 is($absolute->height, $absolute_before->height, 'height not changed');
 
+$absolute_before = $absolute;
+$top_before = $top;
+
 ######################################################################
-# 6) test moving floating window to a specified position
+# 7) test moving floating containers with a specific amount of ppt
+######################################################################
+
+cmd 'move right 25 ppt';
+
+sync_with_i3;
+
+($absolute, $top) = $floatwin->rect;
+
+is($absolute->x, int($x->root->rect->width * 0.25) + $absolute_before->x, 'moved 25 ppt to the right');
+is($absolute->y, $absolute_before->y, 'y not changed');
+is($absolute->width, $absolute_before->width, 'width not changed');
+is($absolute->height, $absolute_before->height, 'height not changed');
+
+######################################################################
+# 8) test moving floating window to a specified position
 #    and to absolute center
 ######################################################################
 
 $tmp = fresh_workspace;
 open_floating_window; my @floatcon;
 
+# Move to specified position with px
 cmd 'move position 5 px 15 px';
 
 @floatcon = @{get_ws($tmp)->{floating_nodes}};
 
-is($floatcon[0]->{rect}->{x}, 5, 'moved to position 5 x');
-is($floatcon[0]->{rect}->{y}, 15, 'moved to position 15 y');
+is($floatcon[0]->{rect}->{x}, 5, 'moved to position 5 (px) x');
+is($floatcon[0]->{rect}->{y}, 15, 'moved to position 15 (px) y');
+
+# Move to specified position with ppt
+cmd 'move position 20 ppt 20 ppt';
+
+@floatcon = @{get_ws($tmp)->{floating_nodes}};
+
+is($floatcon[0]->{rect}->{x}, int($x->root->rect->width * 0.20), "moved to position 20 (ppt) x");
+is($floatcon[0]->{rect}->{y}, int($x->root->rect->height * 0.20), "moved to position 20 (ppt) y");
 
+# Move to absolute center
 cmd 'move absolute position center';
 
 @floatcon = @{get_ws($tmp)->{floating_nodes}};
diff --git a/testcases/t/132-move-workspace.t b/testcases/t/132-move-workspace.t
index 60705f96..c7721c3d 100644
--- a/testcases/t/132-move-workspace.t
+++ b/testcases/t/132-move-workspace.t
@@ -339,6 +339,53 @@ $ws = get_ws($tmp2);
 is_num_children($tmp2, 0, 'no regular nodes on second workspace');
 is(@{$ws->{floating_nodes}}, 1, 'one floating node on second workspace');
 
+###################################################################
+# Test that when moving a fullscreen floating window to a workspace
+# that already has an other fullscreen container, the second
+# container gets un-fullscreened.
+# See #4124
+###################################################################
+$tmp2 = fresh_workspace;
+$second = open_window;
+cmd 'fullscreen enable';
+$ws = get_ws($tmp2);
+is($ws->{nodes}->[0]->{fullscreen_mode}, 1, 'sanity check: fullscreen enabled');
+
+$tmp = fresh_workspace;
+$first = open_window;
+cmd 'floating enable, fullscreen enable';
+cmd "move workspace $tmp2";
+
+$ws = get_ws($tmp2);
+is_num_children($tmp2, 1, 'one regular node on second workspace');
+is_num_fullscreen($tmp2, 1, 'one fullscreen node on second workspace');
+is(@{$ws->{floating_nodes}}, 1, 'one floating node on second workspace');
+is($ws->{nodes}->[0]->{fullscreen_mode}, 0, 'previous fullscreen disabled');
+
+###################################################################
+# Same as above, but trigger the bug with the parent of a
+# fullscreen container, instead of a CT_FLOATING_CON.
+###################################################################
+$tmp2 = fresh_workspace;
+$second = open_window;
+cmd 'fullscreen enable';
+$ws = get_ws($tmp2);
+is($ws->{nodes}->[0]->{fullscreen_mode}, 1, 'sanity check: fullscreen enabled');
+
+$tmp = fresh_workspace;
+open_window;
+$first = open_window;
+cmd 'layout tabbed';
+cmd 'focus parent, mark a, focus child';
+cmd 'fullscreen enable';
+cmd "[con_mark=a] move workspace $tmp2";
+
+$ws = get_ws($tmp2);
+is(sum_nodes(get_ws_content($tmp2)), 4, '3 leafs & 1 split node in second workspace');
+# is_num_fullscreen does not catch this nested fullscreen container
+is($ws->{nodes}->[0]->{fullscreen_mode}, 0, 'previous fullscreen disabled');
+is($ws->{nodes}->[1]->{nodes}->[1]->{fullscreen_mode}, 1, 'nested fullscreen from moved container preserved');
+
 ###################################################################
 # Check that moving an empty workspace using criteria doesn't
 # create unfocused empty workspace.
diff --git a/testcases/t/193-ipc-version.t b/testcases/t/193-ipc-version.t
index d5f4badf..82449de7 100644
--- a/testcases/t/193-ipc-version.t
+++ b/testcases/t/193-ipc-version.t
@@ -32,6 +32,5 @@ cmp_ok($version->{minor}, '>', 0, 'minor version > 0');
 
 is(int($version->{minor}), $version->{minor}, 'minor version is an integer');
 is(int($version->{patch}), $version->{patch}, 'patch version is an integer');
-like($version->{human_readable}, qr/branch/, 'human readable version contains branch name');
 
 done_testing;
diff --git a/testcases/t/297-assign-workspace-to-output.t b/testcases/t/297-assign-workspace-to-output.t
index eb8f24b2..4e251e5d 100644
--- a/testcases/t/297-assign-workspace-to-output.t
+++ b/testcases/t/297-assign-workspace-to-output.t
@@ -41,7 +41,7 @@ my $pid = launch_with_config($config);
 
 sub check_output {
     my ($workspace, $output, $msg) = @_;
-    is(get_output_for_workspace($workspace), $output, $msg);
+    is(get_output_for_workspace($workspace), $output, "[$workspace->$output] " . $msg);
 }
 
 check_output('9', '', 'Numbered workspace with a big number that is assigned to output that does not exist is not used');
@@ -82,6 +82,10 @@ workspace 4 output whitespace                    fake-0
 workspace foo output doesnotexist1 doesnotexist2 doesnotexist3
 workspace bar output doesnotexist
 workspace bar output fake-0
+workspace 5 output fake-0
+workspace 5:xxx output fake-1
+workspace 6:xxx output fake-0
+workspace 6 output fake-1
 EOT
 
 $pid = launch_with_config($config);
@@ -90,8 +94,11 @@ do_test('1', 'fake-0', 'Multiple assignments do not override a single one');
 do_test('2', 'fake-3', 'First output out of multiple assignments is used');
 do_test('3', 'fake-0', 'First existing output is used');
 do_test('4', 'fake-0', 'Excessive whitespace is ok');
-do_test('5', 'fake-1', 'Numbered initialization for fake-1');
-do_test('6', 'fake-2', 'Numbered initialization for fake-2');
+do_test('5', 'fake-0', 'Numbered assignment ok');
+do_test('5:xxx', 'fake-1', 'Named assignment overrides number');
+do_test('6', 'fake-1', 'Numbered assignment ok');
+do_test('6:xxx', 'fake-0', 'Named assignment overrides number');
+do_test('7', 'fake-2', 'Numbered initialization for fake-2');
 
 cmd 'focus output fake-0, workspace foo';
 check_output('foo', 'fake-0', 'Workspace with only non-existing assigned outputs opened in current output');
@@ -103,5 +110,14 @@ check_output('bar', 'fake-0', 'Second workspace assignment line ignored');
 cmd 'workspace 2, move workspace to output left';
 check_output('2', 'fake-2', 'Moved assigned workspace up');
 
+# Specific name overrides assignment by number after renaming
+# See #4021
+cmd 'workspace 5, rename workspace to 5:xxx';
+check_output('5:xxx', 'fake-1', 'workspace triggered correct, specific assignment after renaming');
+
+# Same but opposite order
+cmd 'workspace 6, rename workspace to 6:xxx';
+check_output('6:xxx', 'fake-0', 'workspace triggered correct, specific assignment after renaming');
+
 exit_gracefully($pid);
 done_testing;
diff --git a/testcases/t/310-client-message-sticky.t b/testcases/t/310-client-message-sticky.t
new file mode 100644
index 00000000..0e7d8b7c
--- /dev/null
+++ b/testcases/t/310-client-message-sticky.t
@@ -0,0 +1,70 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • https://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • https://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • https://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Verify that _NET_WM_DESKTOP sticky requests do not conflict with dock
+# clients, resulting in a crash
+# Ticket: #4039
+# Bug still in: 4.18-238-g4d55bba7f
+use i3test;
+
+sub send_sticky_request {
+    my ($win) = @_;
+
+    my $msg = pack "CCSLLLLLL",
+        X11::XCB::CLIENT_MESSAGE, # response_type
+        32, # format
+        0, # sequence
+        $win->id, # window
+        $x->atom(name => '_NET_WM_DESKTOP')->id, # message type
+        hex '0xFFFFFFFF', # data32[0] = NET_WM_DESKTOP_ALL
+        0, # data32[1]
+        0, # data32[2]
+        0, # data32[3]
+        0; # data32[4]
+
+    $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
+}
+
+# Test the normal functionality first
+my $ws = fresh_workspace;
+my $win = open_window;
+
+is(@{get_ws($ws)->{floating_nodes}}, 0, 'No floating windows yet');
+send_sticky_request($win);
+sync_with_i3;
+
+is(@{get_ws($ws)->{floating_nodes}}, 1, 'One floating (sticky) window');
+$ws = fresh_workspace;
+is(@{get_ws($ws)->{floating_nodes}}, 1, 'Sticky window in new workspace');
+
+# See #4039
+kill_all_windows;
+$win = open_window({
+    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK')
+});
+
+send_sticky_request($win);
+sync_with_i3;
+is(@{get_ws($ws)->{floating_nodes}}, 0, 'Dock client did not get sticky/floating');
+
+# Cause a ConfigureRequest by setting the window’s position/size.
+my ($a, $t) = $win->rect;
+$win->rect(X11::XCB::Rect->new(x => 0, y => 0, width => $a->width, height => $a->height));
+
+does_i3_live;
+is(@{get_ws($ws)->{floating_nodes}}, 0, 'Dock client still not sticky/floating');
+
+done_testing;
diff --git a/testcases/t/311-get-binding-modes.t b/testcases/t/311-get-binding-modes.t
new file mode 100644
index 00000000..3a00f695
--- /dev/null
+++ b/testcases/t/311-get-binding-modes.t
@@ -0,0 +1,41 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • https://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • https://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • https://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Verifies the GET_BINDING_MODE IPC command
+# Ticket: #3892
+# Bug still in: 4.18-318-g50160eb1
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+mode "extra" {
+    bindsym Mod1+x nop foo
+}
+EOT
+
+my $i3 = i3(get_socket_path());
+$i3->connect->recv;
+# TODO: use the symbolic name for the command/reply type instead of the
+# numerical 12:
+my $binding_state = $i3->message(12, "")->recv;
+is($binding_state->{name}, 'default', 'at startup, binding mode is default');
+
+cmd 'mode extra';
+
+$binding_state = $i3->message(12, "")->recv;
+is($binding_state->{name}, 'extra', 'after switching, binding mode is extra');
+
+done_testing;
diff --git a/testcases/t/542-layout-restore-remanage.t b/testcases/t/542-layout-restore-remanage.t
index 26b50835..82ec84d0 100644
--- a/testcases/t/542-layout-restore-remanage.t
+++ b/testcases/t/542-layout-restore-remanage.t
@@ -83,4 +83,70 @@ is($nodes[0]->{name}, 'different_title', 'test window got swallowed');
 
 close($fh);
 
+############################################################
+# Make sure window only gets swallowed once
+############################################################
+# Regression, issue #3888
+$ws = fresh_workspace;
+
+($fh, $filename) = tempfile(UNLINK => 1);
+print $fh <<'EOT';
+// vim:ts=4:sw=4:et
+{
+    // splith split container with 2 children
+    "layout": "splith",
+    "type": "con",
+    "nodes": [
+        {
+            "type": "con",
+            "swallows": [
+               {
+               "class": "^foo$"
+               }
+            ]
+        },
+        {
+            // splitv split container with 2 children
+            "layout": "splitv",
+            "type": "con",
+            "nodes": [
+                {
+                    "type": "con",
+                    "swallows": [
+                       {
+                        "class": "^foo$"
+                       }
+                    ]
+                },
+                {
+                    "type": "con",
+                    "swallows": [
+                       {
+                        "class": "^foo$"
+                       }
+                    ]
+                }
+            ]
+        }
+    ]
+}
+EOT
+$fh->flush;
+cmd "append_layout $filename";
+
+$window = open_window wm_class => 'foo';
+
+# Changing an unrelated window property originally resulted in the window
+# getting remanaged and swallowd by a different placeholder, even though the
+# matching property (class for the layout above) didn't change.
+change_window_title($window, "different_title");
+
+@content = @{get_ws_content($ws)};
+
+subtest 'regression test that window gets only swallowed once', sub {
+    is($content[0]->{nodes}[0]->{window}, $window->id, 'first placeholder swallowed window');
+    isnt($content[0]->{nodes}[1]->{nodes}[0]->{window}, $window->id, 'second placeholder did not swallow window');
+    isnt($content[0]->{nodes}[1]->{nodes}[1]->{window}, $window->id, 'thid placeholder did not swallow window');
+};
+
 done_testing;
diff --git a/travis/bintray-autobuild-debian.json b/travis/bintray-autobuild-debian.json
index 23fe6050..d01b7dba 100644
--- a/travis/bintray-autobuild-debian.json
+++ b/travis/bintray-autobuild-debian.json
@@ -13,7 +13,7 @@
 
     "files": [
         {
-            "includePattern": "build/deb/debian-amd64/(.*\\.deb)$",
+            "includePattern": "distbuild/deb/debian-amd64/(.*\\.deb)$",
 	    "matrixParams": {
                 "deb_distribution": "sid",
                 "deb_component": "main",
@@ -22,7 +22,7 @@
 	    "uploadPattern": "$1"
         },
         {
-            "includePattern": "build/deb/debian-i386/(.*\\.deb)$",
+            "includePattern": "distbuild/deb/debian-i386/(.*\\.deb)$",
 	    "matrixParams": {
                 "deb_distribution": "sid",
                 "deb_component": "main",
diff --git a/travis/bintray-autobuild-ubuntu.json b/travis/bintray-autobuild-ubuntu.json
index 37f2f180..4c0d6114 100644
--- a/travis/bintray-autobuild-ubuntu.json
+++ b/travis/bintray-autobuild-ubuntu.json
@@ -13,7 +13,7 @@
 
     "files": [
         {
-            "includePattern": "build/deb/ubuntu-amd64/(.*\\.deb)$",
+            "includePattern": "distbuild/deb/ubuntu-amd64/(.*\\.deb)$",
 	    "matrixParams": {
                 "deb_distribution": "bionic",
                 "deb_component": "main",
@@ -22,7 +22,7 @@
 	    "uploadPattern": "$1"
         },
         {
-            "includePattern": "build/deb/ubuntu-i386/(.*\\.deb)$",
+            "includePattern": "distbuild/deb/ubuntu-i386/(.*\\.deb)$",
 	    "matrixParams": {
                 "deb_distribution": "bionic",
                 "deb_component": "main",
diff --git a/travis/check-formatting.sh b/travis/check-formatting.sh
index 7aa2f565..3424513f 100755
--- a/travis/check-formatting.sh
+++ b/travis/check-formatting.sh
@@ -3,4 +3,4 @@
 set -e
 set -x
 
-clang-format-6.0 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
+clang-format-9 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
diff --git a/travis/check-spelling.pl b/travis/check-spelling.pl
index 45770c5e..f2e79b51 100755
--- a/travis/check-spelling.pl
+++ b/travis/check-spelling.pl
@@ -33,12 +33,12 @@ my $binary_spelling_exceptions = {
 };
 my @binaries = qw(
     build/i3
-    build/i3-config-wizard/i3-config-wizard
-    build/i3-dump-log/i3-dump-log
-    build/i3-input/i3-input
-    build/i3-msg/i3-msg
-    build/i3-nagbar/i3-nagbar
-    build/i3bar/i3bar
+    build/i3-config-wizard
+    build/i3-dump-log
+    build/i3-input
+    build/i3-msg
+    build/i3-nagbar
+    build/i3bar
 );
 for my $binary (@binaries) {
     check_spelling(slurp($binary), $binary_spelling_exceptions, sub {
diff --git a/travis/debian-build.sh b/travis/debian-build.sh
index 9ce5a5af..6e66155a 100755
--- a/travis/debian-build.sh
+++ b/travis/debian-build.sh
@@ -5,14 +5,10 @@ set -x
 
 DEST=$1
 
-mkdir -p build
-cd build
-../configure
-make echo-version > ../I3_VERSION
-make dist-bzip2
+cd distbuild
 # unpack dist tarball
 mkdir -p "${DEST}"
-tar xf *.tar.bz2 -C "${DEST}" --strip-components=1
+tar xf meson-dist/*.tar.xz -C "${DEST}" --strip-components=1
 cp -r ../debian "${DEST}"
 sed -i '/^\s*libxcb-xrm-dev/d' deb/ubuntu-*/DIST/debian/control || true
 cd "${DEST}"
diff --git a/travis/docker-build-and-push.sh b/travis/docker-build-and-push.sh
index 686b81b0..243d36a9 100755
--- a/travis/docker-build-and-push.sh
+++ b/travis/docker-build-and-push.sh
@@ -5,10 +5,6 @@ set -e
 BASENAME=$1
 DOCKERFILE=$2
 
-# .dockerignore is created on demand so that release.sh and other scripts are
-# not influenced by our travis setup.
-echo .git > .dockerignore
-
 docker build --pull --no-cache --rm -t=${BASENAME} -f ${DOCKERFILE} .
 # For pull requests, travis does not add secure environment variables to the
 # environment (because pull requests could then steal their values), so skip
diff --git a/travis/docs.sh b/travis/docs.sh
index 043104a9..501946fa 100755
--- a/travis/docs.sh
+++ b/travis/docs.sh
@@ -3,20 +3,24 @@
 set -e
 set -x
 
-for f in $(grep '\.html$' debian/i3-wm.docs | grep -v 'docs/refcard.html' | grep -v 'docs/lib-i3test')
+# TODO: install the docs via meson, inject styles with an option
+
+for f in $(sed -n "s/^\s*'\(docs\/.*\)',$/\1/gp" meson.build | grep -vF .)
 do
 	asciidoc -a linkcss -a stylesdir=https://i3wm.org/css -a scriptsdir=https://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf $(dirname $f)/$(basename $f .html)
 done
+
 ./docs/i3-pod2html --stylesurl=https://i3wm.org/css i3-dmenu-desktop man/i3-dmenu-desktop.html
 ./docs/i3-pod2html --stylesurl=https://i3wm.org/css i3-save-tree man/i3-save-tree.html
-./docs/i3-pod2html --stylesurl=https://i3wm.org/css build/testcases/lib/i3test.pm docs/lib-i3test.html
+./docs/i3-pod2html --stylesurl=https://i3wm.org/css build/i3test.pm docs/lib-i3test.html
 ./docs/i3-pod2html --stylesurl=https://i3wm.org/css testcases/lib/i3test/Test.pm docs/lib-i3test-test.html
-for file in $(sed 's/\.1$/.man/g' debian/i3-wm.manpages)
+
+for file in $(sed -n "s/^\s*'\(man\/.*\).man',$/\1.man/gp" meson.build)
 do
 	[ -f "$file" ] && asciidoc -a linkcss -a stylesdir=https://i3wm.org/css -a scriptsdir=https://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf "$file"
 done
 
 mkdir -p deb/COPY-DOCS
 
-cp $(tr "\n" ' ' < debian/i3-wm.docs) deb/COPY-DOCS/
-cp $(sed 's/\.1$/.html/g' debian/i3-wm.manpages | tr "\n" ' ') deb/COPY-DOCS/
+cp $(sed -n "s/^\s*'\(docs\/.*\)',$/\1/gp" meson.build | grep -F .) deb/COPY-DOCS/
+cp $(sed -n "s/^\s*'\(man\/.*\).man',$/\1.html/gp" meson.build) deb/COPY-DOCS/
diff --git a/travis/run-tests.sh b/travis/run-tests.sh
index eac2ea8a..4cc70479 100755
--- a/travis/run-tests.sh
+++ b/travis/run-tests.sh
@@ -26,7 +26,7 @@ fi
 
 # Try running the tests in parallel so that the common case (tests pass) is
 # quick, but fall back to running them in sequence to make debugging easier.
-if ! make check
+if ! ninja test
 then
-	./testcases/complete-run.pl --parallel=1 || (cat latest/complete-run.log; false)
+	./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false)
 fi
diff --git a/travis/travis-base-386.Dockerfile b/travis/travis-base-386.Dockerfile
index 355c2588..fc035034 100644
--- a/travis/travis-base-386.Dockerfile
+++ b/travis/travis-base-386.Dockerfile
@@ -13,12 +13,12 @@ RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
 # (3608 kB/s)). Hence, let’s stick with httpredir.debian.org (default) for now.
 
 # Install mk-build-deps (for installing the i3 build dependencies),
-# clang and clang-format-6.0 (for checking formatting and building with clang),
+# clang and clang-format-9 (for checking formatting and building with clang),
 # lintian (for checking spelling errors),
 RUN linux32 apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
     dpkg-dev devscripts git equivs \
-    clang clang-format-6.0 \
+    build-essential clang clang-format-9 \
     lintian && \
     rm -rf /var/lib/apt/lists/*
 
diff --git a/travis/travis-base-ubuntu-386.Dockerfile b/travis/travis-base-ubuntu-386.Dockerfile
index d52df4b8..747330aa 100644
--- a/travis/travis-base-ubuntu-386.Dockerfile
+++ b/travis/travis-base-ubuntu-386.Dockerfile
@@ -13,12 +13,12 @@ RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
 # (3608 kB/s)). Hence, let’s stick with httpredir.debian.org (default) for now.
 
 # Install mk-build-deps (for installing the i3 build dependencies),
-# clang and clang-format-6.0 (for checking formatting and building with clang),
+# clang and clang-format-9 (for checking formatting and building with clang),
 # lintian (for checking spelling errors),
 RUN linux32 apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
     dpkg-dev devscripts git equivs \
-    clang clang-format-6.0 \
+    build-essential clang clang-format-9 \
     lintian && \
     rm -rf /var/lib/apt/lists/*
 
diff --git a/travis/travis-base-ubuntu.Dockerfile b/travis/travis-base-ubuntu.Dockerfile
index d1057a39..8a728af3 100644
--- a/travis/travis-base-ubuntu.Dockerfile
+++ b/travis/travis-base-ubuntu.Dockerfile
@@ -13,13 +13,13 @@ RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
 # (3608 kB/s)). Hence, let’s stick with httpredir.debian.org (default) for now.
 
 # Install mk-build-deps (for installing the i3 build dependencies),
-# clang and clang-format-6.0 (for checking formatting and building with clang),
+# clang and clang-format-9 (for checking formatting and building with clang),
 # lintian (for checking spelling errors),
 # test suite dependencies (for running tests)
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
     dpkg-dev devscripts git equivs \
-    clang clang-format-6.0 \
+    build-essential clang clang-format-9 \
     lintian && \
     rm -rf /var/lib/apt/lists/*
 
diff --git a/travis/travis-base.Dockerfile b/travis/travis-base.Dockerfile
index def7598d..e5552c14 100644
--- a/travis/travis-base.Dockerfile
+++ b/travis/travis-base.Dockerfile
@@ -11,13 +11,13 @@ RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
 # (3608 kB/s)). Hence, let’s stick with httpredir.debian.org (default) for now.
 
 # Install mk-build-deps (for installing the i3 build dependencies),
-# clang and clang-format-6.0 (for checking formatting and building with clang),
+# clang and clang-format-9 (for checking formatting and building with clang),
 # lintian (for checking spelling errors),
 # test suite dependencies (for running tests)
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
     dpkg-dev devscripts git equivs \
-    clang clang-format-6.0 \
+    build-essential clang clang-format-9 \
     lintian \
     libmodule-install-perl libanyevent-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libinline-c-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libipc-run-perl libxcb-xtest0-dev libx11-xcb-perl libjson-xs-perl x11-xserver-utils && \
     rm -rf /var/lib/apt/lists/*