diff -Nru i3-gaps-wm-4.18.2/AnyEvent-I3/lib/AnyEvent/I3.pm i3-gaps-wm-4.19/AnyEvent-I3/lib/AnyEvent/I3.pm --- i3-gaps-wm-4.18.2/AnyEvent-I3/lib/AnyEvent/I3.pm 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/AnyEvent-I3/lib/AnyEvent/I3.pm 2020-11-26 21:53:03.000000000 +0000 @@ -101,11 +101,13 @@ 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 -Nru i3-gaps-wm-4.18.2/.clang-format i3-gaps-wm-4.19/.clang-format --- i3-gaps-wm-4.18.2/.clang-format 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/.clang-format 2020-11-26 21:53:03.000000000 +0000 @@ -9,3 +9,5 @@ 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 -Nru i3-gaps-wm-4.18.2/configure.ac i3-gaps-wm-4.19/configure.ac --- i3-gaps-wm-4.18.2/configure.ac 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/configure.ac 1970-01-01 00:00:00.000000000 +0000 @@ -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.2], [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_open(0, 0)])], , - [LIBS="-liconv $LIBS" - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [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 -Nru i3-gaps-wm-4.18.2/debian/changelog i3-gaps-wm-4.19/debian/changelog --- i3-gaps-wm-4.18.2/debian/changelog 2020-08-23 16:08:24.000000000 +0000 +++ i3-gaps-wm-4.19/debian/changelog 2020-11-26 23:00:16.000000000 +0000 @@ -1,73 +1,58 @@ -i3-gaps-wm (4.18.2-1~regolith2) bionic; urgency=medium +i3-gaps-wm (4.19-1ubuntu1) bionic; urgency=medium - * Package source from upstream https://github.com/Airblader/i3/releases/tag/4.18.2 + [ Ken Gilmer ] + * Update debian packaging for naming of package from i3 to i3-gaps. Add binary package for Xsession. - -- Ken Gilmer Sun, 23 Aug 2020 09:08:24 -0700 + -- Regolith Linux Thu, 26 Nov 2020 14:17:27 -0800 -i3-gaps-wm (4.17.1-1ubuntu1~ppa5) eoan; urgency=medium +i3-wm (4.19-1) unstable; urgency=medium - * Apply patch from https://github.com/i3/i3/pull/3799 to resolve bar label - issue. + [ Michael Stapelberg ] + * New upstream release. - -- Ken Gilmer Sat, 25 Jan 2020 18:26:55 -0800 + -- Regolith Linux Thu, 26 Nov 2020 13:59:53 -0800 -i3-gaps-wm (4.17.1-1ubuntu1~ppa4) disco; urgency=medium +i3-wm (4.18.3-1) unstable; urgency=medium - * Bug fix: ship i3 header file. - * Bug fix: use correct delimiter in package version spec. - * Cleanup: remove nonstandard zip specification. - * Cleanup: Update control file Standards-Version. - * Cleanup: Add source format specification. + * New upstream release. - -- Ken Gilmer Sat, 14 Dec 2019 09:33:01 -0800 + -- Michael Stapelberg Mon, 19 Oct 2020 22:48:30 +0200 -i3-gaps-wm (4.17.1~1ubuntu1~ppa3) disco; urgency=medium +i3-wm (4.18.2-1) unstable; urgency=medium - * Remove Debian file for non-existant html userguide. + * New upstream release. - -- Ken Gilmer Thu, 07 Nov 2019 20:10:36 -0800 + -- Michael Stapelberg Sun, 26 Jul 2020 10:24:46 +0200 -i3-gaps-wm (4.17.1~1ubuntu1~ppa2) disco; urgency=medium +i3-wm (4.18.1-1) unstable; urgency=medium - * Extract xsession from i3-gaps-wm package. + * New upstream release. - -- Ken Gilmer Tue, 05 Nov 2019 07:19:15 -0800 + -- Michael Stapelberg Mon, 17 Feb 2020 18:25:47 +0100 -i3-gaps-wm (4.17.1~1ubuntu1~ppa1) disco; urgency=medium +i3-wm (4.18-1) unstable; urgency=medium - * Package upstream release 4.17.1. + * New upstream release. - -- Ken Gilmer Mon, 04 Nov 2019 20:40:04 -0800 + -- Michael Stapelberg Mon, 17 Feb 2020 18:25:47 +0100 -i3-gaps-wm (4.16.1-1ubuntu1ppa14) disco; urgency=medium +i3-wm (4.17.1-1) unstable; urgency=medium - * Extract xsession into seperate binary package. + * New upstream release. - -- Ken Gilmer Mon, 09 Sep 2019 21:10:26 -0700 + -- Michael Stapelberg Fri, 30 Aug 2019 23:06:40 +0200 -i3-gaps-wm (4.16.1-1ubuntu1ppa9) disco; urgency=medium +i3-wm (4.17-1) unstable; urgency=medium - * Renable manpage generation, previously disabled due to errors. + * New upstream release. - -- Ken Gilmer Fri, 23 Aug 2019 08:17:29 -0700 + -- Michael Stapelberg Sat, 03 Aug 2019 15:14:28 +0200 -i3-gaps-wm (4.16.1-1ubuntu1ppa6) disco; urgency=medium +i3-wm (4.16.1-1) unstable; urgency=medium - * Add debian metadata to prevent collision between i3 and i3-gaps. + * New upstream release. - -- Ken Gilmer Thu, 22 Aug 2019 07:50:00 -0700 - -i3-gaps-wm (4.16.1-1ubuntu1ppa5) disco; urgency=medium - - * Rename to i3-gaps-wm to prevent collision with baseline package. - - -- Ken Gilmer Sun, 18 Aug 2019 19:00:46 -0700 - -i3-wm (4.16.1-1ubuntu1ppa1) bionic; urgency=medium - - * Adding correct version number and email for publishing to PPA. - - -- Ken Gilmer Sun, 30 Jun 2019 21:01:09 -0700 + -- Michael Stapelberg Sun, 27 Jan 2019 16:45:11 +0100 i3-wm (4.16-1) unstable; urgency=medium diff -Nru i3-gaps-wm-4.18.2/debian/compat i3-gaps-wm-4.19/debian/compat --- i3-gaps-wm-4.18.2/debian/compat 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/compat 2020-11-26 21:53:03.000000000 +0000 @@ -1 +1 @@ -9 +10 diff -Nru i3-gaps-wm-4.18.2/debian/control i3-gaps-wm-4.19/debian/control --- i3-gaps-wm-4.18.2/debian/control 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/control 2020-11-26 23:00:16.000000000 +0000 @@ -2,8 +2,8 @@ Section: x11 Priority: extra Maintainer: Michael Stapelberg -Build-Depends: debhelper (>= 9), - dh-autoreconf, +Build-Depends: debhelper (>= 10), + meson, libx11-dev, libxcb-util0-dev (>= 0.3.8), libxcb-keysyms1-dev, diff -Nru i3-gaps-wm-4.18.2/debian/i3-gaps-wm.docs i3-gaps-wm-4.19/debian/i3-gaps-wm.docs --- i3-gaps-wm-4.18.2/debian/i3-gaps-wm.docs 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-gaps-wm.docs 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -docs/debugging -docs/hacking-howto -docs/i3bar-protocol -docs/userguide -docs/bigpicture.png -docs/single_terminal.png -docs/snapping.png -docs/two_columns.png -docs/two_terminals.png -docs/modes.png -docs/ipc -docs/multi-monitor -docs/wsbar -docs/wsbar.png -docs/keyboard-layer1.png -docs/keyboard-layer2.png -docs/testsuite -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 -docs/layout-saving-1.png diff -Nru i3-gaps-wm-4.18.2/debian/i3-gaps-wm.install i3-gaps-wm-4.19/debian/i3-gaps-wm.install --- i3-gaps-wm-4.18.2/debian/i3-gaps-wm.install 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-gaps-wm.install 2020-11-26 23:00:16.000000000 +0000 @@ -1,6 +1,6 @@ -debian/tmp/etc/ -debian/tmp/usr/bin/ -debian/tmp/usr/include/ +debian/tmp/etc +debian/tmp/usr/bin +debian/tmp/usr/include contrib/dump-asy.pl usr/share/doc/i3-wm/examples/ contrib/gtk-tree-watch.pl usr/share/doc/i3-wm/examples/ contrib/i3-wsbar usr/share/doc/i3-wm/examples/ diff -Nru i3-gaps-wm-4.18.2/debian/i3-gaps-wm.links i3-gaps-wm-4.19/debian/i3-gaps-wm.links --- i3-gaps-wm-4.18.2/debian/i3-gaps-wm.links 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-gaps-wm.links 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/share/man/man1/i3.1.gz usr/share/man/man1/i3-with-shmlog.1.gz diff -Nru i3-gaps-wm-4.18.2/debian/i3-gaps-wm.manpages i3-gaps-wm-4.19/debian/i3-gaps-wm.manpages --- i3-gaps-wm-4.18.2/debian/i3-gaps-wm.manpages 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-gaps-wm.manpages 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +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/i3bar.1 diff -Nru i3-gaps-wm-4.18.2/debian/i3-gaps-wm.wm i3-gaps-wm-4.19/debian/i3-gaps-wm.wm --- i3-gaps-wm-4.18.2/debian/i3-gaps-wm.wm 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-gaps-wm.wm 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -/usr/bin/i3 diff -Nru i3-gaps-wm-4.18.2/debian/i3-wm.doc-base i3-gaps-wm-4.19/debian/i3-wm.doc-base --- i3-gaps-wm-4.18.2/debian/i3-wm.doc-base 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-wm.doc-base 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1,10 @@ +Document: i3-wm +Title: i3 documentation +Author: Michael Stapelberg +Abstract: The documentation explains how to use and modify the i3 window + manager. +Section: Window Managers + +Format: HTML +Files: /usr/share/doc/i3-wm/*.html +Index: /usr/share/doc/i3-wm/userguide.html diff -Nru i3-gaps-wm-4.18.2/debian/i3-wm.install i3-gaps-wm-4.19/debian/i3-wm.install --- i3-gaps-wm-4.18.2/debian/i3-wm.install 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-wm.install 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1,7 @@ +debian/tmp/etc +debian/tmp/usr +contrib/dump-asy.pl usr/share/doc/i3-wm/examples/ +contrib/gtk-tree-watch.pl usr/share/doc/i3-wm/examples/ +contrib/i3-wsbar usr/share/doc/i3-wm/examples/ +contrib/per-workspace-layout.pl usr/share/doc/i3-wm/examples/ +contrib/trivial-bar-script.sh usr/share/doc/i3-wm/examples/ diff -Nru i3-gaps-wm-4.18.2/debian/i3-wm.links i3-gaps-wm-4.19/debian/i3-wm.links --- i3-gaps-wm-4.18.2/debian/i3-wm.links 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-wm.links 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1 @@ +usr/share/man/man1/i3.1.gz usr/share/man/man1/i3-with-shmlog.1.gz diff -Nru i3-gaps-wm-4.18.2/debian/i3-wm.wm i3-gaps-wm-4.19/debian/i3-wm.wm --- i3-gaps-wm-4.18.2/debian/i3-wm.wm 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/debian/i3-wm.wm 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1 @@ +/usr/bin/i3 diff -Nru i3-gaps-wm-4.18.2/debian/patches/extract-xsessions.patch i3-gaps-wm-4.19/debian/patches/extract-xsessions.patch --- i3-gaps-wm-4.18.2/debian/patches/extract-xsessions.patch 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/patches/extract-xsessions.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -Index: i3-gaps/Makefile.am -=================================================================== ---- i3-gaps.orig/Makefile.am -+++ i3-gaps/Makefile.am -@@ -36,12 +36,8 @@ dist_i3conf_DATA = \ - 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 - diff -Nru i3-gaps-wm-4.18.2/debian/patches/series i3-gaps-wm-4.19/debian/patches/series --- i3-gaps-wm-4.18.2/debian/patches/series 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -extract-xsessions.patch diff -Nru i3-gaps-wm-4.18.2/debian/README.source i3-gaps-wm-4.19/debian/README.source --- i3-gaps-wm-4.18.2/debian/README.source 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/README.source 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Refer to /usr/share/doc/quilt/README.source. \ No newline at end of file diff -Nru i3-gaps-wm-4.18.2/debian/rules i3-gaps-wm-4.19/debian/rules --- i3-gaps-wm-4.18.2/debian/rules 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/rules 2020-11-26 23:00:16.000000000 +0000 @@ -1,9 +1,10 @@ #!/usr/bin/make -f # vi: ts=8 sw=8 noet + export V:=1 export DEB_BUILD_MAINT_OPTIONS = hardening=+all -override_dh_installman: +#override_dh_installman: # Disable this task override_dh_installchangelogs: @@ -16,8 +17,12 @@ # TODO: enable tests override_dh_auto_configure: - # The default is /usr/share/doc/i3 - dh_auto_configure -- --docdir=/usr/share/doc/i3-gaps-wm --disable-sanitizers + # Set -Ddocdir; the default is /usr/share/doc/i3 + dh_auto_configure -- -Ddocdir=/usr/share/doc/i3-gaps-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 -Nru i3-gaps-wm-4.18.2/debian/source/format i3-gaps-wm-4.19/debian/source/format --- i3-gaps-wm-4.18.2/debian/source/format 2020-08-23 15:57:03.000000000 +0000 +++ i3-gaps-wm-4.19/debian/source/format 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -3.0 (quilt) diff -Nru i3-gaps-wm-4.18.2/debian/upstream/signing-key.asc i3-gaps-wm-4.19/debian/upstream/signing-key.asc --- i3-gaps-wm-4.18.2/debian/upstream/signing-key.asc 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/debian/upstream/signing-key.asc 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1,56 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQINBEoVG4cBEADX2160pBoUf2vSWKbUa8soEMscBFjmb/NajCxwX/BlD1sVNyDm +twZ74CNPS7X5GgNQoXCzkm7v18zOpON69/pwQ0C4T4P+dvewaDzi2+4/bZsXSor1 +mA3C9lHcKDbpH7jHkN2AbMnY3Z4LD46LA1qfCISAAKtx1h4peBF6Xhu743dKXrBa +zg/TEJwWIWSyPKgIhur95yebD/Tws+gWlOfBKkF1v1PA+5sPmC8LyK5Rd1n9Sg1D +j//4sWl8A4EwM4QUzSliZME775klV4mOBGbsTnhNjCymgDiXVNjoWdEIHoNfDsut +E2czgSwsSrSPls/Kl1KuHyBiOWi4dl6MFaypcuSNEVNi5K+oJ7gmX/sy/TlF5Ofw +KoBEPrcvulVT8aAM3azMfb/Fgo+GcEEYljV1yvSg7jSjCHxXgMyh/yMfZcPkwajp +fNE5D7WAXgygpolM9dLIOBemDJxwWr0G7uhXNv3vSHpuUheb2REaJJwWHw1IuCmn +gigD5mebQWRSmbEl66ygOFkps9FEq6KSmbHkj7dIrSVmK5DtQRRI5fMPI+E+atul +Lnpgm/R2p2yvPKoS/pr9mwvKIf9F5C20wm1iAaGW1pTDSIl2y2ZpzcJIyS+jhyCX +3d6D7FNEFlI2p9Tnbt9aE04ASLlZFGjxNWweU8zAkNOr1MyPTiWrYtsCtwARAQAB +tCpNaWNoYWVsIFN0YXBlbGJlcmcgPG1pY2hhZWxAc3RhcGVsYmVyZy5kZT6JAkAE +EwEKACoCGy8CHgECF4AFCwkIBwMFFQoJCAsFFgIDAQACGQEFAlNdKVoFCRKuD04A +CgkQTnFg7UrI7h1HYw//R7WBr/MrqevbaB6Uh7Koy3rN1GqXXY7L4kQAO1XSrmC9 +IQ/giwg7+655tDWq4cAjefiBWRv0I1WWqZwdgUGwfhzW20DBx2sPkGKZ29pcvU/k +LuMyWs49o2lcsb4cQqgDpH/uzi22fc4BhO91o/uZYOAXrrSlLuzkCa1SDCRymwdw +lIXIXktROd+r6Fpc1FAinOQgn5BQjf7gbSZSlqBLeYZdR+qSxZWufrhsVUy03nVx +mF9hc/aTFNYZHHHh0yFzYfBKisqsuwJW94uW80xw17HoBMSb10eNGEq5xWqh4Owu +8heJePlcoh2F5JnO6cFWoz1bHCZGjeeIm0OdPJXTLDQdcA5Hy4K3ADidqW0y+Iza +Wbs3TpLprLw91LaPcwzZf+vzRgsQCwPKODjhcetEaYGIKweCkNQRQCW6wEl7kRAw +/eG1wdn4YfEcnCz4ye1MW67au3omvBy41BNmGb20rEc9JIQ37HhAJy5MwuguuO1Z +xZWyu3fV39YLwvsa8EYFPb/DOUTmSCBCyvfTOCEC94Vl2kcPXicIpaRnCFZNqVEJ +FAMKY/tSVjPsBEXTFx3aiX3am4CCtc8R95z2DrYtW5UU/yA5o6lDnfRX6Smdl620 +kTM/gFEgAI8+x56XsWJ/CnG//EbgKMy8u2u5y7x1SdpZFxLf722EryF0yPJt+im0 +OU1pY2hhZWwgU3RhcGVsYmVyZyAoUkVOVC1BLUdVUlUpIDxtaWNoYWVsQHJlbnQt +YS1ndXJ1LmRlPokCPQQTAQoAJwIbLwIeAQIXgAULCQgHAwUVCgkICwUWAgMBAAUC +U10pWgUJEq4PTgAKCRBOcWDtSsjuHUTREACcBXnZwinxZ8S0DOl2OR7qm8ao4U/n +h71tkJX9klnXYY8KQD54tuGjYjCA+UvTOX7c0Rzj3WijgyRxefmOhPQzkk/zUheH +bsaYSbj2mCvA/IkMRe6G0+wyFU5ydssLVApx/+bwdL3CiIoFPwyHMgWPjYuijIts +UMbq7jtnF26l7O0GSY5uHSUQb7caz+Mu0CcF95h3oxRxHVAhHIMtwzkilbjbshEf +nxDH5L0s2hT0amkEB2jw3US2v+YrThk0ZQPoB+tgNLL2Li7yAuwbaEaK37aDTtkX +NdFiAwcOHrLhlD0SnNya4nEVwgnCu5B9f+OwalPq3a0+G394L+a+XHWWwXc6MlFz +WhzAMeE1uFJfbIIGEL/Q3URBbhIUf0xsZEagsjNExgYtJY5XJRitgyxPwAuxusia +VhfTmbr3Mr5yu7QEt1oACq3j0bNr7hzcPk+ckHYbsSvuoo21Ef5vhQetXpAzrot/ ++62c7i1xcAvY7MVBT2f/7BC4cYvLXALhcvLAabzOcD8lBPEIBkxgJj42EckEzFzC +c9s5htWdhWYIAIIdIOxZejxfRiTvujg5CaJo+Fg/BL4TTlBgjU7ASzMQBfos3cII +F8O9Yc+W04uMyG9QPqk//rIUFdKgUyd4tzXkQrJs7Jom7duxJPpr5dDMLqTwINL5 +LBBKw/lpA/nAkLQqTWljaGFlbCBTdGFwZWxiZXJnIDxzdGFwZWxiZXJnQGRlYmlh +bi5vcmc+iQI9BBMBCgAnAhsvBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheABQJTXSla +BQkSrg9OAAoJEE5xYO1KyO4dGBkP/353sh8feSyxJNXMdgAfe+okYE4B9iWS2zRy +GAEGKyliaMWDJLRhT4ln1glr05pFoy52Hxe4+NBpSYuZ9NV390HRmPxbFaUvHs74 +HPkuABxy2XKH94IS1nrb+fleR00w0rLEin4aW3mtwKDHCJtNUW0/DNtC7Uz028SL +9TftaqJrsf+paTFJkZF0ShJ2A6XFxwlvBiVz4f2UDEi7GQuAaYI5V5ZosWIAxdYG +631tKf1EU9YtjPYdBk3YF4Q44AhVbe478Ji24Of0G/l+dGFRzbdARNayBwegJoMu +djXQrPfR1jNVSaw65/AinbbtadlULSTrvhoD6ommGeU6fQS3WtNBbF4T5SuNRBeK +QhWuLSOllmjWfERaj6omg8GXd1rctIFnUhT/dl6bmhooMPRUW1DNxVzqd7UFnqXE ++CaJ7cGfI/9/MITTG32QpjkVeowOcuoZ+qLoOu7dBbagDaEn8T6yFCLACyf9LGBm +MC9bNSGsMRCFV33N/PbVcgf6M0z46d0ysPgGiR5MX1O6CQkJGrwolfA5sK3VIsyp +PaCCHpAuPdEiWB57WleURqCIGWV2ccElyA4M4auDGt1SSNl0NfWu8vde6wNIPir/ +DBqh1265QLrLS239UT0u5hYE5hkfiKP2dzgWKh9NT9xm2Dyjjv/PaqWlVedvsVIp +VRx/oRxr +=1n/p +-----END PGP PUBLIC KEY BLOCK----- diff -Nru i3-gaps-wm-4.18.2/debian/watch i3-gaps-wm-4.19/debian/watch --- i3-gaps-wm-4.18.2/debian/watch 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/debian/watch 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1,3 @@ +version=3 +opts=pgpsigurlmangle=s/$/.asc/ \ + https://i3wm.org/downloads/ /downloads/i3-(.*)\.tar\.bz2 diff -Nru i3-gaps-wm-4.18.2/.dockerignore i3-gaps-wm-4.19/.dockerignore --- i3-gaps-wm-4.18.2/.dockerignore 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/.dockerignore 2020-11-26 21:53:03.000000000 +0000 @@ -0,0 +1 @@ +.git diff -Nru i3-gaps-wm-4.18.2/docs/bigpicture.asy i3-gaps-wm-4.19/docs/bigpicture.asy --- i3-gaps-wm-4.18.2/docs/bigpicture.asy 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/docs/bigpicture.asy 2020-11-26 21:53:03.000000000 +0000 @@ -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)); Binary files /tmp/tmpjmUaRd/DQw7RTybig/i3-gaps-wm-4.18.2/docs/bigpicture.png and /tmp/tmpjmUaRd/3KR342LFER/i3-gaps-wm-4.19/docs/bigpicture.png differ Binary files /tmp/tmpjmUaRd/DQw7RTybig/i3-gaps-wm-4.18.2/docs/bigpicture.xcf and /tmp/tmpjmUaRd/3KR342LFER/i3-gaps-wm-4.19/docs/bigpicture.xcf differ diff -Nru i3-gaps-wm-4.18.2/docs/hacking-howto i3-gaps-wm-4.19/docs/hacking-howto --- i3-gaps-wm-4.18.2/docs/hacking-howto 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/hacking-howto 2020-11-26 21:53:03.000000000 +0000 @@ -8,83 +8,82 @@ you understand why things are like they are. If it does not mention something you find necessary, please do not hesitate to contact me. +++++ +
+WARNING! +

+++++ +This document is not 100% up to date. Specifically, everything up to and +including <> has been updated recently. The rest might contain +outdated information. +++++ +

+
+++++ + == 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. - -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. +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. 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 @@ 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 @@ === 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 @@ == 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. +unmapping, key presses, button presses, …). This is a very important file to +understand how i3 interacts with changes to its environment. -src/load_layout.c:: -Contains code for loading layouts from JSON files. - -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 @@ 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 +The following picture is generated by the +contrib/dump-asy.pl+ script. -image:bigpicture.png[The Big Picture] - -///////////////////////////////////////////////////////////////////////////////// +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 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 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 (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 @@ === 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 @@ 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 @@ 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 @@ == 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 @@ 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 @@ == 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 @@ 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 @@ === 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 @@ === 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 @@ 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 @@ 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 @@ 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 @@ == 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 -Nru i3-gaps-wm-4.18.2/docs/i3bar-protocol i3-gaps-wm-4.19/docs/i3bar-protocol --- i3-gaps-wm-4.18.2/docs/i3bar-protocol 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/i3bar-protocol 2020-11-26 21:53:03.000000000 +0000 @@ -103,9 +103,11 @@ 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 @@ 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 @@ "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 -Nru i3-gaps-wm-4.18.2/docs/i3-pod2html i3-gaps-wm-4.19/docs/i3-pod2html --- i3-gaps-wm-4.18.2/docs/i3-pod2html 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/i3-pod2html 2020-11-26 21:53:03.000000000 +0000 @@ -4,8 +4,14 @@ use strict; use warnings; use Pod::Simple::HTML; +use Getopt::Long; use v5.10; +my $stylesurl = ''; + +GetOptions("stylesurl=s" => \$stylesurl) + or die "parsing flags"; + $Pod::Simple::HTML::Tagmap{'Verbatim'} = '
';
 $Pod::Simple::HTML::Tagmap{'VerbatimFormatted'} = '
';
 $Pod::Simple::HTML::Tagmap{'/Verbatim'} = '
'; @@ -22,8 +28,9 @@ my $parser = Pod::Simple::HTML->new(); $parser->index(1); -$parser->html_header_before_title( -<<'EOF' +if ($stylesurl ne '') { + $parser->html_header_before_title( +< @@ -31,7 +38,7 @@ - + EOF -); + ); +} $parser->html_header_after_title( <<'EOF' diff -Nru i3-gaps-wm-4.18.2/docs/ipc i3-gaps-wm-4.19/docs/ipc --- i3-gaps-wm-4.18.2/docs/ipc 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/ipc 2020-11-26 21:53:03.000000000 +0000 @@ -1,7 +1,7 @@ IPC interface (interprocess communication) ========================================== Michael Stapelberg -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 @@ | 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 @@ 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 @@ [{ "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 @@ 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 @@ { "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 -Nru i3-gaps-wm-4.18.2/docs/refcard.html i3-gaps-wm-4.19/docs/refcard.html --- i3-gaps-wm-4.18.2/docs/refcard.html 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/refcard.html 2020-11-26 21:53:03.000000000 +0000 @@ -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 -Nru i3-gaps-wm-4.18.2/docs/testsuite i3-gaps-wm-4.19/docs/testsuite --- i3-gaps-wm-4.18.2/docs/testsuite 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/testsuite 2020-11-26 21:53:03.000000000 +0000 @@ -120,13 +120,11 @@ --------------------------------------- $ 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 @@ --------------------------------------- $ cd ~/i3 -$ autoreconf -fi - $ mkdir -p build && cd build -$ ../configure +$ meson .. -$ make -j8 +$ ninja # output omitted because it is very long $ make check diff -Nru i3-gaps-wm-4.18.2/docs/userguide i3-gaps-wm-4.19/docs/userguide --- i3-gaps-wm-4.18.2/docs/userguide 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/docs/userguide 2020-11-26 21:53:03.000000000 +0000 @@ -54,7 +54,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ === 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 @@ === 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 @@ === 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 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 <>. *Syntax*: @@ -1350,9 +1350,11 @@ 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 @@ -1409,6 +1411,11 @@ Scroll wheel up. button5:: Scroll wheel down. +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 @@ -1477,9 +1484,16 @@ 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 primary|nonprimary| --------------- *Example*: @@ -1635,7 +1649,7 @@ 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 @@ -1645,7 +1659,7 @@ 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*: @@ -1675,8 +1689,8 @@ 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*: ------------------------------ @@ -1912,8 +1926,18 @@ to match only the currently focused window. floating:: Only matches floating windows. This criterion requires no value. +floating_from:: + Like +floating+ but this criterion takes two possible values: "auto" + and "user". With "auto", only windows that were automatically opened as + floating are matched. With "user", only windows that the user made + floating are matched. tiling:: Only matches tiling windows. This criterion requires no value. +tiling_from:: + Like +tiling+ but this criterion takes two possible values: "auto" and + "user". With "auto", only windows that were automatically opened as + 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 @@ -2140,12 +2164,15 @@ # 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 [ 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 [ [px|ppt]] # Moves the container to the specified pos_x and pos_y # coordinates on the screen. -move position [px] [px] +move position [px|ppt] [px|ppt] # Moves the container to the center of the screen. # If 'absolute' is used, it is moved to the center of @@ -2461,7 +2488,7 @@ === 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 @@ -2496,7 +2523,7 @@ 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. @@ -2595,7 +2622,7 @@ 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*: @@ -2858,7 +2885,7 @@ === 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 -Nru i3-gaps-wm-4.18.2/etc/config i3-gaps-wm-4.19/etc/config --- i3-gaps-wm-4.18.2/etc/config 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/etc/config 2020-11-26 21:53:03.000000000 +0000 @@ -52,10 +52,11 @@ 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 -Nru i3-gaps-wm-4.18.2/etc/config.keycodes i3-gaps-wm-4.19/etc/config.keycodes --- i3-gaps-wm-4.18.2/etc/config.keycodes 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/etc/config.keycodes 2020-11-26 21:53:03.000000000 +0000 @@ -46,11 +46,12 @@ 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 -Nru i3-gaps-wm-4.18.2/generate-command-parser.pl i3-gaps-wm-4.19/generate-command-parser.pl --- i3-gaps-wm-4.18.2/generate-command-parser.pl 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/generate-command-parser.pl 2020-11-26 21:53:03.000000000 +0000 @@ -117,6 +117,7 @@ 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 @@ # 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 @@ # 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}; @@ -218,6 +221,8 @@ # quote of the literal. We can do strdup(literal + 1); then :). $token_name =~ s/'$//; } + # Escape double quotes: + $token_name =~ s,",\\",g; my $next_state = $token->{next_state}; if ($next_state =~ /^call /) { ($call_identifier) = ($next_state =~ /^call ([0-9]+)$/); diff -Nru i3-gaps-wm-4.18.2/.github/CONTRIBUTING.md i3-gaps-wm-4.19/.github/CONTRIBUTING.md --- i3-gaps-wm-4.18.2/.github/CONTRIBUTING.md 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/.github/CONTRIBUTING.md 2020-11-26 21:53:03.000000000 +0000 @@ -26,10 +26,16 @@ * 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 a very good [overview of the codebase](https://i3wm.org/docs/hacking-howto.html) available to get you started. +There's an [overview of the codebase](https://i3wm.org/docs/hacking-howto.html) available to get you started. diff -Nru i3-gaps-wm-4.18.2/.github/ISSUE_TEMPLATE/bug_report.md i3-gaps-wm-4.19/.github/ISSUE_TEMPLATE/bug_report.md --- i3-gaps-wm-4.18.2/.github/ISSUE_TEMPLATE/bug_report.md 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/.github/ISSUE_TEMPLATE/bug_report.md 2020-11-26 21:53:03.000000000 +0000 @@ -49,8 +49,9 @@ 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. --> -
+
Config file
 
+
click properties: mod = %d, destination = %d\n", mod_pressed, dest); DLOG("--> OUTCOME = %p\n", con); DLOG("type = %d, name = %s\n", con->type, con->name); @@ -156,26 +155,20 @@ if (con->parent->type == CT_DOCKAREA) goto done; - const bool is_left_or_right_click = (event->detail == XCB_BUTTON_CLICK_LEFT || - event->detail == XCB_BUTTON_CLICK_RIGHT); - /* if the user has bound an action to this click, it should override the * default behavior. */ - if (dest == CLICK_DECORATION || dest == CLICK_INSIDE || dest == CLICK_BORDER) { - Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event); + Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event); + if (bind && ((dest == CLICK_DECORATION && !bind->exclude_titlebar) || + (dest == CLICK_INSIDE && bind->whole_window) || + (dest == CLICK_BORDER && bind->border))) { + CommandResult *result = run_binding(bind, con); - if (bind != NULL && ((dest == CLICK_DECORATION && !bind->exclude_titlebar) || - (dest == CLICK_INSIDE && bind->whole_window) || - (dest == CLICK_BORDER && bind->border))) { - CommandResult *result = run_binding(bind, con); - - /* ASYNC_POINTER eats the event */ - xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time); - xcb_flush(conn); + /* ASYNC_POINTER eats the event */ + xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time); + xcb_flush(conn); - command_result_free(result); - return 0; - } + command_result_free(result); + return; } /* There is no default behavior for button release events so we are done. */ @@ -203,14 +196,16 @@ const bool proportional = (event->state & XCB_KEY_BUT_MASK_SHIFT) == XCB_KEY_BUT_MASK_SHIFT; const bool in_stacked = (con->parent->layout == L_STACKED || con->parent->layout == L_TABBED); const bool was_focused = focused == con; + const bool is_left_click = (event->detail == XCB_BUTTON_CLICK_LEFT); + const bool is_right_click = (event->detail == XCB_BUTTON_CLICK_RIGHT); + const bool is_left_or_right_click = (is_left_click || is_right_click); + const bool is_scroll = (event->detail == XCB_BUTTON_SCROLL_UP || + event->detail == XCB_BUTTON_SCROLL_DOWN || + event->detail == XCB_BUTTON_SCROLL_LEFT || + event->detail == XCB_BUTTON_SCROLL_RIGHT); /* 1: see if the user scrolled on the decoration of a stacked/tabbed con */ - if (in_stacked && - dest == CLICK_DECORATION && - (event->detail == XCB_BUTTON_SCROLL_UP || - event->detail == XCB_BUTTON_SCROLL_DOWN || - event->detail == XCB_BUTTON_SCROLL_LEFT || - event->detail == XCB_BUTTON_SCROLL_RIGHT)) { + if (in_stacked && dest == CLICK_DECORATION && is_scroll) { DLOG("Scrolling on a window decoration\n"); /* Use the focused child of the tabbed / stacked container, not the * container the user scrolled on. */ @@ -231,63 +226,65 @@ Con *fs = con_get_fullscreen_covering_ws(ws); if (floatingcon != NULL && fs != con) { /* 4: floating_modifier plus left mouse button drags */ - if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) { + if (mod_pressed && is_left_click) { floating_drag_window(floatingcon, event, false); - return 1; + return; } /* 5: resize (floating) if this was a (left or right) click on the * left/right/bottom border, or a right click on the decoration. - * also try resizing (tiling) if it was a click on the top */ - if (mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) { + * also try resizing (tiling) if possible */ + if (mod_pressed && is_right_click) { DLOG("floating resize due to floatingmodifier\n"); floating_resize_window(floatingcon, proportional, event); - return 1; + return; } - if (!in_stacked && dest == CLICK_DECORATION && + if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) && is_left_or_right_click) { /* try tiling resize, but continue if it doesn’t work */ DLOG("tiling resize with fallback\n"); - if (tiling_resize(con, event, dest, !was_focused)) + if (tiling_resize(con, event, dest, dest == CLICK_DECORATION && !was_focused)) goto done; } - if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_RIGHT) { + if (dest == CLICK_DECORATION && is_right_click) { DLOG("floating resize due to decoration right click\n"); floating_resize_window(floatingcon, proportional, event); - return 1; + return; } if (dest == CLICK_BORDER && is_left_or_right_click) { DLOG("floating resize due to border click\n"); floating_resize_window(floatingcon, proportional, event); - return 1; + return; } /* 6: dragging, if this was a click on a decoration (which did not lead * to a resize) */ - if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_LEFT) { + if (dest == CLICK_DECORATION && is_left_click) { floating_drag_window(floatingcon, event, !was_focused); - return 1; + return; } goto done; } /* 7: floating modifier pressed, initiate a resize */ - if (dest == CLICK_INSIDE && mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) { - if (floating_mod_on_tiled_client(con, event)) - return 1; + if (dest == CLICK_INSIDE && mod_pressed && is_right_click) { + 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. */ + 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"); - /* Since we updated the tree (con_activate() above), we need to - * re-render the tree before returning to the event loop (drag_pointer() - * inside tiling_resize() runs its own event-loop). */ - tree_render(); tiling_resize(con, event, dest, dest == CLICK_DECORATION && !was_focused); } @@ -295,8 +292,6 @@ xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_flush(conn); tree_render(); - - return 0; } /* @@ -307,7 +302,7 @@ * Then, route_click is called on the appropriate con. * */ -int handle_button_press(xcb_button_press_event_t *event) { +void handle_button_press(xcb_button_press_event_t *event) { Con *con; DLOG("Button %d (state %d) %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n", event->detail, event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"), @@ -319,8 +314,10 @@ const uint32_t mod = (config.floating_modifier & 0xFFFF); const bool mod_pressed = (mod != 0 && (event->state & mod) == mod); DLOG("floating_mod = %d, detail = %d\n", mod_pressed, event->detail); - if ((con = con_by_window_id(event->event))) - return route_click(con, event, mod_pressed, CLICK_INSIDE); + if ((con = con_by_window_id(event->event))) { + route_click(con, event, mod_pressed, CLICK_INSIDE); + return; + } if (!(con = con_by_frame_id(event->event))) { /* Run bindings on the root window as well, see #2097. We only run it @@ -338,7 +335,7 @@ * click coordinates and focus the output's active workspace. */ if (event->event == root && event->response_type == XCB_BUTTON_PRESS) { Con *output, *ws; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { if (con_is_internal(output) || !rect_contains(output->rect, event->event_x, event->event_y)) continue; @@ -348,30 +345,32 @@ workspace_show(ws); tree_render(); } - return 1; + return; } - return 0; + return; } ELOG("Clicked into unknown window?!\n"); xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_flush(conn); - return 0; + return; } /* Check if the click was on the decoration of a child */ Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH_REVERSE (child, &(con->nodes_head), nodes_head, nodes) { if (!rect_contains(child->deco_rect, event->event_x, event->event_y)) continue; - return route_click(child, event, mod_pressed, CLICK_DECORATION); + route_click(child, event, mod_pressed, CLICK_DECORATION); + return; } if (event->child != XCB_NONE) { DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n"); - return route_click(con, event, mod_pressed, CLICK_INSIDE); + route_click(con, event, mod_pressed, CLICK_INSIDE); + return; } - return route_click(con, event, mod_pressed, CLICK_BORDER); + route_click(con, event, mod_pressed, CLICK_BORDER); } diff -Nru i3-gaps-wm-4.18.2/src/commands.c i3-gaps-wm-4.19/src/commands.c --- i3-gaps-wm-4.18.2/src/commands.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/commands.c 2020-11-26 21:53:03.000000000 +0000 @@ -8,14 +8,11 @@ * */ #include "all.h" +#include "shmlog.h" +#include #include -#include -#include #include -#include - -#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 @@ */ typedef struct owindow { Con *con; - - TAILQ_ENTRY(owindow) - owindows; + TAILQ_ENTRY(owindow) owindows; } owindow; typedef TAILQ_HEAD(owindows_head, owindow) owindows_head; @@ -160,7 +155,7 @@ } TAILQ_INIT(&owindows); /* copy all_cons */ - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { ow = smalloc(sizeof(owindow)); ow->con = con; TAILQ_INSERT_TAIL(&owindows, ow, owindows); @@ -210,7 +205,7 @@ bool matched_by_mark = false; mark_t *mark; - TAILQ_FOREACH(mark, &(current->con->marks_head), marks) { + TAILQ_FOREACH (mark, &(current->con->marks_head), marks) { if (!regex_matches(current_match->mark, mark->name)) continue; @@ -245,7 +240,7 @@ } } - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); } } @@ -261,7 +256,7 @@ static void move_matches_to_workspace(Con *ws) { owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false, false); } @@ -362,7 +357,7 @@ 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 @@ 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) { @@ -524,7 +519,7 @@ /* Ensure all the other children have a percentage set. */ Con *child; - TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { LOG("child->percent = %f (child %p)\n", child->percent, child); if (child->percent == 0.0) child->percent = percentage; @@ -549,7 +544,7 @@ LOG("subtract_percent = %f\n", subtract_percent); /* Ensure that the new percentages are positive. */ if (subtract_percent >= 0.0) { - TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { if (child == current) { continue; } @@ -563,7 +558,7 @@ current->percent = new_current_percent; LOG("current->percent after = %f\n", current->percent); - TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { if (child == current) continue; child->percent -= subtract_percent; @@ -587,7 +582,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { /* Don't handle dock windows (issue #1201) */ if (current->con->window && current->con->window->dock) { DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con); @@ -667,7 +662,7 @@ owindow *current; bool success = true; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *floating_con; if ((floating_con = con_inside_floating(current->con))) { Con *output = con_get_output(floating_con); @@ -735,7 +730,7 @@ HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); border_style_t border_style; @@ -766,7 +761,7 @@ */ void cmd_nop(I3_CMD, const char *comment) { LOG("-------------------------------------------------\n"); - LOG(" NOP: %s\n", comment); + LOG(" NOP: %.4000s\n", comment); LOG("-------------------------------------------------\n"); ysuccess(true); } @@ -1006,7 +1001,7 @@ con_unmark(NULL, mark); } else { owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { con_unmark(current->con, mark); } } @@ -1038,7 +1033,7 @@ owindow *current; bool had_error = false; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); had_error |= !con_move_to_output_name(current->con, name, true); @@ -1059,7 +1054,7 @@ bool result = true; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("moving matched window %p / %s to mark \"%s\"\n", current->con, current->con->name, mark); result &= con_move_to_mark(current->con, mark); } @@ -1079,7 +1074,7 @@ HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); if (strcmp(floating_mode, "toggle") == 0) { DLOG("should toggle mode\n"); @@ -1109,7 +1104,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *ws = con_get_workspace(current->con); if (con_is_internal(ws)) { continue; @@ -1138,7 +1133,7 @@ owindow *current; LOG("splitting in direction %c\n", direction[0]); - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { if (con_is_docked(current->con)) { ELOG("Cannot split a docked container, skipping.\n"); continue; @@ -1191,7 +1186,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { con_close(current->con, kill_mode); } @@ -1211,7 +1206,7 @@ int count = 0; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { count++; } @@ -1221,7 +1216,7 @@ count); } - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id); start_application(command, no_startup_id); } @@ -1233,7 +1228,7 @@ do { \ int count = 0; \ owindow *current; \ - TAILQ_FOREACH(current, &owindows, owindows) { \ + TAILQ_FOREACH (current, &owindows, owindows) { \ count++; \ } \ \ @@ -1265,7 +1260,7 @@ } owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *ws = con_get_workspace(current->con); if (!ws || con_is_internal(ws)) { continue; @@ -1292,7 +1287,7 @@ const position_t direction = (STARTS_WITH(direction_str, "prev")) ? BEFORE : AFTER; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *ws = con_get_workspace(current->con); if (!ws || con_is_internal(ws)) { continue; @@ -1336,7 +1331,7 @@ Con *ws = con_get_workspace(focused); Con *current; bool success = false; - TAILQ_FOREACH(current, &(ws->focus_head), focused) { + TAILQ_FOREACH (current, &(ws->focus_head), focused) { if ((to_floating && current->type != CT_FLOATING_CON) || (!to_floating && current->type == CT_FLOATING_CON)) continue; @@ -1402,9 +1397,9 @@ 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) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *ws = con_get_workspace(current->con); /* If no workspace could be found, this was a dock window. * Just skip it, you cannot focus dock windows. */ @@ -1440,7 +1435,7 @@ HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); if (strcmp(action, "toggle") == 0) { con_toggle_fullscreen(current->con, mode); @@ -1465,7 +1460,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { if (current->con->window == NULL) { ELOG("only containers holding a window can be made sticky, skipping con = %p\n", current->con); continue; @@ -1495,34 +1490,37 @@ } /* - * Implementation of 'move [ [px]]'. + * Implementation of 'move [ [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); - TAILQ_FOREACH(current, &owindows, owindows) { - DLOG("moving in direction %s, px %ld\n", direction_str, move_px); + 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) { 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; } @@ -1559,7 +1557,7 @@ DLOG("changing layout to %s (%d)\n", layout_str, layout); owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { if (con_is_docked(current->con)) { ELOG("cannot change layout of a docked container, skipping it.\n"); continue; @@ -1590,7 +1588,7 @@ if (match_is_empty(current_match)) con_toggle_layout(focused, toggle_mode); else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_toggle_layout(current->con, toggle_mode); } @@ -1618,14 +1616,25 @@ */ 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,16 +1724,16 @@ } /* - * Implementation of 'move [window|container] [to] [absolute] position [px] [px] + * Implementation of 'move [window|container] [to] [absolute] position [ [px|ppt] [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; HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { if (!con_is_floating(current->con)) { ELOG("Cannot change position. The window/container is not floating\n"); @@ -1737,10 +1746,11 @@ } 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."); @@ -1761,7 +1771,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *floating_con = con_inside_floating(current->con); if (floating_con == NULL) { ELOG("con %p / %s is not floating, cannot move it to the center.\n", @@ -1804,7 +1814,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { Con *floating_con = con_inside_floating(current->con); if (floating_con == NULL) { DLOG("con %p / %s is not floating, cannot move it to the mouse position.\n", @@ -1830,7 +1840,7 @@ HANDLE_EMPTY_MATCH; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); scratchpad_move(current->con); } @@ -1852,7 +1862,7 @@ if (match_is_empty(current_match)) { result = scratchpad_show(NULL); } else { - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); result |= scratchpad_show(current->con); } @@ -1930,7 +1940,7 @@ HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH(current, &owindows, owindows) { + TAILQ_FOREACH (current, &owindows, owindows) { DLOG("setting title_format for %p / %s\n", current->con, current->con->name); FREE(current->con->title_format); @@ -2018,26 +2028,9 @@ 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 @@ * Implementation of 'bar mode dock|hide|invisible|toggle []' * */ -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 @@ 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) + TAILQ_FOREACH (current, &barconfigs, configs) { + 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 []' * */ -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 @@ 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) + TAILQ_FOREACH (current, &barconfigs, configs) { + 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) []' - * - */ -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,52 +2243,80 @@ int pixels = logical_px(atoi(value)); Con *workspace = con_get_workspace(focused); -#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; \ - } \ - \ - if (current_value < 0) \ - current_value = 0; \ - \ - if (!strcmp(scope, "all")) { \ - Con *output, *cur_ws = NULL; \ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { \ - Con *content = output_get_content(output); \ - 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; \ - } \ - } \ - \ - config.gaps.type = current_value; \ - } else { \ - workspace->gaps.type = current_value - config.gaps.type; \ - } \ +#define CMD_SET_GAPS_VALUE(type, value, reset) \ + do { \ + if (!strcmp(scope, "all")) { \ + Con *output, *cur_ws = NULL; \ + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { \ + Con *content = output_get_content(output); \ + TAILQ_FOREACH (cur_ws, &(content->nodes_head), nodes) { \ + if (reset) \ + cur_ws->gaps.type = 0; \ + else if (value + cur_ws->gaps.type < 0) \ + cur_ws->gaps.type = -value; \ + } \ + } \ + \ + config.gaps.type = value; \ + } else { \ + 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 -Nru i3-gaps-wm-4.18.2/src/commands_parser.c i3-gaps-wm-4.19/src/commands_parser.c --- i3-gaps-wm-4.18.2/src/commands_parser.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/commands_parser.c 2020-11-26 21:53:03.000000000 +0000 @@ -25,13 +25,6 @@ */ #include "all.h" -#include -#include -#include -#include -#include -#include - // 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) @@ -263,7 +256,7 @@ * Free the returned CommandResult with command_result_free(). */ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client) { - DLOG("COMMAND: *%s*\n", input); + DLOG("COMMAND: *%.4000s*\n", input); state = INITIAL; CommandResult *result = scalloc(1, sizeof(CommandResult)); diff -Nru i3-gaps-wm-4.18.2/src/con.c i3-gaps-wm-4.19/src/con.c --- i3-gaps-wm-4.18.2/src/con.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/con.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,7 +10,6 @@ * */ #include "all.h" - #include "yajl_utils.h" static void con_on_remove_child(Con *con); @@ -92,8 +91,8 @@ 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) { @@ -132,13 +131,37 @@ goto add_to_focus_head; } + if (parent->type == CT_DOCKAREA) { + /* Insert dock client, sorting alphanumerically by class and then + * instance name. This makes dock client order deterministic. As a side + * effect, bars without a custom bar id will be sorted according to + * their declaration order in the config file. See #3491. */ + current = NULL; + TAILQ_FOREACH (loop, nodes_head, nodes) { + int result = strcasecmp_nullable(con->window->class_class, loop->window->class_class); + if (result == 0) { + result = strcasecmp_nullable(con->window->class_instance, loop->window->class_instance); + } + if (result < 0) { + current = loop; + break; + } + } + if (current) { + TAILQ_INSERT_BEFORE(loop, con, nodes); + } else { + TAILQ_INSERT_TAIL(nodes_head, con, nodes); + } + goto add_to_focus_head; + } + if (con->type == CT_FLOATING_CON) { DLOG("Inserting into floating containers\n"); TAILQ_INSERT_TAIL(&(parent->floating_head), con, floating_windows); } else { if (!ignore_focus) { /* Get the first tiling container in focus stack */ - TAILQ_FOREACH(loop, &(parent->focus_head), focused) { + TAILQ_FOREACH (loop, &(parent->focus_head), focused) { if (loop->type == CT_FLOATING_CON) continue; current = loop; @@ -404,7 +427,7 @@ return true; Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (con_is_sticky(child)) return true; } @@ -491,8 +514,7 @@ struct bfs_entry { Con *con; - TAILQ_ENTRY(bfs_entry) - entries; + TAILQ_ENTRY(bfs_entry) entries; }; /* @@ -504,9 +526,7 @@ /* 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); @@ -527,13 +547,13 @@ TAILQ_REMOVE(&bfs_head, entry, entries); free(entry); - TAILQ_FOREACH(child, &(current->nodes_head), nodes) { + TAILQ_FOREACH (child, &(current->nodes_head), nodes) { entry = smalloc(sizeof(struct bfs_entry)); entry->con = child; TAILQ_INSERT_TAIL(&bfs_head, entry, entries); } - TAILQ_FOREACH(child, &(current->floating_head), floating_windows) { + TAILQ_FOREACH (child, &(current->floating_head), floating_windows) { entry = smalloc(sizeof(struct bfs_entry)); entry->con = child; TAILQ_INSERT_TAIL(&bfs_head, entry, entries); @@ -646,9 +666,11 @@ */ Con *con_by_window_id(xcb_window_t window) { Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) - if (con->window != NULL && con->window->id == window) - return con; + TAILQ_FOREACH (con, &all_cons, all_cons) { + if (con->window != NULL && con->window->id == window) { + return con; + } + } return NULL; } @@ -659,7 +681,7 @@ */ Con *con_by_con_id(long target) { Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { if (con == (Con *)target) { return con; } @@ -684,9 +706,11 @@ */ Con *con_by_frame_id(xcb_window_t frame) { Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) - if (con->frame.id == frame) - return con; + TAILQ_FOREACH (con, &all_cons, all_cons) { + if (con->frame.id == frame) { + return con; + } + } return NULL; } @@ -697,7 +721,7 @@ */ Con *con_by_mark(const char *mark) { Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { if (con_has_mark(con, mark)) return con; } @@ -711,7 +735,7 @@ */ bool con_has_mark(Con *con, const char *mark) { mark_t *current; - TAILQ_FOREACH(current, &(con->marks_head), marks) { + TAILQ_FOREACH (current, &(con->marks_head), marks) { if (strcmp(current->name, mark) == 0) return true; } @@ -774,7 +798,7 @@ Con *current; if (name == NULL) { DLOG("Unmarking all containers.\n"); - TAILQ_FOREACH(current, &all_cons, all_cons) { + TAILQ_FOREACH (current, &all_cons, all_cons) { if (con != NULL && current != con) continue; @@ -805,7 +829,7 @@ current->mark_changed = true; mark_t *mark; - TAILQ_FOREACH(mark, &(current->marks_head), marks) { + TAILQ_FOREACH (mark, &(current->marks_head), marks) { if (strcmp(mark->name, name) != 0) continue; @@ -830,8 +854,8 @@ //DLOG("searching con for window %p starting at con %p\n", window, con); //DLOG("class == %s\n", window->class_class); - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { - TAILQ_FOREACH(match, &(child->swallow_head), matches) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (match, &(child->swallow_head), matches) { if (!match_matches_window(match, window)) continue; if (store_match != NULL) @@ -843,8 +867,8 @@ return result; } - TAILQ_FOREACH(child, &(con->floating_head), floating_windows) { - TAILQ_FOREACH(match, &(child->swallow_head), matches) { + TAILQ_FOREACH (child, &(con->floating_head), floating_windows) { + TAILQ_FOREACH (match, &(child->swallow_head), matches) { if (!match_matches_window(match, window)) continue; if (store_match != NULL) @@ -863,7 +887,7 @@ int focus_heads = 0; Con *current; - TAILQ_FOREACH(current, &(con->focus_head), focused) { + TAILQ_FOREACH (current, &(con->focus_head), focused) { focus_heads++; } @@ -880,7 +904,7 @@ Con **focus_order = smalloc(focus_heads * sizeof(Con *)); Con *current; int idx = 0; - TAILQ_FOREACH(current, &(con->focus_head), focused) { + TAILQ_FOREACH (current, &(con->focus_head), focused) { assert(idx < focus_heads); focus_order[idx++] = current; } @@ -923,8 +947,9 @@ Con *child; int children = 0; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) - children++; + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + children++; + } return children; } @@ -940,7 +965,7 @@ int children = 0; Con *current = NULL; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { /* Visible leaf nodes are a child. */ if (!con_is_hidden(current) && con_is_leaf(current)) children++; @@ -965,11 +990,11 @@ int num = 0; Con *current = NULL; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { num += con_num_windows(current); } - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) { + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { num += con_num_windows(current); } @@ -990,7 +1015,7 @@ // with a percentage set we have double total = 0.0; int children_with_percent = 0; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (child->percent > 0.0) { total += child->percent; ++children_with_percent; @@ -1000,7 +1025,7 @@ // if there were children without a percentage set, set to a value that // will make those children proportional to all others if (children_with_percent != children) { - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (child->percent <= 0.0) { if (children_with_percent == 0) { total += (child->percent = 1.0); @@ -1014,11 +1039,11 @@ // if we got a zero, just distribute the space equally, otherwise // distribute according to the proportions we got if (total == 0.0) { - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { child->percent = 1.0 / children; } } else if (total != 1.0) { - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { child->percent /= total; } } @@ -1233,9 +1258,17 @@ } /* 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; } @@ -1295,7 +1328,7 @@ * delete it so child windows won't be created on the old workspace. */ if (!con_is_leaf(con)) { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (!child->window) continue; startup_sequence_delete_by_window(child->window); @@ -1334,7 +1367,7 @@ } /* 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; @@ -1536,7 +1569,7 @@ return next; do { before = next; - TAILQ_FOREACH(child, &(next->focus_head), focused) { + TAILQ_FOREACH (child, &(next->focus_head), focused) { if (child->type == CT_FLOATING_CON) continue; @@ -1571,7 +1604,7 @@ /* Wrong orientation. We use the last focused con. Within that con, * we recurse to chose the left/right con or at least the last * focused one. */ - TAILQ_FOREACH(current, &(con->focus_head), focused) { + TAILQ_FOREACH (current, &(con->focus_head), focused) { if (current->type != CT_FLOATING_CON) { most = current; break; @@ -1596,7 +1629,7 @@ /* Wrong orientation. We use the last focused con. Within that con, * we recurse to chose the top/bottom con or at least the last * focused one. */ - TAILQ_FOREACH(current, &(con->focus_head), focused) { + TAILQ_FOREACH (current, &(con->focus_head), focused) { if (current->type != CT_FLOATING_CON) { most = current; break; @@ -2024,7 +2057,7 @@ if (con->layout == L_STACKED || con->layout == L_TABBED) { uint32_t max_width = 0, max_height = 0, deco_height = 0; Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { Rect min = con_minimum_size(child); deco_height += child->deco_rect.height; max_width = max(max_width, min.width); @@ -2041,7 +2074,7 @@ if (con_is_split(con)) { uint32_t width = 0, height = 0; Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { Rect min = con_minimum_size(child); if (con->layout == L_SPLITH) { width += min.width; @@ -2129,7 +2162,7 @@ /* We are not interested in floating windows since they can only be * attached to a workspace → nodes_head instead of focus_head */ - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (con_has_urgent_child(child)) return true; } @@ -2251,7 +2284,7 @@ /* 2) append representation of children */ Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { char *child_txt = con_get_tree_representation(child); char *tmp_buf; @@ -2344,11 +2377,11 @@ 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; } @@ -2508,7 +2541,7 @@ con_set_urgency(new, old->urgent); mark_t *mark; - TAILQ_FOREACH(mark, &(old->marks_head), marks) { + TAILQ_FOREACH (mark, &(old->marks_head), marks) { TAILQ_INSERT_TAIL(&(new->marks_head), mark, marks); ipc_send_window_event("mark", new); } diff -Nru i3-gaps-wm-4.18.2/src/config.c i3-gaps-wm-4.19/src/config.c --- i3-gaps-wm-4.18.2/src/config.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/config.c 2020-11-26 21:53:03.000000000 +0000 @@ -28,17 +28,6 @@ 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); @@ -141,7 +130,7 @@ } Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { /* Assignments changed, previously ran assignments are invalid. */ if (con->window) { con->window->nr_assignments = 0; @@ -185,6 +174,7 @@ 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 -Nru i3-gaps-wm-4.18.2/src/config_directives.c i3-gaps-wm-4.19/src/config_directives.c --- i3-gaps-wm-4.18.2/src/config_directives.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/config_directives.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,9 +9,6 @@ */ #include "all.h" -#include -#include - /******************************************************************************* * Criteria functions. ******************************************************************************/ @@ -128,7 +125,7 @@ } struct Mode *mode; - SLIST_FOREACH(mode, &modes, modes) { + SLIST_FOREACH (mode, &modes, modes) { if (strcmp(mode->name, modename) == 0) { ELOG("The binding mode with name \"%s\" is defined at least twice.\n", modename); } @@ -168,7 +165,7 @@ DLOG("Setting gaps for workspace %s", workspace); struct Workspace_Assignment *assignment; - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { if (strcasecmp(assignment->name, workspace) == 0) { if (!strcmp(scope, "inner")) { assignment->gaps.inner = gaps.inner; @@ -504,7 +501,7 @@ if (workspace) { FREE(current_workspace); - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { if (strcasecmp(assignment->name, workspace) == 0) { if (assignment->output != NULL) { ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n", @@ -704,7 +701,7 @@ const bool release_bool = release != NULL; struct Barbinding *current; - TAILQ_FOREACH(current, &(current_bar->bar_bindings), bindings) { + TAILQ_FOREACH (current, &(current_bar->bar_bindings), bindings) { if (current->input_code == input_code && current->release == release_bool) { ELOG("command for button %s was already specified, ignoring.\n", button); return; diff -Nru i3-gaps-wm-4.18.2/src/config_parser.c i3-gaps-wm-4.19/src/config_parser.c --- i3-gaps-wm-4.18.2/src/config_parser.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/config_parser.c 2020-11-26 21:53:03.000000000 +0000 @@ -25,16 +25,17 @@ */ #include "all.h" +#include +#include +#include #include #include #include -#include -#include -#include +#include #include #include -#include -#include +#include + #include // Macros to make the YAJL API a bit easier to use. @@ -820,7 +821,7 @@ */ static void upsert_variable(struct variables_head *variables, char *key, char *value) { struct Variable *current; - SLIST_FOREACH(current, variables, variables) { + SLIST_FOREACH (current, variables, variables) { if (strcmp(current->key, key) != 0) { continue; } @@ -838,7 +839,7 @@ new->value = sstrdup(value); /* ensure that the correct variable is matched in case of one being * the prefix of another */ - SLIST_FOREACH(test, variables, variables) { + SLIST_FOREACH (test, variables, variables) { if (strlen(new->key) >= strlen(test->key)) break; loc = test; @@ -1013,7 +1014,7 @@ * variables (otherwise we will count them twice, which is bad when * 'extra' is negative) */ char *bufcopy = sstrdup(buf); - SLIST_FOREACH(current, &variables, variables) { + SLIST_FOREACH (current, &variables, variables) { int extra = (strlen(current->value) - strlen(current->key)); char *next; for (next = bufcopy; @@ -1033,11 +1034,12 @@ destwalk = new; while (walk < (buf + stbuf.st_size)) { /* Find the next variable */ - SLIST_FOREACH(current, &variables, variables) - current->next_match = strcasestr(walk, current->key); + SLIST_FOREACH (current, &variables, variables) { + current->next_match = strcasestr(walk, current->key); + } nearest = NULL; int distance = stbuf.st_size; - SLIST_FOREACH(current, &variables, variables) { + SLIST_FOREACH (current, &variables, variables) { if (current->next_match == NULL) continue; if ((current->next_match - walk) < distance) { diff -Nru i3-gaps-wm-4.18.2/src/display_version.c i3-gaps-wm-4.19/src/display_version.c --- i3-gaps-wm-4.18.2/src/display_version.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/display_version.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,13 +10,9 @@ */ #include "all.h" -#include -#include -#include -#include -#include #include #include +#include static bool human_readable_key, loaded_config_file_name_key; static char *human_readable_version, *loaded_config_file_name; @@ -98,7 +94,8 @@ if (state != yajl_status_ok) errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply); - printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom); + printf("\r\x1b[K"); + printf("Running i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom); if (loaded_config_file_name) { struct stat sb; diff -Nru i3-gaps-wm-4.18.2/src/drag.c i3-gaps-wm-4.19/src/drag.c --- i3-gaps-wm-4.18.2/src/drag.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/drag.c 2020-11-26 21:53:03.000000000 +0000 @@ -175,7 +175,7 @@ xcb_window_t confine_to, int cursor, bool use_threshold, callback_t callback, const void *extra) { - xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE; + xcb_cursor_t xcursor = cursor ? xcursor_get_cursor(cursor) : XCB_NONE; /* Grab the pointer */ xcb_grab_pointer_cookie_t cookie; diff -Nru i3-gaps-wm-4.18.2/src/ewmh.c i3-gaps-wm-4.19/src/ewmh.c --- i3-gaps-wm-4.18.2/src/ewmh.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/ewmh.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,12 +9,14 @@ */ #include "all.h" +#include "i3-atoms_NET_SUPPORTED.xmacro.h" + xcb_window_t ewmh_window; -#define FOREACH_NONINTERNAL \ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) \ - TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) \ - if (!con_is_internal(ws)) +#define FOREACH_NONINTERNAL \ + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) \ + TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) \ + if (!con_is_internal(ws)) /* * Updates _NET_CURRENT_DESKTOP with the current desktop number. @@ -125,13 +127,13 @@ Con *child; /* Recursively call this to descend through the entire subtree. */ - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { ewmh_update_wm_desktop_recursively(child, desktop); } /* If con is a workspace, we also need to go through the floating windows on it. */ if (con->type == CT_WORKSPACE) { - TAILQ_FOREACH(child, &(con->floating_head), floating_windows) { + TAILQ_FOREACH (child, &(con->floating_head), floating_windows) { ewmh_update_wm_desktop_recursively(child, desktop); } } @@ -183,9 +185,9 @@ uint32_t desktop = 0; Con *output; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { Con *workspace; - TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) { + TAILQ_FOREACH (workspace, &(output_get_content(output)->nodes_head), nodes) { ewmh_update_wm_desktop_recursively(workspace, desktop); if (!con_is_internal(workspace)) { @@ -305,7 +307,7 @@ 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 -Nru i3-gaps-wm-4.18.2/src/fake_outputs.c i3-gaps-wm-4.19/src/fake_outputs.c --- i3-gaps-wm-4.18.2/src/fake_outputs.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/fake_outputs.c 2020-11-26 21:53:03.000000000 +0000 @@ -18,9 +18,11 @@ */ static Output *get_screen_at(unsigned int x, unsigned int y) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) - if (output->rect.x == x && output->rect.y == y) - return output; + TAILQ_FOREACH (output, &outputs, outputs) { + if (output->rect.x == x && output->rect.y == y) { + return output; + } + } return NULL; } diff -Nru i3-gaps-wm-4.18.2/src/floating.c i3-gaps-wm-4.19/src/floating.c --- i3-gaps-wm-4.18.2/src/floating.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/floating.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,6 +9,8 @@ */ #include "all.h" +#include + #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif @@ -24,7 +26,7 @@ Output *output; /* Use Rect to encapsulate dimensions, ignoring x/y */ Rect outputs_dimensions = {0, 0, 0, 0}; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { outputs_dimensions.height += output->rect.height; outputs_dimensions.width += output->rect.width; } @@ -39,7 +41,7 @@ static void floating_set_hint_atom(Con *con, bool floating) { if (!con_is_leaf(con)) { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { floating_set_hint_atom(child, floating); } } @@ -221,22 +223,22 @@ } } -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; @@ -328,7 +330,7 @@ if (rect_equals(nc->rect, (Rect){0, 0, 0, 0})) { DLOG("Geometry not set, combining children\n"); Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height); nc->rect.width += child->geometry.width; nc->rect.height = max(nc->rect.height, child->geometry.height); @@ -419,6 +421,7 @@ floating_set_hint_atom(nc, true); ipc_send_window_event("floating", con); + return true; } void floating_disable(Con *con) { diff -Nru i3-gaps-wm-4.18.2/src/handlers.c i3-gaps-wm-4.19/src/handlers.c --- i3-gaps-wm-4.18.2/src/handlers.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/handlers.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,9 +10,9 @@ */ #include "all.h" -#include -#include #include +#include + #include #define SN_API_NOT_YET_FROZEN 1 #include @@ -62,7 +62,7 @@ event = SLIST_NEXT(event, ignore_events); } - SLIST_FOREACH(event, &ignore_events, ignore_events) { + SLIST_FOREACH (event, &ignore_events, ignore_events) { if (event->sequence != sequence) continue; @@ -160,11 +160,12 @@ layout_t layout = (enter_child ? con->parent->layout : con->layout); if (layout == L_DEFAULT) { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) - if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { - LOG("using child %p / %s instead!\n", child, child->name); - con = child; - break; + TAILQ_FOREACH_REVERSE (child, &(con->nodes_head), nodes_head, nodes) { + if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { + LOG("using child %p / %s instead!\n", child, child->name); + con = child; + break; + } } } @@ -216,7 +217,7 @@ /* see over which rect the user is */ Con *current; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH_REVERSE (current, &(con->nodes_head), nodes_head, nodes) { if (!rect_contains(current->deco_rect, event->event_x, event->event_y)) continue; @@ -557,12 +558,7 @@ * Called when a window changes its title * */ -static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_windowname_change(Con *con, xcb_get_property_reply_t *prop) { char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); window_update_name(con->window, prop); @@ -584,12 +580,7 @@ * window_update_name_legacy(). * */ -static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_windowname_change_legacy(Con *con, xcb_get_property_reply_t *prop) { char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); window_update_name_legacy(con->window, prop); @@ -610,12 +601,7 @@ * Called when a window changes its WM_WINDOW_ROLE. * */ -static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_windowrole_change(Con *con, xcb_get_property_reply_t *prop) { window_update_role(con->window, prop); con = remanage_window(con); @@ -741,7 +727,7 @@ 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; } @@ -855,11 +841,13 @@ * 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); + 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) { @@ -955,12 +943,7 @@ } } -static bool 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 *reply) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - +static bool handle_window_type(Con *con, xcb_get_property_reply_t *reply) { window_update_type(con->window, reply); return true; } @@ -972,14 +955,7 @@ * See ICCCM 4.1.2.3 for more details * */ -static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { - Con *con = con_by_window_id(window); - if (con == NULL) { - DLOG("Received WM_NORMAL_HINTS for unknown client\n"); - return false; - } - +static bool handle_normal_hints(Con *con, xcb_get_property_reply_t *reply) { bool changed = window_update_normal_hints(con->window, reply, NULL); if (changed) { @@ -998,21 +974,11 @@ * Handles the WM_HINTS property for extracting the urgency state of the window. * */ -static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { - Con *con = con_by_window_id(window); - if (con == NULL) { - DLOG("Received WM_HINTS for unknown client\n"); - return false; - } - +static bool handle_hints(Con *con, xcb_get_property_reply_t *reply) { bool urgency_hint; - if (reply == NULL) - reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL); window_update_hints(con->window, reply, &urgency_hint); con_set_urgency(con, urgency_hint); tree_render(); - return true; } @@ -1023,24 +989,8 @@ * See ICCCM 4.1.2.6 for more details * */ -static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { - DLOG("No such window\n"); - return false; - } - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), - NULL); - if (prop == NULL) - return false; - } - +static bool handle_transient_for(Con *con, xcb_get_property_reply_t *prop) { window_update_transient_for(con->window, prop); - return true; } @@ -1049,21 +999,8 @@ * toolwindow (or similar) and to which window it belongs (logical parent). * */ -static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), - NULL); - if (prop == NULL) - return false; - } - +static bool handle_clientleader_change(Con *con, xcb_get_property_reply_t *prop) { window_update_leader(con->window, prop); - return true; } @@ -1143,24 +1080,9 @@ * Handles the WM_CLASS property for assignments and criteria selection. * */ -static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32), - NULL); - - if (prop == NULL) - return false; - } - +static bool handle_class_change(Con *con, xcb_get_property_reply_t *prop) { window_update_class(con->window, prop); - con = remanage_window(con); - return true; } @@ -1168,20 +1090,7 @@ * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings. * */ -static bool handle_motif_hints_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) - return false; - - if (prop == NULL) { - prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A__MOTIF_WM_HINTS, XCB_GET_PROPERTY_TYPE_ANY, 0, 5 * sizeof(uint64_t)), - NULL); - - if (prop == NULL) - return false; - } - +static bool handle_motif_hints_change(Con *con, xcb_get_property_reply_t *prop) { border_style_t motif_border_style; window_update_motif_hints(con->window, prop, &motif_border_style); @@ -1199,34 +1108,7 @@ * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients. * */ -static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { - DLOG("strut partial change for window 0x%08x\n", window); - - Con *con; - if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { - return false; - } - - if (prop == NULL) { - xcb_generic_error_t *err = NULL; - xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL, - XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX); - prop = xcb_get_property_reply(conn, strut_cookie, &err); - - if (err != NULL) { - DLOG("got error when getting strut partial property: %d\n", err->error_code); - free(err); - return false; - } - - if (prop == NULL) { - return false; - } - } - - DLOG("That is con %p / %s\n", con, con->name); - +static bool handle_strut_partial_change(Con *con, xcb_get_property_reply_t *prop) { window_update_strut_partial(con->window, prop); /* we only handle this change for dock clients */ @@ -1267,18 +1149,35 @@ /* attach the dock to the dock area */ con_detach(con); - con->parent = dockarea; - TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused); - TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes); + con_attach(con, dockarea, true); tree_render(); return true; } +/* + * Handles the _I3_FLOATING_WINDOW property to properly run assignments for + * floating window changes. + * + * This is needed to correctly run the assignments after changes in floating + * windows which are triggered by user commands (floating enable | disable). In + * that case, we can't call run_assignments because it will modify the parser + * state when it needs to parse the user-specified action, breaking the parser + * state for the original command. + * + */ +static bool handle_i3_floating(Con *con, xcb_get_property_reply_t *prop) { + DLOG("floating change for con %p\n", con); + + remanage_window(con); + + return true; +} + /* Returns false if the event could not be processed (e.g. the window could not * be found), true otherwise */ -typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property); +typedef bool (*cb_property_handler_t)(Con *con, xcb_get_property_reply_t *property); struct property_handler_t { xcb_atom_t atom; @@ -1297,6 +1196,7 @@ {0, 128, handle_class_change}, {0, UINT_MAX, handle_strut_partial_change}, {0, UINT_MAX, handle_window_type}, + {0, UINT_MAX, handle_i3_floating}, {0, 5 * sizeof(uint64_t), handle_motif_hints_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) @@ -1318,12 +1218,15 @@ property_handlers[7].atom = XCB_ATOM_WM_CLASS; property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; property_handlers[9].atom = A__NET_WM_WINDOW_TYPE; - property_handlers[10].atom = A__MOTIF_WM_HINTS; + property_handlers[10].atom = A_I3_FLOATING_WINDOW; + property_handlers[11].atom = A__MOTIF_WM_HINTS; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { struct property_handler_t *handler = NULL; xcb_get_property_reply_t *propr = NULL; + xcb_generic_error_t *err = NULL; + Con *con; for (size_t c = 0; c < NUM_HANDLERS; c++) { if (property_handlers[c].atom != atom) @@ -1338,13 +1241,23 @@ return; } + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { + DLOG("Received property for atom %d for unknown client\n", atom); + return; + } + if (state != XCB_PROPERTY_DELETE) { xcb_get_property_cookie_t cookie = xcb_get_property(conn, 0, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, handler->long_len); - propr = xcb_get_property_reply(conn, cookie, 0); + propr = xcb_get_property_reply(conn, cookie, &err); + if (err != NULL) { + DLOG("got error %d when getting property of atom %d\n", err->error_code, atom); + FREE(err); + return; + } } /* the handler will free() the reply unless it returns false */ - if (!handler->cb(NULL, conn, state, window, atom, propr)) + if (!handler->cb(con, propr)) FREE(propr); } diff -Nru i3-gaps-wm-4.18.2/src/ipc.c i3-gaps-wm-4.19/src/ipc.c --- i3-gaps-wm-4.18.2/src/ipc.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/ipc.c 2020-11-26 21:53:03.000000000 +0000 @@ -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 +#include +#include +#include #include #include #include -#include -#include -#include +#include + #include #include 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 @@ -160,7 +162,7 @@ */ void ipc_send_event(const char *event, uint32_t message_type, const char *payload) { ipc_client *current; - TAILQ_FOREACH(current, &all_clients, clients) { + TAILQ_FOREACH (current, &all_clients, clients) { for (int i = 0; i < current->num_events; i++) { if (strcasecmp(current->events[i], event) == 0) { ipc_send_client_message(current, strlen(payload), message_type, (uint8_t *)payload); @@ -217,15 +219,14 @@ } /* - * Executes the command and returns whether it could be successfully parsed - * or not (at the moment, always returns true). + * Executes the given command. * */ IPC_HANDLER(run_command) { /* To get a properly terminated buffer, we copy * message_size bytes out of the buffer */ char *command = sstrndup((const char *)message, message_size); - LOG("IPC: received: *%s*\n", command); + LOG("IPC: received: *%.4000s*\n", command); yajl_gen gen = yajl_gen_alloc(NULL); CommandResult *result = parse_command(command, gen, client); @@ -432,17 +433,13 @@ ystr("urgent"); y(bool, con->urgent); - if (!TAILQ_EMPTY(&(con->marks_head))) { - ystr("marks"); - y(array_open); - - mark_t *mark; - TAILQ_FOREACH(mark, &(con->marks_head), marks) { - ystr(mark->name); - } - - y(array_close); + ystr("marks"); + y(array_open); + mark_t *mark; + TAILQ_FOREACH (mark, &(con->marks_head), marks) { + ystr(mark->name); } + y(array_close); ystr("focused"); y(bool, (con == focused)); @@ -619,7 +616,7 @@ y(array_open); Con *node; if (con->type != CT_DOCKAREA || !inplace_restart) { - TAILQ_FOREACH(node, &(con->nodes_head), nodes) { + TAILQ_FOREACH (node, &(con->nodes_head), nodes) { dump_node(gen, node, inplace_restart); } } @@ -627,14 +624,14 @@ ystr("floating_nodes"); y(array_open); - TAILQ_FOREACH(node, &(con->floating_head), floating_windows) { + TAILQ_FOREACH (node, &(con->floating_head), floating_windows) { dump_node(gen, node, inplace_restart); } y(array_close); ystr("focus"); y(array_open); - TAILQ_FOREACH(node, &(con->focus_head), focused) { + TAILQ_FOREACH (node, &(con->focus_head), focused) { y(integer, (uintptr_t)node); } y(array_close); @@ -664,7 +661,7 @@ ystr("swallows"); y(array_open); Match *match; - TAILQ_FOREACH(match, &(con->swallow_head), matches) { + TAILQ_FOREACH (match, &(con->swallow_head), matches) { /* We will generate a new restart_mode match specification after this * loop, so skip this one. */ if (match->restart_mode) @@ -727,7 +724,7 @@ y(array_open); struct Barbinding *current; - TAILQ_FOREACH(current, &(config->bar_bindings), bindings) { + TAILQ_FOREACH (current, &(config->bar_bindings), bindings) { y(map_open); ystr("input_code"); @@ -776,7 +773,7 @@ y(array_open); struct tray_output_t *tray_output; - TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) { + TAILQ_FOREACH (tray_output, &(config->tray_outputs), tray_outputs) { ystr(canonicalize_output_name(tray_output->output)); } @@ -927,11 +924,11 @@ Con *focused_ws = con_get_workspace(focused); Con *output; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { if (con_is_internal(output)) continue; Con *ws; - TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) { assert(ws->type == CT_WORKSPACE); y(map_open); @@ -992,7 +989,7 @@ y(array_open); Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { y(map_open); ystr("name"); @@ -1046,9 +1043,9 @@ y(array_open); Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { mark_t *mark; - TAILQ_FOREACH(mark, &(con->marks_head), marks) { + TAILQ_FOREACH (mark, &(con->marks_head), marks) { ystr(mark->name); } } @@ -1108,7 +1105,7 @@ if (message_size == 0) { y(array_open); Barconfig *current; - TAILQ_FOREACH(current, &barconfigs, configs) { + TAILQ_FOREACH (current, &barconfigs, configs) { ystr(current->id); } y(array_close); @@ -1128,7 +1125,7 @@ sasprintf(&bar_id, "%.*s", message_size, message); LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id); Barconfig *current, *config = NULL; - TAILQ_FOREACH(current, &barconfigs, configs) { + TAILQ_FOREACH (current, &barconfigs, configs) { if (strcmp(current->id, bar_id) != 0) continue; @@ -1167,7 +1164,7 @@ y(array_open); struct Mode *mode; - SLIST_FOREACH(mode, &modes, modes) { + SLIST_FOREACH (mode, &modes, modes) { ystr(mode->name); } y(array_close); @@ -1368,9 +1365,27 @@ 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, @@ -1383,6 +1398,7 @@ handle_get_config, handle_send_tick, handle_sync, + handle_get_binding_state, }; /* diff -Nru i3-gaps-wm-4.18.2/src/load_layout.c i3-gaps-wm-4.19/src/load_layout.c --- i3-gaps-wm-4.18.2/src/load_layout.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/load_layout.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,10 +10,9 @@ */ #include "all.h" -#include -#include +#include + #include -#include /* TODO: refactor the whole parsing thing */ @@ -46,9 +45,7 @@ * 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 = @@ -145,7 +142,7 @@ if (rect_equals(json_node->rect, (Rect){0, 0, 0, 0})) { DLOG("Geometry not set, combining children\n"); Con *child; - TAILQ_FOREACH(child, &(json_node->nodes_head), nodes) { + TAILQ_FOREACH (child, &(json_node->nodes_head), nodes) { DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height); json_node->rect.width += child->geometry.width; json_node->rect.height = max(json_node->rect.height, child->geometry.height); @@ -219,10 +216,10 @@ if (parsing_focus) { /* Clear the list of focus mappings */ struct focus_mapping *mapping; - TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) { + TAILQ_FOREACH_REVERSE (mapping, &focus_mappings, focus_mappings_head, focus_mappings) { LOG("focus (reverse) %d\n", mapping->old_id); Con *con; - TAILQ_FOREACH(con, &(json_node->focus_head), focused) { + TAILQ_FOREACH (con, &(json_node->focus_head), focused) { if (con->old_id != mapping->old_id) continue; LOG("got it! %p\n", con); diff -Nru i3-gaps-wm-4.18.2/src/log.c i3-gaps-wm-4.19/src/log.c --- i3-gaps-wm-4.18.2/src/log.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/log.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,27 +9,24 @@ */ #include +#include "all.h" +#include "shmlog.h" + +#include +#include #include -#include -#include #include +#include #include -#include -#include -#include +#include #include #include -#include +#include +#include #if !defined(__OpenBSD__) #include #endif -#include "util.h" -#include "log.h" -#include "i3.h" -#include "libi3.h" -#include "shmlog.h" - #if defined(__APPLE__) #include #endif diff -Nru i3-gaps-wm-4.18.2/src/main.c i3-gaps-wm-4.19/src/main.c --- i3-gaps-wm-4.18.2/src/main.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/main.c 2020-11-26 21:53:03.000000000 +0000 @@ -8,18 +8,22 @@ * */ #include "all.h" +#include "shmlog.h" #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include +#include +#include #include -#include -#include "shmlog.h" +#include +#include +#include +#include #ifdef I3_ASAN_ENABLED #include @@ -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 @@ /* 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); @@ -87,7 +95,6 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignments); /* We hope that those are supported and set them to true */ -bool xcursor_supported = true; bool xkb_supported = true; bool shape_supported = true; @@ -95,7 +102,8 @@ /* 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 /* @@ -175,6 +183,10 @@ 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. */ @@ -373,6 +385,11 @@ 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); } @@ -556,7 +573,8 @@ /* 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; @@ -602,7 +620,8 @@ 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); @@ -641,10 +660,7 @@ /* Set a cursor for the root window (otherwise the root window will show no cursor until the first client is launched). */ - if (xcursor_supported) - xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); - else - xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); + xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); const xcb_query_extension_reply_t *extreply; xcb_prefetch_extension_data(conn, &xcb_xkb_id); @@ -785,9 +801,9 @@ * and restarting i3. See #2326. */ if (layout_path != NULL && randr_base > -1) { Con *con; - TAILQ_FOREACH(con, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (con, &(croot->nodes_head), nodes) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (output->active || strcmp(con->name, output_primary_name(output)) != 0) continue; @@ -1010,10 +1026,10 @@ /* Start i3bar processes for all configured bars */ Barconfig *barconfig; - TAILQ_FOREACH(barconfig, &barconfigs, configs) { + TAILQ_FOREACH (barconfig, &barconfigs, configs) { char *command = NULL; sasprintf(&command, "%s %s --bar_id=%s --socket=\"%s\"", - barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar", + barconfig->i3bar_command ? barconfig->i3bar_command : "exec i3bar", barconfig->verbose ? "-V" : "", barconfig->id, current_socketpath); LOG("Starting bar process: %s\n", command); diff -Nru i3-gaps-wm-4.18.2/src/manage.c i3-gaps-wm-4.19/src/manage.c --- i3-gaps-wm-4.18.2/src/manage.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/manage.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,10 +9,6 @@ */ #include "all.h" -#include "yajl_utils.h" - -#include - /* * 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. @@ -29,7 +25,7 @@ } /* - * Remove all match criteria, the first swallowed window wins. + * Remove all match criteria, the first swallowed window wins. * */ static void _remove_matches(Con *con) { @@ -83,15 +79,16 @@ DLOG("Restoring geometry\n"); Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) - if (con->window) { - DLOG("Re-adding X11 border of %d px\n", con->border_width); - con->window_rect.width += (2 * con->border_width); - con->window_rect.height += (2 * con->border_width); - xcb_set_window_rect(conn, con->window->id, con->window_rect); - DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y); - xcb_reparent_window(conn, con->window->id, root, - con->rect.x, con->rect.y); + TAILQ_FOREACH (con, &all_cons, all_cons) { + if (con->window) { + DLOG("Re-adding X11 border of %d px\n", con->border_width); + con->window_rect.width += (2 * con->border_width); + con->window_rect.height += (2 * con->border_width); + xcb_set_window_rect(conn, con->window->id, con->window_rect); + DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y); + xcb_reparent_window(conn, con->window->id, root, + con->rect.x, con->rect.y); + } } /* Strictly speaking, this line doesn’t really belong here, but since we @@ -273,6 +270,7 @@ 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); @@ -294,7 +292,7 @@ /* 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); @@ -325,7 +323,7 @@ } 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); @@ -361,6 +359,8 @@ match_free(match); FREE(match); } + + cwindow->swallowed = true; } DLOG("new container = %p\n", nc); @@ -536,7 +536,9 @@ * was not specified */ bool automatic_border = (motif_border_style == BS_NORMAL); - floating_enable(nc, automatic_border); + if (floating_enable(nc, automatic_border)) { + nc->floating = FLOATING_AUTO_ON; + } } /* explicitly set the border width to the default */ @@ -696,6 +698,11 @@ * */ 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) { @@ -741,5 +748,6 @@ ewmh_update_wm_desktop(); } + nc->window->swallowed = true; return nc; } diff -Nru i3-gaps-wm-4.18.2/src/match.c i3-gaps-wm-4.19/src/match.c --- i3-gaps-wm-4.18.2/src/match.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/match.c 2020-11-26 21:53:03.000000000 +0000 @@ -92,11 +92,9 @@ #define CHECK_WINDOW_FIELD(match_field, window_field, type) \ do { \ if (match->match_field != NULL) { \ - if (window->window_field == NULL) { \ - return false; \ - } \ - \ - const char *window_field_str = GET_FIELD_##type(window->window_field); \ + const char *window_field_str = window->window_field == NULL \ + ? "" \ + : GET_FIELD_##type(window->window_field); \ if (strcmp(match->match_field->pattern, "__focused__") == 0 && \ focused && focused->window && focused->window->window_field && \ strcmp(window_field_str, GET_FIELD_##type(focused->window->window_field)) == 0) { \ @@ -139,7 +137,7 @@ return false; } /* if we find a window that is newer than this one, bail */ - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { if ((con->window != NULL) && _i3_timercmp(con->window->urgent, window->urgent, >)) { return false; @@ -154,7 +152,7 @@ return false; } /* if we find a window that is older than this one (and not 0), bail */ - TAILQ_FOREACH(con, &all_cons, all_cons) { + TAILQ_FOREACH (con, &all_cons, all_cons) { if ((con->window != NULL) && (con->window->urgent.tv_sec != 0) && _i3_timercmp(con->window->urgent, window->urgent, <)) { @@ -201,7 +199,7 @@ bool matched = false; mark_t *mark; - TAILQ_FOREACH(mark, &(con->marks_head), marks) { + TAILQ_FOREACH (mark, &(con->marks_head), marks) { if (regex_matches(match->mark, mark->name)) { matched = true; break; @@ -217,15 +215,43 @@ } if (match->window_mode != WM_ANY) { - if ((con = con_by_window_id(window->id)) == NULL) + if ((con = con_by_window_id(window->id)) == NULL) { return false; + } - const bool floating = (con_inside_floating(con) != NULL); - - if ((match->window_mode == WM_TILING && floating) || - (match->window_mode == WM_FLOATING && !floating)) { - LOG("window_mode does not match\n"); - return false; + switch (match->window_mode) { + case WM_TILING_AUTO: + if (con->floating != FLOATING_AUTO_OFF) { + return false; + } + break; + case WM_TILING_USER: + if (con->floating != FLOATING_USER_OFF) { + return false; + } + break; + case WM_TILING: + if (con_inside_floating(con) != NULL) { + return false; + } + break; + case WM_FLOATING_AUTO: + if (con->floating != FLOATING_AUTO_ON) { + return false; + } + break; + case WM_FLOATING_USER: + if (con->floating != FLOATING_USER_ON) { + return false; + } + break; + case WM_FLOATING: + if (con_inside_floating(con) == NULL) { + return false; + } + break; + case WM_ANY: + assert(false); } LOG("window_mode matches\n"); @@ -369,10 +395,38 @@ return; } + if (strcmp(ctype, "tiling_from") == 0 && + cvalue != NULL && + strcmp(cvalue, "auto") == 0) { + match->window_mode = WM_TILING_AUTO; + return; + } + + if (strcmp(ctype, "tiling_from") == 0 && + cvalue != NULL && + strcmp(cvalue, "user") == 0) { + match->window_mode = WM_TILING_USER; + return; + } + if (strcmp(ctype, "floating") == 0) { match->window_mode = WM_FLOATING; return; } + if (strcmp(ctype, "floating_from") == 0 && + cvalue != NULL && + strcmp(cvalue, "auto") == 0) { + match->window_mode = WM_FLOATING_AUTO; + return; + } + + if (strcmp(ctype, "floating_from") == 0 && + cvalue != NULL && + strcmp(cvalue, "user") == 0) { + match->window_mode = WM_FLOATING_USER; + return; + } + ELOG("Unknown criterion: %s\n", ctype); } diff -Nru i3-gaps-wm-4.18.2/src/move.c i3-gaps-wm-4.19/src/move.c --- i3-gaps-wm-4.18.2/src/move.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/move.c 2020-11-26 21:53:03.000000000 +0000 @@ -91,7 +91,7 @@ } else { /* Look at the focus stack order of the children of the lowest common ancestor. */ Con *current; - TAILQ_FOREACH(current, &(lca->focus_head), focused) { + TAILQ_FOREACH (current, &(lca->focus_head), focused) { if (current == con_ancestor || current == target_ancestor) { break; } diff -Nru i3-gaps-wm-4.18.2/src/output.c i3-gaps-wm-4.19/src/output.c --- i3-gaps-wm-4.18.2/src/output.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/output.c 2020-11-26 21:53:03.000000000 +0000 @@ -16,9 +16,11 @@ Con *output_get_content(Con *output) { Con *child; - TAILQ_FOREACH(child, &(output->nodes_head), nodes) - if (child->type == CT_CON) - return child; + TAILQ_FOREACH (child, &(output->nodes_head), nodes) { + if (child->type == CT_CON) { + return child; + } + } return NULL; } @@ -74,7 +76,7 @@ */ void output_push_sticky_windows(Con *old_focus) { Con *output; - TAILQ_FOREACH(output, &(croot->focus_head), focused) { + TAILQ_FOREACH (output, &(croot->focus_head), focused) { Con *workspace, *visible_ws = NULL; GREP_FIRST(visible_ws, output_get_content(output), workspace_is_visible(child)); diff -Nru i3-gaps-wm-4.18.2/src/randr.c i3-gaps-wm-4.19/src/randr.c --- i3-gaps-wm-4.18.2/src/randr.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/randr.c 2020-11-26 21:53:03.000000000 +0000 @@ -12,6 +12,7 @@ #include "all.h" #include + #include /* Pointer to the result of the query for primary output */ @@ -32,9 +33,11 @@ */ static Output *get_output_by_id(xcb_randr_output_t id) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) - if (output->id == id) - return output; + TAILQ_FOREACH (output, &outputs, outputs) { + if (output->id == id) { + return output; + } + } return NULL; } @@ -47,7 +50,7 @@ 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) { + TAILQ_FOREACH (output, &outputs, outputs) { if (require_active && !output->active) { continue; } @@ -55,7 +58,7 @@ return output; } struct output_name *output_name; - SLIST_FOREACH(output_name, &output->names_head, names) { + SLIST_FOREACH (output_name, &output->names_head, names) { if (strcasecmp(output_name->name, name) == 0) { return output; } @@ -72,7 +75,7 @@ Output *get_first_output(void) { Output *output, *result = NULL; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (output->active) { if (output->primary) { return output; @@ -97,7 +100,7 @@ static bool any_randr_output_active(void) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (output != root_output && !output->to_be_disabled && output->active) return true; } @@ -112,7 +115,7 @@ */ Output *get_output_containing(unsigned int x, unsigned int y) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active) continue; DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n", @@ -146,7 +149,7 @@ */ Output *get_output_with_dimensions(Rect rect) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active) continue; DLOG("comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n", @@ -173,7 +176,7 @@ int rx = rect.x + rect.width, by = rect.y + rect.height; long max_area = 0; Output *result = NULL; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active) continue; int lx_o = (int)output->rect.x, uy_o = (int)output->rect.y; @@ -241,7 +244,7 @@ *other; Output *output, *best = NULL; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active) continue; @@ -331,7 +334,7 @@ /* Search for a Con with that name directly below the root node. There * might be one from a restored layout. */ - TAILQ_FOREACH(current, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (current, &(croot->nodes_head), nodes) { if (strcmp(current->name, output_primary_name(output)) != 0) continue; @@ -435,34 +438,29 @@ 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. */ @@ -482,7 +480,8 @@ } /* otherwise, we create the first assigned ws for this output */ - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + struct Workspace_Assignment *assignment; + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { if (!output_triggers_assignment(output, assignment)) { continue; } @@ -526,8 +525,8 @@ /* Fix the position of all floating windows on this output. * The 'rect' of each workspace will be updated in src/render.c. */ - TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) { - TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) { + TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) { + TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) { floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect)); } } @@ -536,7 +535,7 @@ * the workspaces and their children depending on output resolution. This is * only done for workspaces with maximum one child. */ if (config.default_orientation == NO_ORIENTATION) { - TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) { + TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) { /* Workspaces with more than one child are left untouched because * we do not want to change an existing layout. */ if (con_num_children(workspace) > 1) @@ -582,7 +581,7 @@ /* Mark all outputs as to_be_disabled, since xcb_randr_get_monitors() will * only return active outputs. */ Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (output != root_output) { output->to_be_disabled = true; } @@ -825,6 +824,76 @@ } /* + * Move the content of an outputs container to the first output. + * + * TODO: Maybe use an on_destroy callback which is implement differently for + * different container types (CT_CONTENT vs. CT_DOCKAREA)? + * + */ +static void move_content(Con *con) { + Con *first = get_first_output()->con; + Con *first_content = output_get_content(first); + + /* We need to move the workspaces from the disappearing output to the first output */ + /* 1: Get the con to focus next */ + Con *next = focused; + + /* 2: iterate through workspaces and re-assign them, fixing the coordinates + * of floating containers as we go */ + Con *current; + Con *old_content = output_get_content(con); + while (!TAILQ_EMPTY(&(old_content->nodes_head))) { + current = TAILQ_FIRST(&(old_content->nodes_head)); + if (current != next && TAILQ_EMPTY(&(current->focus_head))) { + /* the workspace is empty and not focused, get rid of it */ + DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name); + tree_close_internal(current, DONT_KILL_WINDOW, false); + continue; + } + DLOG("Detaching current = %p / %s\n", current, current->name); + con_detach(current); + DLOG("Re-attaching current = %p / %s\n", current, current->name); + con_attach(current, first_content, false); + DLOG("Fixing the coordinates of floating containers\n"); + Con *floating_con; + TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows) { + floating_fix_coordinates(floating_con, &(con->rect), &(first->rect)); + } + } + + /* Restore focus after con_detach / con_attach. next can be NULL, see #3523. */ + if (next) { + DLOG("now focusing next = %p\n", next); + con_focus(next); + workspace_show(con_get_workspace(next)); + } + + /* 3: move the dock clients to the first output */ + Con *child; + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + if (child->type != CT_DOCKAREA) { + continue; + } + DLOG("Handling dock con %p\n", child); + Con *dock; + while (!TAILQ_EMPTY(&(child->nodes_head))) { + dock = TAILQ_FIRST(&(child->nodes_head)); + Con *nc; + Match *match; + nc = con_for_window(first, dock->window, &match); + DLOG("Moving dock client %p to nc %p\n", dock, nc); + con_detach(dock); + DLOG("Re-attaching\n"); + con_attach(dock, nc, false); + DLOG("Done\n"); + } + } + + DLOG("Destroying disappearing con %p\n", con); + tree_close_internal(con, DONT_KILL_WINDOW, true); +} + +/* * (Re-)queries the outputs via RandR and stores them in the list of outputs. * * If no outputs are found use the root window. @@ -850,7 +919,7 @@ /* Check for clones, disable the clones and reduce the mode to the * lowest common mode */ - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active || output->to_be_disabled) continue; DLOG("output %p / %s, position (%d, %d), checking for clones\n", @@ -866,7 +935,7 @@ other->rect.y != output->rect.y) continue; - DLOG("output %p has the same position, his mode = %d x %d\n", + DLOG("output %p has the same position, its mode = %d x %d\n", other, other->rect.width, other->rect.height); uint32_t width = min(other->rect.width, output->rect.width); uint32_t height = min(other->rect.height, output->rect.height); @@ -891,7 +960,7 @@ * necessary because in the next step, a clone might get disabled. Example: * LVDS1 active, VGA1 gets activated as a clone of LVDS1 (has no con). * LVDS1 gets disabled. */ - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (output->active && output->con == NULL) { DLOG("Need to initialize a Con for output %s\n", output_primary_name(output)); output_init_con(output); @@ -899,9 +968,24 @@ } } + /* Ensure that all containers with type CT_OUTPUT have a valid + * corresponding entry in outputs. This can happen in situations related to + * those mentioned #3767 e.g. when a CT_OUTPUT is created from an in-place + * restart's layout but the output is disabled by a randr query happening + * at the same time. */ + Con *con; + for (con = TAILQ_FIRST(&(croot->nodes_head)); con;) { + Con *next = TAILQ_NEXT(con, nodes); + if (!con_is_internal(con) && get_output_by_name(con->name, true) == NULL) { + DLOG("No output %s found, moving its old content to first output\n", con->name); + move_content(con); + } + con = next; + } + /* Handle outputs which have a new mode or are disabled now (either * because the user disabled them or because they are clones) */ - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (output->to_be_disabled) { randr_disable_output(output); } @@ -913,7 +997,7 @@ } /* Just go through each active output and assign one workspace */ - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active) continue; Con *content = output_get_content(output->con); @@ -924,7 +1008,7 @@ } /* Focus the primary screen, if possible */ - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->primary || !output->con) continue; @@ -935,6 +1019,7 @@ } /* render_layout flushes */ + ewmh_update_desktop_properties(); tree_render(); FREE(primary); @@ -950,74 +1035,11 @@ output->active = false; DLOG("Output %s disabled, re-assigning workspaces/docks\n", output_primary_name(output)); - Output *first = get_first_output(); - - /* TODO: refactor the following code into a nice function. maybe - * use an on_destroy callback which is implement differently for - * different container types (CT_CONTENT vs. CT_DOCKAREA)? */ - Con *first_content = output_get_content(first->con); - if (output->con != NULL) { - /* We need to move the workspaces from the disappearing output to the first output */ - /* 1: Get the con to focus next */ - Con *next = focused; - - /* 2: iterate through workspaces and re-assign them, fixing the coordinates - * of floating containers as we go */ - Con *current; - Con *old_content = output_get_content(output->con); - while (!TAILQ_EMPTY(&(old_content->nodes_head))) { - current = TAILQ_FIRST(&(old_content->nodes_head)); - if (current != next && TAILQ_EMPTY(&(current->focus_head))) { - /* the workspace is empty and not focused, get rid of it */ - DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name); - tree_close_internal(current, DONT_KILL_WINDOW, false); - continue; - } - DLOG("Detaching current = %p / %s\n", current, current->name); - con_detach(current); - DLOG("Re-attaching current = %p / %s\n", current, current->name); - con_attach(current, first_content, false); - DLOG("Fixing the coordinates of floating containers\n"); - Con *floating_con; - TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows) { - floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect)); - } - } - - /* Restore focus after con_detach / con_attach. next can be NULL, see #3523. */ - if (next) { - DLOG("now focusing next = %p\n", next); - con_focus(next); - workspace_show(con_get_workspace(next)); - } - - /* 3: move the dock clients to the first output */ - Con *child; - TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) { - if (child->type != CT_DOCKAREA) - continue; - DLOG("Handling dock con %p\n", child); - Con *dock; - while (!TAILQ_EMPTY(&(child->nodes_head))) { - dock = TAILQ_FIRST(&(child->nodes_head)); - Con *nc; - Match *match; - nc = con_for_window(first->con, dock->window, &match); - DLOG("Moving dock client %p to nc %p\n", dock, nc); - con_detach(dock); - DLOG("Re-attaching\n"); - con_attach(dock, nc, false); - DLOG("Done\n"); - } - } - - DLOG("destroying disappearing con %p\n", output->con); + /* clear the pointer before move_content calls tree_close_internal in which the memory is freed */ Con *con = output->con; - /* clear the pointer before calling tree_close_internal in which the memory is freed */ output->con = NULL; - tree_close_internal(con, DONT_KILL_WINDOW, true); - DLOG("Done. Should be fine now\n"); + move_content(con); } output->to_be_disabled = false; diff -Nru i3-gaps-wm-4.18.2/src/render.c i3-gaps-wm-4.19/src/render.c --- i3-gaps-wm-4.18.2/src/render.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/render.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,6 +10,8 @@ */ #include "all.h" +#include + /* Forward declarations */ static int *precalculate_sizes(Con *con, render_params *p); static void render_root(Con *con, Con *fullscreen); @@ -93,6 +95,8 @@ inset->width -= (2 * con->border_width); inset->height -= (2 * con->border_width); + *inset = rect_sanitize_dimensions(*inset); + /* NB: We used to respect resize increment size hints for tiling * windows up until commit 0db93d9 here. However, since all terminal * emulators cope with ignoring the size hints in a better way than we @@ -137,7 +141,7 @@ render_root(con, fullscreen); } else { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { assert(params.children > 0); if (con->layout == L_SPLITH || con->layout == L_SPLITV) { @@ -150,6 +154,8 @@ render_con_dockarea(con, child, ¶ms); } + child->rect = rect_sanitize_dimensions(child->rect); + DLOG("child at (%d, %d) with (%d x %d)\n", child->rect.x, child->rect.y, child->rect.width, child->rect.height); x_raise_con(child); @@ -159,8 +165,9 @@ /* in a stacking or tabbed container, we ensure the focused client is raised */ if (con->layout == L_STACKED || con->layout == L_TABBED) { - TAILQ_FOREACH_REVERSE(child, &(con->focus_head), focus_head, focused) - x_raise_con(child); + TAILQ_FOREACH_REVERSE (child, &(con->focus_head), focus_head, focused) { + x_raise_con(child); + } if ((child = TAILQ_FIRST(&(con->focus_head)))) { /* By rendering the stacked container again, we handle the case * that we have a non-leaf-container inside the stack. In that @@ -193,7 +200,7 @@ Con *child; int i = 0, assigned = 0; int total = con_rect_size_in_orientation(con); - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { double percentage = child->percent > 0.0 ? child->percent : 1.0 / p->children; assigned += sizes[i++] = lround(percentage * total); } @@ -214,7 +221,7 @@ static void render_root(Con *con, Con *fullscreen) { Con *output; if (!fullscreen) { - TAILQ_FOREACH(output, &(con->nodes_head), nodes) { + TAILQ_FOREACH (output, &(con->nodes_head), nodes) { render_con(output, false); } } @@ -224,7 +231,7 @@ * all times. This is important when the user places floating * windows/containers so that they overlap on another output. */ DLOG("Rendering floating windows:\n"); - TAILQ_FOREACH(output, &(con->nodes_head), nodes) { + TAILQ_FOREACH (output, &(con->nodes_head), nodes) { if (con_is_internal(output)) continue; /* Get the active workspace of that output */ @@ -236,7 +243,7 @@ Con *workspace = TAILQ_FIRST(&(content->focus_head)); Con *fullscreen = con_get_fullscreen_covering_ws(workspace); Con *child; - TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) { + TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) { if (fullscreen != NULL) { /* Don’t render floating windows when there is a fullscreen * window on that workspace. Necessary to make floating @@ -301,7 +308,7 @@ /* Find the content container and ensure that there is exactly one. Also * check for any non-CT_DOCKAREA clients. */ Con *content = NULL; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (child->type == CT_CON) { if (content != NULL) { DLOG("More than one CT_CON on output container\n"); @@ -337,19 +344,20 @@ /* First pass: determine the height of all CT_DOCKAREAs (the sum of their * children) and figure out how many pixels we have left for the rest */ - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (child->type != CT_DOCKAREA) continue; child->rect.height = 0; - TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes) - child->rect.height += dockchild->geometry.height; + TAILQ_FOREACH (dockchild, &(child->nodes_head), nodes) { + child->rect.height += dockchild->geometry.height; + } height -= child->rect.height; } /* Second pass: Set the widths/heights */ - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { if (child->type == CT_CON) { child->rect.x = x; child->rect.y = y; @@ -447,7 +455,7 @@ /* 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 -Nru i3-gaps-wm-4.18.2/src/resize.c i3-gaps-wm-4.19/src/resize.c --- i3-gaps-wm-4.18.2/src/resize.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/resize.c 2020-11-26 21:53:03.000000000 +0000 @@ -238,6 +238,10 @@ const struct callback_params params = {orientation, output, helpwin, &new_position, &threshold_exceeded}; + /* Re-render the tree before returning to the event loop (drag_pointer() + * runs its own event-loop) in case if there are unrendered updates. */ + tree_render(); + /* `drag_pointer' blocks until the drag is completed. */ drag_result_t drag_result = drag_pointer(NULL, event, grabwin, 0, use_threshold, resize_callback, ¶ms); diff -Nru i3-gaps-wm-4.18.2/src/restore_layout.c i3-gaps-wm-4.19/src/restore_layout.c --- i3-gaps-wm-4.18.2/src/restore_layout.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/restore_layout.c 2020-11-26 21:53:03.000000000 +0000 @@ -29,8 +29,7 @@ /** 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 = @@ -140,7 +139,7 @@ Match *swallows; int n = 0; - TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) { + TAILQ_FOREACH (swallows, &(state->con->swallow_head), matches) { char *serialized = NULL; #define APPEND_REGEX(re_name) \ @@ -234,10 +233,10 @@ } Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { open_placeholder_window(child); } - TAILQ_FOREACH(child, &(con->floating_head), floating_windows) { + TAILQ_FOREACH (child, &(con->floating_head), floating_windows) { open_placeholder_window(child); } } @@ -251,10 +250,10 @@ */ void restore_open_placeholder_windows(Con *parent) { Con *child; - TAILQ_FOREACH(child, &(parent->nodes_head), nodes) { + TAILQ_FOREACH (child, &(parent->nodes_head), nodes) { open_placeholder_window(child); } - TAILQ_FOREACH(child, &(parent->floating_head), floating_windows) { + TAILQ_FOREACH (child, &(parent->floating_head), floating_windows) { open_placeholder_window(child); } @@ -270,7 +269,7 @@ */ bool restore_kill_placeholder(xcb_window_t placeholder) { placeholder_state *state; - TAILQ_FOREACH(state, &state_head, state) { + TAILQ_FOREACH (state, &state_head, state) { if (state->window != placeholder) continue; @@ -288,7 +287,7 @@ static void expose_event(xcb_expose_event_t *event) { placeholder_state *state; - TAILQ_FOREACH(state, &state_head, state) { + TAILQ_FOREACH (state, &state_head, state) { if (state->window != event->window) continue; @@ -310,7 +309,7 @@ */ static void configure_notify(xcb_configure_notify_event_t *event) { placeholder_state *state; - TAILQ_FOREACH(state, &state_head, state) { + TAILQ_FOREACH (state, &state_head, state) { if (state->window != event->window) continue; diff -Nru i3-gaps-wm-4.18.2/src/scratchpad.c i3-gaps-wm-4.19/src/scratchpad.c --- i3-gaps-wm-4.18.2/src/scratchpad.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/scratchpad.c 2020-11-26 21:53:03.000000000 +0000 @@ -32,7 +32,7 @@ } 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 @@ */ 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 @@ -112,7 +112,7 @@ * unfocused scratchpad on the current workspace and focus it */ Con *walk_con; Con *focused_ws = con_get_workspace(focused); - TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) { + TAILQ_FOREACH (walk_con, &(focused_ws->floating_head), floating_windows) { if (!con && (floating = con_inside_floating(walk_con)) && floating->scratchpad_state != SCRATCHPAD_NONE && floating != con_inside_floating(focused)) { @@ -130,7 +130,7 @@ * visible scratchpad window on another workspace. In this case we move it * to the current workspace. */ focused_ws = con_get_workspace(focused); - TAILQ_FOREACH(walk_con, &all_cons, all_cons) { + TAILQ_FOREACH (walk_con, &all_cons, all_cons) { Con *walk_ws = con_get_workspace(walk_con); if (!con && walk_ws && !con_is_internal(walk_ws) && focused_ws != walk_ws && @@ -245,7 +245,7 @@ * */ 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, @@ -253,7 +253,7 @@ Con *output; int new_width = -1, new_height = -1; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { if (output == __i3_output) continue; DLOG("output %s's resolution: (%d, %d) %d x %d\n", @@ -284,7 +284,7 @@ DLOG("Fixing coordinates of scratchpad windows\n"); Con *con; - TAILQ_FOREACH(con, &(__i3_scratch->floating_head), floating_windows) { + TAILQ_FOREACH (con, &(__i3_scratch->floating_head), floating_windows) { floating_fix_coordinates(con, &old_rect, &new_rect); } } diff -Nru i3-gaps-wm-4.18.2/src/sd-daemon.c i3-gaps-wm-4.19/src/sd-daemon.c --- i3-gaps-wm-4.18.2/src/sd-daemon.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/sd-daemon.c 2020-11-26 21:53:03.000000000 +0000 @@ -28,21 +28,21 @@ #define _GNU_SOURCE #endif -#include -#include -#include -#include -#include -#include -#include +#include "sd-daemon.h" + #include -#include -#include +#include #include -#include #include - -#include "sd-daemon.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include int sd_listen_fds(int unset_environment) { int r, fd; diff -Nru i3-gaps-wm-4.18.2/src/sighandler.c i3-gaps-wm-4.19/src/sighandler.c --- i3-gaps-wm-4.18.2/src/sighandler.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/sighandler.c 2020-11-26 21:53:03.000000000 +0000 @@ -7,23 +7,16 @@ */ #include "all.h" -#include -#include #include #include - -#include - -#include +#include 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); @@ -155,7 +148,7 @@ static void sighandler_create_dialogs(void) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) { + TAILQ_FOREACH (output, &outputs, outputs) { if (!output->active) { continue; } @@ -230,7 +223,7 @@ static void sighandler_handle_expose(void) { dialog_t *current; - TAILQ_FOREACH(current, &dialogs, dialogs) { + TAILQ_FOREACH (current, &dialogs, dialogs) { sighandler_draw_dialog(current); } diff -Nru i3-gaps-wm-4.18.2/src/startup.c i3-gaps-wm-4.19/src/startup.c --- i3-gaps-wm-4.18.2/src/startup.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/startup.c 2020-11-26 21:53:03.000000000 +0000 @@ -11,12 +11,12 @@ * */ #include "all.h" - #include "sd-daemon.h" +#include #include #include -#include +#include #define SN_API_NOT_YET_FROZEN 1 #include @@ -36,7 +36,7 @@ DLOG("Timeout for startup sequence %s\n", id); struct Startup_Sequence *current, *sequence = NULL; - TAILQ_FOREACH(current, &startup_sequences, sequences) { + TAILQ_FOREACH (current, &startup_sequences, sequences) { if (strcmp(current->id, id) != 0) continue; @@ -201,10 +201,7 @@ if (!no_startup_id) { /* Change the pointer of the root window to indicate progress */ - if (xcursor_supported) - xcursor_set_root_cursor(XCURSOR_CURSOR_WATCH); - else - xcb_set_root_cursor(XCURSOR_CURSOR_WATCH); + xcursor_set_root_cursor(XCURSOR_CURSOR_WATCH); } } @@ -220,7 +217,7 @@ /* Get the corresponding internal startup sequence */ const char *id = sn_startup_sequence_get_id(snsequence); struct Startup_Sequence *current, *sequence = NULL; - TAILQ_FOREACH(current, &startup_sequences, sequences) { + TAILQ_FOREACH (current, &startup_sequences, sequences) { if (strcmp(current->id, id) != 0) continue; @@ -246,10 +243,7 @@ if (_prune_startup_sequences() == 0) { DLOG("No more startup sequences running, changing root window cursor to default pointer.\n"); /* Change the pointer of the root window to indicate progress */ - if (xcursor_supported) - xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); - else - xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); + xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER); } break; default: @@ -264,7 +258,7 @@ */ void startup_sequence_rename_workspace(const char *old_name, const char *new_name) { struct Startup_Sequence *current; - TAILQ_FOREACH(current, &startup_sequences, sequences) { + TAILQ_FOREACH (current, &startup_sequences, sequences) { if (strcmp(current->workspace, old_name) != 0) continue; DLOG("Renaming workspace \"%s\" to \"%s\" in startup sequence %s.\n", @@ -320,7 +314,7 @@ sasprintf(&startup_id, "%.*s", xcb_get_property_value_length(startup_id_reply), (char *)xcb_get_property_value(startup_id_reply)); struct Startup_Sequence *current, *sequence = NULL; - TAILQ_FOREACH(current, &startup_sequences, sequences) { + TAILQ_FOREACH (current, &startup_sequences, sequences) { if (strcmp(current->id, startup_id) != 0) continue; diff -Nru i3-gaps-wm-4.18.2/src/tree.c i3-gaps-wm-4.19/src/tree.c --- i3-gaps-wm-4.18.2/src/tree.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/tree.c 2020-11-26 21:53:03.000000000 +0000 @@ -431,13 +431,15 @@ Con *current; con->mapped = false; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) - mark_unmapped(current); + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + mark_unmapped(current); + } if (con->type == CT_WORKSPACE) { /* We need to call mark_unmapped on floating nodes as well since we can * make containers floating. */ - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) - mark_unmapped(current); + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { + mark_unmapped(current); + } } } diff -Nru i3-gaps-wm-4.18.2/src/util.c i3-gaps-wm-4.19/src/util.c --- i3-gaps-wm-4.18.2/src/util.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/util.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,19 +10,16 @@ */ #include "all.h" +#include +#include +#include +#include +#include #include -#include +#include #if defined(__OpenBSD__) #include #endif -#include -#include -#include -#include -#include - -#define SN_API_NOT_YET_FROZEN 1 -#include int min(int a, int b) { return (a < b ? a : b); @@ -53,6 +50,12 @@ a.height - b.height}; } +Rect rect_sanitize_dimensions(Rect rect) { + rect.width = (int32_t)rect.width <= 0 ? 1 : rect.width; + rect.height = (int32_t)rect.height <= 0 ? 1 : rect.height; + return rect; +} + bool rect_equals(Rect a, Rect b) { return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height; } @@ -103,14 +106,12 @@ * interpreted as a "named workspace". * */ -long ws_name_to_number(const char *name) { +int ws_name_to_number(const char *name) { /* positive integers and zero are interpreted as numbers */ char *endptr = NULL; - long parsed_num = strtol(name, &endptr, 10); - if (parsed_num == LONG_MIN || - parsed_num == LONG_MAX || - parsed_num < 0 || - endptr == name) { + errno = 0; + long long parsed_num = strtoll(name, &endptr, 10); + if (errno != 0 || parsed_num > INT32_MAX || parsed_num < 0 || endptr == name) { parsed_num = -1; } @@ -286,8 +287,8 @@ 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(); @@ -335,30 +336,19 @@ 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; } } @@ -394,27 +384,20 @@ 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) @@ -424,7 +407,7 @@ * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD * for us and we would end up with a process. Therefore we * waitpid() here. */ - waitpid(*nagbar_pid, NULL, 0); + waitpid(nagbar_pid, NULL, 0); } /* diff -Nru i3-gaps-wm-4.18.2/src/window.c i3-gaps-wm-4.19/src/window.c --- i3-gaps-wm-4.18.2/src/window.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/window.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,6 +9,8 @@ */ #include "all.h" +#include + /* * Frees an i3Window and all its members. * @@ -288,7 +290,7 @@ ASSIGN_IF_CHANGED(win->max_width, max_width); ASSIGN_IF_CHANGED(win->max_height, max_height); } else { - DLOG("Clearing maximum size \n"); + DLOG("Clearing maximum size\n"); ASSIGN_IF_CHANGED(win->max_width, 0); ASSIGN_IF_CHANGED(win->max_height, 0); diff -Nru i3-gaps-wm-4.18.2/src/workspace.c i3-gaps-wm-4.19/src/workspace.c --- i3-gaps-wm-4.18.2/src/workspace.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/workspace.c 2020-11-26 21:53:03.000000000 +0000 @@ -29,7 +29,7 @@ */ Con *get_existing_workspace_by_name(const char *name) { Con *output, *workspace = NULL; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, name)); } @@ -43,7 +43,7 @@ */ Con *get_existing_workspace_by_num(int num) { Con *output, *workspace = NULL; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { GREP_FIRST(workspace, output_get_content(output), child->num == num); } @@ -72,25 +72,26 @@ /* * 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) { + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { if (assignment->output == NULL) { continue; } 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 @@ 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 @@ * 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}; - - /* 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); + LOG("Creating new workspace \"%s\"\n", num); + gaps_t gaps = (gaps_t){0, 0, 0, 0, 0}; - 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; - } + /* 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); - } - - 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; } @@ -203,7 +198,7 @@ } FREE(binding_workspace_names); } - TAILQ_FOREACH(bind, bindings, bindings) { + TAILQ_FOREACH (bind, bindings, bindings) { DLOG("binding with command %s\n", bind->command); if (strlen(bind->command) < strlen("workspace ") || strncasecmp(bind->command, "workspace", strlen("workspace")) != 0) @@ -302,7 +297,7 @@ } struct Workspace_Assignment *assignment; - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->name, ws->name) == 0) { ws->gaps = assignment->gaps; break; @@ -346,7 +341,7 @@ static Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) { Con *current; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { if (current != exclude && current->sticky_group != NULL && current->window != NULL && @@ -358,7 +353,7 @@ return recurse; } - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) { + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { if (current != exclude && current->sticky_group != NULL && current->window != NULL && @@ -385,7 +380,7 @@ /* 1: go through all containers */ /* handle all children and floating windows of this node */ - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { if (current->sticky_group == NULL) { workspace_reassign_sticky(current); continue; @@ -413,8 +408,9 @@ LOG("re-assigned window from src %p to dest %p\n", src, current); } - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) - workspace_reassign_sticky(current); + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { + workspace_reassign_sticky(current); + } } /* @@ -452,7 +448,7 @@ /* disable fullscreen for the other workspaces and get the workspace we are * currently on. */ - TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes) { + TAILQ_FOREACH (current, &(workspace->parent->nodes_head), nodes) { if (current->fullscreen_mode == CF_OUTPUT) old = current; current->fullscreen_mode = CF_NONE; @@ -576,9 +572,7 @@ * */ void workspace_show_by_name(const char *num) { - Con *workspace; - workspace = workspace_get(num, NULL); - workspace_show(workspace); + workspace_show(workspace_get(num)); } /* @@ -595,11 +589,11 @@ if ((next = TAILQ_NEXT(current, nodes)) != NULL) return next; bool found_current = false; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH(output_get_content(output)) { + NODES_FOREACH (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!first) @@ -616,11 +610,11 @@ } } else { /* If currently a numbered workspace, find next numbered workspace. */ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH(output_get_content(output)) { + NODES_FOREACH (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!first || (child->num != -1 && child->num < first->num)) @@ -660,11 +654,11 @@ prev = NULL; if (!prev) { bool found_current = false; - TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { + TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH_REVERSE(output_get_content(output)) { + NODES_FOREACH_REVERSE (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!last) @@ -682,11 +676,11 @@ } } else { /* If numbered workspace, find previous numbered workspace. */ - TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { + TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH_REVERSE(output_get_content(output)) { + NODES_FOREACH_REVERSE (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!last || (child->num != -1 && last->num < child->num)) @@ -724,7 +718,7 @@ next = TAILQ_NEXT(current, nodes); } else { /* If currently a numbered workspace, find next numbered workspace. */ - NODES_FOREACH(output_get_content(output)) { + NODES_FOREACH (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child->num == -1) @@ -740,7 +734,7 @@ /* Find next named workspace. */ if (!next) { bool found_current = false; - NODES_FOREACH(output_get_content(output)) { + NODES_FOREACH (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child == current) { @@ -754,7 +748,7 @@ /* Find first workspace. */ if (!next) { - NODES_FOREACH(output_get_content(output)) { + NODES_FOREACH (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!next || (child->num != -1 && child->num < next->num)) @@ -782,7 +776,7 @@ prev = NULL; } else { /* If numbered workspace, find previous numbered workspace. */ - NODES_FOREACH_REVERSE(output_get_content(output)) { + NODES_FOREACH_REVERSE (output_get_content(output)) { if (child->type != CT_WORKSPACE || child->num == -1) continue; /* Need to check child against current and previous because we @@ -796,7 +790,7 @@ /* Find previous named workspace. */ if (!prev) { bool found_current = false; - NODES_FOREACH_REVERSE(output_get_content(output)) { + NODES_FOREACH_REVERSE (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child == current) { @@ -810,7 +804,7 @@ /* Find last workspace. */ if (!prev) { - NODES_FOREACH_REVERSE(output_get_content(output)) { + NODES_FOREACH_REVERSE (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!prev || child->num > prev->num) @@ -845,21 +839,22 @@ 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) { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) - if (child->urgent || get_urgency_flag(child)) - return true; - - TAILQ_FOREACH(child, &(con->floating_head), floating_windows) - if (child->urgent || get_urgency_flag(child)) - return true; + TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + if (child->urgent || get_urgency_flag(child)) { + return true; + } + } + + TAILQ_FOREACH (child, &(con->floating_head), floating_windows) { + if (child->urgent || get_urgency_flag(child)) { + return true; + } + } return false; } @@ -1016,7 +1011,7 @@ /* check if we can find a workspace assigned to this output */ bool used_assignment = false; struct Workspace_Assignment *assignment; - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { bool attached; int num; if (!output_triggers_assignment(current_output, assignment)) { @@ -1031,7 +1026,7 @@ /* 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; } @@ -1059,7 +1054,7 @@ /* fix the coordinates of the floating containers */ Con *floating_con; - TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows) { + TAILQ_FOREACH (floating_con, &(ws->floating_head), floating_windows) { floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect)); } @@ -1080,7 +1075,7 @@ * order/number of other workspaces on the output. Instead, we loop through * the available workspaces and only work with previously_visible_ws if we * still find it. */ - TAILQ_FOREACH(ws, &(content->nodes_head), nodes) { + TAILQ_FOREACH (ws, &(content->nodes_head), nodes) { if (ws != previously_visible_ws) { continue; } diff -Nru i3-gaps-wm-4.18.2/src/x.c i3-gaps-wm-4.19/src/x.c --- i3-gaps-wm-4.18.2/src/x.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/x.c 2020-11-26 21:53:03.000000000 +0000 @@ -10,6 +10,8 @@ */ #include "all.h" +#include + #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif @@ -61,26 +63,18 @@ 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); /* @@ -92,9 +86,11 @@ */ static con_state *state_for_frame(xcb_window_t window) { con_state *state; - CIRCLEQ_FOREACH(state, &state_head, state) - if (state->id == window) - return state; + CIRCLEQ_FOREACH (state, &state_head, state) { + if (state->id == window) { + return state; + } + } /* TODO: better error handling? */ ELOG("No state found for window 0x%08x\n", window); @@ -588,11 +584,6 @@ } } - /* 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) @@ -606,6 +597,11 @@ 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); @@ -624,7 +620,7 @@ bool had_visible_mark = false; mark_t *mark; - TAILQ_FOREACH(mark, &(con->marks_head), marks) { + TAILQ_FOREACH (mark, &(con->marks_head), marks) { if (mark->name[0] == '_') continue; had_visible_mark = true; @@ -726,11 +722,13 @@ con_state *state = state_for_frame(con->frame.id); if (!leaf) { - TAILQ_FOREACH(current, &(con->nodes_head), nodes) - x_deco_recurse(current); + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + x_deco_recurse(current); + } - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) - x_deco_recurse(current); + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { + x_deco_recurse(current); + } if (state->mapped) { draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height); @@ -858,7 +856,7 @@ /* Calculate the height of all window decorations which will be drawn on to * this frame. */ uint32_t max_y = 0, max_height = 0; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { Rect *dr = &(current->deco_rect); if (dr->y >= max_y && dr->height >= max_height) { max_y = dr->y; @@ -1070,7 +1068,7 @@ /* Handle all children and floating windows of this node. We recurse * in focus order to display the focused client in a stack first when * switching workspaces (reduces flickering). */ - TAILQ_FOREACH(current, &(con->focus_head), focused) { + TAILQ_FOREACH (current, &(con->focus_head), focused) { x_push_node(current); } } @@ -1116,11 +1114,13 @@ } /* handle all children and floating windows of this node */ - TAILQ_FOREACH(current, &(con->nodes_head), nodes) - x_push_node_unmaps(current); + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + x_push_node_unmaps(current); + } - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) - x_push_node_unmaps(current); + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { + x_push_node_unmaps(current); + } } /* @@ -1133,7 +1133,7 @@ return false; Con *current; - TAILQ_FOREACH(current, &(con->parent->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->parent->nodes_head), nodes) { if (current == con) return true; } @@ -1167,7 +1167,7 @@ * ConfigureWindow requests and get them applied directly instead of having * them become ConfigureRequests that i3 handles. */ uint32_t values[1] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } @@ -1178,9 +1178,11 @@ /* count first, necessary to (re)allocate memory for the bottom-to-top * stack afterwards */ int cnt = 0; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) - if (con_has_managed_window(state->con)) - cnt++; + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + if (con_has_managed_window(state->con)) { + cnt++; + } + } /* The bottom-to-top window stack of all windows which are managed by i3. * Used for x_get_window_stack(). */ @@ -1195,7 +1197,7 @@ xcb_window_t *walk = client_list_windows; /* X11 correctly represents the stack if we push it from bottom to top */ - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (con_has_managed_window(state->con)) memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t)); @@ -1226,7 +1228,7 @@ walk = client_list_windows; /* reorder by initial mapping */ - TAILQ_FOREACH(state, &initial_mapping_head, initial_mapping_order) { + TAILQ_FOREACH (state, &initial_mapping_head, initial_mapping_order) { if (con_has_managed_window(state->con)) *walk++ = state->con->window->id; } @@ -1261,7 +1263,7 @@ //DLOG("Re-enabling EnterNotify\n"); values[0] = FRAME_EVENT_MASK; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } @@ -1337,7 +1339,7 @@ * unmapped, the second one appears under the cursor and therefore gets an * EnterNotify event. */ values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (!state->unmap_now) continue; xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); @@ -1347,7 +1349,7 @@ x_push_node_unmaps(con); /* save the current stack as old stack */ - CIRCLEQ_FOREACH(state, &state_head, state) { + CIRCLEQ_FOREACH (state, &state_head, state) { CIRCLEQ_REMOVE(&old_state_head, state, old_state); CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state); } @@ -1439,7 +1441,7 @@ uint32_t values[] = {FRAME_EVENT_MASK & mask}; con_state *state; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } diff -Nru i3-gaps-wm-4.18.2/src/xcb.c i3-gaps-wm-4.19/src/xcb.c --- i3-gaps-wm-4.18.2/src/xcb.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/xcb.c 2020-11-26 21:53:03.000000000 +0000 @@ -45,20 +45,8 @@ } /* Set the cursor */ - if (xcursor_supported) { - mask = XCB_CW_CURSOR; - values[0] = xcursor_get_cursor(cursor); - xcb_change_window_attributes(conn, result, mask, values); - } else { - xcb_cursor_t cursor_id = xcb_generate_id(conn); - i3Font cursor_font = load_font("cursor", false); - int xcb_cursor = xcursor_get_xcb_cursor(cursor); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id, - cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0, - 65535, 65535, 65535); - xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, &cursor_id); - xcb_free_cursor(conn, cursor_id); - } + uint32_t cursor_values[] = {xcursor_get_cursor(cursor)}; + xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, cursor_values); /* Map the window (= make it visible) */ if (map) @@ -176,24 +164,6 @@ } /* - * Set the cursor of the root window to the given cursor id. - * This function should only be used if xcursor_supported == false. - * Otherwise, use xcursor_set_root_cursor(). - * - */ -void xcb_set_root_cursor(int cursor) { - xcb_cursor_t cursor_id = xcb_generate_id(conn); - i3Font cursor_font = load_font("cursor", false); - int xcb_cursor = xcursor_get_xcb_cursor(cursor); - xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id, - cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0, - 65535, 65535, 65535); - xcb_change_window_attributes(conn, root, XCB_CW_CURSOR, &cursor_id); - xcb_free_cursor(conn, cursor_id); - xcb_flush(conn); -} - -/* * Get depth of visual specified by visualid * */ diff -Nru i3-gaps-wm-4.18.2/src/xcursor.c i3-gaps-wm-4.19/src/xcursor.c --- i3-gaps-wm-4.18.2/src/xcursor.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/xcursor.c 2020-11-26 21:53:03.000000000 +0000 @@ -9,27 +9,19 @@ */ #include +#include "all.h" + #include -#include +#include -#include "i3.h" -#include "xcb.h" -#include "xcursor.h" +#include static xcb_cursor_context_t *ctx; static xcb_cursor_t cursors[XCURSOR_CURSOR_MAX]; -static const int xcb_cursors[XCURSOR_CURSOR_MAX] = { - XCB_CURSOR_LEFT_PTR, - XCB_CURSOR_SB_H_DOUBLE_ARROW, - XCB_CURSOR_SB_V_DOUBLE_ARROW, - XCB_CURSOR_WATCH}; - void xcursor_load_cursors(void) { if (xcb_cursor_context_new(conn, root_screen, &ctx) < 0) { - ELOG("xcursor support unavailable\n"); - xcursor_supported = false; - return; + errx(EXIT_FAILURE, "Cannot allocate xcursor context"); } #define LOAD_CURSOR(constant, name) \ do { \ @@ -63,8 +55,3 @@ assert(c < XCURSOR_CURSOR_MAX); return cursors[c]; } - -int xcursor_get_xcb_cursor(enum xcursor_cursor_t c) { - assert(c < XCURSOR_CURSOR_MAX); - return xcb_cursors[c]; -} diff -Nru i3-gaps-wm-4.18.2/src/xinerama.c i3-gaps-wm-4.19/src/xinerama.c --- i3-gaps-wm-4.18.2/src/xinerama.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/src/xinerama.c 2020-11-26 21:53:03.000000000 +0000 @@ -21,9 +21,11 @@ */ static Output *get_screen_at(unsigned int x, unsigned int y) { Output *output; - TAILQ_FOREACH(output, &outputs, outputs) - if (output->rect.x == x && output->rect.y == y) - return output; + TAILQ_FOREACH (output, &outputs, outputs) { + if (output->rect.x == x && output->rect.y == y) { + return output; + } + } return NULL; } diff -Nru i3-gaps-wm-4.18.2/testcases/complete-run.pl.in i3-gaps-wm-4.19/testcases/complete-run.pl.in --- i3-gaps-wm-4.18.2/testcases/complete-run.pl.in 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/complete-run.pl.in 2020-11-26 21:53:03.000000000 +0000 @@ -18,7 +18,7 @@ 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 @@ # 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 @@ } $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 -Nru i3-gaps-wm-4.18.2/testcases/inject_randr1.5.c i3-gaps-wm-4.19/testcases/inject_randr1.5.c --- i3-gaps-wm-4.18.2/testcases/inject_randr1.5.c 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/inject_randr1.5.c 2020-11-26 21:53:03.000000000 +0000 @@ -16,15 +16,17 @@ #include #include -#include -#include -#include -#include -#include +#include +#include #include +#include +#include #include +#include +#include +#include #include -#include +#include 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); @@ -59,7 +61,7 @@ ev_io *clientw; /* serverw is a libev watcher for the connection to X11 which we initiated - * on behalf of the client. */ + * on behalf of the client. */ ev_io *serverw; /* sequence is the client-side sequence number counter. In X11’s wire diff -Nru i3-gaps-wm-4.18.2/testcases/lib/i3test/XTEST.pm i3-gaps-wm-4.19/testcases/lib/i3test/XTEST.pm --- i3-gaps-wm-4.18.2/testcases/lib/i3test/XTEST.pm 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/lib/i3test/XTEST.pm 2020-11-26 21:53:03.000000000 +0000 @@ -7,7 +7,7 @@ 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 -Nru i3-gaps-wm-4.18.2/testcases/lib/i3test.pm.in i3-gaps-wm-4.19/testcases/lib/i3test.pm.in --- i3-gaps-wm-4.18.2/testcases/lib/i3test.pm.in 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/lib/i3test.pm.in 2020-11-26 21:53:03.000000000 +0000 @@ -7,7 +7,7 @@ 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 -Nru i3-gaps-wm-4.18.2/testcases/t/116-nestedcons.t i3-gaps-wm-4.19/testcases/t/116-nestedcons.t --- i3-gaps-wm-4.18.2/testcases/t/116-nestedcons.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/116-nestedcons.t 2020-11-26 21:53:03.000000000 +0000 @@ -72,6 +72,7 @@ 'floating_nodes' => $ignore, workspace_layout => 'default', current_border_width => -1, + marks => $ignore, }; # a shallow copy is sufficient, since we only ignore values at the root diff -Nru i3-gaps-wm-4.18.2/testcases/t/117-workspace.t i3-gaps-wm-4.19/testcases/t/117-workspace.t --- i3-gaps-wm-4.18.2/testcases/t/117-workspace.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/117-workspace.t 2020-11-26 21:53:03.000000000 +0000 @@ -130,6 +130,21 @@ ok(defined($ws), "workspace aa: $tmp was created"); is($ws->{num}, -1, 'workspace number is -1'); +cmd "workspace -42: $tmp"; +$ws = get_ws("-42: $tmp"); +ok(defined($ws), "workspace -42: $tmp was created"); +is($ws->{num}, -1, 'negative workspace number is ignored'); + +cmd "workspace 2147483647: $tmp"; +$ws = get_ws("2147483647: $tmp"); +ok(defined($ws), "workspace 2147483647: $tmp was created"); +is($ws->{num}, 2147483647, 'workspace number is 2147483647'); + +cmd "workspace 2147483648: $tmp"; +$ws = get_ws("2147483648: $tmp"); +ok(defined($ws), "workspace 2147483648: $tmp was created"); +is($ws->{num}, -1, 'workspace number past the limit is ignored'); + ################################################################################ # Check that we can go to workspace "4: foo" with the command # "workspace number 4". diff -Nru i3-gaps-wm-4.18.2/testcases/t/119-match.t i3-gaps-wm-4.19/testcases/t/119-match.t --- i3-gaps-wm-4.18.2/testcases/t/119-match.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/119-match.t 2020-11-26 21:53:03.000000000 +0000 @@ -33,9 +33,7 @@ # not match it ###################################################################### # TODO: specify more match types -# we can match on any (non-empty) class here since that window does not have -# WM_CLASS set -cmd q|[class=".*"] kill|; +# Try matching with an empty pattern since there isn't a WM_CLASS set. cmd q|[con_id="99999"] kill|; is_num_children($tmp, 1, 'window still there'); @@ -103,4 +101,18 @@ wait_for_unmap $left; is_num_children($tmp, 0, 'window killed'); +###################################################################### +# check that we can match empty properties +###################################################################### + +$tmp = fresh_workspace; + +$left = open_window(name => 'class is empty', wm_class => ''); +ok($left->mapped, 'left window mapped'); +is_num_children($tmp, 1, 'window opened'); + +cmd '[class="^$"] kill'; +wait_for_unmap $left; +is_num_children($tmp, 0, 'window killed'); + done_testing; diff -Nru i3-gaps-wm-4.18.2/testcases/t/124-move.t i3-gaps-wm-4.19/testcases/t/124-move.t --- i3-gaps-wm-4.18.2/testcases/t/124-move.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/124-move.t 2020-11-26 21:53:03.000000000 +0000 @@ -220,21 +220,49 @@ is($absolute->width, $absolute_before->width, 'width not changed'); is($absolute->height, $absolute_before->height, 'height not changed'); +$absolute_before = $absolute; +$top_before = $top; + +###################################################################### +# 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'); + ###################################################################### -# 6) test moving floating window to a specified position +# 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 -Nru i3-gaps-wm-4.18.2/testcases/t/132-move-workspace.t i3-gaps-wm-4.19/testcases/t/132-move-workspace.t --- i3-gaps-wm-4.18.2/testcases/t/132-move-workspace.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/132-move-workspace.t 2020-11-26 21:53:03.000000000 +0000 @@ -340,6 +340,53 @@ 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 -Nru i3-gaps-wm-4.18.2/testcases/t/180-fd-leaks.t i3-gaps-wm-4.19/testcases/t/180-fd-leaks.t --- i3-gaps-wm-4.18.2/testcases/t/180-fd-leaks.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/180-fd-leaks.t 2020-11-26 21:53:03.000000000 +0000 @@ -29,6 +29,10 @@ mkfifo($tmp, 0600) or die "Could not create FIFO in $tmp"; my ($outfh, $outname) = tempfile('/tmp/i3-ls-output.XXXXXX', UNLINK => 1); +# Get fds from a clean shell +my $shoutput = `sh -c "ls -l /proc/self/fd"`; + +# Get fds from i3 cmd qq|exec ls -l /proc/self/fd >$outname && echo done >$tmp|; open(my $fh, '<', $tmp); @@ -38,29 +42,39 @@ unlink($tmp); # Get the ls /proc/self/fd output -my $output; +my $i3output; { local $/; - $output = <$outfh>; + $i3output = <$outfh>; } close($outfh); -# Split lines, keep only those which are symlinks. -my @lines = grep { /->/ } split("\n", $output); +sub extract_fds { + my $output = @_; + + # Split lines, keep only those which are symlinks. + my @lines = grep { /->/ } split("\n", $output); + + my %fds = map { /([0-9]+) -> (.+)$/; ($1, $2) } @lines; -my %fds = map { /([0-9]+) -> (.+)$/; ($1, $2) } @lines; + # Filter out 0, 1, 2 (stdin, stdout, stderr). + delete $fds{0}; + delete $fds{1}; + delete $fds{2}; -# Filter out 0, 1, 2 (stdin, stdout, stderr). -delete $fds{0}; -delete $fds{1}; -delete $fds{2}; - -# Filter out the fd which is caused by ls calling readdir(). -for my $fd (keys %fds) { - delete $fds{$fd} if $fds{$fd} =~ m,^/proc/\d+/fd$,; + # filter out the fd which is caused by ls calling readdir(). + for my $fd (keys %fds) { + delete $fds{$fd} if $fds{$fd} =~ m,^/proc/\d+/fd$,; + } + + return %fds; } -is(scalar keys %fds, 0, 'No file descriptors leaked'); +my %i3fds = extract_fds($i3output); +my %shfds = extract_fds($shoutput); + +# Diff the fds to account for services that keep fds open, such as the System Security Services Daemon (sssd) +is(scalar keys %i3fds, scalar keys %shfds, 'No file descriptors leaked'); } diff -Nru i3-gaps-wm-4.18.2/testcases/t/193-ipc-version.t i3-gaps-wm-4.19/testcases/t/193-ipc-version.t --- i3-gaps-wm-4.18.2/testcases/t/193-ipc-version.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/193-ipc-version.t 2020-11-26 21:53:03.000000000 +0000 @@ -32,6 +32,5 @@ 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 -Nru i3-gaps-wm-4.18.2/testcases/t/201-config-parser.t i3-gaps-wm-4.19/testcases/t/201-config-parser.t --- i3-gaps-wm-4.18.2/testcases/t/201-config-parser.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/201-config-parser.t 2020-11-26 21:53:03.000000000 +0000 @@ -98,18 +98,53 @@ ################################################################################ $config = <<'EOT'; +for_window [] nop empty for_window [class="^Chrome"] floating enable +for_window [class=^Chrome] floating enable +for_window [floating_from = "auto" class= ==Class== ] nop floating +for_window [tiling_from=auto class="==Class=="]nop floating EOT $expected = <<'EOT'; +cfg_for_window(nop empty) cfg_criteria_add(class, ^Chrome) cfg_for_window(floating enable) +cfg_criteria_add(class, ^Chrome) +cfg_for_window(floating enable) +cfg_criteria_add(floating_from, auto) +cfg_criteria_add(class, ==Class==) +cfg_for_window(nop floating) +cfg_criteria_add(tiling_from, auto) +cfg_criteria_add(class, ==Class==) +cfg_for_window(nop floating) EOT is(parser_calls($config), $expected, 'for_window okay'); +$config = <<'EOT'; +for_window [tiling_from=typo] nop typo +for_window [tiling_from="typo"] nop typo +EOT + +$expected = <<'EOT'; +ERROR: CONFIG: Expected one of these tokens: '"', 'auto', 'user' +ERROR: CONFIG: (in file ) +ERROR: CONFIG: Line 1: for_window [tiling_from=typo] nop typo +ERROR: CONFIG: ^^^^^^^^^^^^^^ +ERROR: CONFIG: Line 2: for_window [tiling_from="typo"] nop typo +ERROR: CONFIG: Expected one of these tokens: 'auto', 'user' +ERROR: CONFIG: (in file ) +ERROR: CONFIG: Line 1: for_window [tiling_from=typo] nop typo +ERROR: CONFIG: Line 2: for_window [tiling_from="typo"] nop typo +ERROR: CONFIG: ^^^^^^^^^^^^^^^ +EOT + +is(parser_calls($config), + $expected, + 'for_window errors okay'); + ################################################################################ # assign ################################################################################ diff -Nru i3-gaps-wm-4.18.2/testcases/t/210-mark-unmark.t i3-gaps-wm-4.19/testcases/t/210-mark-unmark.t --- i3-gaps-wm-4.18.2/testcases/t/210-mark-unmark.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/210-mark-unmark.t 2020-11-26 21:53:03.000000000 +0000 @@ -98,7 +98,7 @@ cmd 'mark important'; is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'first container now has the mark'); -ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the mark'); +is_deeply(get_mark_for_window_on_workspace($tmp, $second), [], 'second container lost the mark'); ############################################################## # 5: mark a con, toggle the mark, check that the mark is gone @@ -107,7 +107,7 @@ $con = open_window; cmd 'mark important'; cmd 'mark --toggle important'; -ok(!get_mark_for_window_on_workspace($tmp, $con), 'container no longer has the mark'); +is_deeply(get_mark_for_window_on_workspace($tmp, $con), [], 'container no longer has the mark'); ############################################################## # 6: toggle a mark on an unmarked con, check it is marked @@ -140,7 +140,7 @@ cmd 'mark --toggle important'; is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'left container has the mark now'); -ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark'); +is_deeply(get_mark_for_window_on_workspace($tmp, $second), [], 'second containr no longer has the mark'); ############################################################## # 9: try to mark two cons with the same mark and check that @@ -154,8 +154,8 @@ is($result->[0]->{success}, 0, 'command was unsuccessful'); is($result->[0]->{error}, 'A mark must not be put onto more than one window', 'correct error is returned'); -ok(!get_mark_for_window_on_workspace($tmp, $first), 'first container is not marked'); -ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr is not marked'); +is_deeply(get_mark_for_window_on_workspace($tmp, $first), [], 'first container is not marked'); +is_deeply(get_mark_for_window_on_workspace($tmp, $second), [], 'second containr is not marked'); ############################################################## diff -Nru i3-gaps-wm-4.18.2/testcases/t/271-for_window_tilingfloating.t i3-gaps-wm-4.19/testcases/t/271-for_window_tilingfloating.t --- i3-gaps-wm-4.18.2/testcases/t/271-for_window_tilingfloating.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/271-for_window_tilingfloating.t 2020-11-26 21:53:03.000000000 +0000 @@ -17,27 +17,69 @@ use i3test i3_config => <{nodes}}; cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); -is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion"); +is_deeply($nodes[0]->{marks}, [ 'tiling', 'tiling_auto' ], "mark set for 'tiling' criterion"); + +@nodes = @{get_ws($tmp)->{floating_nodes}}; +cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); +is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floating', 'floating_auto' ], "mark set for 'floating' criterion"); + +################################################################################ +# Check that the user tiling / floating criteria work. +# The following rules are triggered here: 'tiling', 'tiling_user', 'floating', +# 'floating_user'. Therefore, the old marks 'tiling' and 'floating' are +# replaced but the 'tiling_auto' and 'floating_auto' marks are preserved. +################################################################################ + +cmd '[id=' . $A->{id} . '] floating enable'; +cmd '[id=' . $B->{id} . '] floating disable'; + +@nodes = @{get_ws($tmp)->{nodes}}; +cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); +is_deeply($nodes[0]->{marks}, [ 'floating_auto', 'tiling', 'tiling_user' ], "Marks updated after 'floating_user' criterion"); + +@nodes = @{get_ws($tmp)->{floating_nodes}}; +cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); +is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'tiling_auto', 'floating', 'floating_user' ], "Marks updated after 'tiling_user' criterion"); + +################################################################################ +# Check that the default and auto rules do not re-trigger +# Here, the windows are returned to their original state but since the rules +# `tiling`, `tiling_auto`, `floating` and `floating_auto` where already +# triggered, only the `tiling_user` and `floating_user` rules should trigger. +################################################################################ + +# Use 'mark' to clear old marks +cmd '[id=' . $A->{id} . '] mark A, floating disable'; +cmd '[id=' . $B->{id} . '] mark B, floating enable'; + +@nodes = @{get_ws($tmp)->{nodes}}; +cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); +is_deeply($nodes[0]->{marks}, [ 'A', 'tiling_user' ], "Only 'tiling_user' rule triggered"); @nodes = @{get_ws($tmp)->{floating_nodes}}; cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); -is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion"); +is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'B', 'floating_user' ], "Only 'floating_user' rule triggered"); ############################################################## diff -Nru i3-gaps-wm-4.18.2/testcases/t/297-assign-workspace-to-output.t i3-gaps-wm-4.19/testcases/t/297-assign-workspace-to-output.t --- i3-gaps-wm-4.18.2/testcases/t/297-assign-workspace-to-output.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/297-assign-workspace-to-output.t 2020-11-26 21:53:03.000000000 +0000 @@ -41,7 +41,7 @@ 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 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('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 @@ 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 -Nru i3-gaps-wm-4.18.2/testcases/t/310-client-message-sticky.t i3-gaps-wm-4.19/testcases/t/310-client-message-sticky.t --- i3-gaps-wm-4.18.2/testcases/t/310-client-message-sticky.t 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/310-client-message-sticky.t 2020-11-26 21:53:03.000000000 +0000 @@ -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 -Nru i3-gaps-wm-4.18.2/testcases/t/311-get-binding-modes.t i3-gaps-wm-4.19/testcases/t/311-get-binding-modes.t --- i3-gaps-wm-4.18.2/testcases/t/311-get-binding-modes.t 1970-01-01 00:00:00.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/311-get-binding-modes.t 2020-11-26 21:53:03.000000000 +0000 @@ -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 => <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 -Nru i3-gaps-wm-4.18.2/testcases/t/542-layout-restore-remanage.t i3-gaps-wm-4.19/testcases/t/542-layout-restore-remanage.t --- i3-gaps-wm-4.18.2/testcases/t/542-layout-restore-remanage.t 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/testcases/t/542-layout-restore-remanage.t 2020-11-26 21:53:03.000000000 +0000 @@ -83,4 +83,70 @@ 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 -Nru i3-gaps-wm-4.18.2/travis/bintray-autobuild-debian.json i3-gaps-wm-4.19/travis/bintray-autobuild-debian.json --- i3-gaps-wm-4.18.2/travis/bintray-autobuild-debian.json 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/bintray-autobuild-debian.json 2020-11-26 21:53:03.000000000 +0000 @@ -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 -Nru i3-gaps-wm-4.18.2/travis/bintray-autobuild-ubuntu.json i3-gaps-wm-4.19/travis/bintray-autobuild-ubuntu.json --- i3-gaps-wm-4.18.2/travis/bintray-autobuild-ubuntu.json 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/bintray-autobuild-ubuntu.json 2020-11-26 21:53:03.000000000 +0000 @@ -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 -Nru i3-gaps-wm-4.18.2/travis/check-formatting.sh i3-gaps-wm-4.19/travis/check-formatting.sh --- i3-gaps-wm-4.18.2/travis/check-formatting.sh 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/check-formatting.sh 2020-11-26 21:53:03.000000000 +0000 @@ -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 -Nru i3-gaps-wm-4.18.2/travis/check-spelling.pl i3-gaps-wm-4.19/travis/check-spelling.pl --- i3-gaps-wm-4.18.2/travis/check-spelling.pl 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/check-spelling.pl 2020-11-26 21:53:03.000000000 +0000 @@ -17,8 +17,11 @@ # Lintian complains if we don’t set a vendor. use Lintian::Data; use Lintian::Profile; -Lintian::Data->set_vendor( - Lintian::Profile->new('debian', ['/usr/share/lintian'], {})); + +my $profile = Lintian::Profile->new; +$profile->load('debian', ['/usr/share/lintian']); + +Lintian::Data->set_vendor($profile); my $exitcode = 0; @@ -30,12 +33,12 @@ }; 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 -Nru i3-gaps-wm-4.18.2/travis/debian-build.sh i3-gaps-wm-4.19/travis/debian-build.sh --- i3-gaps-wm-4.18.2/travis/debian-build.sh 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/debian-build.sh 2020-11-26 21:53:03.000000000 +0000 @@ -5,14 +5,10 @@ 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 -Nru i3-gaps-wm-4.18.2/travis/docker-build-and-push.sh i3-gaps-wm-4.19/travis/docker-build-and-push.sh --- i3-gaps-wm-4.18.2/travis/docker-build-and-push.sh 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/docker-build-and-push.sh 2020-11-26 21:53:03.000000000 +0000 @@ -5,10 +5,6 @@ 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 -Nru i3-gaps-wm-4.18.2/travis/docs.sh i3-gaps-wm-4.19/travis/docs.sh --- i3-gaps-wm-4.18.2/travis/docs.sh 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/docs.sh 2020-11-26 21:53:03.000000000 +0000 @@ -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 i3-dmenu-desktop man/i3-dmenu-desktop.html -./docs/i3-pod2html i3-save-tree man/i3-save-tree.html -./docs/i3-pod2html build/testcases/lib/i3test.pm docs/lib-i3test.html -./docs/i3-pod2html testcases/lib/i3test/Test.pm docs/lib-i3test-test.html -for file in $(sed 's/\.1$/.man/g' debian/i3-wm.manpages) + +./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/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 -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 -Nru i3-gaps-wm-4.18.2/travis/run-tests.sh i3-gaps-wm-4.19/travis/run-tests.sh --- i3-gaps-wm-4.18.2/travis/run-tests.sh 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/run-tests.sh 2020-11-26 21:53:03.000000000 +0000 @@ -26,7 +26,7 @@ # 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 -Nru i3-gaps-wm-4.18.2/travis/travis-base-386.Dockerfile i3-gaps-wm-4.19/travis/travis-base-386.Dockerfile --- i3-gaps-wm-4.18.2/travis/travis-base-386.Dockerfile 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/travis-base-386.Dockerfile 2020-11-26 21:53:03.000000000 +0000 @@ -13,12 +13,12 @@ # (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 -Nru i3-gaps-wm-4.18.2/travis/travis-base.Dockerfile i3-gaps-wm-4.19/travis/travis-base.Dockerfile --- i3-gaps-wm-4.18.2/travis/travis-base.Dockerfile 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/travis-base.Dockerfile 2020-11-26 21:53:03.000000000 +0000 @@ -11,13 +11,13 @@ # (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/* diff -Nru i3-gaps-wm-4.18.2/travis/travis-base-ubuntu-386.Dockerfile i3-gaps-wm-4.19/travis/travis-base-ubuntu-386.Dockerfile --- i3-gaps-wm-4.18.2/travis/travis-base-ubuntu-386.Dockerfile 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/travis-base-ubuntu-386.Dockerfile 2020-11-26 21:53:03.000000000 +0000 @@ -13,12 +13,12 @@ # (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 -Nru i3-gaps-wm-4.18.2/travis/travis-base-ubuntu.Dockerfile i3-gaps-wm-4.19/travis/travis-base-ubuntu.Dockerfile --- i3-gaps-wm-4.18.2/travis/travis-base-ubuntu.Dockerfile 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/travis/travis-base-ubuntu.Dockerfile 2020-11-26 21:53:03.000000000 +0000 @@ -13,13 +13,13 @@ # (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 -Nru i3-gaps-wm-4.18.2/.travis.yml i3-gaps-wm-4.19/.travis.yml --- i3-gaps-wm-4.18.2/.travis.yml 2020-07-26 08:35:49.000000000 +0000 +++ i3-gaps-wm-4.19/.travis.yml 2020-11-26 21:53:03.000000000 +0000 @@ -31,9 +31,10 @@ 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