diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 40fd839..f033a48 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,7 +4,7 @@ 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 +## i3 bug reports 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). @@ -18,6 +18,20 @@ encountered the issue you are about to report while using a compositor, please try reproducing it without a compositor. +## i3 feature requests + +1. Read the [project goals](https://i3wm.org) on the website and make sure that + they are compatible with the feature you want to suggest. +2. We are generally happy with the current feature set of i3 and instead focus + on maintenance such as stability and fixing bugs. New features will rarely + be considered if they require additional configuration and/or commands, or + if they add significant complexity (either through the exposed configuration + or mental complexity) to the project. +3. Explain in detail what problem the feature addresses and why existing + features fall short. +4. Consider whether the feature could instead be implemented using the + [IPC](https://i3wm.org/docs/ipc.html) or other external tooling. + ## Pull requests * Before sending a pull request for new features, please check with us that the @@ -27,11 +41,14 @@ * 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. + add a small single-line file starting with a number (see examples) containing + a short explanation of your change either in the + [changes](../release-notes/changes) or the + [bugfixes](../release-notes/bugfixes/) 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 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0225148 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Ask a question or request support for using i3 + url: https://github.com/i3/i3/discussions/new + about: Ask a question or request support for using i3 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e1c169a..c41d1ca 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -8,7 +8,7 @@ --> ## I'm submitting a… - +
 [ ] Bug
 [x] Feature Request
@@ -28,6 +28,15 @@
 e.g., »The window left next to the current window should be focused.«
 -->
 
+## Impact
+
+
+[ ] This feature requires new configuration and/or commands
+
+ ## Environment ## I'm submitting a… - +
 [ ] Bug
 [ ] Feature Request
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..22c6132
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,102 @@
+name: GitHub Actions
+
+on:
+  push:
+    branches: [ gaps-next, gaps, actions ]
+  pull_request:
+    branches: [ gaps-next ]
+
+jobs:
+  build:
+    name: build and test
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        compiler: [gcc, clang]
+    env:
+      CC: ${{ matrix.compiler }}
+      DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
+      DOCKER_EMAIL: ${{ secrets.DOCKER_EMAIL }}
+      DOCKER_USER: ${{ secrets.DOCKER_USER }}
+      GH_TOKEN: ${{ secrets.GH_TOKEN }}
+      BALTO_TOKEN: ${{ secrets.BALTO_TOKEN }}
+
+    steps:
+    - uses: actions/checkout@v2
+    - run: git fetch --prune --unshallow
+    - name: construct container name
+      run: |
+        echo "BASENAME=i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base.Dockerfile)" >> $GITHUB_ENV
+        echo "BASENAME_386=i3wm/travis-base-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-386.Dockerfile)" >> $GITHUB_ENV
+        echo "BASENAME_UBUNTU=i3wm/travis-base-ubuntu:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu.Dockerfile)" >> $GITHUB_ENV
+        echo "BASENAME_UBUNTU_386=i3wm/travis-base-ubuntu-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu-386.Dockerfile)" >> $GITHUB_ENV
+    - name: fetch or build Docker container
+      run: |
+        docker pull ${{ env.BASENAME }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME }} travis/travis-base.Dockerfile
+    - name: fetch or build extra Docker containers
+      run: |
+        echo "::group::Ubuntu amd64"
+        ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_UBUNTU }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU }} travis/travis-base-ubuntu.Dockerfile
+        echo "::endgroup::"
+        echo "::group::Debian i386"
+        ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_386 }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_386 }} travis/travis-base-386.Dockerfile
+        echo "::endgroup::"
+        echo "::group::Ubuntu i386"
+        ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_UBUNTU_386 }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU_386 }} travis/travis-base-ubuntu-386.Dockerfile
+        echo "::endgroup::"
+    - name: build i3
+      run: |
+        docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.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'
+    - name: check spelling
+      run: |
+        docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/check-spelling.pl
+    - name: run i3 tests
+      run: |
+        docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} ./travis/run-tests.sh
+    - name: Archive test logs
+      uses: actions/upload-artifact@v2
+      with:
+        name: test-logs
+        path: build/testsuite-*
+      if: ${{ failure() }}
+    - name: build dist tarball
+      run: |
+        docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf distbuild; mkdir distbuild && cd distbuild && meson .. -Ddocs=true -Dmans=true && ninja -v dist'
+    - name: build Debian packages
+      run: |
+        echo "::group::Debian amd64"
+        ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/debian-build.sh deb/debian-amd64/DIST
+        echo "::endgroup::"
+        echo "::group::Ubuntu amd64"
+        ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_UBUNTU }} ./travis/debian-build.sh deb/ubuntu-amd64/DIST
+        echo "::endgroup::"
+        echo "::group::Debian i386"
+        ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_386 }} linux32 ./travis/debian-build.sh deb/debian-i386/DIST
+        echo "::endgroup::"
+        echo "::group::Ubuntu i386"
+        ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_UBUNTU_386 }} linux32 ./travis/debian-build.sh deb/ubuntu-i386/DIST
+        echo "::endgroup::"
+    - name: push Debian packages to balto
+      run: |
+        ./travis/skip-pkg.sh || travis/push-balto.sh
+    - name: build docs
+      run: |
+        ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/docs.sh
+    - name: push docs to GitHub pages
+      run: |
+        ./travis/skip-pkg.sh || travis/deploy-github-pages.sh
+  formatting:
+    name: Check formatting
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: check & print release notes
+      run: ./release-notes/generator.pl
+    - name: Install dependencies
+      run: |
+        sudo apt-get install -y clang-format-10
+    - name: Check formatting
+      run: clang-format-10 --dry-run --Werror $(git ls-files '*.c' '*.h')
+    - name: Verify safe wrapper functions are used
+      run: ./travis/check-safe-wrappers.sh
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9935cb6..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-dist: trusty
-services:
-  - docker
-language: c
-compiler:
-  - gcc
-  - clang
-addons:
-  apt:
-    packages:
-    # For https support in HTTP::Tiny.
-    - libio-socket-ssl-perl
-env:
-  global:
-  - BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base.Dockerfile)"
-  - BASENAME_386="i3wm/travis-base-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-386.Dockerfile)"
-  - BASENAME_UBUNTU="i3wm/travis-base-ubuntu:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu.Dockerfile)"
-  - BASENAME_UBUNTU_386="i3wm/travis-base-ubuntu-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu-386.Dockerfile)"
-  - secure: "B5IICA8MPx/FKaB50rTPqL8P1NU+Q0yuWl+lElL4+a9xSyLikfm3NzUPHoVwx8lNw2AVK6br7p0OmF7vMFjqAgrgc1cajTtEae5uFRKNUrWLpXM046YgNEYLLIHsQOjInxE+S4O6EFVzsUqsu8aeo2Xhq4sm4iUocG7e5isYgYo=" # DOCKER_PASS
-  - secure: "EIvrq8PG7lRjidppG0RCv4F0X4GP3DT9F5+ixVuGPfhK/hZT3jYC2AVY9G+NnUcXVwQEpW92rlqpftQ/qZ13FoyWokC8ZyoyD06fr5FPCfoFF3OczZwAJzZYkObI/hE9+/hXcylx/Os6N4INd2My1ntGk3JPsWL9riopod5EjSg=" # DOCKER_EMAIL
-  - secure: "hvhBunS4xXTgnIOsk/BPT7I7FrJhvVwCSt5PfxxvMqNaztOJI9BuK7ZrZ5Cy38KyHwlh3VHAH5AaCygJcPauoSQCV3bpnlbaWn3ruq2F0Q697Q5uNf73liXzyUqb9/Zvfvge4y4WWOhP5tVz1C6ZBe/NfhU7pqKLMA+6ads+99c=" # DOCKER_USER
-  - secure: "uJuuefmnJUuEH15ZD8xQilibx7EeBvMHBLoIZ8bgGHeleEImBD0XbD1ypvhYJKpviOmw5BkZmc9bVO8DGDEHYbSlIa2xDlF6vGrwgCEaxcMIhOAhv+dW9C/maJVieLOEPM01/fK2qdKESZaLvlopkWmxZwDyMObI9L7AMW9zQD8=" # BINTRAY_USER
-  - secure: "L3aPSNLySPXtWCW+xf8h/AAdquwNgxyTQpYOwexJmTPav82Qx8uQlp1yJkUmt+a+FLZDFfQeMivaHq0311RvuQVmkAJx49DjaddrwqOJut2UPsoVDn1WeuAcSHIXOq/0H+zgFMr/PGY0HXIsw1mTMhgheGJNqg09BvYWROCEAcA=" # BINTRAY_KEY
-  - secure: "sBMVn4C/WRWgoAytEFGx4CC5O55Q63h02AcuBnb1jXcBm0RenoBpzUPtxSseJwDPUA1o/UkuEDDjm3PosT5NF+dvED01VDFMsPVE11K0u6+avYy3jYXqyUEDW3G2o6Wo/2aqNjmd++8jskBdS9+Cx9gaFbgxfzSp0Yfu3oJm/4c=" # GH_TOKEN
-install:
-  - if [ -a .git/shallow ]; then git fetch --unshallow; fi
-  - docker pull ${BASENAME} || ./travis/docker-build-and-push.sh ${BASENAME} travis/travis-base.Dockerfile
-  - ./travis/skip-pkg.sh || docker pull ${BASENAME_UBUNTU} || ./travis/docker-build-and-push.sh ${BASENAME_UBUNTU} travis/travis-base-ubuntu.Dockerfile
-  - ./travis/skip-pkg.sh || docker pull ${BASENAME_386} || ./travis/docker-build-and-push.sh ${BASENAME_386} travis/travis-base-386.Dockerfile
-  - ./travis/skip-pkg.sh || docker pull ${BASENAME_UBUNTU_386} || ./travis/docker-build-and-push.sh ${BASENAME_UBUNTU_386} travis/travis-base-ubuntu-386.Dockerfile
-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 '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
-  - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU_386} linux32 ./travis/debian-build.sh deb/ubuntu-i386/DIST
-  - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/docs.sh
-  - ./travis/skip-pkg.sh || travis/prep-bintray.sh
-
-deploy:
-  - provider: bintray
-    file: travis/bintray-autobuild-debian.json
-    user: $BINTRAY_USER
-    key: $BINTRAY_KEY
-    skip_cleanup: true
-    on:
-      branch: next
-      condition: $CC = gcc
-  - provider: bintray
-    file: travis/bintray-autobuild-ubuntu.json
-    user: $BINTRAY_USER
-    key: $BINTRAY_KEY
-    skip_cleanup: true
-    on:
-      branch: next
-      condition: $CC = gcc
-  - provider: script
-    script: travis/deploy-github-pages.sh
-    skip_cleanup: true
-    on:
-      branch: next
-      condition: $CC = gcc
-
-after_deploy:
-  - travis/cleanup-bintray.pl i3-autobuild
-  - travis/cleanup-bintray.pl i3-autobuild-ubuntu
diff --git a/README.md b/README.md
index d94d62c..a470295 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Travis](https://img.shields.io/travis/Airblader/i3.svg)](https://travis-ci.org/Airblader/i3)
+[![Build Status](https://github.com/Airblader/i3/actions/workflows/main.yml/badge.svg)](https://github.com/Airblader/i3/actions/workflows/main.yml)
 [![Issues](https://img.shields.io/github/issues/Airblader/i3.svg)](https://github.com/Airblader/i3/issues)
 [![Forks](https://img.shields.io/github/forks/Airblader/i3.svg)](https://github.com/Airblader/i3/network)
 [![Stars](https://img.shields.io/github/stars/Airblader/i3.svg)](https://github.com/Airblader/i3/stargazers)
@@ -19,7 +19,7 @@
 
 For bug reports or feature requests regarding i3-gaps specifically, open an issue on [GitHub](https://www.github.com/Airblader/i3). If your issue is with core i3 functionality, please report it [upstream](https://www.github.com/i3/i3).
 
-For support & all other kinds of questions, you can ask your question on the official [subreddit /r/i3wm](https://www.reddit.com/r/i3wm).
+For support & all other kinds of questions, you can ask your question on [GitHub Discussions](https://github.com/i3/i3/discussions).
 
 # Features
 
diff --git a/RELEASE-NOTES-4.19.1 b/RELEASE-NOTES-4.19.1
deleted file mode 100644
index 72e479c..0000000
--- a/RELEASE-NOTES-4.19.1
+++ /dev/null
@@ -1,27 +0,0 @@
-
- ┌──────────────────────────────┐
- │ Release notes for i3 v4.19.1 │
- └──────────────────────────────┘
-
-This is i3 v4.19. This version is considered stable. All users of i3 are
-strongly encouraged to upgrade.
-
-This is a bugfix release for v4.19
-
- ┌────────────────────────────┐
- │ Bugfixes                   │
- └────────────────────────────┘
-
-  • fix workspaces not moving to assigned output after output becomes available
-  • fix duplicate bindcode after i3-config-wizard
-  • fix commented-out rofi call in default i3 config
-
- ┌────────────────────────────┐
- │ Thanks!                    │
- └────────────────────────────┘
-
-Thanks for testing, bugfixes, discussions and everything I forgot go out to:
-
-  Anaël Beutot, Imran Virani, Orestis Floros
-
--- Michael Stapelberg, 2021-02-01
diff --git a/RELEASE-NOTES-4.21.1 b/RELEASE-NOTES-4.21.1
new file mode 100644
index 0000000..4f8a900
--- /dev/null
+++ b/RELEASE-NOTES-4.21.1
@@ -0,0 +1,44 @@
+
+ ┌──────────────────────────────┐
+ │ Release notes for i3 v4.21.1 │
+ └──────────────────────────────┘
+
+This is i3 v4.21.1. This version is considered stable. All users of i3 are
+strongly encouraged to upgrade.
+
+This release fixes a few rough edges with regards to the newly-introduced
+tiling drag feature, which is now configurable:
+https://i3wm.org/docs/userguide.html#config_tiling_drag
+
+ ┌────────────────────────────┐
+ │ Changes in i3 v4.21.1      │
+ └────────────────────────────┘
+
+  • tiling drag: allow configuration
+  • tiling drag: allow click immediately, to focus on decoration click
+  • tiling drag: fix cursor (wrong argument passed)
+  • tiling drag: increase drag threshold, run it through logical_px
+  • tiling drag: left-click needs threshold, mod-click doesn’t
+  • tiling drag: ignore scratchpad windows when locating drop targets
+  • tiling drag: only start when there are drop targets
+  • Raise floating windows when their border is clicked
+
+ ┌────────────────────────────┐
+ │ Bugfixes                   │
+ └────────────────────────────┘
+
+  • docs/ipc: document sticky field of GET_TREE
+  • man/i3-config-wizard: escape ~ to prevent interpretation as subscript
+  • Motif hints: respect maximum border style configuration set by user
+  • i3-dmenu-desktop: fix quoting bug
+  • Fix segfault during config validation
+
+ ┌────────────────────────────┐
+ │ Thanks!                    │
+ └────────────────────────────┘
+
+Thanks for testing, bugfixes, discussions and everything I forgot go out to:
+
+  Erich Heine, Matias Goldfeld, Orestis Floros, Tudor Brindus, bodea
+
+-- Michael Stapelberg, 2022-10-24
diff --git a/docs/hacking-howto b/docs/hacking-howto
index cc08cd1..01e5c7f 100644
--- a/docs/hacking-howto
+++ b/docs/hacking-howto
@@ -400,8 +400,8 @@
 Then, it looks through all bindings and gets the one which matches the received
 event.
 
-The bound command is parsed by the cmdparse lexer/parser, see +parse_cmd+ in
-+src/cmdparse.y+.
+The bound command is parsed by the i3 parser, see +parse_command+ in
++src/commands_parser.c+.
 
 == Manage windows (src/main.c, manage_window() and reparent_window())
 
diff --git a/docs/i3-pod2html b/docs/i3-pod2html
index e2de51b..80e98a4 100755
--- a/docs/i3-pod2html
+++ b/docs/i3-pod2html
@@ -83,7 +83,7 @@
 			
diff --git a/docs/i3-sync-working.dia b/docs/i3-sync-working.dia
index 9f1c3bc..2580382 100644
Binary files a/docs/i3-sync-working.dia and b/docs/i3-sync-working.dia differ
diff --git a/docs/i3-sync-working.png b/docs/i3-sync-working.png
index dce44ac..419334d 100644
Binary files a/docs/i3-sync-working.png and b/docs/i3-sync-working.png differ
diff --git a/docs/i3-sync.dia b/docs/i3-sync.dia
index 0945ae2..c8fb835 100644
Binary files a/docs/i3-sync.dia and b/docs/i3-sync.dia differ
diff --git a/docs/i3-sync.png b/docs/i3-sync.png
index b64cce2..c9a5a01 100644
Binary files a/docs/i3-sync.png and b/docs/i3-sync.png differ
diff --git a/docs/i3bar-protocol b/docs/i3bar-protocol
index c1bf8f8..82c2f6c 100644
--- a/docs/i3bar-protocol
+++ b/docs/i3bar-protocol
@@ -191,7 +191,7 @@
 	is 9 pixels), since the separator line is drawn in the middle.
 markup::
 	A string that indicates how the text of the block should be parsed. Set to
-	+"pango"+ to use https://developer.gnome.org/pango/stable/pango-Markup.html[Pango markup].
+	+"pango"+ to use https://developer.gnome.org/pango/1.46/[Pango markup].
 	Set to +"none"+ to not use any markup (default). Pango markup only works
 	if you use a pango font.
 
diff --git a/docs/ipc b/docs/ipc
index bb8719c..0151b24 100644
--- a/docs/ipc
+++ b/docs/ipc
@@ -14,7 +14,8 @@
 in +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username, +%p+ is
 the PID of i3 and XXXXXX is a string of random characters from the portable
 filename character set (see mkdtemp(3)). You can get the socketpath from i3 by
-calling +i3 --get-socketpath+.
+executing +i3 --get-socketpath+, which will print the path to the standard
+output (plus a newline).
 
 All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
 X11 property, stored on the X11 root window.
@@ -39,12 +40,12 @@
 
 == Sending messages to i3
 
-To send a message to i3, you have to format in the binary message format which
-i3 expects. This format specifies a magic string in the beginning to ensure
-the integrity of messages (to prevent follow-up errors). Following the magic
-string comes the length of the payload of the message as 32-bit integer, and
-the type of the message as 32-bit integer (the integers are not converted, so
-they are in native byte order).
+To send a message to i3, you have to format it in the binary message format
+which i3 expects. This format specifies a magic string in the beginning to
+ensure the integrity of messages (to prevent follow-up errors). Following the
+magic string comes the length of the payload of the message as a 32-bit
+integer, and the type of the message as a 32-bit integer (the integers are not
+converted, so they are in native byte order).
 
 The magic string currently is "i3-ipc" and will only be changed when a change
 in the IPC API is done which breaks compatibility (we hope that we don’t need
@@ -96,17 +97,29 @@
 
 == Receiving replies from i3
 
-Replies from i3 usually consist of a simple string (the length of the string
-is the message_length, so you can consider them length-prefixed) which in turn
-contain the JSON serialization of a data structure. For example, the
-GET_WORKSPACES message returns an array of workspaces (each workspace is a map
-with certain attributes).
-
-=== Reply format
+Each message sent to i3 will cause exactly one reply to be sent in return. The
+order of the sent replies will always correspond to the order of the sent
+requests. The only exception to this is <>, which (once subscribed to)
+may be sent at any time (though never in the middle of another event or reply).
+
+It is generally safe to send several messages to i3 without first waiting for a
+reply for each one (pipelining) -- though, note that depending on the language /
+network library you use, writing to the socket without also reading from it may
+cause a deadlock due to the socket buffers getting full.
 
 The reply format is identical to the normal message format. There also is
 the magic string, then the message length, then the message type and the
 payload.
+
+The payload of replies from i3 usually consists of a simple string (the length
+of the string is the message_length, so you can consider them length-prefixed),
+which in turn contain the JSON serialization of a data structure. For example,
+the GET_WORKSPACES message returns an array of workspaces (each workspace is a
+map with certain attributes).
+
+Replies currently have a 1:1 correspondence to messages, with the message type
+of the reply corresponding to the message type of the message which caused the
+reply to be sent.
 
 The following reply types are implemented:
 
@@ -127,16 +140,30 @@
 VERSION (7)::
 	Reply to the GET_VERSION message.
 BINDING_MODES (8)::
-        Reply to the GET_BINDING_MODES message.
+	Reply to the GET_BINDING_MODES message.
 GET_CONFIG (9)::
 	Reply to the GET_CONFIG message.
 TICK (10)::
 	Reply to the SEND_TICK message.
+SYNC (11)::
+	Reply to the SYNC message.
 GET_BINDING_STATE (12)::
 	Reply to the GET_BINDING_STATE message.
 
+== Messages and replies
+
 [[_command_reply]]
-=== COMMAND reply
+=== RUN_COMMAND / COMMAND
+
+Run the payload as an https://i3wm.org/docs/userguide.html#list_of_commands[i3
+command] (like the commands you can bind to keys).
+
+*Message:*
+
+The message payload is the string containing the command to execute. There is
+no JSON encoding or trailing newline.
+
+*Reply:*
 
 The reply consists of a list of serialized maps for each command that was
 parsed. Each has the property +success (bool)+ and may also include a
@@ -170,7 +197,15 @@
 -------------------
 
 [[_workspaces_reply]]
-=== WORKSPACES reply
+=== GET_WORKSPACES / WORKSPACES
+
+Get the list of current workspaces.
+
+*Message:*
+
+No payload.
+
+*Reply:*
 
 The reply consists of a serialized list of workspaces. Each workspace has the
 following properties:
@@ -234,7 +269,16 @@
 -------------------
 
 [[_subscribe_reply]]
-=== SUBSCRIBE reply
+=== SUBSCRIBE
+
+Subscribe this IPC connection to the event types specified in the message
+payload. See <>.
+
+*Message:*
+
+A JSON-encoded array of event types to subscribe to.
+
+*Reply:*
 
 The reply consists of a single serialized map. The only property is
 +success (bool)+, indicating whether the subscription was successful (the
@@ -246,7 +290,15 @@
 -------------------
 
 [[_outputs_reply]]
-=== OUTPUTS reply
+=== GET_OUTPUTS / OUTPUTS
+
+Get the list of current outputs.
+
+*Message:*
+
+No payload.
+
+*Reply:*
 
 The reply consists of a serialized list of outputs. Each output has the
 following properties:
@@ -257,7 +309,7 @@
 	Whether this output is currently active (has a valid mode).
 primary (boolean)::
 	Whether this output is currently the primary output.
-current_workspace (string)::
+current_workspace (string or null)::
 	The name of the current workspace that is visible on this output. +null+ if
 	the output is not active.
 rect (map)::
@@ -293,7 +345,15 @@
 -------------------
 
 [[_tree_reply]]
-=== TREE reply
+=== GET_TREE / TREE
+
+Get the i3 layout tree.
+
+*Message:*
+
+No payload.
+
+*Reply:*
 
 The reply consists of a serialized tree. Each node in the tree (representing
 one container) has at least the properties listed below. While the nodes might
@@ -329,7 +389,7 @@
 	"vertical".
 	THIS FIELD IS OBSOLETE. It is still present, but your code should not
 	use it. Instead, rely on the layout field.
-percent (float)::
+percent (float or null)::
 	The percentage which this container takes in its parent. A value of
 	+null+ means that the percent property does not make sense for this
 	container, for example for the root container.
@@ -353,18 +413,19 @@
 geometry (map)::
 	The original geometry the window specified when i3 mapped it. Used when
 	switching a window to floating mode, for example.
-window (integer)::
+window (integer or null)::
 	The X11 window ID of the *actual client window* inside this container.
-	This field is set to null for split containers or otherwise empty
+	This field is set to +null+ for split containers or otherwise empty
 	containers. This ID corresponds to what xwininfo(1) and other
 	X11-related tools display (usually in hex).
 window_properties (map)::
 	This optional field contains all available X11 window properties from the
-	following list: *title*, *instance*, *class*, *window_role* and *transient_for*.
+	following list: *title*, *instance*, *class*, *window_role*, *machine*
+	and *transient_for*.
 window_type (string)::
-	The window type (_NET_WM_WINDOW_TYPE). Possible values are undefined, normal,
-	dialog, utility, toolbar, splash, menu, dropdown_menu, popup_menu, tooltip and
-	notification.
+	The window type (_NET_WM_WINDOW_TYPE). Possible values are `undefined`,
+	unknown, normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
+	popup_menu, tooltip and notification.
 urgent (bool)::
 	Whether this container (window, split container, floating container or
 	workspace) has the urgency hint set, directly or indirectly. All parent
@@ -379,6 +440,9 @@
 	order. Traversing the tree by following the first entry in this array
 	will result in eventually reaching the one node with +focused+ set to
 	true.
+sticky (bool)::
+	Whether this window is "sticky". If it is also floating, this window will
+	be present on all workspaces on the same output.
 fullscreen_mode (integer)::
 	Whether this container is in fullscreen state or not.
     Possible values are
@@ -386,12 +450,18 @@
         +1+ (fullscreened on output) or
         +2+ (fullscreened globally).
     Note that all workspaces are considered fullscreened on their respective output.
-
+floating (string)::
+	Floating state of container.
+	Can be either "auto_on", "auto_off", "user_on" or "user_off"
 nodes (array of node)::
 	The tiling (i.e. non-floating) child containers of this node.
 floating_nodes (array of node)::
 	The floating child containers of this node. Only non-empty on nodes with
 	type +workspace+.
+scratchpad_state (string)::
+	Whether the window is not in the scratchpad ("none"), freshly moved to
+	the scratchpad but not yet resized ("fresh") or moved to the scratchpad
+	and resized ("changed").
 
 Please note that in the following example, I have left out some keys/values
 which are not relevant for the type of the node. Otherwise, the example would
@@ -532,7 +602,15 @@
 -----------------------
 
 [[_marks_reply]]
-=== MARKS reply
+=== GET_MARKS / MARKS
+
+Gets the names of all currently set marks.
+
+*Message:*
+
+No payload.
+
+*Reply:*
 
 The reply consists of a single array of strings for each container that has a
 mark. A mark can only be set on one container, so the array is unique.
@@ -541,7 +619,15 @@
 If no window has a mark the response will be the empty array [].
 
 [[_bar_config_reply]]
-=== BAR_CONFIG reply
+=== GET_BAR_CONFIG / BAR_CONFIG
+
+Gets the specified bar configuration or the names of all bar configurations if payload is empty.
+
+*Message:*
+
+No payload, or the ID of the bar whose configuration to retrieve.
+
+*Reply:*
 
 This can be used by third-party workspace bars (especially i3bar, but others
 are free to implement compatible alternatives) to get the +bar+ block
@@ -641,7 +727,15 @@
 --------------
 
 [[_version_reply]]
-=== VERSION reply
+=== GET_VERSION / VERSION
+
+Gets the i3 version.
+
+*Message:*
+
+No payload.
+
+*Reply:*
 
 The reply consists of a single JSON dictionary with the following keys:
 
@@ -674,7 +768,15 @@
 -------------------
 
 [[_binding_modes_reply]]
-=== BINDING_MODES reply
+=== GET_BINDING_MODES / BINDING_MODES
+
+Gets the names of all currently configured binding modes.
+
+*Message:*
+
+No payload.
+
+*Reply:*
 
 The reply consists of an array of all currently configured binding modes.
 
@@ -684,18 +786,66 @@
 ---------------------
 
 [[_config_reply]]
-=== CONFIG reply
-
-The config reply is a map which currently only contains the "config" member,
-which is a string containing the config file as loaded by i3 most recently.
-
-*Example:*
--------------------
-{ "config": "font pango:monospace 8\nbindsym Mod4+q exit\n" }
+=== GET_CONFIG / CONFIG
+
+Returns the last loaded i3 config.
+
+*Message:*
+
+No payload.
+
+*Reply:*
+
+The config reply is a map which contains the following fields:
+
+config (string)::
+	The top-level config file contents that i3 has loaded most recently.
+	This field is kept for backwards compatibility. See +included_configs+
+	instead.
+included_configs (array of maps)::
+	i3 adds one entry to this array for each config file it loads, in
+	order. The first entry’s +raw_contents+ are identical to the +config+
+	field.
+
+Each +included_configs+ entry contains the following fields
+
+path (string)::
+	Absolute path name to the config file that i3 loaded.
+raw_contents (string)::
+	The raw contents of the file as i3 read them.
+variable_replaced_contents (string)::
+	The contents of the file after i3 replaced all variables. This is useful
+	for debugging variable replacement.
+
+*Example:*
+-------------------
+{
+  "config": "include font.cfg\n",
+  "included_configs": [
+    {
+      "path": "/home/michael/configfiles/i3/config",
+      "raw_contents": "include font.cfg\n",
+      "variable_replaced_contents": "include font.cfg\n"
+    },
+    {
+      "path": "/home/michael/configfiles/i3/font.cfg",
+      "raw_contents": "set $font pango:monospace 8\nfont $font",
+      "variable_replaced_contents": "set pango:monospace 8 pango:monospace 8\nfont pango:monospace 8\n"
+    }
+  ],
+}
 -------------------
 
 [[_tick_reply]]
-=== TICK reply
+=== SEND_TICK / TICK
+
+Sends a tick event with the specified payload.
+
+*Message:*
+
+The payload of the tick event to send to IPC event listeners.
+
+*Reply:*
 
 The reply is a map containing the "success" member. After the reply was
 received, the tick event has been written to all IPC connections which subscribe
@@ -709,7 +859,15 @@
 -------------------
 
 [[_sync_reply]]
-=== SYNC reply
+=== SYNC
+
+Sends an i3 sync event with the specified random value to the specified window.
+
+*Message:*
+
+A JSON-encoded map with the properties "rnd" and "window" (both integer).
+
+*Reply:*
 
 The reply is a map containing the "success" member. After the reply was
 received, the https://i3wm.org/docs/testsuite.html#i3_sync[i3 sync message] was
@@ -721,7 +879,15 @@
 -------------------
 
 [[_binding_state_reply]]
-=== GET_BINDING_STATE reply
+=== GET_BINDING_STATE
+
+Request the current binding state, i.e. the currently active binding mode name.
+
+*Message:*
+
+No payload.
+
+*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.
diff --git a/docs/layout-saving b/docs/layout-saving
index 4f0ffcc..380ffff 100644
--- a/docs/layout-saving
+++ b/docs/layout-saving
@@ -185,9 +185,9 @@
 container. Only if you start Emacs with the proper instance name (+emacs24
 --name notmuch+), it will get swallowed.
 
-You can match on "class", "instance", "window_role" and "title". All values are
-case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click into a
-window to see its properties:
+You can match on "class", "instance", "window_role", "title" and "machine". All
+values are case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click
+into a window to see its properties:
 
 --------------------------------------------------------------------------------
 $ xprop
diff --git a/docs/testsuite b/docs/testsuite
index 145da15..ec87429 100644
--- a/docs/testsuite
+++ b/docs/testsuite
@@ -4,10 +4,9 @@
 September 2012
 
 This document explains how the i3 testsuite works, how to use it and extend it.
-It is targeted at developers who not necessarily have been doing testing before
-or have not been testing in Perl before. In general, the testsuite is not of
+It is targeted at developers who haven't necessarily done testing before,
+or have not used Perl for testing before. In general, the testsuite is not of
 interest for end users.
-
 
 == Introduction
 
@@ -481,7 +480,7 @@
 IPC anymore.
 
 [[i3_sync]]
-== Appendix A: The i3 sync protocol
+== Appendix A: The I3_SYNC protocol
 
 Consider the following situation: You open two windows in your testcase, then
 you use +focus left+ and want to verify that the X11 focus has been updated
@@ -499,9 +498,9 @@
 However, the test fails. Sometimes. Apparently, there is a race condition in
 your test. If you think about it, this is because you are using two different
 pieces of software: You tell i3 to update focus, i3 confirms that, and then you
-ask X11 to give you the current focus. There is a certain time i3 needs to
-update the X11 state. If the testcase gets CPU time before X11 processed i3's
-requests, the test will fail.
+ask X11 to give you the current focus. There is a certain time that the X11
+server needs to process the requests from i3. If the testcase's request for the
+input focus is processed before i3's requests, the test will fail.
 
 image::i3-sync.png["Diagram of the race condition", title="Diagram of the race condition"]
 
@@ -531,10 +530,10 @@
 
 The real solution for this problem is a mechanism which I call "the i3 sync
 protocol". The idea is to send a request (which does not modify state) via X11
-to i3 which will then be answered. Due to the request's position in the event
-queue (*after* all previous events), you can be sure that by the time you
-receive the reply, all other events have been dealt with by i3 (and, more
-importantly, X11).
+to i3 which will then be answered, again via X11. Because this answer is
+generated via an X11 request, it will be sent to the X11 server *after* all
+previous requests. Thus, you can be sure that by the time you receive the reply,
+all other events have been dealt with by i3 (and, more importantly, X11).
 
 image::i3-sync-working.png["Diagram of the i3 sync solution", title="Diagram of the i3 sync solution"]
 
@@ -569,7 +568,35 @@
 request. You should use a random value in +data[1]+ and check that you received
 the same one when getting the reply.
 
-== Appendix B: Socket activation
+== Appendix B: The sync IPC command
+
+The above I3_SYNC protocol allows to synchronise with i3. However, it is not
+enough for tests that also involve i3bar: There might still be messages from
+i3bar in-flight even after synchronising with i3. Thus, there also exists a sync
+IPC command, that is however not meant to be used directly. Instead, i3bar uses
+it for implementing the I3_SYNC protocol.
+
+The intended usage works like this:
+
+1. You send an I3_SYNC message to i3bar's window. See <>.
+2. i3bar sends a SYNC IPC command to i3 with payload
+   +{"window":your-window-here,"rnd":your-random-value}+.
+3. i3 reacts to this IPC command as if it received an I3_SYNC request via X11.
+
+This protocol is used, for example, in t/525-i3bar-mouse-bindings.t: A mouse
+button press on i3bar is triggered. i3bar reacts to this by sending IPC commands
+to i3.
+
+The necessary synchronisation is achieved by sending an I3_SYNC event to i3bar:
+Because i3bar reacts with a sync IPC command to i3, all previous IPC commands from
+i3bar will be handled first. Because i3 reacts via X11, all previous X11
+requests from i3 will be handled by the X11 server first.
+
+The actual test also has to sync with i3 first due to how X11 handling works.
+For more details, refer to the documentation for +XAllowEvents+ with mode
++ReplayPointer+.
+
+== Appendix C: Socket activation
 
 Socket activation is a mechanism which was made popular by systemd, an init
 replacement. It basically describes creating a listening socket before starting
diff --git a/docs/userguide b/docs/userguide
index 2ad3418..d1bb557 100644
--- a/docs/userguide
+++ b/docs/userguide
@@ -3,9 +3,9 @@
 Michael Stapelberg 
 
 This document contains all the information you need to configure and use the i3
-window manager. If it does not, please check https://www.reddit.com/r/i3wm/
-first, then contact us on IRC (preferred) or post your question(s) on the
-mailing list.
+window manager. If it does not you can https://i3wm.org/contact/[contact us] on
+https://github.com/i3/i3/discussions[GitHub Discussions], IRC, or the mailing
+list.
 
 == Default keybindings
 
@@ -53,12 +53,23 @@
 
 image:two_terminals.png[Two terminals]
 
-To move the focus between the two terminals, you can use the direction keys
-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
-terminals, use +$mod+k+ or +$mod+l+. Of course, you can also use the arrow keys.
+To move the focus between the two terminals, you can use the arrow keys. For
+convenience, the arrows are also available directly on the
+https://en.wikipedia.org/wiki/Touch_typing[keyboard’s home row] underneath your
+right hand:
+
+|===
+| `$mod+j` | left
+| `$mod+k` | down
+| `$mod+l` | up
+| `$mod+;` | right
+|===
+
+Note that this differs by one key from the popular text editor `vi`, which was
+https://twitter.com/hillelogram/status/1326600125569961991[developed on an
+ADM-3A terminal and therefore uses `hjkl` instead of `jkl;`] -- i3’s default is
+meant to require minimal finger movement, but some `vi` users change their i3
+config for consistency.
 
 At the moment, your workspace is split (it contains two terminals) in a
 specific direction (horizontal by default). Every window can be split
@@ -67,7 +78,7 @@
 or browser) and "split container" for containers that consist of one or more
 windows.
 
-TODO: picture of the tree
+//TODO: picture of the tree
 
 To split a window vertically, press +$mod+v+ before you create the new window.
 To split it horizontally, press +$mod+h+.
@@ -185,6 +196,49 @@
 
 Floating windows are always on top of tiling windows.
 
+[[tiling_drag]]
+=== Moving tiling containers with the mouse
+
+Since i3 4.21, it's possible to drag tiling containers using the mouse. The
+drag can be initiated either by dragging the window's titlebar or by pressing
+the <> and dragging the container while holding the
+left-click button.
+
+Once the drag is initiated and the cursor has left the original container, drop
+indicators are created according to the position of the cursor relatively to
+the target container. These indicators help you understand what the resulting
+<> layout is going to be after you release the mouse button.
+
+The possible drop positions are:
+
+Drop on container::
+    This happens when the mouse is relatively near the center of a container.
+    If the mouse is released, the result is exactly as if you had run the
+    +move container to mark+ command. See <>.
+Drop as sibling::
+    This happens when the mouse is relatively near the edge of a container. If
+    the mouse is released, the dragged container will become a sibling of the
+    target container, placed left/right/up/down according to the position of
+    the indicator.
+    This might or might not create a new v-split or h-split according to the
+    previous layout of the target container. For example, if the target
+    container is in an h-split and you drop the dragged container below it, the
+    new layout will have to be a v-split.
+Drop to parent::
+    This happens when the mouse is relatively near the edge of a container (but
+    even closer to the border in comparison to the sibling case above) *and* if
+    that edge is also the parent container's edge. For example, if three
+    containers are in a horizontal layout then edges where this can happen is
+    the left edge of the left container, the right edge of the right container
+    and all bottom and top edges of all three containers.
+    If the mouse is released, the container is first dropped as a sibling to
+    the target container, like in the case above, and then is moved
+    directionally like with the +move left|right|down|up+ command. See
+    <>.
+
+The color of the indicator matches the +client.focused+ setting. See <>.
+
+[[tree]]
 == Tree
 
 i3 stores all information about the X11 outputs, workspaces and layout of the
@@ -308,6 +362,90 @@
 # i3 config file (v4)
 ---------------------
 
+[[include]]
+=== Include directive
+
+Since i3 v4.20, it is possible to include other configuration files from your i3
+configuration.
+
+*Syntax*:
+-----------------
+include 
+-----------------
+
+i3 expands `pattern` using shell-like word expansion, specifically using the
+https://manpages.debian.org/wordexp.3[`wordexp(3)` C standard library function].
+
+*Examples*:
+--------------------------------------------------------------------------------
+# Tilde expands to the user’s home directory:
+include ~/.config/i3/assignments.conf
+
+# Environment variables are expanded:
+include $HOME/.config/i3/assignments.conf
+
+# Wildcards are expanded:
+include ~/.config/i3/config.d/*.conf
+
+# Command substitution:
+include ~/.config/i3/`hostname`.conf
+
+# i3 loads each path only once, so including the i3 config will not result
+# in an endless loop, but in an error:
+include ~/.config/i3/config
+
+# i3 changes the working directory while parsing a config file
+# so that relative paths are interpreted relative to the directory
+# of the config file that contains the path:
+include assignments.conf
+--------------------------------------------------------------------------------
+
+If a specified file cannot be read, for example because of a lack of file
+permissions, or because of a dangling symlink, i3 will report an error and
+continue processing your remaining configuration.
+
+To list all loaded configuration files, run `i3 --moreversion`:
+
+--------------------------------------------------------------------------------
+% i3 --moreversion
+Binary i3 version:  4.19.2-87-gfcae64f7+ © 2009 Michael Stapelberg and contributors
+Running i3 version: 4.19.2-87-gfcae64f7+ (pid 963940)
+Loaded i3 config:
+  /tmp/i3.cfg (main) (last modified: 2021-05-13T16:42:31 CEST, 463 seconds ago)
+  /tmp/included.cfg (included) (last modified: 2021-05-13T16:42:43 CEST, 451 seconds ago)
+  /tmp/another.cfg (included) (last modified: 2021-05-13T16:42:46 CEST, 448 seconds ago)
+--------------------------------------------------------------------------------
+
+Variables are shared between all config files, but beware of the following limitation:
+
+* You can define a variable and use it within an included file.
+* You cannot use (in the parent file) a variable that was defined within an included file.
+
+This is a technical limitation: variable expansion happens in a separate stage
+before parsing include directives.
+
+Conceptually, included files can only add to the configuration, not undo the
+effects of already-processed configuration. For example, you can only add new
+key bindings, not overwrite or remove existing key bindings. This means:
+
+* The `include` directive is suitable for organizing large configurations into
+  separate files, possibly selecting files based on conditionals.
+
+* The `include` directive is not suitable for expressing “use the default
+  configuration with the following changes”. For that case, we still recommend
+  copying and modifying the default config.
+
+[NOTE]
+====
+Implementation-wise, i3 does not currently construct one big configuration from
+all `include` directives. Instead, i3’s config file parser interprets all
+configuration directives in its `parse_file()` function. When processing an
+`include` configuration directive, the parser recursively calls `parse_file()`.
+
+This means the evaluation order of files forms a tree, or one could say i3 uses
+depth-first traversal.
+====
+
 === Comments
 
 It is possible and recommended to use comments in your configuration file to
@@ -443,7 +581,7 @@
 # The middle button over a titlebar kills the window
 bindsym --release button2 kill
 
-# The middle button and a modifer over any part of the window kills the window
+# The middle button and a modifier over any part of the window kills the window
 bindsym --whole-window $mod+button2 kill
 
 # The right button toggles floating
@@ -597,9 +735,10 @@
 title_align left|center|right
 ---------------------------------------------
 
+[[default_border]]
 === Default border style for new windows
 
-This option determines which border style new windows will have. The default is
+This option determines which border style *new* windows will have.  The default is
 +normal+. Note that default_floating_border applies only to windows which are starting out as
 floating windows, e.g., dialog windows, but not windows that are floated later on.
 
@@ -945,6 +1084,7 @@
 workspace "2: vim" output VGA1
 ---------------------------
 
+[[client_colors]]
 === Changing colors
 
 You can change all colors which i3 uses to draw the window decorations.
@@ -961,6 +1101,10 @@
 client.focused_inactive::
 	A client which is the focused one of its container, but it does not have
 	the focus at the moment.
+client.focused_tab_title::
+    Tab or stack container title that is the parent of the focused container
+    but not directly focused. Defaults to focused_inactive if not specified and
+    does not use the indicator and child_border colors.
 client.unfocused::
 	A client which is not the focused one of its container.
 client.urgent::
@@ -1004,7 +1148,7 @@
 programs to get information from i3, such as the current workspaces
 (to display a workspace bar), and to control i3.
 
-The IPC socket is enabled by default and will be created in
+By default, an IPC socket will be created in
 +$XDG_RUNTIME_DIR/i3/ipc-socket.%p+ if the directory is available, falling back
 to +/tmp/i3-%u.XXXXXX/ipc-socket.%p+, where +%u+ is your UNIX username, +%p+ is
 the PID of i3 and XXXXXX is a string of random characters from the portable
@@ -1258,6 +1402,29 @@
 # this line is not continued \
 bindsym Mod1+F fullscreen toggle
 -------------------
+
+[[config_tiling_drag]]
+=== Tiling drag
+
+You can configure how to initiate the tiling drag feature (see <>).
+
+*Syntax*:
+--------------------------------
+tiling_drag off
+tiling_drag modifier|titlebar [modifier|titlebar]
+--------------------------------
+
+*Examples*:
+--------------------------------
+# Only initiate a tiling drag when the modifier is held:
+tiling_drag modifier
+
+# Initiate a tiling drag on either titlebar click or held modifier:
+tiling_drag modifier titlebar
+
+# Disable tiling drag altogether
+tiling_drag off
+--------------------------------
 
 == Configuring i3bar
 
@@ -1880,12 +2047,17 @@
 # enable floating mode and move container to workspace 4
 for_window [class="^evil-app$"] floating enable, move container to workspace 4
 
+# enable window icons for all windows with extra horizontal padding of 1px
+for_window [all] title_window_icon padding 1px
+
 # move all floating windows to the scratchpad
 bindsym $mod+x [floating] move scratchpad
 ------------------------------------
 
 The criteria which are currently implemented are:
 
+all::
+	Matches all windows. This criterion requires no value.
 class::
 	Compares the window class (the second part of WM_CLASS). Use the
 	special value +\_\_focused__+ to match all windows having the same window
@@ -1902,6 +2074,10 @@
 	Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
 	+normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
 	+popup_menu+, +tooltip+ and +notification+.
+machine::
+	Compares the name of the machine the client window is running on
+	(WM_CLIENT_MACHINE). Usually, it is equal to the hostname of the local
+	machine, but it may differ if remote X11 apps are used.
 id::
 	Compares the X11 window ID, which you can get via +xwininfo+ for example.
 title::
@@ -1939,9 +2115,9 @@
 	tiling are matched. With "user", only windows that the user made tiling
 	are matched.
 
-The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are
-actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
-information on how to use them.
+The criteria +class+, +instance+, +role+, +title+, +workspace+, +machine+ and
++mark+ are actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc
+perlre+ for information on how to use them.
 
 [[exec]]
 === Executing applications (exec)
@@ -2120,7 +2296,7 @@
 focus left|right|down|up
 focus parent|child|floating|tiling|mode_toggle
 focus next|prev [sibling]
-focus output left|right|up|down|primary|
+focus output left|right|down|up|current|primary|next| [output2]…
 ----------------------------------------------
 
 *Examples*:
@@ -2140,6 +2316,9 @@
 # Focus last floating/tiling container
 bindsym $mod+g focus mode_toggle
 
+# Focus the next output (effectively toggles when you only have two outputs)
+bindsym $mod+x move workspace to output next
+
 # Focus the output right to the current one
 bindsym $mod+x focus output right
 
@@ -2148,6 +2327,9 @@
 
 # Focus the primary output
 bindsym $mod+x focus output primary
+
+# Cycle focus between outputs VGA1 and LVDS1 but not DVI0
+bindsym $mod+x move workspace to output VGA1 LVDS1
 -------------------------------------------------
 
 Note that you might not have a primary output configured yet. To do so, run:
@@ -2155,6 +2337,7 @@
 xrandr --output  --primary
 -------------------------
 
+[[move_direction]]
 === Moving containers
 
 Use the +move+ command to move a container.
@@ -2281,9 +2464,8 @@
 See <> for how to move a container/workspace to a different
 RandR output.
 
-Workspace names are parsed as
-https://developer.gnome.org/pango/stable/pango-Markup.html[Pango markup]
-by i3bar.
+Workspace names are parsed as https://developer.gnome.org/pango/1.46/[Pango
+markup] by i3bar.
 
 [[back_and_forth]]
 To switch back to the previously focused workspace, use +workspace
@@ -2390,9 +2572,11 @@
 If a workspace does not exist, the command +workspace number "1: mail"+ will
 create workspace "1: mail".
 
-If a workspace with number 1 does already exist, the command will switch to this
+If a workspace with number 1 already exists, the command will switch to this
 workspace and ignore the text part. So even when the workspace has been renamed
-to "1: web", the above command will still switch to it.
+"1: web", the above command will still switch to it.  The command +workspace 1+
+will however create and move to a new workspace "1" alongside the existing
+"1: mail" workspace.
 
 === Moving workspaces to a different screen
 
@@ -2408,15 +2592,18 @@
 
 *Syntax*:
 ------------------------------------------------------------
-move container to output left|right|down|up|current|primary|
-move workspace to output left|right|down|up|current|primary|
-------------------------------------------------------------
+move container to output left|right|down|up|current|primary|next| [output2]…
+move workspace to output left|right|down|up|current|primary|next| [output2]…
+-------------------------------------------------------------------------------------
 
 *Examples*:
 --------------------------------------------------------
 # Move the current workspace to the next output
 # (effectively toggles when you only have two outputs)
-bindsym $mod+x move workspace to output right
+bindsym $mod+x move workspace to output next
+
+# Cycle this workspace between outputs VGA1 and LVDS1 but not DVI0
+bindsym $mod+x move workspace to output VGA1 LVDS1
 
 # Put this window on the presentation output.
 bindsym $mod+x move container to output VGA1
@@ -2424,12 +2611,18 @@
 # Put this window on the primary output.
 bindsym $mod+x move container to output primary
 --------------------------------------------------------
+
+If you specify more than one output, the container/workspace is cycled through
+them: If it is already in one of the outputs of the list, it will move to the
+next output in the list. If it is in an output not in the list, it will move to
+the first specified output. Non-existing outputs are skipped.
 
 Note that you might not have a primary output configured yet. To do so, run:
 -------------------------
 xrandr --output  --primary
 -------------------------
 
+[[move_to_mark]]
 === Moving containers/windows to marks
 
 To move a container to another container with a specific mark (see <>),
@@ -2575,9 +2768,8 @@
 
 By default, i3 will simply print the X11 window title. Using +title_format+,
 this can be customized by setting the format to the desired output. This
-directive supports
-https://developer.gnome.org/pango/stable/pango-Markup.html[Pango markup]
-and the following placeholders which will be replaced:
+directive supports https://developer.gnome.org/pango/1.46/[Pango markup] and the
+following placeholders which will be replaced:
 
 +%title+::
     For normal windows, this is the X11 window title (_NET_WM_NAME or WM_NAME
@@ -2590,6 +2782,9 @@
 +%instance+::
     The X11 window instance (first part of WM_CLASS). This corresponds to the
     +instance+ criterion, see <>.
++%machine+::
+    The X11 name of the machine (WM_CLIENT_MACHINE). This corresponds to the
+    +machine+ criterion, see <>.
 
 Using the <> directive, you can set the title format for any window
 based on <>.
@@ -2609,6 +2804,32 @@
 
 # print window titles of firefox windows red
 for_window [class="(?i)firefox"] title_format "%title"
+-------------------------------------------------------------------------------------
+
+[[title_window_icon]]
+=== Window title icon
+
+By default, i3 does not display the window icon in the title bar.
+
+Starting with i3 v4.20, you can optionally enable window icons either for
+specific windows or for all windows (using the <> directive).
+
+*Syntax*:
+-----------------------------
+title_window_icon 
+title_window_icon  
+------------------------------
+
+*Examples*:
+-------------------------------------------------------------------------------------
+# show the window icon for the focused window to make it stand out
+bindsym $mod+p title_window_icon on
+
+# enable window icons for all windows
+for_window [all] title_window_icon on
+
+# enable window icons for all windows with extra horizontal padding
+for_window [all] title_window_icon padding 3px
 -------------------------------------------------------------------------------------
 
 === Changing border style
@@ -2640,9 +2861,15 @@
 bindsym $mod+t border normal 0
 # use no window title and a thick border
 bindsym $mod+y border pixel 3
+# use window title *and* a thick border
+bindsym $mod+y border normal 3
 # use neither window title nor border
 bindsym $mod+u border none
+# no border on VLC
+for_window [class="vlc"] border none
 ----------------------------------------------
+
+To change the default for all windows, see the directive <>.
 
 [[shmlog]]
 === Enabling shared memory logging
diff --git a/etc/config b/etc/config
index ce22fd3..40076ca 100644
--- a/etc/config
+++ b/etc/config
@@ -16,6 +16,10 @@
 # This font is widely installed, provides lots of unicode glyphs, right-to-left
 # text rendering and scalability on retina/hidpi displays (thanks to pango).
 #font pango:DejaVu Sans Mono 8
+
+# Start XDG autostart .desktop files using dex. See also
+# https://wiki.archlinux.org/index.php/XDG_Autostart
+exec --no-startup-id dex --autostart --environment i3
 
 # The combination of xss-lock, nm-applet and pactl is a popular choice, so
 # they are included here as an example. Modify as you see fit.
@@ -44,6 +48,10 @@
 
 # use Mouse+Mod1 to drag floating windows to their wanted position
 floating_modifier Mod1
+
+# move tiling windows via drag & drop by left-clicking into the title bar,
+# or left-clicking anywhere into the window while holding the floating modifier.
+tiling_drag modifier titlebar
 
 # start a terminal
 bindsym Mod1+Return exec i3-sensible-terminal
diff --git a/etc/config.keycodes b/etc/config.keycodes
index f76d5a7..7bc5d60 100644
--- a/etc/config.keycodes
+++ b/etc/config.keycodes
@@ -17,6 +17,10 @@
 # This font is widely installed, provides lots of unicode glyphs, right-to-left
 # text rendering and scalability on retina/hidpi displays (thanks to pango).
 #font pango:DejaVu Sans Mono 8
+
+# Start XDG autostart .desktop files using dex. See also
+# https://wiki.archlinux.org/index.php/XDG_Autostart
+exec --no-startup-id dex --autostart --environment i3
 
 # The combination of xss-lock, nm-applet and pactl is a popular choice, so
 # they are included here as an example. Modify as you see fit.
@@ -38,6 +42,10 @@
 
 # Use Mouse+$mod to drag floating windows to their wanted position
 floating_modifier $mod
+
+# move tiling windows via drag & drop by left-clicking into the title bar,
+# or left-clicking anywhere into the window while holding the floating modifier.
+tiling_drag modifier titlebar
 
 # start a terminal
 bindcode $mod+36 exec i3-sensible-terminal
diff --git a/generate-command-parser.pl b/generate-command-parser.pl
index 77502db..1772873 100755
--- a/generate-command-parser.pl
+++ b/generate-command-parser.pl
@@ -133,7 +133,7 @@
 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 "static void GENERATED_call(Match *current_match, struct stack *stack, const int call_identifier, struct $resultname *result) {";
 say $callfh '    switch (call_identifier) {';
 my $call_id = 0;
 for my $state (@keys) {
@@ -150,8 +150,8 @@
         # calls to get_string(). Also replaces state names (like FOR_WINDOW)
         # with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.).
         $cmd =~ s/$_/$statenum{$_}/g for @keys;
-        $cmd =~ s/\$([a-z_]+)/get_string("$1")/g;
-        $cmd =~ s/\&([a-z_]+)/get_long("$1")/g;
+        $cmd =~ s/\$([a-z_]+)/get_string(stack, "$1")/g;
+        $cmd =~ s/\&([a-z_]+)/get_long(stack, "$1")/g;
         # For debugging/testing, we print the call using printf() and thus need
         # to generate a format string. The format uses %d for s,
         # literal numbers or state IDs and %s for NULL, s and literal
@@ -175,9 +175,9 @@
         say $callfh '#ifndef TEST_PARSER';
         my $real_cmd = $cmd;
         if ($real_cmd =~ /\(\)/) {
-            $real_cmd =~ s/\(/(¤t_match, result/;
+            $real_cmd =~ s/\(/(current_match, result/;
         } else {
-            $real_cmd =~ s/\(/(¤t_match, result, /;
+            $real_cmd =~ s/\(/(current_match, result, /;
         }
         say $callfh "             $real_cmd;";
         say $callfh '#else';
@@ -228,8 +228,15 @@
             ($call_identifier) = ($next_state =~ /^call ([0-9]+)$/);
             $next_state = '__CALL';
         }
-        my $identifier = $token->{identifier};
-        say $tokfh qq|    { "$token_name", "$identifier", $next_state, { $call_identifier } },|;
+        my $identifier;
+        # Set $identifier to NULL if there is no identifier
+        if ($token->{identifier} eq ""){
+            $identifier = "NULL"
+        }
+        else{
+            $identifier = qq|"$token->{identifier}"|;
+        }
+        say $tokfh qq|    { "$token_name", $identifier, $next_state, { $call_identifier } },|;
     }
     say $tokfh '};';
 }
diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c
index 25117f2..c5ec071 100644
--- a/i3-config-wizard/main.c
+++ b/i3-config-wizard/main.c
@@ -331,8 +331,6 @@
          * separate configuration directives. */
         while ((*walk == ' ' || *walk == '\t') && *walk != '\0')
             walk++;
-
-        //printf("remaining input: %s\n", walk);
 
         cmdp_token_ptr *ptr = &(tokens[state]);
         for (c = 0; c < ptr->n; c++) {
@@ -426,15 +424,13 @@
             }
 
             if (strcmp(token->name, "end") == 0) {
-                //printf("checking for end: *%s*\n", walk);
                 if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
                     if ((result = next_state(token)) != NULL)
                         return result;
-                    /* To make sure we start with an appropriate matching
-                     * datastructure for commands which do *not* specify any
+                    /* To make sure we start with an appropriate matching data
+                     * structure for commands which do *not* specify any
                      * criteria, we re-initialize the criteria system after
                      * every command. */
-                    // TODO: make this testable
                     walk++;
                     break;
                 }
diff --git a/i3-dmenu-desktop b/i3-dmenu-desktop
index 07fe833..bb69335 100755
--- a/i3-dmenu-desktop
+++ b/i3-dmenu-desktop
@@ -154,6 +154,8 @@
         },
         no_chdir => 1,
         follow_fast => 1,
+        # Ignore any duplicate files and directories and proceed normally:
+        follow_skip => 2,
     },
     @searchdirs
 );
@@ -411,7 +413,7 @@
 my $location = $app->{_Location};
 
 # Quote as described by “The Exec key”:
-# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
+# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s07.html
 sub quote {
     my ($str) = @_;
     $str =~ s/("|`|\$|\\)/\\$1/g;
@@ -422,6 +424,17 @@
 $choice = quote($choice);
 $location = quote($location);
 $name = quote($name);
+
+# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s07.html:
+#
+# Note that the general escape rule for values of type string states that the
+# backslash character can be escaped as ("\\") as well and that this escape rule
+# is applied before the quoting rule. As such, to unambiguously represent a
+# literal backslash character in a quoted argument in a desktop entry file
+# requires the use of four successive backslash characters ("\\\\"). Likewise, a
+# literal dollar sign in a quoted argument in a desktop entry file is
+# unambiguously represented with ("\\$").
+$exec =~ s/\\\\/\\/g;
 
 # Remove deprecated field codes, as the spec dictates.
 $exec =~ s/%[dDnNvm]//g;
@@ -479,8 +492,10 @@
     # starts with a double quote ("), everything is parsed as-is until the next
     # double quote which is NOT preceded by a backslash (\).
     #
-    # Therefore, we escape all double quotes (") by replacing them with \"
-    $exec =~ s/"/\\"/g;
+    # Therefore, we escape all double quotes (") by replacing them with \".
+    # To not change the meaning of any double quote, backslashes need to be
+    # escaped as well.
+    $exec =~ s/(["\\])/\\$1/g;
 
     if (exists($app->{StartupNotify}) && !$app->{StartupNotify}) {
         $nosn = '--no-startup-id';
diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c
index e58b0c3..0ce2226 100644
--- a/i3-dump-log/main.c
+++ b/i3-dump-log/main.c
@@ -25,22 +25,16 @@
 #include 
 #include 
 #include 
-
-#if !defined(__OpenBSD__)
-static uint32_t offset_next_write;
-#endif
+#include 
+#include 
+#include 
+
 static uint32_t wrap_count;
 
 static i3_shmlog_header *header;
 static char *logbuffer,
     *walk;
 static int ipcfd = -1;
-
-static volatile bool interrupted = false;
-
-static void sighandler(int signal) {
-    interrupted = true;
-}
 
 static void disable_shmlog(void) {
     const char *disablecmd = "debuglog off; shmlog off";
@@ -188,22 +182,25 @@
 
     /* NB: While we must never write, we need O_RDWR for the pthread condvar. */
     int logbuffer_shm = shm_open(shmname, O_RDWR, 0);
-    if (logbuffer_shm == -1)
+    if (logbuffer_shm == -1) {
         err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname);
-
-    if (fstat(logbuffer_shm, &statbuf) != 0)
+    }
+
+    if (fstat(logbuffer_shm, &statbuf) != 0) {
         err(EXIT_FAILURE, "stat(%s)", shmname);
-
-    /* NB: While we must never write, we need PROT_WRITE for the pthread condvar. */
-    logbuffer = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
-    if (logbuffer == MAP_FAILED)
+    }
+
+    logbuffer = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0);
+    if (logbuffer == MAP_FAILED) {
         err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log");
+    }
 
     header = (i3_shmlog_header *)logbuffer;
 
-    if (verbose)
+    if (verbose) {
         printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
                header->offset_next_write, header->offset_last_wrap, header->size, shmname);
+    }
     free(shmname);
     walk = logbuffer + header->offset_next_write;
 
@@ -233,25 +230,38 @@
         return 0;
     }
 
-    /* Handle SIGINT gracefully to invoke atexit handlers, if any. */
-    struct sigaction action;
-    action.sa_handler = sighandler;
-    sigemptyset(&action.sa_mask);
-    action.sa_flags = 0;
-    sigaction(SIGINT, &action, NULL);
-
-    /* Since pthread_cond_wait() expects a mutex, we need to provide one.
-     * To not lock i3 (that’s bad, mhkay?) we just define one outside of
-     * the shared memory. */
-    pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER;
-    pthread_mutex_lock(&dummy_mutex);
-    while (!interrupted) {
-        pthread_cond_wait(&(header->condvar), &dummy_mutex);
-        /* If this was not a spurious wakeup, print the new lines. */
-        if (header->offset_next_write != offset_next_write) {
-            offset_next_write = header->offset_next_write;
-            print_till_end();
-        }
+    char *log_stream_socket_path = root_atom_contents("I3_LOG_STREAM_SOCKET_PATH", NULL, 0);
+    if (log_stream_socket_path == NULL) {
+        errx(EXIT_FAILURE, "could not determine i3 log stream socket path: possible i3-dump-log and i3 version mismatch");
+    }
+
+    int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (sockfd == -1) {
+        err(EXIT_FAILURE, "Could not create socket");
+    }
+
+    (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
+
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(struct sockaddr_un));
+    addr.sun_family = AF_LOCAL;
+    strncpy(addr.sun_path, log_stream_socket_path, sizeof(addr.sun_path) - 1);
+    if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
+        err(EXIT_FAILURE, "Could not connect to i3 on socket %s", log_stream_socket_path);
+    }
+
+    /* Same size as the buffer used in log.c vlog(): */
+    char buf[4096];
+    for (;;) {
+        const int n = read(sockfd, buf, sizeof(buf));
+        if (n == -1) {
+            err(EXIT_FAILURE, "read(log-stream-socket):");
+        }
+        if (n == 0) {
+            exit(0); /* i3 closed the socket */
+        }
+        buf[n] = '\0';
+        swrite(STDOUT_FILENO, buf, n);
     }
 
 #endif
diff --git a/i3-input/keysym.map b/i3-input/keysym.map
index b198dd6..aaecdfc 100644
--- a/i3-input/keysym.map
+++ b/i3-input/keysym.map
@@ -1,6 +1,6 @@
 # This list can be used to convert X11 Keysyms to Unicode 2.1 character. 
 # The list is not checked for correctness by Unicode officials.  Use it 
-# at your own risk and the creator is not responsable for any damage that 
+# at your own risk and the creator is not responsible for any damage that 
 # occurred due to using this list. 
 # 
 # The list is created by looking at the Keysym names and the Unicode data 
diff --git a/i3-msg/main.c b/i3-msg/main.c
index c1c8bb8..239ac46 100644
--- a/i3-msg/main.c
+++ b/i3-msg/main.c
@@ -156,6 +156,7 @@
     char *payload = NULL;
     bool quiet = false;
     bool monitor = false;
+    bool raw_reply = false;
 
     static struct option long_options[] = {
         {"socket", required_argument, 0, 's'},
@@ -164,9 +165,10 @@
         {"quiet", no_argument, 0, 'q'},
         {"monitor", no_argument, 0, 'm'},
         {"help", no_argument, 0, 'h'},
+        {"raw", no_argument, 0, 'r'},
         {0, 0, 0, 0}};
 
-    char *options_string = "s:t:vhqm";
+    char *options_string = "s:t:vhqmr";
 
     while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
         if (o == 's') {
@@ -217,6 +219,8 @@
             return 0;
         } else if (o == '?') {
             exit(EXIT_FAILURE);
+        } else if (o == 'r') {
+            raw_reply = true;
         }
     }
 
@@ -262,32 +266,38 @@
     /* For the reply of commands, have a look if that command was successful.
      * If not, nicely format the error message. */
     if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
-        yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
-        yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
-        yajl_free(handle);
-
-        switch (state) {
-            case yajl_status_ok:
-                break;
-            case yajl_status_client_canceled:
-            case yajl_status_error:
-                errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
-        }
-
-        if (!quiet) {
+        if (!raw_reply) {
+            yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
+            yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
+            yajl_free(handle);
+
+            switch (state) {
+                case yajl_status_ok:
+                    break;
+                case yajl_status_client_canceled:
+                case yajl_status_error:
+                    errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
+            }
+        }
+
+        if (!quiet || raw_reply) {
             printf("%.*s\n", reply_length, reply);
         }
     } else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) {
-        yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
-        yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
-        yajl_free(handle);
-
-        switch (state) {
-            case yajl_status_ok:
-                break;
-            case yajl_status_client_canceled:
-            case yajl_status_error:
-                errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
+        if (raw_reply) {
+            printf("%.*s\n", reply_length, reply);
+        } else {
+            yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
+            yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
+            yajl_free(handle);
+
+            switch (state) {
+                case yajl_status_ok:
+                    break;
+                case yajl_status_client_canceled:
+                case yajl_status_error:
+                    errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
+            }
         }
     } else if (reply_type == I3_IPC_REPLY_TYPE_SUBSCRIBE) {
         do {
diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c
index b13ee13..7d9c090 100644
--- a/i3-nagbar/main.c
+++ b/i3-nagbar/main.c
@@ -266,13 +266,9 @@
 }
 
 /**
- * Return the position and size the i3-nagbar window should use.
- * This will be the primary output or a fallback if it cannot be determined.
- */
-static xcb_rectangle_t get_window_position(void) {
-    /* Default values if we cannot determine the primary output or its CRTC info. */
-    xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + 2 * MSG_PADDING + BAR_BORDER};
-
+ * Tries to position the rectangle on the primary output.
+ */
+static void set_window_position_primary(xcb_rectangle_t *result) {
     xcb_randr_get_screen_resources_current_cookie_t rcookie = xcb_randr_get_screen_resources_current(conn, root);
     xcb_randr_get_output_primary_cookie_t pcookie = xcb_randr_get_output_primary(conn, root);
 
@@ -314,14 +310,61 @@
         goto free_resources;
     }
 
-    result.x = crtc->x;
-    result.y = crtc->y;
+    result->x = crtc->x;
+    result->y = crtc->y;
     goto free_resources;
 
 free_resources:
     free(res);
     free(primary);
-    return result;
+}
+
+/**
+ * Tries to position the rectangle on the output with input focus.
+ * If unsuccessful, try to position on primary output.
+ */
+static void set_window_position_focus(xcb_rectangle_t *result) {
+    bool success = false;
+    xcb_get_input_focus_reply_t *input_focus = NULL;
+    xcb_get_geometry_reply_t *geometry = NULL;
+    xcb_translate_coordinates_reply_t *coordinates = NULL;
+
+    /* To avoid the input window disappearing while determining its position */
+    xcb_grab_server(conn);
+
+    input_focus = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
+    if (input_focus == NULL || input_focus->focus == XCB_NONE) {
+        LOG("Failed to receive the current input focus or no window has the input focus right now.\n");
+        goto free_resources;
+    }
+
+    geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL);
+    if (geometry == NULL) {
+        LOG("Failed to received window geometry.\n");
+        goto free_resources;
+    }
+
+    coordinates = xcb_translate_coordinates_reply(
+        conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL);
+    if (coordinates == NULL) {
+        LOG("Failed to translate coordinates.\n");
+        goto free_resources;
+    }
+
+    LOG("Found current focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y);
+    result->x = coordinates->dst_x;
+    result->y = coordinates->dst_y;
+    success = true;
+
+free_resources:
+    xcb_ungrab_server(conn);
+    free(input_focus);
+    free(coordinates);
+    free(geometry);
+    if (!success) {
+        LOG("Could not position on focused output, trying to position on primary output.\n");
+        set_window_position_primary(result);
+    }
 }
 
 int main(int argc, char *argv[]) {
@@ -360,6 +403,7 @@
 
     argv0 = argv[0];
 
+    bool position_on_primary = false;
     char *pattern = sstrdup("pango:monospace 8");
     int o, option_index = 0;
     enum { TYPE_ERROR = 0,
@@ -373,9 +417,10 @@
         {"help", no_argument, 0, 'h'},
         {"message", required_argument, 0, 'm'},
         {"type", required_argument, 0, 't'},
+        {"primary", no_argument, 0, 'p'},
         {0, 0, 0, 0}};
 
-    char *options_string = "b:B:f:m:t:vh";
+    char *options_string = "b:B:f:m:t:vhp";
 
     prompt = i3string_from_utf8("Please do not run this program.");
 
@@ -399,8 +444,11 @@
             case 'h':
                 free(pattern);
                 printf("i3-nagbar " I3_VERSION "\n");
-                printf("i3-nagbar [-m ] [-b