diff --git a/.gitignore b/.gitignore index 617421ac..c6fd81c8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,30 @@ i3-command-parser.stamp i3-config-parser.stamp .clang_complete LAST_VERSION + +################################################################################ +# https://raw.githubusercontent.com/github/gitignore/master/Autotools.gitignore +################################################################################ + +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/test-driver + +# http://www.gnu.org/software/autoconf + +/autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.h.in +/config.guess +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 diff --git a/.travis.yml b/.travis.yml index aa574d09..f9744335 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,18 +6,67 @@ language: c compiler: - gcc - clang +addons: + apt: + packages: + # For https support in HTTP::Tiny. + - libio-socket-ssl-perl env: global: - - BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh)" + - BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base.Dockerfile)" + - BASENAME_386="i3wm/travis-base-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-386.Dockerfile)" + - BASENAME_UBUNTU="i3wm/travis-base-ubuntu:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu.Dockerfile)" + - BASENAME_UBUNTU_386="i3wm/travis-base-ubuntu-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu-386.Dockerfile)" - secure: "B5IICA8MPx/FKaB50rTPqL8P1NU+Q0yuWl+lElL4+a9xSyLikfm3NzUPHoVwx8lNw2AVK6br7p0OmF7vMFjqAgrgc1cajTtEae5uFRKNUrWLpXM046YgNEYLLIHsQOjInxE+S4O6EFVzsUqsu8aeo2Xhq4sm4iUocG7e5isYgYo=" # DOCKER_PASS - secure: "EIvrq8PG7lRjidppG0RCv4F0X4GP3DT9F5+ixVuGPfhK/hZT3jYC2AVY9G+NnUcXVwQEpW92rlqpftQ/qZ13FoyWokC8ZyoyD06fr5FPCfoFF3OczZwAJzZYkObI/hE9+/hXcylx/Os6N4INd2My1ntGk3JPsWL9riopod5EjSg=" # DOCKER_EMAIL - secure: "hvhBunS4xXTgnIOsk/BPT7I7FrJhvVwCSt5PfxxvMqNaztOJI9BuK7ZrZ5Cy38KyHwlh3VHAH5AaCygJcPauoSQCV3bpnlbaWn3ruq2F0Q697Q5uNf73liXzyUqb9/Zvfvge4y4WWOhP5tVz1C6ZBe/NfhU7pqKLMA+6ads+99c=" # DOCKER_USER + - secure: "uJuuefmnJUuEH15ZD8xQilibx7EeBvMHBLoIZ8bgGHeleEImBD0XbD1ypvhYJKpviOmw5BkZmc9bVO8DGDEHYbSlIa2xDlF6vGrwgCEaxcMIhOAhv+dW9C/maJVieLOEPM01/fK2qdKESZaLvlopkWmxZwDyMObI9L7AMW9zQD8=" # BINTRAY_USER + - secure: "L3aPSNLySPXtWCW+xf8h/AAdquwNgxyTQpYOwexJmTPav82Qx8uQlp1yJkUmt+a+FLZDFfQeMivaHq0311RvuQVmkAJx49DjaddrwqOJut2UPsoVDn1WeuAcSHIXOq/0H+zgFMr/PGY0HXIsw1mTMhgheGJNqg09BvYWROCEAcA=" # BINTRAY_KEY + - secure: "sBMVn4C/WRWgoAytEFGx4CC5O55Q63h02AcuBnb1jXcBm0RenoBpzUPtxSseJwDPUA1o/UkuEDDjm3PosT5NF+dvED01VDFMsPVE11K0u6+avYy3jYXqyUEDW3G2o6Wo/2aqNjmd++8jskBdS9+Cx9gaFbgxfzSp0Yfu3oJm/4c=" # GH_TOKEN install: - if [ -a .git/shallow ]; then git fetch --unshallow; fi - - docker pull ${BASENAME} || ./travis/docker-build-and-push.sh + - docker pull ${BASENAME} || ./travis/docker-build-and-push.sh ${BASENAME} travis/travis-base.Dockerfile + - ./travis/skip-pkg.sh || docker pull ${BASENAME_UBUNTU} || ./travis/docker-build-and-push.sh ${BASENAME_UBUNTU} travis/travis-base-ubuntu.Dockerfile + - ./travis/skip-pkg.sh || docker pull ${BASENAME_386} || ./travis/docker-build-and-push.sh ${BASENAME_386} travis/travis-base-386.Dockerfile + - ./travis/skip-pkg.sh || docker pull ${BASENAME_UBUNTU_386} || ./travis/docker-build-and-push.sh ${BASENAME_UBUNTU_386} travis/travis-base-ubuntu-386.Dockerfile script: - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh - - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC -e CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" ${BASENAME} make all mans -j ASAN=1 + - 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 -Werror"' - 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 ${BASENAME} ./travis/run-tests.sh + - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} ./travis/run-tests.sh + - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/debian-build.sh deb/debian-amd64/DIST + - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU} ./travis/debian-build.sh deb/ubuntu-amd64/DIST + - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_386} linux32 ./travis/debian-build.sh deb/debian-i386/DIST + - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU_386} linux32 ./travis/debian-build.sh deb/ubuntu-i386/DIST + - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/clang-analyze.sh + - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/docs.sh + - ./travis/skip-pkg.sh || travis/prep-bintray.sh + +deploy: + - provider: bintray + file: travis/bintray-autobuild-debian.json + user: $BINTRAY_USER + key: $BINTRAY_KEY + skip_cleanup: true + on: + branch: next + condition: $CC = gcc + - provider: bintray + file: travis/bintray-autobuild-ubuntu.json + user: $BINTRAY_USER + key: $BINTRAY_KEY + skip_cleanup: true + on: + branch: next + condition: $CC = gcc + - provider: script + script: travis/deploy-github-pages.sh + skip_cleanup: true + on: + branch: next + condition: $CC = gcc + +after_deploy: + - travis/cleanup-bintray.pl i3-autobuild + - travis/cleanup-bintray.pl i3-autobuild-ubuntu diff --git a/DEPENDS b/DEPENDS index fad11da5..167827ff 100644 --- a/DEPENDS +++ b/DEPENDS @@ -4,28 +4,29 @@ "min" means minimum required version "lkgv" means last known good version -┌──────────────┬────────┬────────┬────────────────────────────────────────┐ -│ dependency │ min. │ lkgv │ URL │ -├──────────────┼────────┼────────┼────────────────────────────────────────┤ -│ pkg-config │ 0.25 │ 0.28 │ http://pkgconfig.freedesktop.org/ │ -│ libxcb │ 1.1.93 │ 1.11 │ http://xcb.freedesktop.org/dist/ │ -│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │ -│ xkbcommon │ 0.4.0 │ 0.5.0 │ http://xkbcommon.org/ │ -│ xkbcommon-x11│ 0.4.0 │ 0.5.0 │ http://xkbcommon.org/ │ -│ util-cursor³⁴│ 0.0.99 │ 0.1.2 │ http://xcb.freedesktop.org/dist/ │ -│ util-wm⁴ │ 0.3.8 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │ -│ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ http://xcb.freedesktop.org/dist/ │ -│ libev │ 4.0 │ 4.19 │ http://libev.schmorp.de/ │ -│ yajl │ 2.0.1 │ 2.1.0 │ http://lloyd.github.com/yajl/ │ -│ asciidoc │ 8.3.0 │ 8.6.8 │ http://www.methods.co.nz/asciidoc/ │ -│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │ -│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/ -│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │ -│ PCRE │ 8.12 │ 8.35 │ http://www.pcre.org/ │ -│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification -│ pango │ 1.30.0 | 1.36.8 │ http://www.pango.org/ │ -│ cairo │ 1.14.4 │ 1.14.4 │ http://cairographics.org/ │ -└──────────────┴────────┴────────┴────────────────────────────────────────┘ +┌──────────────┬────────┬────────┬───────────────────────────────────────────────────────────┐ +│ dependency │ min. │ lkgv │ URL │ +├──────────────┼────────┼────────┼───────────────────────────────────────────────────────────┤ +│ pkg-config │ 0.25 │ 0.29 │ http://pkgconfig.freedesktop.org/ │ +│ libxcb │ 1.1.93 │ 1.12 │ http://xcb.freedesktop.org/dist/ │ +│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │ +│ xkbcommon │ 0.4.0 │ 0.6.1 │ http://xkbcommon.org/ │ +│ xkbcommon-x11│ 0.4.0 │ 0.6.1 │ http://xkbcommon.org/ │ +│ util-cursor³⁴│ 0.0.99 │ 0.1.3 │ http://xcb.freedesktop.org/dist/ │ +│ util-wm⁴ │ 0.3.8 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │ +│ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ http://xcb.freedesktop.org/dist/ │ +│ util-xrm⁴ │ 1.0.0 │ 1.0.0 │ https://github.com/Airblader/xcb-util-xrm │ +│ libev │ 4.0 │ 4.19 │ http://libev.schmorp.de/ │ +│ yajl │ 2.0.1 │ 2.1.0 │ http://lloyd.github.com/yajl/ │ +│ asciidoc │ 8.3.0 │ 8.6.9 │ http://www.methods.co.nz/asciidoc/ │ +│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │ +│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/ │ +│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │ +│ PCRE │ 8.12 │ 8.38 │ http://www.pcre.org/ │ +│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification │ +│ pango │ 1.30.0 | 1.40.1 │ http://www.pango.org/ │ +│ cairo │ 1.14.4 │ 1.14.6 │ http://cairographics.org/ │ +└──────────────┴────────┴────────┴───────────────────────────────────────────────────────────┘ ¹ libsn = libstartup-notification ² Pod::Simple is a Perl module required for converting the testsuite documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple diff --git a/I3_VERSION b/I3_VERSION new file mode 100644 index 00000000..d4cfa42a --- /dev/null +++ b/I3_VERSION @@ -0,0 +1 @@ +4.13-non-git diff --git a/Makefile b/Makefile deleted file mode 100644 index fd302101..00000000 --- a/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -TOPDIR=$(shell pwd) - -include $(TOPDIR)/common.mk - -SUBDIRS:= - -ALL_TARGETS = -INSTALL_TARGETS = -CLEAN_TARGETS = -DISTCLEAN_TARGETS = - -all: real-all - -include libi3/libi3.mk -include src/i3.mk -include i3-config-wizard/i3-config-wizard.mk -include i3-msg/i3-msg.mk -include i3-input/i3-input.mk -include i3-nagbar/i3-nagbar.mk -include i3bar/i3bar.mk -include i3-dump-log/i3-dump-log.mk -include docs/docs.mk -include man/man.mk - -# Update $(TOPDIR)/LAST_VERSION if it differs from $I3_VERSION -CACHED_VERSION := '$(shell [ -f $(TOPDIR)/LAST_VERSION ] && cat $(TOPDIR)/LAST_VERSION)' -ifneq ($(CACHED_VERSION),$(I3_VERSION)) -$(shell echo -n ${I3_VERSION} > $(TOPDIR)/LAST_VERSION) -endif - -real-all: $(ALL_TARGETS) - -install: $(INSTALL_TARGETS) - -dist: distclean - [ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION} - [ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2 - mkdir i3-${VERSION} - cp i3-migrate-config-to-v4 i3-save-tree generate-command-parser.pl i3-sensible-* i3-dmenu-desktop i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3-with-shmlog.xsession.desktop i3.applications.desktop pseudo-doc.doxygen common.mk Makefile i3-${VERSION} - cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log include man parser-specs testcases i3-${VERSION} - # Only copy toplevel documentation (important stuff) - mkdir i3-${VERSION}/docs - # Pre-generate documentation - $(MAKE) docs - # Cleanup τεχ output files - find docs -regex ".*\.\(aux\|out\|log\|toc\|bm\|dvi\|log\)" -exec rm '{}' \; - find docs -maxdepth 1 -type f ! \( -name "*.xcf" -or -name "*.svg" \) -exec cp '{}' i3-${VERSION}/docs \; - # Only copy source code from i3-input - mkdir i3-${VERSION}/i3-input - find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \; - echo -n ${I3_VERSION} > i3-${VERSION}/I3_VERSION - echo -n ${VERSION} > i3-${VERSION}/VERSION - # Pre-generate a manpage to allow distributors to skip this step and save some dependencies - $(MAKE) mans - cp man/*.1 i3-${VERSION}/man/ - tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION} - rm -rf i3-${VERSION} - -clean: $(CLEAN_TARGETS) - (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true - -distclean: clean $(DISTCLEAN_TARGETS) - -coverage: - rm -f /tmp/i3-coverage.info - rm -rf /tmp/i3-coverage - lcov -d . -b . --capture -o /tmp/i3-coverage.info - genhtml -o /tmp/i3-coverage/ /tmp/i3-coverage.info diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..c90e26c7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,563 @@ +@CODE_COVERAGE_RULES@ + +echo-version: + @echo "@I3_VERSION@" + +bin_PROGRAMS = \ + i3 \ + i3bar/i3bar \ + i3-config-wizard/i3-config-wizard \ + i3-dump-log/i3-dump-log \ + i3-input/i3-input \ + i3-msg/i3-msg \ + i3-nagbar/i3-nagbar + +install-exec-hook: + $(LN_S) -f i3 $(DESTDIR)$(bindir)/i3-with-shmlog + +uninstall-hook: + rm -f $(DESTDIR)$(bindir)/i3-with-shmlog + +i3includedir=$(includedir)/i3 +i3include_HEADERS = \ + include/i3/ipc.h + +dist_bin_SCRIPTS = \ + i3-dmenu-desktop \ + i3-migrate-config-to-v4 \ + i3-save-tree \ + i3-sensible-editor \ + i3-sensible-pager \ + i3-sensible-terminal + +i3confdir = $(sysconfdir)/i3 +dist_i3conf_DATA = \ + etc/config \ + etc/config.keycodes + +applicationsdir = $(datarootdir)/applications +xsessionsdir = $(datarootdir)/xsessions +dist_applications_DATA = \ + share/applications/i3.desktop +dist_xsessions_DATA = \ + share/xsessions/i3.desktop \ + share/xsessions/i3-with-shmlog.desktop + +noinst_LIBRARIES = libi3.a + +check_PROGRAMS = test.commands_parser test.config_parser + +check_SCRIPTS = \ + testcases/complete-run.pl + +clean-check: + rm -rf testsuite-* latest i3-cfg-for-* _Inline +clean-local: clean-check + +TESTS = testcases/complete-run.pl + +EXTRA_DIST = \ + $(dist_docs_toc_DATA:.html=) \ + $(dist_docs_notoc_DATA:.html=) \ + docs/asciidoc-git.conf \ + docs/bigpicture.png \ + docs/i3-pod2html \ + docs/i3-sync.dia \ + docs/i3-sync.png \ + docs/i3-sync-working.dia \ + docs/i3-sync-working.png \ + docs/keyboard-layer1.png \ + docs/keyboard-layer2.png \ + docs/layout-saving-1.png \ + docs/logo-30.png \ + docs/modes.png \ + docs/refcard.html \ + docs/refcard_style.css \ + docs/single_terminal.png \ + docs/snapping.png \ + docs/tree-layout1.png \ + docs/tree-layout2.png \ + docs/tree-shot1.png \ + docs/tree-shot2.png \ + docs/tree-shot3.png \ + docs/tree-shot4.png \ + docs/two_columns.png \ + docs/two_terminals.png \ + docs/wsbar.dia \ + docs/wsbar.png \ + i3bar/LICENSE \ + libi3/README \ + $(asciidoc_MANS:.1=.man) \ + $(asciidoc_MANS:.1=.man) \ + man/asciidoc.conf.in \ + DEPENDS \ + I3_VERSION \ + LICENSE \ + PACKAGE-MAINTAINER \ + RELEASE-NOTES-4.13 \ + generate-command-parser.pl \ + parser-specs/commands.spec \ + parser-specs/config.spec \ + parser-specs/highlighting.vim \ + pseudo-doc.doxygen \ + testcases/complete-run.pl.in \ + testcases/i3-test.config \ + testcases/lib/i3test/Test.pm \ + testcases/lib/i3test/Util.pm \ + testcases/lib/i3test/XTEST.pm \ + testcases/lib/i3test.pm.in \ + testcases/lib/SocketActivation.pm \ + testcases/lib/StartXServer.pm \ + testcases/lib/StatusLine.pm \ + testcases/lib/TestWorker.pm \ + testcases/Makefile.PL \ + testcases/new-test \ + testcases/restart-state.golden \ + testcases/t \ + testcases/valgrind.supp + +# dirstamps contains directories which we want to be created in $(top_builddir) +# so that our custom rules can store files in them. +dirstamp = .dirstamp +dirstamps = \ + docs/$(dirstamp) \ + man/$(dirstamp) \ + parser/$(dirstamp) +DISTCLEANFILES = $(dirstamps) + +$(dirstamps): + @stamp='$@'; $(MKDIR_P) "$${stamp%/*}" + @: > $@ + +################################################################################ +# docs generation +################################################################################ + +docs_tocdir = ${docdir} +docs_notocdir = ${docdir} +docs_poddir = ${docdir} +if BUILD_DOCS +dist_docs_toc_DATA = \ + docs/hacking-howto.html \ + docs/userguide.html \ + docs/ipc.html \ + docs/multi-monitor.html \ + docs/wsbar.html \ + docs/testsuite.html \ + docs/i3bar-protocol.html \ + docs/layout-saving.html + +dist_docs_notoc_DATA = \ + docs/debugging.html + +dist_docs_pod_DATA = \ + docs/lib-i3test.html \ + docs/lib-i3test-test.html + +$(dist_docs_toc_DATA): docs/%.html: docs/% docs/$(dirstamp) + $(AM_V_GEN) @PATH_ASCIIDOC@ -a toc -n -o $@ $< + +$(dist_docs_notoc_DATA): docs/%.html: docs/% docs/$(dirstamp) + $(AM_V_GEN) @PATH_ASCIIDOC@ -n -o $@ $< + +docs/lib-i3test.html: testcases/lib/i3test.pm docs/$(dirstamp) + $(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@ + +docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm docs/$(dirstamp) + $(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@ + +else +dist_docs_toc_DATA = +dist_docs_notoc_DATA = +dist_docs_pod_DATA = +endif + +################################################################################ +# manpage generation +################################################################################ + +if BUILD_MANS +dist_man1_MANS = \ + $(asciidoc_MANS) \ + $(pod_MANS) + +asciidoc_MANS = \ + man/i3.1 \ + man/i3bar.1 \ + man/i3-msg.1 \ + man/i3-input.1 \ + man/i3-nagbar.1 \ + man/i3-config-wizard.1 \ + man/i3-migrate-config-to-v4.1 \ + man/i3-sensible-editor.1 \ + man/i3-sensible-pager.1 \ + man/i3-sensible-terminal.1 \ + man/i3-dump-log.1 + +pod_MANS = \ + man/i3-dmenu-desktop.1 \ + man/i3-save-tree.1 + +$(asciidoc_MANS): man/%.1: man/%.xml man/$(dirstamp) + $(AM_V_GEN) out='$@'; @PATH_XMLTO@ man -o "$${out%/*}" $< + @stamp='$@'; $(MKDIR_P) "$${stamp%/*}" + +man/%.xml: man/%.man man/asciidoc.conf man/$(dirstamp) + $(AM_V_GEN) @PATH_ASCIIDOC@ -d manpage -b docbook -f $(top_builddir)/man/asciidoc.conf -o $@ $< + +$(pod_MANS): man/%.1: % man/$(dirstamp) + $(AM_V_GEN) @PATH_POD2MAN@ --utf8 $< > $@ +else +asciidoc_MANS = +endif + +AM_CPPFLAGS = \ + -I$(top_builddir)/parser \ + -I$(top_srcdir)/include \ + @AX_EXTEND_SRCDIR_CPPFLAGS@ + +i3_CFLAGS = \ + $(AM_CFLAGS) \ + $(libi3_CFLAGS) \ + $(LIBSN_CFLAGS) \ + $(XCB_CFLAGS) \ + $(XCB_UTIL_CURSOR_CFLAGS) \ + $(XCB_UTIL_KEYSYM_CFLAGS) \ + $(XCB_UTIL_WM_CFLAGS) \ + $(XCB_UTIL_XRM_CFLAGS) \ + $(XKBCOMMON_CFLAGS) \ + $(YAJL_CFLAGS) \ + $(LIBPCRE_CFLAGS) \ + $(PTHREAD_CFLAGS) \ + $(CODE_COVERAGE_CFLAGS) + +i3_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(CODE_COVERAGE_CPPFLAGS) + +i3_LDADD = \ + $(libi3_LIBS) \ + $(LIBSN_LIBS) \ + $(XCB_LIBS) \ + $(XCB_UTIL_CURSOR_LIBS) \ + $(XCB_UTIL_KEYSYMS_LIBS) \ + $(XCB_UTIL_WM_LIBS) \ + $(XCB_UTIL_XRM_LIBS) \ + $(XKBCOMMON_LIBS) \ + $(YAJL_LIBS) \ + $(LIBPCRE_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(PTHREAD_LIBS) \ + $(CODE_COVERAGE_LDFLAGS) + +libi3_CFLAGS = \ + $(AM_CFLAGS) \ + $(XCB_CFLAGS) \ + $(XCB_UTIL_CFLAGS) \ + $(XCB_UTIL_XRM_CFLAGS) \ + $(YAJL_CFLAGS) \ + $(PANGOCAIRO_CFLAGS) + +libi3_LIBS = \ + $(top_builddir)/libi3.a \ + $(XCB_LIBS) \ + $(XCB_UTIL_LIBS) \ + $(XCB_UTIL_XRM_LIBS) \ + $(YAJL_LIBS) \ + $(PANGOCAIRO_LIBS) + +libi3_a_CFLAGS = \ + $(libi3_CFLAGS) + +libi3_a_SOURCES = \ + include/libi3.h \ + libi3/dpi.c \ + libi3/draw_util.c \ + libi3/fake_configure_notify.c \ + libi3/font.c \ + libi3/format_placeholders.c \ + libi3/get_colorpixel.c \ + libi3/get_config_path.c \ + libi3/get_exe_path.c \ + libi3/get_mod_mask.c \ + libi3/get_process_filename.c \ + libi3/get_visualtype.c \ + libi3/ipc_connect.c \ + libi3/ipc_recv_message.c \ + libi3/ipc_send_message.c \ + libi3/is_debug_build.c \ + libi3/mkdirp.c \ + libi3/resolve_tilde.c \ + libi3/root_atom_contents.c \ + libi3/safewrappers.c \ + libi3/string.c \ + libi3/strndup.c \ + libi3/ucs2_conversion.c + +i3_dump_log_i3_dump_log_CFLAGS = \ + $(AM_CFLAGS) \ + $(PTHREAD_CFLAGS) \ + $(libi3_CFLAGS) + +i3_dump_log_i3_dump_log_LDADD = \ + $(PTHREAD_LIBS) \ + $(libi3_LIBS) + +i3_dump_log_i3_dump_log_SOURCES = \ + i3-dump-log/main.c + +i3_input_i3_input_CFLAGS = \ + $(AM_CFLAGS) \ + $(libi3_CFLAGS) + +i3_input_i3_input_LDADD = \ + $(libi3_LIBS) \ + $(XCB_UTIL_KEYSYMS_LIBS) + +i3_input_i3_input_SOURCES = \ + i3-input/i3-input.h \ + i3-input/keysym2ucs.c \ + i3-input/keysym2ucs.h \ + i3-input/main.c + +i3_msg_i3_msg_CFLAGS = \ + $(AM_CFLAGS) \ + $(libi3_CFLAGS) + +i3_msg_i3_msg_LDADD = \ + $(libi3_LIBS) + +i3_msg_i3_msg_SOURCES = \ + i3-msg/main.c + +i3_nagbar_i3_nagbar_CFLAGS = \ + $(AM_CFLAGS) \ + $(libi3_CFLAGS) + +i3_nagbar_i3_nagbar_LDADD = \ + $(libi3_LIBS) \ + $(XCB_UTIL_CURSOR_LIBS) + +i3_nagbar_i3_nagbar_SOURCES = \ + i3-nagbar/atoms.xmacro \ + i3-nagbar/i3-nagbar.h \ + i3-nagbar/main.c + +i3bar_i3bar_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/i3bar/include + +i3bar_i3bar_CFLAGS = \ + $(AM_CFLAGS) \ + $(libi3_CFLAGS) \ + $(XCB_CFLAGS) \ + $(XKBCOMMON_CFLAGS) \ + $(PANGOCAIRO_CFLAGS) \ + $(YAJL_CFLAGS) + +i3bar_i3bar_LDADD = \ + $(libi3_LIBS) \ + $(XCB_LIBS) \ + $(XCB_UTIL_CURSOR_LIBS) \ + $(XKBCOMMON_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(YAJL_LIBS) + +i3bar_i3bar_SOURCES = \ + i3bar/include/child.h \ + i3bar/include/common.h \ + i3bar/include/configuration.h \ + i3bar/include/ipc.h \ + i3bar/include/mode.h \ + i3bar/include/outputs.h \ + i3bar/include/parse_json_header.h \ + i3bar/include/trayclients.h \ + i3bar/include/util.h \ + i3bar/include/workspaces.h \ + i3bar/include/xcb_atoms.def \ + i3bar/include/xcb.h \ + i3bar/src/child.c \ + i3bar/src/config.c \ + i3bar/src/ipc.c \ + i3bar/src/main.c \ + i3bar/src/mode.c \ + i3bar/src/outputs.c \ + i3bar/src/parse_json_header.c \ + i3bar/src/workspaces.c \ + i3bar/src/xcb.c + +i3_config_wizard_i3_config_wizard_CFLAGS = \ + $(AM_CFLAGS) \ + $(libi3_CFLAGS) \ + $(XKBCOMMON_CFLAGS) + +i3_config_wizard_i3_config_wizard_LDADD = \ + $(libi3_LIBS) \ + $(XCB_UTIL_KEYSYMS_LIBS) \ + $(XKBCOMMON_LIBS) + +i3_config_wizard_i3_config_wizard_SOURCES = \ + i3-config-wizard/atoms.xmacro \ + i3-config-wizard/main.c \ + i3-config-wizard/xcb.h + +test_commands_parser_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DTEST_PARSER + +test_commands_parser_CFLAGS = \ + $(AM_CFLAGS) \ + $(i3_CFLAGS) + +test_commands_parser_SOURCES = \ + src/commands_parser.c + +test_commands_parser_LDADD = \ + $(i3_LDADD) + +test_config_parser_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DTEST_PARSER + +test_config_parser_CFLAGS = \ + $(AM_CFLAGS) \ + $(i3_CFLAGS) + +test_config_parser_SOURCES = \ + src/config_parser.c + +test_config_parser_LDADD = \ + $(i3_LDADD) + +command_parser_SOURCES = \ + parser/GENERATED_command_enums.h \ + parser/GENERATED_command_tokens.h \ + parser/GENERATED_command_call.h + +config_parser_SOURCES = \ + parser/GENERATED_config_enums.h \ + parser/GENERATED_config_tokens.h \ + parser/GENERATED_config_call.h + +i3_SOURCES = \ + $(command_parser_SOURCES) \ + $(config_parser_SOURCES) \ + include/all.h \ + include/assignments.h \ + include/atoms_NET_SUPPORTED.xmacro \ + include/atoms_rest.xmacro \ + include/atoms.xmacro \ + include/bindings.h \ + include/click.h \ + include/cmdparse.h \ + include/commands.h \ + include/commands_parser.h \ + include/config_directives.h \ + include/configuration.h \ + include/config_parser.h \ + include/con.h \ + include/data.h \ + include/debug.h \ + include/display_version.h \ + include/ewmh.h \ + include/fake_outputs.h \ + include/floating.h \ + include/handlers.h \ + include/i3.h \ + include/ipc.h \ + include/key_press.h \ + include/load_layout.h \ + include/log.h \ + include/main.h \ + include/manage.h \ + include/match.h \ + include/move.h \ + include/output.h \ + include/queue.h \ + include/randr.h \ + include/regex.h \ + include/render.h \ + include/resize.h \ + include/restore_layout.h \ + include/scratchpad.h \ + include/sd-daemon.h \ + include/shmlog.h \ + include/sighandler.h \ + include/startup.h \ + include/tree.h \ + include/util.h \ + include/window.h \ + include/workspace.h \ + include/xcb.h \ + include/xcursor.h \ + include/x.h \ + include/xinerama.h \ + include/yajl_utils.h \ + src/assignments.c \ + src/bindings.c \ + src/click.c \ + src/commands.c \ + src/commands_parser.c \ + src/con.c \ + src/config.c \ + src/config_directives.c \ + src/config_parser.c \ + src/debug.c \ + src/display_version.c \ + src/ewmh.c \ + src/fake_outputs.c \ + src/floating.c \ + src/handlers.c \ + src/ipc.c \ + src/key_press.c \ + src/load_layout.c \ + src/log.c \ + src/main.c \ + src/manage.c \ + src/match.c \ + src/move.c \ + src/output.c \ + src/randr.c \ + src/regex.c \ + src/render.c \ + src/resize.c \ + src/restore_layout.c \ + src/scratchpad.c \ + src/sd-daemon.c \ + src/sighandler.c \ + src/startup.c \ + src/tree.c \ + src/util.c \ + src/version.c \ + src/window.c \ + src/workspace.c \ + src/x.c \ + src/xcb.c \ + src/xcursor.c \ + src/xinerama.c + +################################################################################ +# parser generation +################################################################################ + +$(command_parser_SOURCES): %.h: i3-command-parser.stamp + +$(config_parser_SOURCES): %.h: i3-config-parser.stamp + +src/i3-commands_parser.$(OBJEXT): i3-command-parser.stamp + +src/i3-config_parser.$(OBJEXT): i3-config-parser.stamp + +i3-command-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/commands.spec + $(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/commands.spec --prefix=command + $(AM_V_at) mv GENERATED_command_* $(top_builddir)/parser + $(AM_V_at) touch $@ + +i3-config-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/config.spec + $(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/config.spec --prefix=config + $(AM_V_at) mv GENERATED_config_* $(top_builddir)/parser + $(AM_V_at) touch $@ + +CLEANFILES = \ + i3-command-parser.stamp \ + i3-config-parser.stamp diff --git a/PACKAGE-MAINTAINER b/PACKAGE-MAINTAINER index 22e10997..953fac53 100644 --- a/PACKAGE-MAINTAINER +++ b/PACKAGE-MAINTAINER @@ -23,14 +23,10 @@ x-terminal-emulator symlink in Debian, please use it in i3-sensible-terminal. On debian, compilation and installing the manpages looks like this: - # Compilation - $(MAKE) - $(MAKE) -C man - - # Installation - $(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install - mkdir -p $(CURDIR)/debian/i3-wm/usr/share/man/man1 - cp man/*.1 $(CURDIR)/debian/i3-wm/usr/share/man/man1 + autoreconf -fi + mkdir -p build && cd build + ../configure + make -j8 install Please make sure that i3-migrate-config-to-v4 and i3-config-wizard are installed with i3. The Perl script is necessary to (automatically) convert v3 @@ -42,7 +38,7 @@ If you have any questions, ideas, hints, problems or whatever, please do not hesitate to contact me. I will help you out. Just drop me an E-Mail (find the address at https://michael.stapelberg.de/Impressum/, scroll down to bottom), contact me using the same address in jabber or ask on our IRC channel: -(#i3 on irc.twice-irc.de). +(#i3 on irc.freenode.net). Thanks again for your efforts, Michael diff --git a/RELEASE-NOTES-4.12 b/RELEASE-NOTES-4.12 deleted file mode 100644 index 57cd9553..00000000 --- a/RELEASE-NOTES-4.12 +++ /dev/null @@ -1,132 +0,0 @@ - - ┌────────────────────────────┐ - │ Release notes for i3 v4.12 │ - └────────────────────────────┘ - -This is i3 v4.12. This version is considered stable. All users of i3 are -strongly encouraged to upgrade. - -If cairo ≥ 1.14.4 is available, i3 and i3bar will use cairo for rendering -(instead of raw X11 drawing primitives). While this is currently optional, -having cairo ≥ 1.14.4 will be a hard requirement in future release. - -This release contains a good number of detail improvements and fixes. - - ┌────────────────────────────┐ - │ Changes in i3 v4.12 │ - └────────────────────────────┘ - - • use https instead of git/http, update contact information, add GPG key - • docs/hacking-howto: fix old cfgparse.y reference to config_parser.c - • docs/ipc: added link to i3ipcpp (C++ library) - • docs/userguide: clarify no_focus documentation - • docs/userguide: add documentation for binding modes - • docs/userguide: fix rendering of __focused__ - • docs/userguide: improve placement of explicit IDs for headings - • docs/userguide: make rendering of key bindings more consistent - • docs/userguide: clarify quoting of “exec” commands - • man/i3-nagbar: fix example invocation - • man/i3: add “floating window” to terminology - • i3-sensible-*: quote variables correctly - • i3-sensible-editor: add neovim - • i3-sensible-terminal: add termit, st - • i3bar: use cairo for all drawing operations - • i3bar: support per-statusblock border and background colors - • i3bar: support different bar background colors depending on whether the bar - is on the focused output or not - • i3bar: multiple tray_output directives on the same bar are now supported - • i3bar: support disabling the modifier by specifying “modifier none” - • use cairo for all drawing operations - • fix a number of memory leaks, thanks to AddressSanitizer - • no_focus is now suppressed for the first window of a workspace - • “workspace next/prev” now looks for numbered workspaces after reaching the - last workspace (it used to incorrectly only look at named workspaces) - • multiple marks can now be set on a single window (but a mark can still only - be present on one window at a time) - • the “unmark” command now supports criteria - • the “con_id” criterion now supports the special value __focused__ - • the “workspace” command now supports the --no-auto-back-and-forth parameter - • the “move window to workspace” command now supports the - --no-auto-back-and-forth parameter - • the “resize grow|shrink width|height” command now works for a nested split - in the same direction - • support _NET_WM_USER_TIME’s special 0 value, indicating that a window - should not be focused - • use 32-bit visual by default if available. This reduces graphical glitches - when using transparency (which is still not officially supported) - • the “move position center” command now supports criteria - • specifying invalid match criteria now results in an error instead of - blindly applying the operation to the currently focused window - • allow mouse bindings to run on the root window - • support matching _NET_WM_WINDOW_TYPE_NOTIFICATION in criteria - • all criteria are now matched, even when con_id or con_mark are given (used - to be a special case) - • allow the “id” criterion to be specified in any base recognized by - strtol(), not only base 10 - • non-true color displays are now supported again (e.g. the Raspberry Pi) - • the “split” command now has a “toggle” option - • the additional color class “decoration_border” was added - • title_format is now stored on containers instead of windows, allowing the - use of title_format on split containers - • On OpenBSD, i3 now uses pledge(2) - • support _NET_WM_DESKTOP (for pager applications like gnome-panel) - • floating workspaces are no longer available (they were not supported for a - while now) - • floating windows now carry the I3_FLOATING_WINDOW atom so that tools like - compositors can be configured to match on floating windows - - ┌────────────────────────────┐ - │ Bugfixes │ - └────────────────────────────┘ - - • i3bar: display short text only on the monitor(s) on which it is necessary - • i3bar: explicitly set cursor using libxcb-cursor if available - • i3bar: fix XEMBED messages - • i3-nagbar: explicitly set cursor using libxcb-cursor if available - • duplicated keybindings are now also detected when one uses bindcode but the - other(s) use(s) bindsym - • keymap fallback for servers without XKB (e.g. TightVNC) has been added - • using pango markup in mode names is now optional, fixing a regression in i3 - v4.11 where modes which contained characters such as “<” would break. - • moving windows to a workspace by specifying a mark now works - • the root output is now used when any RandR request fails (for x2go) - • assignments are now marked as run before executing them, preventing endless - loops/crashes when assignments cause another assignment evaluation - • splitting/floating a dock container no longer crashes i3 - • correctly compare modifier mask when identifying keybindings (fixes - bindings which use --release) - • no longer fail config validation when there is no newline at the end of - the config file - • scrollwheel buttons are now only grabbed when necessary, allowing the use - of “bindsym button*” or scrolling in windows without focusing them (in case - no “bindsym button*” is present) - • parse con_id in base 16 (affected FreeBSD only) - • fix crash when opening a large number of windows - • reject empty swallow definitions to avoid crashes - • don’t remove SubstructureRedirect event mask temporarily (fixes i3bar - stopping after system suspend) - • move urgent flag before killing the parent to avoid a crash - • correctly validate “kill” command to avoid crashing when “kill” is invoked - on workspace containers - • actually accept the documented “workspace” token as an alternative to “→” - in assign statements - • remove _NET_WM_STATE on withdrawn windows to comply with the spec - • the “border” command now uses logical pixels (relevant for hi-dpi displays) - • “tray_output primary” does not properly fall back and hence was removed - from the default config again - • correctly determine focused workspace when moving workspace to output - • revert to default binding mode before reloading the config file - • correctly interpret _MOTIF_WM_HINTS (endianness-dependent) - - ┌────────────────────────────┐ - │ Thanks! │ - └────────────────────────────┘ - -Thanks for testing, bugfixes, discussions and everything I forgot go out to: - - Adaephon, Airblader, Alexis211, bendem, botovq, brianmillar, DavidMikeSimon, - dcoppa, Florian Merkel, fmthoma, frederik, hwangcc23, jolange, Juuso - Lapinlampi, kneitinger, lotheac, nicklan, norrland, pra85, romanblanco, - sur5r, tbu-, tyll, wodny - --- Michael Stapelberg, 2016-03-06 diff --git a/RELEASE-NOTES-4.13 b/RELEASE-NOTES-4.13 new file mode 100644 index 00000000..0e854569 --- /dev/null +++ b/RELEASE-NOTES-4.13 @@ -0,0 +1,114 @@ + + ┌────────────────────────────┐ + │ Release notes for i3 v4.13 │ + └────────────────────────────┘ + +This is i3 v4.13. This version is considered stable. All users of i3 are +strongly encouraged to upgrade. + +For users, there are two changes to be aware of: + +1. The X server DPI is read from the Xft.dpi X resource (if available). + Previously, i3 used to directly look at the X server’s DPI (based on screen + resolution and physical size). Looking at Xft.dpi is more consistent with + other software, more likely to be correct (because it’s user-specified and + not read from possibly broken hardware information) and allows users to + override the value. + +2. It is now possible to set config file variables from X resources using the + “set_from_resource” directive. This allows users to have a single source of + truth for e.g. theming X11 applications (specify “*color0: #121212” and have + it apply to URxvt and your i3 config). + +For packagers, there are three changes that likely require action: + +1. cairo/pango are now required dependencies, as announced in the i3 v4.12 + release notes. + +2. The aforementioned “set_from_resource” feature requires the new dependency + libxcb-util-xrm. + +3. i3 now uses the GNU build system (autotools). Please see + https://github.com/i3/i3/commit/4a52a7e9fb6fb2e1f0256b2e086cfa313f411cd8 for + a lot more details about the rationale and what this means for your package. + Bottomline, things should get simpler for you, though :). + + ┌────────────────────────────┐ + │ Changes in i3 v4.13 │ + └────────────────────────────┘ + + • build: wire up version handling for non-release tarballs (as opposed to git + checkouts) + • build: switch to the GNU build system + • i3bar: disable pango markup for plain-text input + • man/i3-msg: point out default ipc message type + • config: introduce support for specifying variables from X resources + • config: ensure variables match on longest-length, eliminating problems + where one variable was a prefix of another + • config: do not count '\' in comment lines as line continuation + • ipc: introduce a new GET_BINDING_MODES command + • ipc: implement new window::mark event + • ipc: add “output” to IPC events referencing a container + • make fullscreen windows open on the output which is indicated by their + geometry (fixes LibreOffice Impress multi-monitor presentations) + • focus newly managed windows only if they don’t use the globally active + input mode (fixes issues with RubyMine) + • remove title indentation in nested containers (rationale was unclear, + nobody spoke up when we asked about the feature on i3-discuss) + • use the last known timestamp when calling xcb_set_input_focus (might fix + rare race conditions in focus handling) + • introduce the “smart” option for hide_edge_borders, which will hide borders + when there is precisely one window on the workspace + • handle _MOTIF_WM_HINTS changes (_MOTIF_WM_HINTS were previously only + considered when managing a new window) + • don’t change border style if BS_NORMAL is requested in _MOTIF_WM_HINTS + • only add numlock fallback for keybindings where necessary (allows users to + correctly bind keys on the numpad) + • do not match docks in config and command criteria + • get DPI from the Xft.dpi resource instead of directly looking at the screen + resolution/size + • handle _NET_ACTIVE_WINDOW for scratchpad windows (for pagers) + • set _NET_WM_DESKTOP to sticky for scratchpad windows + • add new criteria “tiling” and “floating” + • implement special output name “current” for commands + • handle ResizeRequests for tray clients (fixes VLC tray icon) + + ┌────────────────────────────┐ + │ Bugfixes │ + └────────────────────────────┘ + + • i3bar: fix crash when the I3SOCK environment variable is present + • i3-dmenu-desktop: do not die on failed open + • i3-input: properly position in non-standard cases (fixes an issue where + i3-input would launch off-screen) + • i3-save-tree: rename “mark” to “marks” to reflect our recent change to + allow multiple marks + • mouse bindings: only grab the mouse buttons that need to be grabbed + • no_focus: correctly count the number of windows (makes no_focus work with + tabbed/stacked workspace layouts). + • properly close disabled outputs restored during a restart (this fixes state + handling when RandR changes happen during i3 restarts) + • don’t trigger bindings on window border clicks unless --border was + specified for the binding + • traverse numbered workspaces in correct order + • fix transition from named to numbered workspaces in “workspace next|prev” + • avoid setting urgency hint on content containers and above (fixes crashes) + • don’t trigger unrelated key bindings for --release bindings + • fix colormap handling for containers (fixes taking screenshots using xwd) + • check output crossing on ENTER_NOTIFY to dockarea (fixes pointer jumping) + • fix a use-after-free bug (fixes “floating enable” on single split windows) + + ┌────────────────────────────┐ + │ Thanks! │ + └────────────────────────────┘ + +Thanks for testing, bugfixes, discussions and everything I forgot go out to: + + Benedikt Heine, Cedric Buissart, Chih-Chyuan Hwang, Denton Liu, eplanet, Eric + Engeström, EvilPudding, Ferdinand Bachmann, Hong, Ingo Bürk, Jakob Schnell, + Jakub Wilk, johannes karoff, Johannes Lange, joshrosso, Julien Lequertier, + Kacper Kowalik, Kenneth Lyons, Kyle Kneitinger, madroach, Michael Vetter, + Nathan Schulte, Øsse, Peder Stray, Tony Crisci, Trevor Merrifield, wentasah, + yshui, Zamarin Arthur + +-- Michael Stapelberg, 2016-11-08 diff --git a/common.mk b/common.mk deleted file mode 100644 index 4fe8f2b0..00000000 --- a/common.mk +++ /dev/null @@ -1,218 +0,0 @@ -UNAME=$(shell uname) -DEBUG=1 -ASAN=0 -INSTALL=install -LN=ln -PKG_CONFIG=pkg-config -ifndef PREFIX - PREFIX=/usr -endif -ifndef EXEC_PREFIX - EXEC_PREFIX=$(PREFIX) -endif -ifndef SYSCONFDIR - ifeq ($(PREFIX),/usr) - SYSCONFDIR=/etc - else - SYSCONFDIR=$(PREFIX)/etc - endif -endif - -# In dist tarballs, the version is stored in the I3_VERSION and VERSION files. -I3_VERSION := '$(shell [ -f $(TOPDIR)/I3_VERSION ] && cat $(TOPDIR)/I3_VERSION)' -VERSION := '$(shell [ -f $(TOPDIR)/VERSION ] && cat $(TOPDIR)/VERSION)' -ifeq ('',$(I3_VERSION)) -VERSION := $(shell git describe --tags --abbrev=0) -I3_VERSION := '$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch \"$(shell git describe --tags --always --all | sed s:heads/::)\")' -endif - -MAJOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 1) -MINOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 2) -PATCH_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 3) -ifeq (${PATCH_VERSION},) -PATCH_VERSION := 0 -endif - -## Generic flags - -# Default CFLAGS that users should be able to override -ifeq ($(DEBUG),1) -# Extended debugging flags, macros shall be available in gcc -CFLAGS ?= -pipe -gdwarf-2 -g3 -else -CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition -endif - -ifeq ($(ASAN),1) -CFLAGS += -fsanitize=address -DI3_ASAN_ENABLED -LDFLAGS += -fsanitize=address -endif - -# Default LDFLAGS that users should be able to override -LDFLAGS ?= $(as_needed_LDFLAG) - -# Common CFLAGS for all i3 related binaries -I3_CFLAGS = -std=c99 -I3_CFLAGS += -Wall -# unused-function, unused-label, unused-variable are turned on by -Wall -# We don’t want unused-parameter because of the use of many callbacks -I3_CFLAGS += -Wunused-value -I3_CFLAGS += -Iinclude - -I3_CPPFLAGS = -DI3_VERSION=\"${I3_VERSION}\" -I3_CPPFLAGS += -DMAJOR_VERSION=${MAJOR_VERSION} -I3_CPPFLAGS += -DMINOR_VERSION=${MINOR_VERSION} -I3_CPPFLAGS += -DPATCH_VERSION=${PATCH_VERSION} -I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\" -I3_CPPFLAGS += -DI3__FILE__=__FILE__ - - -## Libraries flags - -ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null 1>/dev/null || echo 1),1) -$(error "pkg-config was not found") -endif - -# An easier way to get CFLAGS and LDFLAGS falling back in case there's -# no pkg-config support for certain libraries. -# -# NOTE that you must not use a blank after comma when calling this: -# $(call ldflags_for_lib name, fallback) # bad -# $(call ldflags_for_lib name,fallback) # good -# Otherwise, the compiler will get -l foo instead of -lfoo -# -# We redirect stderr to /dev/null because pkg-config prints an error if support -# for gnome-config was enabled but gnome-config is not actually installed. -cflags_for_lib = $(shell $(PKG_CONFIG) --silence-errors --cflags $(1) 2>/dev/null) -ldflags_for_lib = $(shell $(PKG_CONFIG) --exists 2>/dev/null $(1) && $(PKG_CONFIG) --libs $(1) 2>/dev/null || echo -l$(2)) - -# XCB common stuff -XCB_CFLAGS := $(call cflags_for_lib, xcb) -XCB_CFLAGS += $(call cflags_for_lib, xcb-event) -XCB_LIBS := $(call ldflags_for_lib, xcb,xcb) -XCB_LIBS += $(call ldflags_for_lib, xcb-event,xcb-event) -ifeq ($(shell $(PKG_CONFIG) --exists xcb-util 2>/dev/null || echo 1),1) -XCB_CFLAGS += $(call cflags_for_lib, xcb-atom) -XCB_CFLAGS += $(call cflags_for_lib, xcb-aux) -XCB_LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom) -XCB_LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux) -XCB_CPPFLAGS+= -DXCB_COMPAT -else -XCB_CFLAGS += $(call cflags_for_lib, xcb-util) -XCB_LIBS += $(call ldflags_for_lib, xcb-util) -endif -XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb) - -# XCB keyboard stuff -XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms) -XCB_KBD_LIBS := $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms) - -# XCB WM stuff -XCB_WM_CFLAGS := $(call cflags_for_lib, xcb-icccm) -XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-xinerama) -XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-randr) -XCB_WM_LIBS := $(call ldflags_for_lib, xcb-icccm,xcb-icccm) -XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama) -XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) - -# XCB cursor -XCB_CURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor) -XCB_CURSOR_LIBS := $(call ldflags_for_lib, xcb-cursor,xcb-cursor) - -XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon) -XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon) -XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11) -XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11) - -# yajl -YAJL_CFLAGS := $(call cflags_for_lib, yajl) -YAJL_LIBS := $(call ldflags_for_lib, yajl,yajl) - -#libev -LIBEV_CFLAGS := $(call cflags_for_lib, libev) -LIBEV_LIBS := $(call ldflags_for_lib, libev,ev) - -# libpcre -PCRE_CFLAGS := $(call cflags_for_lib, libpcre) -ifeq ($(shell $(PKG_CONFIG) --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1) -I3_CPPFLAGS += -DPCRE_HAS_UCP=1 -endif -PCRE_LIBS := $(call ldflags_for_lib, libpcre,pcre) - -# startup-notification -LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0) -LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1) - -# Pango -PANGO_CFLAGS := $(call cflags_for_lib, cairo) -PANGO_CFLAGS += $(call cflags_for_lib, pangocairo) -I3_CPPFLAGS += -DPANGO_SUPPORT=1 -ifeq ($(shell $(PKG_CONFIG) --atleast-version=1.14.4 cairo 2>/dev/null && echo 1),1) -I3_CPPFLAGS += -DCAIRO_SUPPORT=1 -endif -PANGO_LIBS := $(call ldflags_for_lib, cairo) -PANGO_LIBS += $(call ldflags_for_lib, pangocairo) - -# libi3 -LIBS = -L$(TOPDIR) -li3 -lm - -## Platform-specific flags - -# Please test if -Wl,--as-needed works on your platform and send me a patch. -# it is known not to work on Darwin (Mac OS X) -ifneq (,$(filter Linux GNU GNU/%, $(UNAME))) -as_needed_LDFLAG = -Wl,--as-needed -endif - -ifeq ($(UNAME),NetBSD) -# We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv -I3_CFLAGS += -idirafter /usr/pkg/include -I3_LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib -endif - -ifeq ($(UNAME),OpenBSD) -I3_CFLAGS += -I${X11BASE}/include -LIBS += -liconv -I3_LDFLAGS += -L${X11BASE}/lib -endif - -ifeq ($(UNAME),FreeBSD) -LIBS += -liconv -endif - -ifeq ($(UNAME),Darwin) -LIBS += -liconv -else ifneq ($(UNAME),OpenBSD) -# Darwin (Mac OS X) and OpenBSD do not have librt -LIBS += -lrt -endif - -ifeq ($(UNAME),SunOS) -LIBS += -lsocket -liconv -lgen -endif - -ifneq (,$(filter Linux GNU GNU/%, $(UNAME))) -I3_CPPFLAGS += -D_GNU_SOURCE -endif - - -ifeq ($(COVERAGE),1) -I3_CFLAGS += -fprofile-arcs -ftest-coverage -LIBS += -lgcov -endif - -V ?= 0 -ifeq ($(V),0) -# Don’t print command lines which are run -.SILENT: - -# echo-ing vars -V_ASCIIDOC = echo ASCIIDOC $@; -V_POD2HTML = echo POD2HTML $@; -V_POD2MAN = echo POD2MAN $@; -V_A2X = echo A2X $@; -endif - -# Always remake the following targets -.PHONY: install clean dist distclean - diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..1a91b3b1 --- /dev/null +++ b/configure.ac @@ -0,0 +1,174 @@ +# -*- Autoconf -*- +# Run autoreconf -fi to generate a configure script from this file. + +AC_PREREQ([2.69]) +AC_INIT([i3], [4.13], [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]) + +# 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 -d ${srcdir}/.git], + [ + VERSION="$(git describe --tags --abbrev=0)" + I3_VERSION="$(git describe --tags --always) ($(git log --pretty=format:%cd --date=short -n1), branch \\\"$(git 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 + +AC_DEFINE_UNQUOTED(SYSCONFDIR, "`eval echo $sysconfdir`", [Location of system configuration files]) + +# 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 strndup strrchr strspn strstr strtol strtoul], , [AC_MSG_FAILURE([cannot find the $ac_func function, which i3 requires])]) + +# 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]) + +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]) +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]) + +# Checks for programs. +AC_PROG_AWK +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_MAKE_SET +AC_PROG_RANLIB +AC_PROG_LN_S + +AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc]) +AC_PATH_PROG([PATH_XMLTO], [xmlto]) +AC_PATH_PROG([PATH_POD2MAN], [pod2man]) + +AM_CONDITIONAL([BUILD_MANS], [test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x]) +AM_CONDITIONAL([BUILD_DOCS], [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 + +git_dir=`git rev-parse --git-dir 2>/dev/null` +if test -n "$git_dir"; then + srcdir=`dirname "$git_dir"` + exclude_dir=`pwd | sed "s,^$srcdir,,g"` + if ! grep -q "^$exclude_dir" "$git_dir/info/exclude"; then + echo "$exclude_dir" >> "$git_dir/info/exclude" + fi +fi + +echo \ +"-------------------------------------------------------------------------------- +build configured: + +AS_HELP_STRING([i3 version:], [`echo ${I3_VERSION} | sed 's,\\\\,,g'`]) +AS_HELP_STRING([is release version:], [${is_release}]) + +AS_HELP_STRING([build manpages:], [${print_BUILD_MANS}]) +AS_HELP_STRING([build docs:], [${print_BUILD_DOCS}]) +AS_HELP_STRING([enable debug flags:], [${ax_enable_debug}]) +AS_HELP_STRING([code coverage:], [${CODE_COVERAGE_ENABLED}]) +AS_HELP_STRING([enabled sanitizers:], [${ax_enabled_sanitizers}]) + +To compile, run: + + cd `pwd` && make -j8 +--------------------------------------------------------------------------------" diff --git a/debian/changelog b/debian/changelog index bfbc8382..80e2d5ec 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,27 @@ +i3-wm (4.12.1-1) unstable; urgency=medium + + * New upstream release. + * Move to debhelper 9 + * Bump Standards-Version to 3.9.7 (no changes necessary) + * debian/watch: verify signature, use https + + -- Michael Stapelberg Fri, 01 Apr 2016 16:34:35 +0200 + +i3-wm (4.12-2) unstable; urgency=medium + + * Override lintian warning desktop-entry-contains-unknown-key for + DesktopNames, gdm needs that key. + * Remove i3-wm.menu to conform to the tech-ctte decision on #741573. + + -- Michael Stapelberg Fri, 01 Apr 2016 15:51:23 +0200 + i3-wm (4.12-1) unstable; urgency=medium * New upstream release. * Move to debhelper 9 * Bump Standards-Version to 3.9.7 (no changes necessary) * debian/watch: verify signature, use https + * Enable full hardening -- Michael Stapelberg Sun, 06 Mar 2016 14:20:38 +0100 diff --git a/debian/control b/debian/control index 577bb4d4..76a84a5a 100644 --- a/debian/control +++ b/debian/control @@ -3,6 +3,7 @@ Section: x11 Priority: extra Maintainer: Michael Stapelberg Build-Depends: debhelper (>= 9), + dh-autoreconf, libx11-dev, libxcb-util0-dev (>= 0.3.8), libxcb-keysyms1-dev, @@ -10,6 +11,7 @@ Build-Depends: debhelper (>= 9), libxcb-randr0-dev, libxcb-icccm4-dev, libxcb-cursor-dev, + libxcb-xrm-dev, libxcb-xkb-dev, libxkbcommon-dev (>= 0.4.0), libxkbcommon-x11-dev (>= 0.4.0), @@ -19,12 +21,12 @@ Build-Depends: debhelper (>= 9), pkg-config, libev-dev (>= 1:4.04), libyajl-dev (>= 2.0.4), - libpcre3-dev, + libpcre3-dev (>= 1:8.10), libstartup-notification0-dev (>= 0.10), - libcairo2-dev, + libcairo2-dev (>= 1.14.4), libpango1.0-dev, libpod-simple-perl -Standards-Version: 3.9.7 +Standards-Version: 3.9.8 Homepage: http://i3wm.org/ Package: i3 diff --git a/debian/i3-wm.install b/debian/i3-wm.install index a75e2371..bff5028c 100644 --- a/debian/i3-wm.install +++ b/debian/i3-wm.install @@ -1,2 +1,2 @@ -etc -usr +debian/tmp/etc +debian/tmp/usr diff --git a/debian/i3-wm.menu b/debian/i3-wm.menu deleted file mode 100644 index 6c0704fb..00000000 --- a/debian/i3-wm.menu +++ /dev/null @@ -1,5 +0,0 @@ -?package(i3-wm): \ - needs="wm" \ - section="Window Managers" \ - title="i3" \ - command="/usr/bin/i3" diff --git a/debian/rules b/debian/rules index 843655ee..e4da3db8 100755 --- a/debian/rules +++ b/debian/rules @@ -4,14 +4,18 @@ export V:=1 export DEB_BUILD_MAINT_OPTIONS = hardening=+all -override_dh_auto_build: - dh_auto_build -- all docs mans - override_dh_installchangelogs: dh_installchangelogs RELEASE-NOTES-* override_dh_strip: dh_strip --dbg-package=i3-wm-dbg +override_dh_auto_test: + # TODO: enable tests + +override_dh_auto_configure: + # The default is /usr/share/doc/i3 + dh_auto_configure -- --docdir=/usr/share/doc/i3-wm + %: - dh $@ --parallel + dh $@ --parallel --builddirectory=build --with=autoreconf diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d6e670c9..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - $(MAKE) -C .. docs - -clean: - $(MAKE) -C .. clean-docs - -.PHONY: all clean diff --git a/docs/docs.mk b/docs/docs.mk deleted file mode 100644 index 050df97d..00000000 --- a/docs/docs.mk +++ /dev/null @@ -1,46 +0,0 @@ -DISTCLEAN_TARGETS += clean-docs - -# To pass additional parameters for asciidoc -ASCIIDOC = asciidoc -I3POD2HTML = ./docs/i3-pod2html - -ASCIIDOC_NOTOC_TARGETS = \ - docs/debugging.html - -ASCIIDOC_TOC_TARGETS = \ - docs/hacking-howto.html \ - docs/userguide.html \ - docs/ipc.html \ - docs/multi-monitor.html \ - docs/wsbar.html \ - docs/testsuite.html \ - docs/i3bar-protocol.html \ - docs/layout-saving.html - -ASCIIDOC_TARGETS = \ - $(ASCIIDOC_TOC_TARGETS) \ - $(ASCIIDOC_NOTOC_TARGETS) - -ASCIIDOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -n $(ASCIIDOC_FLAGS) -o $@ $< -ASCIIDOC_TOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -a toc -n $(ASCIIDOC_FLAGS) -o $@ $< - -POD2HTML_TARGETS = \ - docs/lib-i3test.html \ - docs/lib-i3test-test.html - -docs/lib-i3test.html: testcases/lib/i3test.pm - $(V_POD2HTML)$(I3POD2HTML) $< $@ - -docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm - $(V_POD2HTML)$(I3POD2HTML) $< $@ - -docs: $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS) - -$(ASCIIDOC_TOC_TARGETS): docs/%.html: docs/% - $(ASCIIDOC_TOC_CALL) - -$(ASCIIDOC_NOTOC_TARGETS): docs/%.html: docs/% - $(ASCIIDOC_CALL) - -clean-docs: - rm -f $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS) diff --git a/docs/hacking-howto b/docs/hacking-howto index 247a1791..74a690e7 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -187,7 +187,7 @@ 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 dependant on anything. +Contains useful functions which are not really dependent on anything. src/window.c:: Handlers to update X11 window properties like +WM_CLASS+, +_NET_WM_NAME+, @@ -593,7 +593,7 @@ optimize this and call +x_push_node+ on the appropriate con directly. itself for the container’s children. This function actually pushes the state, see the next paragraph. 4. If the pointer needs to be warped to a different position (for example when - changing focus to a differnt output), it will be warped now. + changing focus to a different output), it will be warped now. 5. The eventmask is restored for all mapped windows. 6. Window decorations will be rendered by calling +x_deco_recurse+ on the root container, which then recursively calls itself for the children. @@ -850,7 +850,7 @@ empty. Afterwards, +con_focus+ will be called to fix the focus stack and the tree will be flattened. -=== Case 3: Moving to non-existant top/bottom +=== Case 3: Moving to non-existent top/bottom Like in case 1, the reference layout for this case is a single workspace in horizontal orientation with two containers on it. Focus is on the left @@ -877,7 +877,7 @@ container and the container above/below the current one (on the level of Now, +con_focus+ will be called to fix the focus stack and the tree will be flattened. -=== Case 4: Moving to existant top/bottom +=== Case 4: Moving to existent top/bottom The reference layout for this case is a vertical workspace with two containers. The bottom one is a h-split containing two containers (1 and 2). Focus is on diff --git a/docs/i3bar-protocol b/docs/i3bar-protocol index 3ae14453..ebd3b83a 100644 --- a/docs/i3bar-protocol +++ b/docs/i3bar-protocol @@ -6,7 +6,7 @@ August 2012 This document explains the protocol in which i3bar expects its input. It provides support for colors, urgency, shortening and easy manipulation. -== Rationale for chosing JSON +== Rationale for choosing JSON Before describing the protocol, let’s cover why JSON is a building block of this protocol. @@ -111,7 +111,7 @@ cont_signal:: processing. The default value (if none is specified) is SIGCONT. click_events:: - If specified and true i3bar will write a infinite array (same as above) + If specified and true i3bar will write an infinite array (same as above) to your stdin. === Blocks in detail @@ -233,7 +233,7 @@ name:: instance:: Instance of the block, if set x, y:: - X11 root window coordinates where the click occured + X11 root window coordinates where the click occurred button:: X11 button ID (for example 1 to 3 for left/middle/right mouse button) diff --git a/docs/ipc b/docs/ipc index dc4e6947..fda289a0 100644 --- a/docs/ipc +++ b/docs/ipc @@ -80,6 +80,8 @@ GET_BAR_CONFIG (6):: GET_VERSION (7):: Gets the version of i3. The reply will be a JSON-encoded dictionary with the major, minor, patch and human-readable version. +GET_BINDING_MODES (8):: + Gets a list of currently configured binding modes. So, a typical message could look like this: -------------------------------------------------- @@ -137,6 +139,8 @@ BAR_CONFIG (6):: Reply to the GET_BAR_CONFIG message. VERSION (7):: Reply to the GET_VERSION message. +BINDING_MODES (8):: + Reply to the GET_BINDING_MODES message. === COMMAND reply @@ -285,7 +289,7 @@ type (string):: Type of this container. Can be one of "root", "output", "con", "floating_con", "workspace" or "dockarea". border (string):: - Can be either "normal", "none" or "1pixel", dependending on the + Can be either "normal", "none" or "pixel", depending on the container’s border style. current_border_width (integer):: Number of pixels of the border width. @@ -604,6 +608,15 @@ loaded_config_file_name (string):: } ------------------- +=== BINDING_MODES reply + +The reply consists of an array of all currently configured binding modes. + +*Example:* +--------------------- +["default", "resize"] +--------------------- + == Events [[events]] @@ -741,14 +754,15 @@ defines whether pango markup shall be used for displaying this mode. This event consists of a single serialized map containing a property +change (string)+ which indicates the type of the change -* +new+ - the window has become managed by i3 -* +close+ - the window has closed -* +focus+ - the window has received input focus -* +title+ - the window's title has changed -* +fullscreen_mode+ - the window has entered or exited fullscreen mode -* +move+ - the window has changed its position in the tree -* +floating+ - the window has transitioned to or from floating -* +urgent+ - the window has become urgent or lost its urgent status +* +new+ – the window has become managed by i3 +* +close+ – the window has closed +* +focus+ – the window has received input focus +* +title+ – the window's title has changed +* +fullscreen_mode+ – the window has entered or exited fullscreen mode +* +move+ – the window has changed its position in the tree +* +floating+ – the window has transitioned to or from floating +* +urgent+ – the window has become urgent or lost its urgent status +* +mark+ – a mark has been added to or removed from the window Additionally a +container (object)+ field will be present, which consists of the window's parent container. Be aware that for the "new" event, the diff --git a/docs/testsuite b/docs/testsuite index 8fdb9635..71c6a427 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -109,7 +109,7 @@ run the tests on a separate X server instance (using Xephyr). Xephyr will open a window where you can inspect the running test. You can run the tests without an X session with Xvfb, such as with +xvfb-run -./complete-run+. This will also speed up the tests signficantly especially on +./complete-run+. This will also speed up the tests significantly especially on machines without a powerful video card. .Example invocation of complete-run.pl+ @@ -196,7 +196,7 @@ In order to open new windows, change attributes, get events, etc., the testsuite uses X11::XCB, a new (and quite specific to i3 at the moment) Perl module which uses the XCB protocol description to generate Perl bindings to X11. They work in a very similar way to libxcb (which i3 uses) and provide -relatively high-level interfaces (objects such as +X11::XCB::Window+) aswell as +relatively high-level interfaces (objects such as +X11::XCB::Window+) as well as access to the low-level interface, which is very useful when testing a window manager. @@ -450,7 +450,7 @@ cmd 'focus left'; is($x->input_focus, $left->id, 'left window focused'); ---------- -However, the test fails. Sometimes. Apparantly, there is a race condition in +However, the test fails. Sometimes. Apparently, there is a race condition in your test. If you think about it, this is because you are using two different pieces of software: You tell i3 to update focus, i3 confirms that, and then you ask X11 to give you the current focus. There is a certain time i3 needs to @@ -614,7 +614,7 @@ activation, we decreased the total amount of time necessary to run all tests it significantly more attractive to run the test suite more often (or at all) during development. -An alternative approach to using socket activation is polling for the existance +An alternative approach to using socket activation is polling for the existence of the IPC socket and connecting to it. While this might be slightly easier to implement, it wastes CPU time and is considerably uglier than this solution :). After all, +lib/SocketActivation.pm+ contains only 54 SLOC. diff --git a/docs/userguide b/docs/userguide index f3f80c96..acdc0a58 100644 --- a/docs/userguide +++ b/docs/userguide @@ -152,7 +152,7 @@ and move it to the wanted size. You can also use <> to define a mode for resizing via the keyboard. To see an example for this, look at the -https://github.com/i3/i3/blob/next/i3.config.keycodes[default config] provided +https://github.com/i3/i3/blob/next/etc/config.keycodes[default config] provided by i3. === Restarting i3 inplace @@ -181,7 +181,7 @@ can also do that by using the <>. Another way to resize floating windows using the mouse is to right-click on the titlebar and drag. For resizing floating windows with your keyboard, see the resizing binding mode -provided by the i3 https://github.com/i3/i3/blob/next/i3.config.keycodes[default config]. +provided by the i3 https://github.com/i3/i3/blob/next/etc/config.keycodes[default config]. Floating windows are always on top of tiling windows. @@ -610,11 +610,13 @@ new_window pixel 3 You can hide container borders adjacent to the screen edges using +hide_edge_borders+. This is useful if you are using scrollbars, or do not want -to waste even two pixels in displayspace. Default is none. +to waste even two pixels in displayspace. The "smart" setting hides borders on +workspaces with only one window visible, but keeps them on workspaces with +multiple windows visible. Default is none. *Syntax*: ----------------------------------------------- -hide_edge_borders none|vertical|horizontal|both +hide_edge_borders none|vertical|horizontal|both|smart ----------------------------------------------- *Example*: @@ -700,6 +702,38 @@ absolutely no plans to change this. If you need a more dynamic configuration you should create a little script which generates a configuration file and run it before starting i3 (for example in your +~/.xsession+ file). +Also see <> to learn how to create variables based on resources +loaded from the X resource database. + +[[xresources]] +=== X resources + +<> can also be created using a value configured in the X resource +database. This is useful, for example, to avoid configuring color values within +the i3 configuration. Instead, the values can be configured, once, in the X +resource database to achieve an easily maintainable, consistent color theme +across many X applications. + +Defining a resource will load this resource from the resource database and +assign its value to the specified variable. A fallback must be specified in +case the resource cannot be loaded from the database. + +*Syntax*: +---------------------------------------------------- +set_from_resource $ +---------------------------------------------------- + +*Example*: +---------------------------------------------------------------------------- +# The ~/.Xresources should contain a line such as +# *color0: #121212 +# and must be loaded properly, e.g., by using +# xrdb ~/.Xresources +# This value is picked up on by other applications (e.g., the URxvt terminal +# emulator) and can be used in i3 like this: +set_from_resource $black i3wm.color0 #000000 +---------------------------------------------------------------------------- + [[assign_workspace]] === Automatically putting clients on specific workspaces @@ -1115,11 +1149,15 @@ show_marks yes Config files support line continuation, meaning when you end a line in a backslash character (`\`), the line-break will be ignored by the parser. This feature can be used to create more readable configuration files. +Commented lines are not continued. *Examples*: ------------------- bindsym Mod1+f \ fullscreen toggle + +# this line is not continued \ +bindsym Mod1+F fullscreen toggle ------------------- == Configuring i3bar @@ -1372,7 +1410,7 @@ NetworkManager, VLC, Pidgin, etc. can place little icons. You can configure on which output (monitor) the icons should be displayed or you can turn off the functionality entirely. -You can use mutliple +tray_output+ directives in your config to specify a list +You can use multiple +tray_output+ directives in your config to specify a list of outputs on which you want the tray to appear. The first available output in that list as defined by the order of the directives will be used for the tray output. @@ -1641,6 +1679,9 @@ bindsym $mod+x [class="Firefox" window_role="About"] kill # enable floating mode and move container to workspace 4 for_window [class="^evil-app$"] floating enable, move container to workspace 4 + +# move all floating windows to the scratchpad +bindsym $mod+x [floating] move scratchpad ------------------------------------ The criteria which are currently implemented are: @@ -1683,6 +1724,10 @@ con_id:: Compares the i3-internal container ID, which you can get via the IPC interface. Handy for scripting. Use the special value +\_\_focused__+ to match only the currently focused window. +floating:: + Only matches floating windows. This criterion requires no value. +tiling:: + Only matches tiling windows. This criterion requires no value. The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for @@ -2050,6 +2095,7 @@ See <> for how to move a container/workspace to a different RandR output. [[move_to_outputs]] +[[_moving_containers_workspaces_to_randr_outputs]] === Moving containers/workspaces to RandR outputs To move a container to another RandR output (addressed by names like +LVDS1+ or @@ -2057,10 +2103,10 @@ To move a container to another RandR output (addressed by names like +LVDS1+ or +right+, +up+ or +down+), there are two commands: *Syntax*: ----------------------------------------------------- -move container to output left|right|down|up| -move workspace to output left|right|down|up| ----------------------------------------------------- +------------------------------------------------------------ +move container to output left|right|down|up|current| +move workspace to output left|right|down|up|current| +------------------------------------------------------------ *Examples*: -------------------------------------------------------- @@ -2115,7 +2161,7 @@ floating containers. It is recommended to define bindings for resizing in a dedicated binding mode. See <> and the example in the i3 -https://github.com/i3/i3/blob/next/i3.config.keycodes[default config] for more +https://github.com/i3/i3/blob/next/etc/config.keycodes[default config] for more context. *Example*: @@ -2204,7 +2250,7 @@ bindsym $mod+g exec i3-input -p 'goto ' -l 1 -P 'Goto: ' --------------------------------------- Alternatively, if you do not want to mess with +i3-input+, you could create -seperate bindings for a specific set of labels and then only use those labels. +separate bindings for a specific set of labels and then only use those labels. /////////////////////////////////////////////////////////////////// [[pango_markup]] @@ -2534,7 +2580,7 @@ have more than one monitor: track of which window you put where. Thus, you can use vim-like marks to quickly switch between windows. See <>. 4. For information on how to move existing workspaces between monitors, - see <<_moving_containers_workspaces_to_randr_outputs>>. + see <>. == i3 and the rest of your software world diff --git a/i3.config b/etc/config similarity index 100% rename from i3.config rename to etc/config diff --git a/i3.config.keycodes b/etc/config.keycodes similarity index 100% rename from i3.config.keycodes rename to etc/config.keycodes diff --git a/i3-config-wizard/Makefile b/i3-config-wizard/Makefile deleted file mode 100644 index d5ac18c6..00000000 --- a/i3-config-wizard/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3-config-wizard/i3-config-wizard - -install: - $(MAKE) -C .. install-i3-config-wizard - -clean: - $(MAKE) -C .. clean-i3-config-wizard - -.PHONY: all install clean diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk deleted file mode 100644 index 900893f5..00000000 --- a/i3-config-wizard/i3-config-wizard.mk +++ /dev/null @@ -1,28 +0,0 @@ -ALL_TARGETS += i3-config-wizard/i3-config-wizard -INSTALL_TARGETS += install-i3-config-wizard -CLEAN_TARGETS += clean-i3-config-wizard - -i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c) -i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) -i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) - -i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o) - - -i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS) i3-config-parser.stamp - echo "[i3-config-wizard] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< - -i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS) - echo "[i3-config-wizard] Link i3-config-wizard" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_config_wizard_LIBS) - -install-i3-config-wizard: i3-config-wizard/i3-config-wizard - echo "[i3-config-wizard] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -m 0755 i3-config-wizard/i3-config-wizard $(DESTDIR)$(EXEC_PREFIX)/bin/ - -clean-i3-config-wizard: - echo "[i3-config-wizard] Clean" - rm -f $(i3_config_wizard_OBJECTS) $(i3_config_wizard_SOURCES_GENERATED) i3-config-wizard/i3-config-wizard i3-config-wizard/cfgparse.* diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index 284f15fa..2bb43270 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -8,6 +8,8 @@ * keysyms. * */ +#include + #if defined(__FreeBSD__) #include #endif diff --git a/i3-dmenu-desktop b/i3-dmenu-desktop index a0549ead..3b81cb20 100755 --- a/i3-dmenu-desktop +++ b/i3-dmenu-desktop @@ -25,7 +25,11 @@ binmode STDERR, ':utf8'; # reads in a whole file sub slurp { my ($filename) = @_; - open(my $fh, '<', $filename) or die "$!"; + my $fh; + if (!open($fh, '<', $filename)) { + warn "Could not open $filename: $!"; + return undef; + } local $/; my $result; eval { diff --git a/i3-dump-log/Makefile b/i3-dump-log/Makefile deleted file mode 100644 index 2b9e4fe9..00000000 --- a/i3-dump-log/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3-dump-log/i3-dump-log - -install: - $(MAKE) -C .. install-i3-dump-log - -clean: - $(MAKE) -C .. clean-i3-dump-log - -.PHONY: all install clean diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk deleted file mode 100644 index 7e5d4499..00000000 --- a/i3-dump-log/i3-dump-log.mk +++ /dev/null @@ -1,28 +0,0 @@ -ALL_TARGETS += i3-dump-log/i3-dump-log -INSTALL_TARGETS += install-i3-dump-log -CLEAN_TARGETS += clean-i3-dump-log - -i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c) -i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h) -i3_dump_log_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) -i3_dump_log_LIBS = $(XCB_LIBS) - -i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) - - -i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS) - echo "[i3-dump-log] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< - -i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS) - echo "[i3-dump-log] Link i3-dump-log" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_dump_log_LIBS) - -install-i3-dump-log: i3-dump-log/i3-dump-log - echo "[i3-dump-log] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -m 0755 i3-dump-log/i3-dump-log $(DESTDIR)$(EXEC_PREFIX)/bin/ - -clean-i3-dump-log: - echo "[i3-dump-log] Clean" - rm -f $(i3_dump_log_OBJECTS) i3-dump-log/i3-dump-log diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c index 9d4eefc7..ec9e577d 100644 --- a/i3-dump-log/main.c +++ b/i3-dump-log/main.c @@ -7,6 +7,8 @@ * i3-dump-log/main.c: Dumps the i3 SHM log to stdout. * */ +#include + #include #include #include @@ -28,8 +30,10 @@ #include "shmlog.h" #include -static uint32_t offset_next_write, - wrap_count; +#if !defined(__OpenBSD__) +static uint32_t offset_next_write; +#endif +static uint32_t wrap_count; static i3_shmlog_header *header; static char *logbuffer, @@ -57,17 +61,26 @@ static void print_till_end(void) { int main(int argc, char *argv[]) { int o, option_index = 0; - bool verbose = false, - follow = false; + bool verbose = false; +#if !defined(__OpenBSD__) + bool follow = false; +#endif static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"verbose", no_argument, 0, 'V'}, +#if !defined(__OpenBSD__) {"follow", no_argument, 0, 'f'}, +#endif {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0}}; + {0, 0, 0, 0} + }; +#if !defined(__OpenBSD__) char *options_string = "s:vfVh"; +#else + char *options_string = "vVh"; +#endif while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { if (o == 'v') { @@ -75,11 +88,17 @@ int main(int argc, char *argv[]) { return 0; } else if (o == 'V') { verbose = true; +#if !defined(__OpenBSD__) } else if (o == 'f') { follow = true; +#endif } else if (o == 'h') { printf("i3-dump-log " I3_VERSION "\n"); - printf("i3-dump-log [-f] [-s ]\n"); +#if !defined(__OpenBSD__) + printf("i3-dump-log [-fhVv]\n"); +#else + printf("i3-dump-log [-hVv]\n"); +#endif return 0; } } @@ -162,6 +181,7 @@ int main(int argc, char *argv[]) { walk = logbuffer + sizeof(i3_shmlog_header); print_till_end(); +#if !defined(__OpenBSD__) if (follow) { /* Since pthread_cond_wait() expects a mutex, we need to provide one. * To not lock i3 (that’s bad, mhkay?) we just define one outside of @@ -177,6 +197,7 @@ int main(int argc, char *argv[]) { } } } +#endif return 0; } diff --git a/i3-input/Makefile b/i3-input/Makefile deleted file mode 100644 index e1c8eae5..00000000 --- a/i3-input/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3-input/i3-input - -install: - $(MAKE) -C .. install-i3-input - -clean: - $(MAKE) -C .. clean-i3-input - -.PHONY: all install clean diff --git a/i3-input/i3-input.h b/i3-input/i3-input.h index fcf9edc3..d347506f 100644 --- a/i3-input/i3-input.h +++ b/i3-input/i3-input.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #define die(...) errx(EXIT_FAILURE, __VA_ARGS__); diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk deleted file mode 100644 index be3e2833..00000000 --- a/i3-input/i3-input.mk +++ /dev/null @@ -1,28 +0,0 @@ -ALL_TARGETS += i3-input/i3-input -INSTALL_TARGETS += install-i3-input -CLEAN_TARGETS += clean-i3-input - -i3_input_SOURCES := $(wildcard i3-input/*.c) -i3_input_HEADERS := $(wildcard i3-input/*.h) -i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) -i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) - -i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) - - -i3-input/%.o: i3-input/%.c $(i3_input_HEADERS) - echo "[i3-input] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_input_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< - -i3-input/i3-input: libi3.a $(i3_input_OBJECTS) - echo "[i3-input] Link i3-input" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_input_LIBS) - -install-i3-input: i3-input/i3-input - echo "[i3-input] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -m 0755 i3-input/i3-input $(DESTDIR)$(EXEC_PREFIX)/bin/ - -clean-i3-input: - echo "[i3-input] Clean" - rm -f $(i3_input_OBJECTS) i3-input/i3-input diff --git a/i3-input/keysym.map b/i3-input/keysym.map index 6e587bdc..b198dd6d 100644 --- a/i3-input/keysym.map +++ b/i3-input/keysym.map @@ -1,7 +1,7 @@ # This list can be used to convert X11 Keysyms to Unicode 2.1 character. # The list is not checked for correctness by Unicode officials. Use it # at your own risk and the creator is not responsable for any damage that -# occured due to using this list. +# occurred due to using this list. # # The list is created by looking at the Keysym names and the Unicode data # file. Other mapping tables were used as a reference where needed. diff --git a/i3-input/main.c b/i3-input/main.c index 64a089dd..97d574a2 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -8,6 +8,8 @@ * to i3. * */ +#include "libi3.h" + #include #include #include @@ -31,8 +33,6 @@ #include "i3-input.h" -#include "libi3.h" - /* IPC format string. %s will be replaced with what the user entered, then * the command will be sent to i3 */ static char *format; @@ -80,7 +80,7 @@ void debuglog(char *fmt, ...) { } /* - * Restores the X11 input focus to whereever it was before. + * Restores the X11 input focus to wherever it was before. * This is necessary because i3-input’s window has override_redirect=1 * (→ unmanaged by the window manager) and thus i3-input changes focus itself. * This function is called on exit(). @@ -216,10 +216,6 @@ static void finish_input() { free(full); -#if 0 - free(command); - return 1; -#endif exit(0); } @@ -317,10 +313,31 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press static xcb_rectangle_t get_window_position(void) { xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8)}; + xcb_get_property_reply_t *supporting_wm_reply = NULL; xcb_get_input_focus_reply_t *input_focus = NULL; xcb_get_geometry_reply_t *geometry = NULL; + xcb_get_property_reply_t *wm_class = NULL; xcb_translate_coordinates_reply_t *coordinates = NULL; + xcb_atom_t A__NET_SUPPORTING_WM_CHECK; + xcb_intern_atom_cookie_t nswc_cookie = xcb_intern_atom(conn, 0, strlen("_NET_SUPPORTING_WM_CHECK"), "_NET_SUPPORTING_WM_CHECK"); + xcb_intern_atom_reply_t *nswc_reply = xcb_intern_atom_reply(conn, nswc_cookie, NULL); + if (nswc_reply == NULL) { + ELOG("Could not intern atom _NET_SUPPORTING_WM_CHECK\n"); + exit(-1); + } + A__NET_SUPPORTING_WM_CHECK = nswc_reply->atom; + free(nswc_reply); + + supporting_wm_reply = xcb_get_property_reply( + conn, xcb_get_property(conn, false, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 0, 32), NULL); + xcb_window_t *supporting_wm_win = NULL; + if (supporting_wm_reply == NULL || xcb_get_property_value_length(supporting_wm_reply) == 0) { + DLOG("Could not determine EWMH support window.\n"); + } else { + supporting_wm_win = xcb_get_property_value(supporting_wm_reply); + } + /* In rare cases, the window holding the input focus might disappear while we are figuring out its * position. To avoid this, we grab the server in the meantime. */ xcb_grab_server(conn); @@ -331,29 +348,47 @@ static xcb_rectangle_t get_window_position(void) { goto free_resources; } + /* We need to ignore the EWMH support window to which the focus can be set if there's no suitable window to focus. */ + if (supporting_wm_win != NULL && input_focus->focus == *supporting_wm_win) { + DLOG("Input focus is on the EWMH support window, ignoring.\n"); + goto free_resources; + } + geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL); if (geometry == NULL) { DLOG("Failed to received window geometry.\n"); goto free_resources; } - coordinates = xcb_translate_coordinates_reply( - conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL); - if (coordinates == NULL) { - DLOG("Failed to translate coordinates.\n"); - goto free_resources; - } + wm_class = xcb_get_property_reply( + conn, xcb_get_property(conn, false, input_focus->focus, XCB_ATOM_WM_CLASS, XCB_GET_PROPERTY_TYPE_ANY, 0, 32), NULL); - DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y); - result.x += coordinates->dst_x; - result.y += coordinates->dst_y; + /* We need to find out whether the input focus is on an i3 frame window. If it is, we must not translate the coordinates. */ + if (wm_class == NULL || xcb_get_property_value_length(wm_class) == 0 || strcmp(xcb_get_property_value(wm_class), "i3-frame") != 0) { + coordinates = xcb_translate_coordinates_reply( + conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL); + if (coordinates == NULL) { + DLOG("Failed to translate coordinates.\n"); + goto free_resources; + } + + DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y); + result.x += coordinates->dst_x; + result.y += coordinates->dst_y; + } else { + DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", geometry->x, geometry->y); + result.x += geometry->x; + result.y += geometry->y; + } free_resources: xcb_ungrab_server(conn); xcb_flush(conn); + FREE(supporting_wm_reply); FREE(input_focus); FREE(geometry); + FREE(wm_class); FREE(coordinates); return result; } diff --git a/i3-msg/Makefile b/i3-msg/Makefile deleted file mode 100644 index fedb31e7..00000000 --- a/i3-msg/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3-msg/i3-msg - -install: - $(MAKE) -C .. install-i3-msg - -clean: - $(MAKE) -C .. clean-i3-msg - -.PHONY: all install clean diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk deleted file mode 100644 index 277c43e7..00000000 --- a/i3-msg/i3-msg.mk +++ /dev/null @@ -1,28 +0,0 @@ -ALL_TARGETS += i3-msg/i3-msg -INSTALL_TARGETS += install-i3-msg -CLEAN_TARGETS += clean-i3-msg - -i3_msg_SOURCES := $(wildcard i3-msg/*.c) -i3_msg_HEADERS := $(wildcard i3-msg/*.h) -i3_msg_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) -i3_msg_LIBS = $(XCB_LIBS) $(YAJL_LIBS) - -i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) - - -i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS) - echo "[i3-msg] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_msg_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< - -i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS) - echo "[i3-msg] Link i3-msg" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_msg_LIBS) - -install-i3-msg: i3-msg/i3-msg - echo "[i3-msg] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -m 0755 i3-msg/i3-msg $(DESTDIR)$(EXEC_PREFIX)/bin/ - -clean-i3-msg: - echo "[i3-msg] Clean" - rm -f $(i3_msg_OBJECTS) i3-msg/i3-msg diff --git a/i3-msg/main.c b/i3-msg/main.c index 36691cae..02e156a1 100644 --- a/i3-msg/main.c +++ b/i3-msg/main.c @@ -14,6 +14,8 @@ * Additionally, it’s even useful sometimes :-). * */ +#include "libi3.h" + #include #include #include @@ -34,7 +36,6 @@ #include #include -#include "libi3.h" #include static char *socket_path; @@ -161,11 +162,13 @@ int main(int argc, char *argv[]) { message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS; else if (strcasecmp(optarg, "get_bar_config") == 0) message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG; + else if (strcasecmp(optarg, "get_binding_modes") == 0) + message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES; else if (strcasecmp(optarg, "get_version") == 0) message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION; else { printf("Unknown message type\n"); - printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_version\n"); + printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version\n"); exit(EXIT_FAILURE); } } else if (o == 'q') { @@ -203,7 +206,7 @@ int main(int argc, char *argv[]) { } if (!payload) - payload = ""; + payload = sstrdup(""); int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); if (sockfd == -1) @@ -218,6 +221,7 @@ int main(int argc, char *argv[]) { if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1) err(EXIT_FAILURE, "IPC: write()"); + free(payload); if (quiet) return 0; @@ -236,9 +240,10 @@ int main(int argc, char *argv[]) { /* For the reply of commands, have a look if that command was successful. * If not, nicely format the error message. */ if (reply_type == I3_IPC_MESSAGE_TYPE_COMMAND) { - yajl_handle handle; - handle = yajl_alloc(&reply_callbacks, NULL, NULL); + yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL); yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length); + yajl_free(handle); + switch (state) { case yajl_status_ok: break; diff --git a/i3-nagbar/Makefile b/i3-nagbar/Makefile deleted file mode 100644 index 12748e21..00000000 --- a/i3-nagbar/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3-nagbar/i3-nagbar - -install: - $(MAKE) -C .. install-i3-nagbar - -clean: - $(MAKE) -C .. clean-i3-nagbar - -.PHONY: all install clean diff --git a/i3-nagbar/i3-nagbar.h b/i3-nagbar/i3-nagbar.h index fc076d72..c5e94cc6 100644 --- a/i3-nagbar/i3-nagbar.h +++ b/i3-nagbar/i3-nagbar.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #define die(...) errx(EXIT_FAILURE, __VA_ARGS__); diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk deleted file mode 100644 index b10e389e..00000000 --- a/i3-nagbar/i3-nagbar.mk +++ /dev/null @@ -1,28 +0,0 @@ -ALL_TARGETS += i3-nagbar/i3-nagbar -INSTALL_TARGETS += install-i3-nagbar -CLEAN_TARGETS += clean-i3-nagbar - -i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) -i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) -i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(XCB_CURSOR_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS) -i3_nagbar_LIBS = $(XCB_LIBS) $(XCB_CURSOR_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS) - -i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) - - -i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS) - echo "[i3-nagbar] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< - -i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS) - echo "[i3-nagbar] Link i3-nagbar" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_nagbar_LIBS) - -install-i3-nagbar: i3-nagbar/i3-nagbar - echo "[i3-nagbar] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -m 0755 i3-nagbar/i3-nagbar $(DESTDIR)$(EXEC_PREFIX)/bin/ - -clean-i3-nagbar: - echo "[i3-nagbar] Clean" - rm -f $(i3_nagbar_OBJECTS) i3-nagbar/i3-nagbar diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 674fcb7d..2e9e77d4 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -8,6 +8,8 @@ * when the user has an error in their configuration file. * */ +#include "libi3.h" + #include #include #include @@ -30,7 +32,6 @@ #include #include -#include "libi3.h" #include "i3-nagbar.h" /** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a diff --git a/i3-save-tree b/i3-save-tree index b00e1fe1..1e56a045 100755 --- a/i3-save-tree +++ b/i3-save-tree @@ -98,7 +98,7 @@ my %allowed_keys = map { ($_, 1) } qw( name geometry window_properties - mark + marks rect ); diff --git a/i3-sensible-terminal b/i3-sensible-terminal index a1ada248..4ef1c75f 100755 --- a/i3-sensible-terminal +++ b/i3-sensible-terminal @@ -8,7 +8,7 @@ # We welcome patches that add distribution-specific mechanisms to find the # preferred terminal emulator. On Debian, there is the x-terminal-emulator # symlink for example. -for terminal in "$TERMINAL" x-terminal-emulator urxvt rxvt termit terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology st; do +for terminal in "$TERMINAL" x-terminal-emulator urxvt rxvt termit terminator Eterm aterm uxterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology st qterminal; do if command -v "$terminal" > /dev/null 2>&1; then exec "$terminal" "$@" fi diff --git a/i3bar/CHANGELOG b/i3bar/CHANGELOG deleted file mode 100644 index 32d880cd..00000000 --- a/i3bar/CHANGELOG +++ /dev/null @@ -1,30 +0,0 @@ -v0.7 -===== -- Make i3bar compatible with i3-4.0 -- Implement disabling the workspace buttons -- Add Color for focused ws -- Add support for I3_SOCKET_PATH-atom -- Implement different dock-positions -- Hide-on-modifier is now the default behavior -- Change default socketpath to /tmp/i3-ipc.sock -- Use I3SOCK environment-variable -- Bugfix: Stop the reconn-timer before starting it again, else it's running twice -- Bugfix: Don't SIGSTOP child in dockmode -- Bugfix: If hide-on-modifier is set, stop the child after starting -- Bugfix: Recover from closed socket -- Some minor bugfixes - -v0.6 -===== -- Add manpage -- Implement hide-on-modifier -- Custom colors can be set from the commandline -- Use double-buffering -- Bugfix: Correctly render long text -- Bugfix: Don't segfault on SIGCHILD -- Bugfix: Double-fork() to avoid zombies -- Some minor bugfixes - -v0.5 -===== -- Initial release diff --git a/i3bar/LICENSE b/i3bar/LICENSE index 6fe0f421..c78c062d 100644 --- a/i3bar/LICENSE +++ b/i3bar/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2011, Axel Wagner +Copyright © 2010 Axel Wagner All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/i3bar/Makefile b/i3bar/Makefile deleted file mode 100644 index cd23cd81..00000000 --- a/i3bar/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3bar/i3bar - -install: - $(MAKE) -C .. install-i3bar - -clean: - $(MAKE) -C .. clean-i3bar - -.PHONY: all install clean diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk deleted file mode 100644 index 5aed1902..00000000 --- a/i3bar/i3bar.mk +++ /dev/null @@ -1,28 +0,0 @@ -ALL_TARGETS += i3bar/i3bar -INSTALL_TARGETS += install-i3bar -CLEAN_TARGETS += clean-i3bar - -i3bar_SOURCES := $(wildcard i3bar/src/*.c) -i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(XCB_CFLAGS) $(XCB_CURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) -i3bar_LIBS = $(XCB_LIBS) $(XCB_CURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS) - -i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) - - -i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS) - echo "[i3bar] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3bar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $< - -i3bar/i3bar: libi3.a $(i3bar_OBJECTS) - echo "[i3bar] Link i3bar" - $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3bar_LIBS) - -install-i3bar: i3bar/i3bar - echo "[i3bar] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -m 0755 i3bar/i3bar $(DESTDIR)$(EXEC_PREFIX)/bin/ - -clean-i3bar: - echo "[i3bar] Clean" - rm -f $(i3bar_OBJECTS) i3bar/i3bar diff --git a/i3bar/include/child.h b/i3bar/include/child.h index 873a3465..0871c7f4 100644 --- a/i3bar/include/child.h +++ b/i3bar/include/child.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #define STDIN_CHUNK_SIZE 1024 diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 0d46ab6a..0929e408 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -7,6 +7,8 @@ */ #pragma once +#include + #include #include #include @@ -85,6 +87,6 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; #include "mode.h" #include "trayclients.h" #include "xcb.h" -#include "config.h" +#include "configuration.h" #include "libi3.h" #include "parse_json_header.h" diff --git a/i3bar/include/config.h b/i3bar/include/configuration.h similarity index 98% rename from i3bar/include/config.h rename to i3bar/include/configuration.h index 2a059046..c7c1f5e2 100644 --- a/i3bar/include/config.h +++ b/i3bar/include/configuration.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "common.h" typedef enum { diff --git a/i3bar/include/ipc.h b/i3bar/include/ipc.h index 686c0322..5056e5f3 100644 --- a/i3bar/include/ipc.h +++ b/i3bar/include/ipc.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include /* diff --git a/i3bar/include/mode.h b/i3bar/include/mode.h index 828d4906..5c87d904 100644 --- a/i3bar/include/mode.h +++ b/i3bar/include/mode.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include "common.h" diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 63cfca7f..3067581d 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include diff --git a/i3bar/include/parse_json_header.h b/i3bar/include/parse_json_header.h index 41e97de2..d834fe05 100644 --- a/i3bar/include/parse_json_header.h +++ b/i3bar/include/parse_json_header.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include /** diff --git a/i3bar/include/util.h b/i3bar/include/util.h index dfbfd6bf..3af79ed7 100644 --- a/i3bar/include/util.h +++ b/i3bar/include/util.h @@ -7,6 +7,8 @@ */ #pragma once +#include + #include "queue.h" /* Get the maximum/minimum of x and y */ diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h index 5ed84de8..dde8b6e1 100644 --- a/i3bar/include/workspaces.h +++ b/i3bar/include/workspaces.h @@ -9,10 +9,10 @@ */ #pragma once -#include - #include "common.h" +#include + typedef struct i3_ws i3_ws; TAILQ_HEAD(ws_head, i3_ws); diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index 03bfc51e..7783e877 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -9,13 +9,11 @@ */ #pragma once +#include + #include //#include "outputs.h" -#ifdef XCB_COMPAT -#define XCB_ATOM_CARDINAL CARDINAL -#endif - #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 #define SYSTEM_TRAY_REQUEST_DOCK 0 diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 3570dde9..60ab462a 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -7,6 +7,8 @@ * child.c: Getting input for the statusline * */ +#include "common.h" + #include #include #include @@ -25,8 +27,6 @@ #include #include -#include "common.h" - /* Global variables for child_*() */ i3bar_child child; @@ -105,7 +105,9 @@ __attribute__((format(printf, 1, 2))) static void set_statusline_error(const cha char *message; va_list args; va_start(args, format); - (void)vasprintf(&message, format, args); + if (vasprintf(&message, format, args) == -1) { + return; + } struct status_block *err_block = scalloc(1, sizeof(struct status_block)); err_block->full_text = i3string_from_utf8("Error: "); @@ -359,11 +361,13 @@ static void read_flat_input(char *buffer, int length) { I3STRING_FREE(first->full_text); /* Remove the trailing newline and terminate the string at the same * time. */ - if (buffer[length - 1] == '\n' || buffer[length - 1] == '\r') + if (buffer[length - 1] == '\n' || buffer[length - 1] == '\r') { buffer[length - 1] = '\0'; - else + } else { buffer[length] = '\0'; - first->full_text = i3string_from_markup(buffer); + } + + first->full_text = i3string_from_utf8(buffer); } static bool read_json_input(unsigned char *input, int length) { diff --git a/i3bar/src/config.c b/i3bar/src/config.c index 5c23bc78..d065ff4c 100644 --- a/i3bar/src/config.c +++ b/i3bar/src/config.c @@ -7,6 +7,8 @@ * config.c: Parses the configuration (received from i3). * */ +#include "common.h" + #include #include #include @@ -17,8 +19,6 @@ #include -#include "common.h" - static char *cur_key; static bool parsing_bindings; static bool parsing_tray_outputs; diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c index 34898663..4a090ad7 100644 --- a/i3bar/src/ipc.c +++ b/i3bar/src/ipc.c @@ -7,6 +7,8 @@ * ipc.c: Communicating with i3 * */ +#include "common.h" + #include #include #include @@ -21,8 +23,6 @@ #include #endif -#include "common.h" - ev_io *i3_connection; const char *sock_path; diff --git a/i3bar/src/main.c b/i3bar/src/main.c index 3b5d7546..be684fc5 100644 --- a/i3bar/src/main.c +++ b/i3bar/src/main.c @@ -5,6 +5,8 @@ * © 2010 Axel Wagner and contributors (see also: LICENSE) * */ +#include "common.h" + #include #include #include @@ -15,8 +17,6 @@ #include #include -#include "common.h" - /* * Having verboselog(), errorlog() and debuglog() is necessary when using libi3. * @@ -93,6 +93,9 @@ int main(int argc, char **argv) { int opt; int option_index = 0; char *socket_path = getenv("I3SOCK"); + if (socket_path != NULL) { + socket_path = sstrdup(socket_path); + } char *i3_default_sock_path = "/tmp/i3-ipc.sock"; /* Initialize the standard config to use 0 as default */ diff --git a/i3bar/src/mode.c b/i3bar/src/mode.c index d6767786..17e7b97e 100644 --- a/i3bar/src/mode.c +++ b/i3bar/src/mode.c @@ -7,6 +7,8 @@ * mode.c: Handle mode event and show current binding mode in the bar * */ +#include "common.h" + #include #include #include @@ -14,8 +16,6 @@ #include #include -#include "common.h" - /* A datatype to pass through the callbacks to save the state */ struct mode_json_params { char *json; @@ -119,7 +119,7 @@ void parse_mode_json(char *json) { state = yajl_parse(handle, (const unsigned char *)json, strlen(json)); - /* FIXME: Propper error handling for JSON parsing */ + /* FIXME: Proper error handling for JSON parsing */ switch (state) { case yajl_status_ok: break; diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index 841a7565..480c00be 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -7,6 +7,8 @@ * outputs.c: Maintaining the outputs list * */ +#include "common.h" + #include #include #include @@ -15,8 +17,6 @@ #include #include -#include "common.h" - /* A datatype to pass through the callbacks to save the state */ struct outputs_json_params { struct outputs_head *outputs; @@ -271,7 +271,7 @@ void parse_outputs_json(char *json) { state = yajl_parse(handle, (const unsigned char *)json, strlen(json)); - /* FIXME: Propper errorhandling for JSON-parsing */ + /* FIXME: Proper errorhandling for JSON-parsing */ switch (state) { case yajl_status_ok: break; diff --git a/i3bar/src/parse_json_header.c b/i3bar/src/parse_json_header.c index 1cd95106..3d4c2a67 100644 --- a/i3bar/src/parse_json_header.c +++ b/i3bar/src/parse_json_header.c @@ -8,6 +8,8 @@ * protocol version and features. * */ +#include "common.h" + #include #include #include @@ -25,8 +27,6 @@ #include #include -#include "common.h" - static enum { KEY_VERSION, KEY_STOP_SIGNAL, diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index 77b351e8..23324989 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -7,6 +7,8 @@ * workspaces.c: Maintaining the workspace lists * */ +#include "common.h" + #include #include #include @@ -14,8 +16,6 @@ #include #include -#include "common.h" - /* A datatype to pass through the callbacks to save the state */ struct workspaces_json_params { struct ws_head *workspaces; @@ -233,7 +233,7 @@ void parse_workspaces_json(char *json) { state = yajl_parse(handle, (const unsigned char *)json, strlen(json)); - /* FIXME: Propper error handling for JSON parsing */ + /* FIXME: Proper error handling for JSON parsing */ switch (state) { case yajl_status_ok: break; diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 496035c2..2715e447 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -7,16 +7,14 @@ * xcb.c: Communicating with X * */ +#include "common.h" + #include #include #include #include #include -#ifdef XCB_COMPAT -#include "xcb_compat.h" -#endif - #include #include #include @@ -36,7 +34,6 @@ #include #endif -#include "common.h" #include "libi3.h" /** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a @@ -445,7 +442,7 @@ void init_colors(const struct xcb_color_strings_t *new_colors) { /* * Handle a button press event (i.e. a mouse click on one of our bars). - * We determine, whether the click occured on a workspace button or if the scroll- + * We determine, whether the click occurred on a workspace button or if the scroll- * wheel was used and change the workspace appropriately * */ @@ -689,15 +686,17 @@ static void handle_client_message(xcb_client_message_event_t *event) { if (op == SYSTEM_TRAY_REQUEST_DOCK) { xcb_window_t client = event->data.data32[2]; - /* Listen for PropertyNotify events to get the most recent value of - * the XEMBED_MAPPED atom, also listen for UnmapNotify events */ mask = XCB_CW_EVENT_MASK; - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY; - xcb_change_window_attributes(xcb_connection, - client, - mask, - values); + + /* Needed to get the most recent value of XEMBED_MAPPED. */ + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; + /* Needed for UnmapNotify events. */ + values[0] |= XCB_EVENT_MASK_STRUCTURE_NOTIFY; + /* Needed because some tray applications (e.g., VLC) use + * override_redirect which causes no ConfigureRequest to be sent. */ + values[0] |= XCB_EVENT_MASK_RESIZE_REDIRECT; + + xcb_change_window_attributes(xcb_connection, client, mask, values); /* Request the _XEMBED_INFO property. The XEMBED specification * (which is referred by the tray specification) says this *has* to @@ -1008,13 +1007,11 @@ static void handle_property_notify(xcb_property_notify_event_t *event) { } /* - * Handle ConfigureRequests by denying them and sending the client a - * ConfigureNotify with its actual size. + * If a tray client attempts to change its size we deny the request and respond + * by telling it its actual size. * */ -static void handle_configure_request(xcb_configure_request_event_t *event) { - DLOG("ConfigureRequest for window = %08x\n", event->window); - +static void handle_configuration_change(xcb_window_t window) { trayclient *trayclient; i3_output *output; SLIST_FOREACH(output, outputs, slist) { @@ -1027,7 +1024,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { continue; clients++; - if (trayclient->win != event->window) + if (trayclient->win != window) continue; xcb_rectangle_t rect; @@ -1037,7 +1034,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { rect.height = icon_size; DLOG("This is a tray window. x = %d\n", rect.x); - fake_configure_notify(xcb_connection, rect, event->window, 0); + fake_configure_notify(xcb_connection, rect, window, 0); return; } } @@ -1045,6 +1042,16 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { DLOG("WARNING: Could not find corresponding tray window.\n"); } +static void handle_configure_request(xcb_configure_request_event_t *event) { + DLOG("ConfigureRequest for window = %08x\n", event->window); + handle_configuration_change(event->window); +} + +static void handle_resize_request(xcb_resize_request_event_t *event) { + DLOG("ResizeRequest for window = %08x\n", event->window); + handle_configuration_change(event->window); +} + /* * This function is called immediately before the main loop locks. We flush xcb * then (and only then) @@ -1170,6 +1177,9 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { case XCB_CONFIGURE_REQUEST: /* ConfigureRequest, sent by a tray child */ handle_configure_request((xcb_configure_request_event_t *)event); + case XCB_RESIZE_REQUEST: + /* ResizeRequest sent by a tray child using override_redirect. */ + handle_resize_request((xcb_resize_request_event_t *)event); break; } free(event); diff --git a/include/all.h b/include/all.h index 85397d59..c26835b9 100644 --- a/include/all.h +++ b/include/all.h @@ -10,8 +10,9 @@ * compile-time. * */ -#ifndef I3_ALL_H -#define I3_ALL_H +#pragma once + +#include #include #include @@ -38,11 +39,6 @@ #include #include -/* Contains compatibility definitions for old libxcb versions */ -#ifdef XCB_COMPAT -#include "xcb_compat.h" -#endif - #include "data.h" #include "util.h" #include "ipc.h" @@ -56,7 +52,7 @@ #include "click.h" #include "key_press.h" #include "floating.h" -#include "config.h" +#include "configuration.h" #include "handlers.h" #include "randr.h" #include "xinerama.h" @@ -86,5 +82,3 @@ #include "display_version.h" #include "restore_layout.h" #include "main.h" - -#endif diff --git a/include/assignments.h b/include/assignments.h index 22ebafc3..a4dc766b 100644 --- a/include/assignments.h +++ b/include/assignments.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Checks the list of assignments for the given window and runs all matching * ones (unless they have already been run for this specific window). diff --git a/include/bindings.h b/include/bindings.h index 9b14e988..0fcc4df4 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -9,6 +9,8 @@ */ #pragma once +#include + extern pid_t command_error_nagbar_pid; /** @@ -104,10 +106,10 @@ CommandResult *run_binding(Binding *bind, Con *con); bool load_keymap(void); /** - * Returns true if the current config has any binding to a scroll wheel button - * (4 or 5) which is a whole-window binding. - * We need this to figure out whether we should grab all buttons or just 1-3 - * when managing a window. See #2049. - * + * Returns a list of buttons that should be grabbed on a window. + * This list will always contain 1–3, all higher buttons will only be returned + * if there is a whole-window binding for it on some window in the current + * config. + * The list is terminated by a 0. */ -bool bindings_should_grab_scrollwheel_buttons(void); +int *bindings_get_buttons_to_grab(void); diff --git a/include/click.h b/include/click.h index 7ce80d97..0d57abf7 100644 --- a/include/click.h +++ b/include/click.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * The button press X callback. This function determines whether the floating * modifier is pressed and where the user clicked (decoration, border, inside diff --git a/include/cmdparse.h b/include/cmdparse.h index 2ffa49b0..a2cbb17e 100644 --- a/include/cmdparse.h +++ b/include/cmdparse.h @@ -9,4 +9,6 @@ */ #pragma once +#include + char *parse_cmd(const char *new); diff --git a/include/commands.h b/include/commands.h index 9f83cb21..a57b2925 100644 --- a/include/commands.h +++ b/include/commands.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "commands_parser.h" /** The beginning of the prototype for every cmd_ function. */ diff --git a/include/commands_parser.h b/include/commands_parser.h index 1acb7fae..88b3f6d0 100644 --- a/include/commands_parser.h +++ b/include/commands_parser.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include /* diff --git a/include/con.h b/include/con.h index 5b48120b..0c532207 100644 --- a/include/con.h +++ b/include/con.h @@ -11,6 +11,8 @@ */ #pragma once +#include + /** * Create a new container (and attach it to the given parent, if not NULL). * This function only initializes the data structures. @@ -200,6 +202,19 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match); */ int con_num_children(Con *con); +/** + * Returns the number of visible non-floating children of this container. + * For example, if the container contains a hsplit which has two children, + * this will return 2 instead of 1. + */ +int con_num_visible_children(Con *con); + +/** + * Count the number of windows (i.e., leaf containers). + * + */ +int con_num_windows(Con *con); + /** * Attaches the given container to the given parent. This happens when moving * a container or when inserting a new container at a specific place in the @@ -268,6 +283,13 @@ void con_disable_fullscreen(Con *con); void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp, bool ignore_focus); +/** + * Moves the given container to the currently focused container on the + * visible workspace on the given output. + * + */ +void con_move_to_output(Con *con, Output *output); + /** * Moves the given container to the given mark. * diff --git a/include/config_directives.h b/include/config_directives.h index bcbea111..7f1bfe4a 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "config_parser.h" /** diff --git a/include/config_parser.h b/include/config_parser.h index 9c23a11d..ace4041d 100644 --- a/include/config_parser.h +++ b/include/config_parser.h @@ -9,8 +9,11 @@ */ #pragma once +#include + #include +SLIST_HEAD(variables_head, Variable); extern pid_t config_error_nagbar_pid; /* diff --git a/include/config.h b/include/configuration.h similarity index 99% rename from include/config.h rename to include/configuration.h index acdd2c89..b7cdc804 100644 --- a/include/config.h +++ b/include/configuration.h @@ -4,7 +4,7 @@ * i3 - an improved dynamic tiling window manager * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * - * include/config.h: Contains all structs/variables for the configurable + * include/configuration.h: Contains all structs/variables for the configurable * part of i3 as well as functions handling the configuration file (calling * the parser (src/config_parse.c) with the correct path, switching key * bindings mode). @@ -12,10 +12,11 @@ */ #pragma once +#include "libi3.h" + #include #include "queue.h" #include "i3.h" -#include "libi3.h" typedef struct Config Config; typedef struct Barconfig Barconfig; @@ -125,7 +126,7 @@ struct Config { * This is useful if you are reaching scrollbar on the edge of the * screen or do not want to waste a single pixel of displayspace. * By default, this is disabled. */ - adjacent_t hide_edge_borders; + hide_edge_borders_mode_t hide_edge_borders; /** By default, a workspace bar is drawn at the bottom of the screen. * If you want to have a more fancy bar, it is recommended to replace diff --git a/include/data.h b/include/data.h index 3a059e7b..a729b21e 100644 --- a/include/data.h +++ b/include/data.h @@ -9,6 +9,8 @@ */ #pragma once +#include "libi3.h" + #define SN_API_NOT_YET_FROZEN 1 #include @@ -17,7 +19,6 @@ #include #include -#include "libi3.h" #include "queue.h" /* @@ -75,6 +76,12 @@ typedef enum { ADJ_NONE = 0, ADJ_UPPER_SCREEN_EDGE = (1 << 2), ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t; +typedef enum { HEBM_NONE = ADJ_NONE, + HEBM_VERTICAL = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE, + HEBM_HORIZONTAL = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE, + HEBM_BOTH = HEBM_VERTICAL | HEBM_HORIZONTAL, + HEBM_SMART = (1 << 5) } hide_edge_borders_mode_t; + typedef enum { MM_REPLACE, MM_ADD } mark_mode_t; @@ -237,6 +244,17 @@ struct regex { pcre_extra *extra; }; +/** + * Stores a resolved keycode (from a keysym), including the modifier mask. Will + * be passed to xcb_grab_key(). + * + */ +struct Binding_Keycode { + xcb_keycode_t keycode; + i3_event_state_mask_t modifiers; + TAILQ_ENTRY(Binding_Keycode) keycodes; +}; + /****************************************************************************** * Major types *****************************************************************************/ @@ -275,8 +293,6 @@ struct Binding { * title bar (default). */ bool whole_window; - uint32_t number_keycodes; - /** Keycode to bind */ uint32_t keycode; @@ -290,12 +306,10 @@ struct Binding { * if the keyboard mapping changes (using Xmodmap for example) */ char *symbol; - /** Only in use if symbol != NULL. Gets set to the value to which the - * symbol got translated when binding. Useful for unbinding and - * checking which binding was used when a key press event comes in. - * - * This is an array of number_keycodes size. */ - xcb_keycode_t *translated_to; + /** Only in use if symbol != NULL. Contains keycodes which generate the + * specified symbol. Useful for unbinding and checking which binding was + * used when a key press event comes in. */ + TAILQ_HEAD(keycodes_head, Binding_Keycode) keycodes_head; /** Command, like in command mode */ char *command; @@ -461,9 +475,9 @@ struct Match { M_DOCK_BOTTOM = 3 } dock; xcb_window_t id; - enum { M_ANY = 0, - M_TILING, - M_FLOATING } floating; + enum { WM_ANY = 0, + WM_TILING, + WM_FLOATING } window_mode; Con *con_id; /* Where the window looking for a match should be inserted: @@ -686,4 +700,7 @@ struct Con { /* Depth of the container window */ uint16_t depth; + + /* The colormap for this con if a custom one is used. */ + xcb_colormap_t colormap; }; diff --git a/include/debug.h b/include/debug.h index 3875ec6b..ab5f3808 100644 --- a/include/debug.h +++ b/include/debug.h @@ -10,4 +10,6 @@ */ #pragma once +#include + int handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e); diff --git a/include/display_version.h b/include/display_version.h index b1a5a0e0..d8dac30b 100644 --- a/include/display_version.h +++ b/include/display_version.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Connects to i3 to find out the currently running version. Useful since it * might be different from the version compiled into this binary (maybe the diff --git a/include/ewmh.h b/include/ewmh.h index 2a55ab9f..545e3a6b 100644 --- a/include/ewmh.h +++ b/include/ewmh.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Updates _NET_CURRENT_DESKTOP with the current desktop number. * diff --git a/include/fake_outputs.h b/include/fake_outputs.h index 32b07fa9..27a0d41a 100644 --- a/include/fake_outputs.h +++ b/include/fake_outputs.h @@ -10,6 +10,8 @@ */ #pragma once +#include + /** * Creates outputs according to the given specification. * The specification must be in the format wxh+x+y, for example 1024x768+0+0, diff --git a/include/floating.h b/include/floating.h index 466c0e79..babfafc9 100644 --- a/include/floating.h +++ b/include/floating.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "tree.h" /** Callback for dragging */ @@ -76,25 +78,6 @@ void floating_center(Con *con, Rect rect); */ void floating_move_to_pointer(Con *con); -#if 0 -/** - * Removes the floating client from its workspace and attaches it to the new - * workspace. This is centralized here because it may happen if you move it - * via keyboard and if you move it using your mouse. - * - */ -void floating_assign_to_workspace(Client *client, Workspace *new_workspace); - -/** - * Called whenever the user clicks on a border (not the titlebar!) of a - * floating window. Determines on which border the user clicked and launches - * the drag_pointer function with the resize_callback. - * - */ -int floating_border_click(xcb_connection_t *conn, Client *client, - xcb_button_press_event_t *event); - -#endif /** * Called when the user clicked on the titlebar of a floating window. * Calls the drag_pointer function with the drag_window callback @@ -118,32 +101,6 @@ void floating_resize_window(Con *con, const bool proportional, const xcb_button_ */ void floating_check_size(Con *floating_con); -#if 0 -/** - * Changes focus in the given direction for floating clients. - * - * Changing to the left/right means going to the previous/next floating client, - * changing to top/bottom means cycling through the Z-index. - * - */ -void floating_focus_direction(xcb_connection_t *conn, Client *currently_focused, - direction_t direction); - -/** - * Moves the client 10px to the specified direction. - * - */ -void floating_move(xcb_connection_t *conn, Client *currently_focused, - direction_t direction); - -/** - * Hides all floating clients (or show them if they are currently hidden) on - * the specified workspace. - * - */ -void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace); - -#endif /** * This is the return value of a drag operation like drag_pointer. * diff --git a/include/handlers.h b/include/handlers.h index d80a24f8..1d5a3865 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include extern int randr_base; diff --git a/include/i3.h b/include/i3.h index 254c4d23..4d13d448 100644 --- a/include/i3.h +++ b/include/i3.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include diff --git a/include/i3/ipc.h b/include/i3/ipc.h index 8912bf13..98ac35b0 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -51,34 +51,22 @@ typedef struct i3_ipc_header { /** Request the i3 version */ #define I3_IPC_MESSAGE_TYPE_GET_VERSION 7 +/** Request a list of configured binding modes. */ +#define I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES 8 + /* * Messages from i3 to clients * */ - -/** Command reply type */ #define I3_IPC_REPLY_TYPE_COMMAND 0 - -/** Workspaces reply type */ #define I3_IPC_REPLY_TYPE_WORKSPACES 1 - -/** Subscription reply type */ #define I3_IPC_REPLY_TYPE_SUBSCRIBE 2 - -/** Outputs reply type */ #define I3_IPC_REPLY_TYPE_OUTPUTS 3 - -/** Tree reply type */ #define I3_IPC_REPLY_TYPE_TREE 4 - -/** Marks reply type */ #define I3_IPC_REPLY_TYPE_MARKS 5 - -/** Bar config reply type */ #define I3_IPC_REPLY_TYPE_BAR_CONFIG 6 - -/** i3 version reply type */ #define I3_IPC_REPLY_TYPE_VERSION 7 +#define I3_IPC_REPLY_TYPE_BINDING_MODES 8 /* * Events from i3 to clients. Events have the first bit set high. diff --git a/include/ipc.h b/include/ipc.h index e1c18816..5c528a6d 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include #include @@ -16,7 +18,7 @@ #include "data.h" #include "tree.h" -#include "config.h" +#include "configuration.h" #include "i3/ipc.h" diff --git a/include/key_press.h b/include/key_press.h index bbfec83b..8f23854b 100644 --- a/include/key_press.h +++ b/include/key_press.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * There was a key press. We compare this key code with our bindings table and pass * the bound action to parse_command(). diff --git a/include/libi3.h b/include/libi3.h index 4c722671..11ca3127 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include #include #include @@ -17,12 +19,8 @@ #include #include -#if PANGO_SUPPORT #include -#endif -#ifdef CAIRO_SUPPORT #include -#endif #define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) @@ -73,10 +71,8 @@ struct Font { xcb_charinfo_t *table; } xcb; -#if PANGO_SUPPORT /** The pango font description */ PangoFontDescription *pango_desc; -#endif } specific; }; @@ -95,7 +91,7 @@ void errorlog(char *fmt, ...) #if !defined(DLOG) void debuglog(char *fmt, ...) __attribute__((format(printf, 1, 2))); -#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, STRIPPED__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #endif /** @@ -470,6 +466,13 @@ char *get_process_filename(const char *prefix); */ char *get_exe_path(const char *argv0); +/** + * Initialize the DPI setting. + * This will use the 'Xft.dpi' X resource if available and fall back to + * guessing the correct value otherwise. + */ +void init_dpi(void); + /** * Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI * screen) to a corresponding amount of physical pixels on a standard or retina @@ -518,7 +521,6 @@ typedef struct placeholder_t { */ char *format_placeholders(char *format, placeholder_t *placeholders, int num); -#ifdef CAIRO_SUPPORT /* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ #define CAIRO_SURFACE_FLUSH(surface) \ @@ -526,7 +528,6 @@ char *format_placeholders(char *format, placeholder_t *placeholders, int num); cairo_surface_flush(surface); \ cairo_surface_flush(surface); \ } while (0) -#endif /* A wrapper grouping an XCB drawable and both a graphics context * and the corresponding cairo objects representing it. */ @@ -542,14 +543,12 @@ typedef struct surface_t { int width; int height; -#ifdef CAIRO_SUPPORT /* A cairo surface representing the drawable. */ cairo_surface_t *surface; /* The cairo object representing the drawable. In general, * this is what one should use for any drawing operation. */ cairo_t *cr; -#endif } surface_t; /** diff --git a/include/load_layout.h b/include/load_layout.h index f8b7da20..0dd81318 100644 --- a/include/load_layout.h +++ b/include/load_layout.h @@ -10,6 +10,8 @@ */ #pragma once +#include + typedef enum { // We could not determine the content of the JSON file. This typically // means it’s unreadable or contains garbage. diff --git a/include/log.h b/include/log.h index e7fc8acb..5c530231 100644 --- a/include/log.h +++ b/include/log.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include @@ -27,7 +29,7 @@ is, delete the preceding comma */ #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__) #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__) -#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, STRIPPED__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) extern char *errorfilename; extern char *shmlogname; diff --git a/include/main.h b/include/main.h index 1ce53954..1f213cce 100644 --- a/include/main.h +++ b/include/main.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Enable or disable the main X11 event handling function. * This is used by drag_pointer() which has its own, modal event handler, which diff --git a/include/manage.h b/include/manage.h index b0c42381..55b0a85b 100644 --- a/include/manage.h +++ b/include/manage.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "data.h" /** @@ -35,19 +37,3 @@ void restore_geometry(void); void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie, bool needs_to_be_mapped); - -#if 0 -/** - * reparent_window() gets called when a new window was opened and becomes a - * child of the root window, or it gets called by us when we manage the - * already existing windows at startup. - * - * Essentially, this is the point where we take over control. - * - */ -void reparent_window(xcb_connection_t *conn, xcb_window_t child, - xcb_visualid_t visual, xcb_window_t root, uint8_t depth, - int16_t x, int16_t y, uint16_t width, uint16_t height, - uint32_t border_width); - -#endif diff --git a/include/match.h b/include/match.h index 64a4f22b..4ff8c485 100644 --- a/include/match.h +++ b/include/match.h @@ -13,6 +13,8 @@ */ #pragma once +#include + /* * Initializes the Match data structure. This function is necessary because the * members representing boolean values (like dock) need to be initialized with diff --git a/include/move.h b/include/move.h index 7debcf28..64b12b80 100644 --- a/include/move.h +++ b/include/move.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT, * TOK_UP, TOK_DOWN from cmdparse.l) diff --git a/include/output.h b/include/output.h index 8299a19b..b13c9728 100644 --- a/include/output.h +++ b/include/output.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Returns the output container below the given output container. * @@ -22,6 +24,12 @@ Con *output_get_content(Con *output); */ Output *get_output_from_string(Output *current_output, const char *output_str); +/** + * Returns the output for the given con. + * + */ +Output *get_output_for_con(Con *con); + /** * Iterates over all outputs and pushes sticky windows to the currently visible * workspace on that output. diff --git a/include/randr.h b/include/randr.h index 998f0d59..55068316 100644 --- a/include/randr.h +++ b/include/randr.h @@ -11,6 +11,8 @@ */ #pragma once +#include + #include "data.h" #include @@ -60,6 +62,12 @@ void init_ws_for_output(Output *output, Con *content); */ void randr_query_outputs(void); +/** + * Disables the output and moves its content. + * + */ +void randr_disable_output(Output *output); + /** * Returns the first output which is active. * @@ -79,6 +87,13 @@ Output *get_output_by_name(const char *name); */ Output *get_output_containing(unsigned int x, unsigned int y); +/** + * Returns the active output which spans exactly the area specified by + * rect or NULL if there is no output like this. + * + */ +Output *get_output_with_dimensions(Rect rect); + /* * In contained_by_output, we check if any active output contains part of the container. * We do this by checking if the output rect is intersected by the Rect. diff --git a/include/regex.h b/include/regex.h index 2e2f22fd..abda3cd7 100644 --- a/include/regex.h +++ b/include/regex.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Creates a new 'regex' struct containing the given pattern and a PCRE * compiled regular expression. Also, calls pcre_study because this regex will diff --git a/include/render.h b/include/render.h index d32f0310..750b7d31 100644 --- a/include/render.h +++ b/include/render.h @@ -10,6 +10,8 @@ */ #pragma once +#include + /* This is used to keep a state to pass around when rendering a con in render_con(). */ typedef struct render_params { /* A copy of the coordinates of the container which is being rendered. */ diff --git a/include/resize.h b/include/resize.h index 945678d9..7b33de90 100644 --- a/include/resize.h +++ b/include/resize.h @@ -9,6 +9,8 @@ */ #pragma once +#include + bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction); int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event); diff --git a/include/restore_layout.h b/include/restore_layout.h index f952834c..98b257d9 100644 --- a/include/restore_layout.h +++ b/include/restore_layout.h @@ -10,6 +10,8 @@ */ #pragma once +#include + /** * Opens a separate connection to X11 for placeholder windows when restoring * layouts. This is done as a safety measure (users can xkill a placeholder diff --git a/include/scratchpad.h b/include/scratchpad.h index b385550c..241653c0 100644 --- a/include/scratchpad.h +++ b/include/scratchpad.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Moves the specified window to the __i3_scratch workspace, making it floating * and setting the appropriate scratchpad_state. diff --git a/include/shmlog.h b/include/shmlog.h index 231681ca..b90211ef 100644 --- a/include/shmlog.h +++ b/include/shmlog.h @@ -10,8 +10,12 @@ */ #pragma once +#include + #include +#if !defined(__OpenBSD__) #include +#endif /* Default shmlog size if not set by user. */ extern const int default_shmlog_size; @@ -24,7 +28,7 @@ typedef struct i3_shmlog_header { /* Byte offset where the next line will be written to. */ uint32_t offset_next_write; - /* Byte offset where the last wrap occured. */ + /* Byte offset where the last wrap occurred. */ uint32_t offset_last_wrap; /* The size of the logfile in bytes. Since the size is limited to 25 MiB @@ -37,8 +41,10 @@ typedef struct i3_shmlog_header { * and don’t matter — clients use an equality check (==). */ uint32_t wrap_count; +#if !defined(__OpenBSD__) /* pthread condvar which will be broadcasted whenever there is a new * message in the log. i3-dump-log uses this to implement -f (follow, like * tail -f) in an efficient way. */ pthread_cond_t condvar; +#endif } i3_shmlog_header; diff --git a/include/sighandler.h b/include/sighandler.h index 56680bc4..20ede4eb 100644 --- a/include/sighandler.h +++ b/include/sighandler.h @@ -11,6 +11,8 @@ */ #pragma once +#include + /** * Setup signal handlers to safely handle SIGSEGV and SIGFPE * diff --git a/include/startup.h b/include/startup.h index 9729cdc2..5da221e1 100644 --- a/include/startup.h +++ b/include/startup.h @@ -12,6 +12,8 @@ */ #pragma once +#include + #define SN_API_NOT_YET_FROZEN 1 #include diff --git a/include/tree.h b/include/tree.h index c64c173d..62461def 100644 --- a/include/tree.h +++ b/include/tree.h @@ -9,6 +9,8 @@ */ #pragma once +#include + extern Con *croot; /* TODO: i am not sure yet how much access to the focused container should * be permitted to source files */ diff --git a/include/util.h b/include/util.h index 28655178..e5ba3341 100644 --- a/include/util.h +++ b/include/util.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include #include "data.h" @@ -98,14 +100,6 @@ bool update_if_necessary(uint32_t *destination, const uint32_t new_value); */ void exec_i3_utility(char *name, char *argv[]); -/** - * Checks a generic cookie for errors and quits with the given message if - * there was an error. - * - */ -void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, - char *err_message); - /** * Checks if the given path exists by calling stat(). * diff --git a/include/window.h b/include/window.h index d0b97f1d..77e3f48f 100644 --- a/include/window.h +++ b/include/window.h @@ -9,6 +9,8 @@ */ #pragma once +#include + /** * Frees an i3Window and all its members. * diff --git a/include/workspace.h b/include/workspace.h index 0ff5cd30..8d109e9e 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include "data.h" #include "tree.h" #include "randr.h" @@ -50,17 +52,6 @@ void extract_workspace_names_from_bindings(void); */ Con *create_workspace_on_output(Output *output, Con *content); -#if 0 -/** - * Sets the name (or just its number) for the given workspace. This has to - * be called for every workspace as the rendering function - * (render_internal_bar) relies on workspace->name and workspace->name_len - * being ready-to-use. - * - */ -void workspace_set_name(Workspace *ws, const char *name); -#endif - /** * Returns true if the workspace is currently visible. Especially important for * multi-monitor environments, as they can have multiple currenlty active diff --git a/include/x.h b/include/x.h index b6855cb3..3e81bc36 100644 --- a/include/x.h +++ b/include/x.h @@ -10,6 +10,8 @@ */ #pragma once +#include + /** Stores the X11 window ID of the currently focused window */ extern xcb_window_t focused_id; @@ -18,7 +20,7 @@ extern xcb_window_t focused_id; * every container from con_new(). * */ -void x_con_init(Con *con, uint16_t depth); +void x_con_init(Con *con); /** * Moves a child window from Container src to Container dest. diff --git a/include/xcb.h b/include/xcb.h index 86019c5d..94f2945d 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include "data.h" #include "xcursor.h" @@ -171,4 +173,4 @@ void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_a * Grab the specified buttons on a window when managing it. * */ -void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, bool bind_scrollwheel); +void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, int *buttons); diff --git a/include/xcb_compat.h b/include/xcb_compat.h deleted file mode 100644 index 066d834a..00000000 --- a/include/xcb_compat.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * i3 - an improved dynamic tiling window manager - * © 2009 Michael Stapelberg and contributors (see also: LICENSE) - * - * xcb_compat.h: uses #define to create aliases for xcb functions which got - * renamed. Makes the code work with >= 0.3.8 xcb-util and - * older versions. - * - */ -#pragma once - -#define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t -#define xcb_icccm_get_wm_protocols xcb_get_wm_protocols -#define xcb_icccm_get_wm_protocols_unchecked xcb_get_wm_protocols_unchecked -#define xcb_icccm_get_wm_protocols_reply xcb_get_wm_protocols_reply -#define xcb_icccm_get_wm_protocols_reply_wipe xcb_get_wm_protocols_reply_wipe -#define XCB_ICCCM_WM_STATE_NORMAL XCB_WM_STATE_NORMAL -#define XCB_ICCCM_WM_STATE_WITHDRAWN XCB_WM_STATE_WITHDRAWN -#define xcb_icccm_get_wm_size_hints_from_reply xcb_get_wm_size_hints_from_reply -#define xcb_icccm_get_wm_size_hints_reply xcb_get_wm_size_hints_reply -#define xcb_icccm_get_wm_normal_hints xcb_get_wm_normal_hints -#define xcb_icccm_get_wm_normal_hints_reply xcb_get_wm_normal_hints_reply -#define xcb_icccm_get_wm_normal_hints_unchecked xcb_get_wm_normal_hints_unchecked -#define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE XCB_SIZE_HINT_P_MIN_SIZE -#define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE XCB_SIZE_HINT_P_MAX_SIZE -#define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC XCB_SIZE_HINT_P_RESIZE_INC -#define XCB_ICCCM_SIZE_HINT_BASE_SIZE XCB_SIZE_HINT_BASE_SIZE -#define XCB_ICCCM_SIZE_HINT_P_ASPECT XCB_SIZE_HINT_P_ASPECT -#define xcb_icccm_wm_hints_t xcb_wm_hints_t -#define xcb_icccm_get_wm_hints xcb_get_wm_hints -#define xcb_icccm_get_wm_hints_from_reply xcb_get_wm_hints_from_reply -#define xcb_icccm_get_wm_hints_reply xcb_get_wm_hints_reply -#define xcb_icccm_get_wm_hints_unchecked xcb_get_wm_hints_unchecked -#define xcb_icccm_wm_hints_get_urgency xcb_wm_hints_get_urgency -#define xcb_icccm_get_wm_transient_for_from_reply xcb_get_wm_transient_for_from_reply - -#define XCB_ATOM_CARDINAL CARDINAL -#define XCB_ATOM_WINDOW WINDOW -#define XCB_ATOM_WM_TRANSIENT_FOR WM_TRANSIENT_FOR -#define XCB_ATOM_WM_NAME WM_NAME -#define XCB_ATOM_WM_CLASS WM_CLASS -#define XCB_ATOM_WM_HINTS WM_HINTS -#define XCB_ATOM_ATOM ATOM -#define XCB_ATOM_WM_NORMAL_HINTS WM_NORMAL_HINTS -#define XCB_ATOM_STRING STRING diff --git a/include/xcursor.h b/include/xcursor.h index e0ee9813..804e1f84 100644 --- a/include/xcursor.h +++ b/include/xcursor.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include enum xcursor_cursor_t { diff --git a/include/xinerama.h b/include/xinerama.h index 936b9653..f01ee568 100644 --- a/include/xinerama.h +++ b/include/xinerama.h @@ -11,6 +11,8 @@ */ #pragma once +#include + #include "data.h" /** diff --git a/include/yajl_utils.h b/include/yajl_utils.h index 93c63d8f..6ab1ff1c 100644 --- a/include/yajl_utils.h +++ b/include/yajl_utils.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include #include #include diff --git a/libi3/Makefile b/libi3/Makefile deleted file mode 100644 index 2c2f68ad..00000000 --- a/libi3/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - $(MAKE) -C .. libi3.a - -clean: - $(MAKE) -C .. clean-libi3 - -.PHONY: all clean diff --git a/libi3/dpi.c b/libi3/dpi.c index 897e6e40..a832a689 100644 --- a/libi3/dpi.c +++ b/libi3/dpi.c @@ -6,7 +6,63 @@ * */ #include "libi3.h" + #include +#include +#include + +static long dpi; + +static long init_dpi_fallback(void) { + return (double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters; +} + +/* + * Initialize the DPI setting. + * This will use the 'Xft.dpi' X resource if available and fall back to + * guessing the correct value otherwise. + */ +void init_dpi(void) { + xcb_xrm_database_t *database = NULL; + + if (conn == NULL) { + goto init_dpi_end; + } + + database = xcb_xrm_database_from_default(conn); + if (database == NULL) { + ELOG("Failed to open the resource database.\n"); + goto init_dpi_end; + } + + char *resource; + xcb_xrm_resource_get_string(database, "Xft.dpi", NULL, &resource); + if (resource == NULL) { + DLOG("Resource Xft.dpi not specified, skipping.\n"); + goto init_dpi_end; + } + + char *endptr; + dpi = strtol(resource, &endptr, 10); + if (dpi == LONG_MAX || dpi == LONG_MIN || dpi < 0 || *endptr != '\0' || endptr == resource) { + ELOG("Xft.dpi = %s is an invalid number and couldn't be parsed.\n", resource); + dpi = 0; + goto init_dpi_end; + } + + DLOG("Found Xft.dpi = %ld.\n", dpi); + +init_dpi_end: + if (database != NULL) { + xcb_xrm_database_free(database); + } + + if (dpi == 0) { + DLOG("Using fallback for calculating DPI.\n"); + dpi = init_dpi_fallback(); + DLOG("Using dpi = %ld\n", dpi); + } +} /* * Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI @@ -21,8 +77,6 @@ int logical_px(const int logical) { return logical; } - const int dpi = (double)root_screen->height_in_pixels * 25.4 / - (double)root_screen->height_in_millimeters; /* There are many misconfigurations out there, i.e. systems with screens * whose dpi is in fact higher than 96 dpi, but not significantly higher, * so software was never adapted. We could tell people to reconfigure their diff --git a/libi3/draw_util.c b/libi3/draw_util.c index dcd881c2..e471405b 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -6,16 +6,14 @@ * draw.c: Utility for drawing. * */ +#include "libi3.h" + #include #include #include #include #include -#ifdef CAIRO_SUPPORT #include -#endif - -#include "libi3.h" /* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */ xcb_visualtype_t *visual_type; @@ -50,10 +48,8 @@ void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_draw ELOG("Could not create graphical context. Error code: %d. Please report this bug.\n", error->error_code); } -#ifdef CAIRO_SUPPORT surface->surface = cairo_xcb_surface_create(conn, surface->id, surface->visual_type, width, height); surface->cr = cairo_create(surface->surface); -#endif } /* @@ -62,7 +58,6 @@ void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_draw */ void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) { xcb_free_gc(conn, surface->gc); -#ifdef CAIRO_SUPPORT cairo_surface_destroy(surface->surface); cairo_destroy(surface->cr); @@ -71,7 +66,6 @@ void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) { * when setting the border of a window to none and then closing it. */ surface->surface = NULL; surface->cr = NULL; -#endif } /* @@ -81,9 +75,7 @@ void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) { void draw_util_surface_set_size(surface_t *surface, int width, int height) { surface->width = width; surface->height = height; -#ifdef CAIRO_SUPPORT cairo_xcb_surface_set_size(surface->surface, width, height); -#endif } /* @@ -121,13 +113,7 @@ color_t draw_util_hex_to_color(const char *color) { static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color) { RETURN_UNLESS_SURFACE_INITIALIZED(surface); -#ifdef CAIRO_SUPPORT cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha); -#else - uint32_t colorpixel = color.colorpixel; - xcb_change_gc(conn, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, - (uint32_t[]){colorpixel, colorpixel}); -#endif } /** @@ -139,18 +125,14 @@ static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surfac void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { RETURN_UNLESS_SURFACE_INITIALIZED(surface); -#ifdef CAIRO_SUPPORT /* Flush any changes before we draw the text as this might use XCB directly. */ CAIRO_SURFACE_FLUSH(surface->surface); -#endif set_font_colors(surface->gc, fg_color, bg_color); draw_text(text, surface->id, surface->gc, surface->visual_type, x, y, max_width); -#ifdef CAIRO_SUPPORT /* Notify cairo that we (possibly) used another way to draw on the surface. */ cairo_surface_mark_dirty(surface->surface); -#endif } /** @@ -162,7 +144,6 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_ void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h) { RETURN_UNLESS_SURFACE_INITIALIZED(surface); -#ifdef CAIRO_SUPPORT cairo_save(surface->cr); /* Using the SOURCE operator will copy both color and alpha information directly @@ -179,12 +160,6 @@ void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t col CAIRO_SURFACE_FLUSH(surface->surface); cairo_restore(surface->cr); -#else - draw_util_set_source_color(conn, surface, color); - - xcb_rectangle_t rect = {x, y, w, h}; - xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect); -#endif } /** @@ -194,7 +169,6 @@ void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t col void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color) { RETURN_UNLESS_SURFACE_INITIALIZED(surface); -#ifdef CAIRO_SUPPORT cairo_save(surface->cr); /* Using the SOURCE operator will copy both color and alpha information directly @@ -210,12 +184,6 @@ void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t CAIRO_SURFACE_FLUSH(surface->surface); cairo_restore(surface->cr); -#else - draw_util_set_source_color(conn, surface, color); - - xcb_rectangle_t rect = {0, 0, surface->width, surface->height}; - xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect); -#endif } /** @@ -227,7 +195,6 @@ void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *d RETURN_UNLESS_SURFACE_INITIALIZED(src); RETURN_UNLESS_SURFACE_INITIALIZED(dest); -#ifdef CAIRO_SUPPORT cairo_save(dest->cr); /* Using the SOURCE operator will copy both color and alpha information directly @@ -245,8 +212,4 @@ void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *d CAIRO_SURFACE_FLUSH(dest->surface); cairo_restore(dest->cr); -#else - xcb_copy_area(conn, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y, - (int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height); -#endif } diff --git a/libi3/fake_configure_notify.c b/libi3/fake_configure_notify.c index 2c4d4559..145f4512 100644 --- a/libi3/fake_configure_notify.c +++ b/libi3/fake_configure_notify.c @@ -5,14 +5,14 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include #include -#include "libi3.h" - /* * Generates a configure_notify event and sends it to the given window * Applications need this to think they’ve configured themselves correctly. diff --git a/libi3/font.c b/libi3/font.c index 5a2504fa..fa848481 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -13,15 +15,10 @@ #include #include -#if PANGO_SUPPORT #include -#endif - -#include "libi3.h" static const i3Font *savedFont = NULL; -#if PANGO_SUPPORT static xcb_visualtype_t *root_visual_type; static double pango_font_red; static double pango_font_green; @@ -166,7 +163,6 @@ static int predict_text_width_pango(const char *text, size_t text_len, bool pang return width; } -#endif /* * Loads a font for usage, also getting its metrics. If fallback is true, @@ -187,7 +183,6 @@ i3Font load_font(const char *pattern, const bool fallback) { return font; } -#if PANGO_SUPPORT /* Try to load a pango font if specified */ if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) { const char *font_pattern = pattern + strlen("pango:"); @@ -202,7 +197,6 @@ i3Font load_font(const char *pattern, const bool fallback) { return font; } } -#endif /* Send all our requests first */ font.specific.xcb.id = xcb_generate_id(conn); @@ -297,12 +291,10 @@ void free_font(void) { free(savedFont->specific.xcb.info); break; } -#if PANGO_SUPPORT case FONT_TYPE_PANGO: /* Free the font description */ pango_font_description_free(savedFont->specific.pango_desc); break; -#endif default: assert(false); break; @@ -329,14 +321,12 @@ void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background) xcb_change_gc(conn, gc, mask, values); break; } -#if PANGO_SUPPORT case FONT_TYPE_PANGO: /* Save the foreground font */ pango_font_red = foreground.red; pango_font_green = foreground.green; pango_font_blue = foreground.blue; break; -#endif default: assert(false); break; @@ -348,11 +338,7 @@ void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background) * */ bool font_is_pango(void) { -#if PANGO_SUPPORT return savedFont->type == FONT_TYPE_PANGO; -#else - return false; -#endif } static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len); @@ -409,13 +395,11 @@ void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc, draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text), drawable, gc, x, y, max_width); break; -#if PANGO_SUPPORT case FONT_TYPE_PANGO: /* Render the text using Pango */ draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), drawable, visual, x, y, max_width, i3string_is_markup(text)); return; -#endif default: assert(false); } @@ -448,13 +432,11 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, } break; } -#if PANGO_SUPPORT case FONT_TYPE_PANGO: /* Render the text using Pango */ draw_text_pango(text, strlen(text), drawable, root_visual_type, x, y, max_width, false); return; -#endif default: assert(false); } @@ -545,12 +527,10 @@ int predict_text_width(i3String *text) { return 0; case FONT_TYPE_XCB: return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text)); -#if PANGO_SUPPORT case FONT_TYPE_PANGO: /* Calculate extents using Pango */ return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), i3string_is_markup(text)); -#endif default: assert(false); return 0; diff --git a/libi3/format_placeholders.c b/libi3/format_placeholders.c index c9cbbea4..59e94781 100644 --- a/libi3/format_placeholders.c +++ b/libi3/format_placeholders.c @@ -5,12 +5,12 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include -#include "libi3.h" - #ifndef STARTS_WITH #define STARTS_WITH(string, needle) (strncasecmp((string), (needle), strlen((needle))) == 0) #endif diff --git a/libi3/get_colorpixel.c b/libi3/get_colorpixel.c index a0a9e345..9bdd0a71 100644 --- a/libi3/get_colorpixel.c +++ b/libi3/get_colorpixel.c @@ -5,15 +5,15 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include #include "queue.h" -#include "libi3.h" - struct Colorpixel { - char *hex; + char hex[8]; uint32_t pixel; SLIST_ENTRY(Colorpixel) @@ -74,7 +74,10 @@ uint32_t get_colorpixel(const char *hex) { /* Store the result in the cache */ struct Colorpixel *cache_pixel = scalloc(1, sizeof(struct Colorpixel)); - cache_pixel->hex = sstrdup(hex); + + strncpy(cache_pixel->hex, hex, 7); + cache_pixel->hex[7] = '\0'; + cache_pixel->pixel = pixel; SLIST_INSERT_HEAD(&(colorpixels), cache_pixel, colorpixels); diff --git a/libi3/get_config_path.c b/libi3/get_config_path.c index bad75c4d..c47e6dd8 100644 --- a/libi3/get_config_path.c +++ b/libi3/get_config_path.c @@ -6,6 +6,7 @@ * */ #include "libi3.h" + #include #include #include diff --git a/libi3/get_exe_path.c b/libi3/get_exe_path.c index ef8f23bc..b9d35ea1 100644 --- a/libi3/get_exe_path.c +++ b/libi3/get_exe_path.c @@ -1,3 +1,12 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + */ +#include "libi3.h" + #include #include #include @@ -5,8 +14,6 @@ #include #include -#include "libi3.h" - /* * This function returns the absolute path to the executable it is running in. * diff --git a/libi3/get_mod_mask.c b/libi3/get_mod_mask.c index ac71e6b2..98031d4c 100644 --- a/libi3/get_mod_mask.c +++ b/libi3/get_mod_mask.c @@ -5,13 +5,13 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include #include -#include "libi3.h" - /* * All-in-one function which returns the modifier mask (XCB_MOD_MASK_*) for the * given keysymbol, for example for XCB_NUM_LOCK (usually configured to mod2). diff --git a/libi3/get_process_filename.c b/libi3/get_process_filename.c index a3dee9cd..7e2ecbd2 100644 --- a/libi3/get_process_filename.c +++ b/libi3/get_process_filename.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -17,8 +19,6 @@ #include #include -#include "libi3.h" - /* * Returns the name of a temporary file with the specified prefix. * diff --git a/libi3/ipc_connect.c b/libi3/ipc_connect.c index f493b4f1..2e628342 100644 --- a/libi3/ipc_connect.c +++ b/libi3/ipc_connect.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -14,8 +16,6 @@ #include #include -#include "libi3.h" - /* * Connects to the i3 IPC socket and returns the file descriptor for the * socket. die()s if anything goes wrong. diff --git a/libi3/ipc_recv_message.c b/libi3/ipc_recv_message.c index 0ef4fced..16dda90d 100644 --- a/libi3/ipc_recv_message.c +++ b/libi3/ipc_recv_message.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -14,8 +16,6 @@ #include -#include "libi3.h" - /* * Reads a message from the given socket file descriptor and stores its length * (reply_length) as well as a pointer to its contents (reply). diff --git a/libi3/ipc_send_message.c b/libi3/ipc_send_message.c index a9ba3165..c2cc0127 100644 --- a/libi3/ipc_send_message.c +++ b/libi3/ipc_send_message.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -14,8 +16,6 @@ #include -#include "libi3.h" - /* * Formats a message (payload) of the given size and type and sends it to i3 via * the given socket file descriptor. diff --git a/libi3/is_debug_build.c b/libi3/is_debug_build.c index 3173c122..4e583622 100644 --- a/libi3/is_debug_build.c +++ b/libi3/is_debug_build.c @@ -1,3 +1,12 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + */ +#include "libi3.h" + #include #include diff --git a/libi3/libi3.mk b/libi3/libi3.mk deleted file mode 100644 index 965e3e49..00000000 --- a/libi3/libi3.mk +++ /dev/null @@ -1,21 +0,0 @@ -CLEAN_TARGETS += clean-libi3 - -libi3_SOURCES := $(wildcard libi3/*.c) -libi3_HEADERS := $(wildcard libi3/*.h) -libi3_CFLAGS = $(PANGO_CFLAGS) -libi3_LIBS = - -libi3_OBJECTS := $(libi3_SOURCES:.c=.o) - - -libi3/%.o: libi3/%.c $(libi3_HEADERS) - echo "[libi3] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(libi3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $< - -libi3.a: $(libi3_OBJECTS) - echo "[libi3] AR libi3.a" - $(AR) rcs $@ $^ $(libi3_LIBS) - -clean-libi3: - echo "[libi3] Clean" - rm -f $(libi3_OBJECTS) libi3/libi3.a libi3.a diff --git a/libi3/mkdirp.c b/libi3/mkdirp.c index c64b80a3..1fc8c346 100644 --- a/libi3/mkdirp.c +++ b/libi3/mkdirp.c @@ -1,4 +1,12 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + */ #include "libi3.h" + #include #include #include diff --git a/libi3/resolve_tilde.c b/libi3/resolve_tilde.c index 7a4fb79b..51d642db 100644 --- a/libi3/resolve_tilde.c +++ b/libi3/resolve_tilde.c @@ -5,8 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ - #include "libi3.h" + #include #include #include diff --git a/libi3/root_atom_contents.c b/libi3/root_atom_contents.c index 8d26c307..d6394d4b 100644 --- a/libi3/root_atom_contents.c +++ b/libi3/root_atom_contents.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -15,8 +17,6 @@ #include #include -#include "libi3.h" - /* * Try to get the contents of the given atom (for example I3_SOCKET_PATH) from * the X11 root window and return NULL if it doesn’t work. diff --git a/libi3/safewrappers.c b/libi3/safewrappers.c index 0a233a12..94ad4ee6 100644 --- a/libi3/safewrappers.c +++ b/libi3/safewrappers.c @@ -5,6 +5,8 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include @@ -13,8 +15,6 @@ #include #include -#include "libi3.h" - /* * The s* functions (safe) are wrappers around malloc, strdup, …, which exits if one of * the called functions returns NULL, meaning that there is no more memory available diff --git a/libi3/string.c b/libi3/string.c index 328b41c0..edd588da 100644 --- a/libi3/string.c +++ b/libi3/string.c @@ -9,15 +9,12 @@ * others want UTF-8 (Pango). * */ +#include "libi3.h" #include #include -#if PANGO_SUPPORT #include -#endif - -#include "libi3.h" struct _i3String { char *utf8; @@ -192,15 +189,11 @@ void i3string_set_markup(i3String *str, bool pango_markup) { * Escape pango markup characters in the given string. */ i3String *i3string_escape_markup(i3String *str) { -#if PANGO_SUPPORT const char *text = i3string_as_utf8(str); char *escaped = g_markup_escape_text(text, -1); i3String *result = i3string_from_utf8(escaped); free(escaped); return result; -#else - return str; -#endif } /* diff --git a/libi3/strndup.c b/libi3/strndup.c index c4032dc5..e17f843c 100644 --- a/libi3/strndup.c +++ b/libi3/strndup.c @@ -5,11 +5,11 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include -#include "libi3.h" - #if defined(__APPLE__) /* diff --git a/libi3/ucs2_conversion.c b/libi3/ucs2_conversion.c index b583e32c..75cff78a 100644 --- a/libi3/ucs2_conversion.c +++ b/libi3/ucs2_conversion.c @@ -5,14 +5,14 @@ * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * */ +#include "libi3.h" + #include #include #include #include #include -#include "libi3.h" - static iconv_t utf8_conversion_descriptor = (iconv_t)-1; static iconv_t ucs2_conversion_descriptor = (iconv_t)-1; diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4 new file mode 100644 index 00000000..08f2e07e --- /dev/null +++ b/m4/ax_append_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space +# added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AC_DEFUN([AX_APPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + AS_VAR_APPEND(FLAGS,[" $1"]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_APPEND_FLAG diff --git a/m4/ax_cflags_warn_all.m4 b/m4/ax_cflags_warn_all.m4 new file mode 100644 index 00000000..1f077992 --- /dev/null +++ b/m4/ax_cflags_warn_all.m4 @@ -0,0 +1,122 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cflags_warn_all.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] +# AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] +# AX_FCFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] +# +# DESCRIPTION +# +# Try to find a compiler option that enables most reasonable warnings. +# +# For the GNU compiler it will be -Wall (and -ansi -pedantic) The result +# is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default. +# +# Currently this macro knows about the GCC, Solaris, Digital Unix, AIX, +# HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and +# Intel compilers. For a given compiler, the Fortran flags are much more +# experimental than their C equivalents. +# +# - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS +# - $2 add-value-if-not-found : nothing +# - $3 action-if-found : add value to shellvariable +# - $4 action-if-not-found : nothing +# +# NOTE: These macros depend on AX_APPEND_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2010 Rhys Ulerich +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 15 + +AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl +AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], +VAR,[VAR="no, unknown" +ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-warn all % -warn all" dnl Intel + "-pedantic % -Wall" dnl GCC + "-xstrconst % -v" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix + "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + "-ansi -ansiE % -fullwarn" dnl IRIX + "+ESlit % +w1" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done +FLAGS="$ac_save_[]FLAGS" +]) +AS_VAR_POPDEF([FLAGS])dnl +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; + *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +])dnl AX_FLAGS_WARN_ALL +dnl implementation tactics: +dnl the for-argument contains a list of options. The first part of +dnl these does only exist to detect the compiler - usually it is +dnl a global option to enable -ansi or -extrawarnings. All other +dnl compilers will fail about it. That was needed since a lot of +dnl compilers will give false positives for some option-syntax +dnl like -Woption or -Xoption as they think of it is a pass-through +dnl to later compile stages or something. The "%" is used as a +dnl delimiter. A non-option comment can be given after "%%" marks +dnl which will be shown but not added to the respective C/CXXFLAGS. + +AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl +AC_LANG_PUSH([C]) +AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) +AC_LANG_POP([C]) +]) + +AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl +AC_LANG_PUSH([C++]) +AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) +AC_LANG_POP([C++]) +]) + +AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl +AC_LANG_PUSH([Fortran]) +AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) +AC_LANG_POP([Fortran]) +]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 00000000..ca363971 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_enable_debug.m4 b/m4/ax_check_enable_debug.m4 new file mode 100644 index 00000000..f99d75fe --- /dev/null +++ b/m4/ax_check_enable_debug.m4 @@ -0,0 +1,124 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE]) +# +# DESCRIPTION +# +# Check for the presence of an --enable-debug option to configure, with +# the specified default value used when the option is not present. Return +# the value in the variable $ax_enable_debug. +# +# Specifying 'yes' adds '-g -O0' to the compilation flags for all +# languages. Specifying 'info' adds '-g' to the compilation flags. +# Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to +# the linking flags. Otherwise, nothing is added. +# +# Define the variables listed in the second argument if debug is enabled, +# defaulting to no variables. Defines the variables listed in the third +# argument if debug is disabled, defaulting to NDEBUG. All lists of +# variables should be space-separated. +# +# If debug is not enabled, ensure AC_PROG_* will not add debugging flags. +# Should be invoked prior to any AC_PROG_* compiler checks. +# +# IS-RELEASE can be used to change the default to 'no' when making a +# release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it +# uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE +# macro, there is no need to pass this parameter. +# +# AX_IS_RELEASE([git-directory]) +# AX_CHECK_ENABLE_DEBUG() +# +# LICENSE +# +# Copyright (c) 2011 Rhys Ulerich +# Copyright (c) 2014, 2015 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +#serial 5 + +AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[ + AC_BEFORE([$0],[AC_PROG_CC])dnl + AC_BEFORE([$0],[AC_PROG_CXX])dnl + AC_BEFORE([$0],[AC_PROG_F77])dnl + AC_BEFORE([$0],[AC_PROG_FC])dnl + + AC_MSG_CHECKING(whether to enable debugging) + + ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1]))) + ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],, + [$ax_is_release], + [$4]))) + + # If this is a release, override the default. + AS_IF([test "$ax_enable_debug_is_release" = "yes"], + [ax_enable_debug_default="no"]) + + m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))]) + m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))]) + + AC_ARG_ENABLE(debug, + [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])], + [],enable_debug=$ax_enable_debug_default) + + # empty mean debug yes + AS_IF([test "x$enable_debug" = "x"], + [enable_debug="yes"]) + + # case of debug + AS_CASE([$enable_debug], + [yes],[ + AC_MSG_RESULT(yes) + CFLAGS="${CFLAGS} -g -O0" + CXXFLAGS="${CXXFLAGS} -g -O0" + FFLAGS="${FFLAGS} -g -O0" + FCFLAGS="${FCFLAGS} -g -O0" + OBJCFLAGS="${OBJCFLAGS} -g -O0" + ], + [info],[ + AC_MSG_RESULT(info) + CFLAGS="${CFLAGS} -g" + CXXFLAGS="${CXXFLAGS} -g" + FFLAGS="${FFLAGS} -g" + FCFLAGS="${FCFLAGS} -g" + OBJCFLAGS="${OBJCFLAGS} -g" + ], + [profile],[ + AC_MSG_RESULT(profile) + CFLAGS="${CFLAGS} -g -pg" + CXXFLAGS="${CXXFLAGS} -g -pg" + FFLAGS="${FFLAGS} -g -pg" + FCFLAGS="${FCFLAGS} -g -pg" + OBJCFLAGS="${OBJCFLAGS} -g -pg" + LDFLAGS="${LDFLAGS} -pg" + ], + [ + AC_MSG_RESULT(no) + dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags + dnl by setting any unset environment flag variables + AS_IF([test "x${CFLAGS+set}" != "xset"], + [CFLAGS=""]) + AS_IF([test "x${CXXFLAGS+set}" != "xset"], + [CXXFLAGS=""]) + AS_IF([test "x${FFLAGS+set}" != "xset"], + [FFLAGS=""]) + AS_IF([test "x${FCFLAGS+set}" != "xset"], + [FCFLAGS=""]) + AS_IF([test "x${OBJCFLAGS+set}" != "xset"], + [OBJCFLAGS=""]) + ]) + + dnl Define various variables if debugging is disabled. + dnl assert.h is a NOP if NDEBUG is defined, so define it by default. + AS_IF([test "x$enable_debug" = "xyes"], + [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])], + [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])]) + ax_enable_debug=$enable_debug +]) diff --git a/m4/ax_check_gnu_make.m4 b/m4/ax_check_gnu_make.m4 new file mode 100644 index 00000000..6762e9ed --- /dev/null +++ b/m4/ax_check_gnu_make.m4 @@ -0,0 +1,84 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_GNU_MAKE() +# +# DESCRIPTION +# +# This macro searches for a GNU version of make. If a match is found: +# +# * The makefile variable `ifGNUmake' is set to the empty string, otherwise +# it is set to "#". This is useful for including a special features in a +# Makefile, which cannot be handled by other versions of make. +# * The variable `_cv_gnu_make_command` is set to the command to invoke +# GNU make if it exists, the empty string otherwise. +# * The variable `ax_cv_gnu_make_command` is set to the command to invoke +# GNU make by copying `_cv_gnu_make_command`, otherwise it is unset. +# * If GNU Make is found, its version is extracted from the output of +# `make --version` as the last field of a record of space-separated +# columns and saved into the variable `ax_check_gnu_make_version`. +# +# Here is an example of its use: +# +# Makefile.in might contain: +# +# # A failsafe way of putting a dependency rule into a makefile +# $(DEPEND): +# $(CC) -MM $(srcdir)/*.c > $(DEPEND) +# +# @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) +# @ifGNUmake@ include $(DEPEND) +# @ifGNUmake@ endif +# +# Then configure.in would normally contain: +# +# AX_CHECK_GNU_MAKE() +# AC_OUTPUT(Makefile) +# +# Then perhaps to cause gnu make to override any other make, we could do +# something like this (note that GNU make always looks for GNUmakefile +# first): +# +# if ! test x$_cv_gnu_make_command = x ; then +# mv Makefile GNUmakefile +# echo .DEFAULT: > Makefile ; +# echo \ $_cv_gnu_make_command \$@ >> Makefile; +# fi +# +# Then, if any (well almost any) other make is called, and GNU make also +# exists, then the other make wraps the GNU make. +# +# LICENSE +# +# Copyright (c) 2008 John Darrington +# Copyright (c) 2015 Enrico M. Crisostomo +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_CHECK_GNU_MAKE],dnl + [AC_PROG_AWK + AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl + _cv_gnu_make_command="" ; +dnl Search all the common names for GNU make + for a in "$MAKE" make gmake gnumake ; do + if test -z "$a" ; then continue ; fi ; + if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then + _cv_gnu_make_command=$a ; + AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make") + ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }') + break ; + fi + done ;]) +dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])]) + AC_SUBST([ifGNUmake]) +]) diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 new file mode 100644 index 00000000..eb01a6ce --- /dev/null +++ b/m4/ax_check_link_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4 new file mode 100644 index 00000000..6c985ebc --- /dev/null +++ b/m4/ax_code_coverage.m4 @@ -0,0 +1,273 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CODE_COVERAGE() +# +# DESCRIPTION +# +# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, +# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LDFLAGS which should be +# included in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LDFLAGS variables of +# every build target (program or library) which should be built with code +# coverage support. Also defines CODE_COVERAGE_RULES which should be +# substituted in your Makefile; and $enable_code_coverage which can be +# used in subsequent configure output. CODE_COVERAGE_ENABLED is defined +# and substituted, and corresponds to the value of the +# --enable-code-coverage option, which defaults to being disabled. +# +# Test also for gcov program and create GCOV variable that could be +# substituted. +# +# Note that all optimisation flags in CFLAGS must be disabled when code +# coverage is enabled. +# +# Usage example: +# +# configure.ac: +# +# AX_CODE_COVERAGE +# +# Makefile.am: +# +# @CODE_COVERAGE_RULES@ +# my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ... +# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... +# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... +# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... +# +# This results in a "check-code-coverage" rule being added to any +# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module +# has been configured with --enable-code-coverage). Running `make +# check-code-coverage` in that directory will run the module's test suite +# (`make check`) and build a code coverage report detailing the code which +# was touched, then print the URI for the report. +# +# This code was derived from Makefile.decl in GLib, originally licenced +# under LGPLv2.1+. +# +# LICENSE +# +# Copyright (c) 2012, 2016 Philip Withnall +# Copyright (c) 2012 Xan Lopez +# Copyright (c) 2012 Christian Persch +# Copyright (c) 2012 Paolo Borelli +# Copyright (c) 2012 Dan Winship +# Copyright (c) 2015 Bastien ROUCARIES +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +#serial 15 + +AC_DEFUN([AX_CODE_COVERAGE],[ + dnl Check for --enable-code-coverage + AC_REQUIRE([AC_PROG_SED]) + + # allow to override gcov location + AC_ARG_WITH([gcov], + [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) + + AC_MSG_CHECKING([whether to build with code coverage support]) + AC_ARG_ENABLE([code-coverage], + AS_HELP_STRING([--enable-code-coverage], + [Whether to enable code coverage support]),, + enable_code_coverage=no) + + AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes]) + AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) + AC_MSG_RESULT($enable_code_coverage) + + AS_IF([ test "$enable_code_coverage" = "yes" ], [ + # check for gcov + AC_CHECK_TOOL([GCOV], + [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], + [:]) + AS_IF([test "X$GCOV" = "X:"], + [AC_MSG_ERROR([gcov is needed to do coverage])]) + AC_SUBST([GCOV]) + + dnl Check if gcc is being used + AS_IF([ test "$GCC" = "no" ], [ + AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) + ]) + + # List of supported lcov versions. + lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11 1.12" + + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) + + AS_IF([ test "$LCOV" ], [ + AC_CACHE_CHECK([for lcov version], ax_cv_lcov_version, [ + ax_cv_lcov_version=invalid + lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'` + for lcov_check_version in $lcov_version_list; do + if test "$lcov_version" = "$lcov_check_version"; then + ax_cv_lcov_version="$lcov_check_version (ok)" + fi + done + ]) + ], [ + lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list" + AC_MSG_ERROR([$lcov_msg]) + ]) + + case $ax_cv_lcov_version in + ""|invalid[)] + lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)." + AC_MSG_ERROR([$lcov_msg]) + LCOV="exit 0;" + ;; + esac + + AS_IF([ test -z "$GENHTML" ], [ + AC_MSG_ERROR([Could not find genhtml from the lcov package]) + ]) + + dnl Build the code coverage flags + CODE_COVERAGE_CPPFLAGS="-DNDEBUG" + CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_LDFLAGS="-lgcov" + + AC_SUBST([CODE_COVERAGE_CPPFLAGS]) + AC_SUBST([CODE_COVERAGE_CFLAGS]) + AC_SUBST([CODE_COVERAGE_CXXFLAGS]) + AC_SUBST([CODE_COVERAGE_LDFLAGS]) + ]) + +[CODE_COVERAGE_RULES=' +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: $(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the $(PACKAGE_NAME) and +# $(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. + +# Optional variables +CODE_COVERAGE_DIRECTORY ?= $(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ +$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS) +CODE_COVERAGE_IGNORE_PATTERN ?= + +code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) +code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\ + $(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) +code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\ + $(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) +code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY); +code_coverage_quiet = $(code_coverage_quiet_$(V)) +code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: +ifeq ($(CODE_COVERAGE_ENABLED),yes) + -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture +else + @echo "Need to reconfigure with --enable-code-coverage" +endif + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook +ifeq ($(CODE_COVERAGE_ENABLED),yes) + $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) + $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp + $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) + @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" +else + @echo "Need to reconfigure with --enable-code-coverage" +endif + +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +ifeq ($(CODE_COVERAGE_ENABLED),yes) +clean: code-coverage-clean +code-coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY) + -find . -name "*.gcda" -o -name "*.gcov" -delete +endif + +GITIGNOREFILES ?= +GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage + +.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean +'] + + AC_SUBST([CODE_COVERAGE_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])]) +]) diff --git a/m4/ax_configure_args.m4 b/m4/ax_configure_args.m4 new file mode 100644 index 00000000..0726b1bc --- /dev/null +++ b/m4/ax_configure_args.m4 @@ -0,0 +1,70 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_configure_args.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CONFIGURE_ARGS +# +# DESCRIPTION +# +# Helper macro for AX_ENABLE_BUILDDIR. +# +# The traditional way of starting a subdir-configure is running the script +# with ${1+"$@"} but since autoconf 2.60 this is broken. Instead we have +# to rely on eval'ing $ac_configure_args however some old autoconf +# versions do not provide that. To ensure maximum portability of autoconf +# extension macros this helper can be AC_REQUIRE'd so that +# $ac_configure_args will alsways be present. +# +# Sadly, the traditional "exec $SHELL" of the enable_builddir macros is +# spoiled now and must be replaced by "eval + exit $?". +# +# Example: +# +# AC_DEFUN([AX_ENABLE_SUBDIR],[dnl +# AC_REQUIRE([AX_CONFIGURE_ARGS])dnl +# eval $SHELL $ac_configure_args || exit $? +# ...]) +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 9 + +AC_DEFUN([AX_CONFIGURE_ARGS],[ + # [$]@ is unsable in 2.60+ but earlier autoconf had no ac_configure_args + if test "${ac_configure_args+set}" != "set" ; then + ac_configure_args= + for ac_arg in ${1+"[$]@"}; do + ac_configure_args="$ac_configure_args '$ac_arg'" + done + fi +]) diff --git a/m4/ax_enable_builddir.m4 b/m4/ax_enable_builddir.m4 new file mode 100644 index 00000000..5f4ba327 --- /dev/null +++ b/m4/ax_enable_builddir.m4 @@ -0,0 +1,302 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_enable_builddir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_ENABLE_BUILDDIR [(dirstring-or-command [,Makefile.mk [,-all]])] +# +# DESCRIPTION +# +# If the current configure was run within the srcdir then we move all +# configure-files into a subdir and let the configure steps continue +# there. We provide an option --disable-builddir to suppress the move into +# a separate builddir. +# +# Defaults: +# +# $1 = $host (overridden with $HOST) +# $2 = Makefile.mk +# $3 = -all +# +# This macro must be called before AM_INIT_AUTOMAKE. It creates a default +# toplevel srcdir Makefile from the information found in the created +# toplevel builddir Makefile. It just copies the variables and +# rule-targets, each extended with a default rule-execution that recurses +# into the build directory of the current "HOST". You can override the +# auto-dection through `config.guess` and build-time of course, as in +# +# make HOST=i386-mingw-cross +# +# which can of course set at configure time as well using +# +# configure --host=i386-mingw-cross +# +# After the default has been created, additional rules can be appended +# that will not just recurse into the subdirectories and only ever exist +# in the srcdir toplevel makefile - these parts are read from the $2 = +# Makefile.mk file +# +# The automatic rules are usually scanning the toplevel Makefile for lines +# like '#### $host |$builddir' to recognize the place where to recurse +# into. Usually, the last one is the only one used. However, almost all +# targets have an additional "*-all" rule which makes the script to +# recurse into _all_ variants of the current HOST (!!) setting. The "-all" +# suffix can be overriden for the macro as well. +# +# a special rule is only given for things like "dist" that will copy the +# tarball from the builddir to the sourcedir (or $(PUB)) for reason of +# convenience. +# +# LICENSE +# +# Copyright (c) 2009 Guido U. Draheim +# Copyright (c) 2009 Alan Jenkins +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 25 + +AC_DEFUN([AX_ENABLE_BUILDDIR],[ +AC_REQUIRE([AC_CANONICAL_HOST])[]dnl +AC_REQUIRE([AC_CANONICAL_TARGET])[]dnl +AC_REQUIRE([AX_CONFIGURE_ARGS])[]dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])[]dnl +AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl +AS_VAR_PUSHDEF([SUB],[ax_enable_builddir])dnl +AS_VAR_PUSHDEF([AUX],[ax_enable_builddir_auxdir])dnl +AS_VAR_PUSHDEF([SED],[ax_enable_builddir_sed])dnl +SUB="." +AC_ARG_ENABLE([builddir], AS_HELP_STRING( + [--disable-builddir],[disable automatic build in subdir of sources]) + ,[SUB="$enableval"], [SUB="auto"]) +if test ".$ac_srcdir_defaulted" != ".no" ; then +if test ".$srcdir" = ".." ; then + if test -f config.status ; then + AC_MSG_NOTICE(toplevel srcdir already configured... skipping subdir build) + else + test ".$SUB" = "." && SUB="." + test ".$SUB" = ".no" && SUB="." + test ".$TARGET" = "." && TARGET="$target" + test ".$SUB" = ".auto" && SUB="m4_ifval([$1], [$1],[$TARGET])" + if test ".$SUB" != ".." ; then # we know where to go and + AS_MKDIR_P([$SUB]) + echo __.$SUB.__ > $SUB/conftest.tmp + cd $SUB + if grep __.$SUB.__ conftest.tmp >/dev/null 2>/dev/null ; then + rm conftest.tmp + AC_MSG_RESULT([continue configure in default builddir "./$SUB"]) + else + AC_MSG_ERROR([could not change to default builddir "./$SUB"]) + fi + srcdir=`echo "$SUB" | + sed -e 's,^\./,,;s,[[^/]]$,&/,;s,[[^/]]*/,../,g;s,[[/]]$,,;'` + # going to restart from subdirectory location + test -f $srcdir/config.log && mv $srcdir/config.log . + test -f $srcdir/confdefs.h && mv $srcdir/confdefs.h . + test -f $srcdir/conftest.log && mv $srcdir/conftest.log . + test -f $srcdir/$cache_file && mv $srcdir/$cache_file . + AC_MSG_RESULT(....exec $SHELL $srcdir/[$]0 "--srcdir=$srcdir" "--enable-builddir=$SUB" ${1+"[$]@"}) + case "[$]0" in # restart + [[\\/]]* | ?:[[\\/]]*) # Asbolute name + eval $SHELL "'[$]0'" "'--srcdir=$srcdir'" "'--enable-builddir=$SUB'" $ac_configure_args ;; + *) eval $SHELL "'$srcdir/[$]0'" "'--srcdir=$srcdir'" "'--enable-builddir=$SUB'" $ac_configure_args ;; + esac ; exit $? + fi + fi +fi fi +test ".$SUB" = ".auto" && SUB="." +dnl ac_path_prog uses "set dummy" to override $@ which would defeat the "exec" +AC_PATH_PROG(SED,gsed sed, sed) +AUX="$am_aux_dir" +AS_VAR_POPDEF([SED])dnl +AS_VAR_POPDEF([AUX])dnl +AS_VAR_POPDEF([SUB])dnl +AC_CONFIG_COMMANDS([buildir],[dnl .............. config.status .............. +AS_VAR_PUSHDEF([SUB],[ax_enable_builddir])dnl +AS_VAR_PUSHDEF([TOP],[top_srcdir])dnl +AS_VAR_PUSHDEF([SRC],[ac_top_srcdir])dnl +AS_VAR_PUSHDEF([AUX],[ax_enable_builddir_auxdir])dnl +AS_VAR_PUSHDEF([SED],[ax_enable_builddir_sed])dnl +pushdef([END],[Makefile.mk])dnl +pushdef([_ALL],[ifelse([$3],,[-all],[$3])])dnl + SRC="$ax_enable_builddir_srcdir" + if test ".$SUB" = ".." ; then + if test -f "$TOP/Makefile" ; then + AC_MSG_NOTICE([skipping TOP/Makefile - left untouched]) + else + AC_MSG_NOTICE([skipping TOP/Makefile - not created]) + fi + else + if test -f "$SRC/Makefile" ; then + a=`grep "^VERSION " "$SRC/Makefile"` ; b=`grep "^VERSION " Makefile` + test "$a" != "$b" && rm "$SRC/Makefile" + fi + if test -f "$SRC/Makefile" ; then + echo "$SRC/Makefile : $SRC/Makefile.in" > $tmp/conftemp.mk + echo " []@ echo 'REMOVED,,,' >\$[]@" >> $tmp/conftemp.mk + eval "${MAKE-make} -f $tmp/conftemp.mk 2>/dev/null >/dev/null" + if grep '^REMOVED,,,' "$SRC/Makefile" >/dev/null + then rm $SRC/Makefile ; fi + cp $tmp/conftemp.mk $SRC/makefiles.mk~ ## DEBUGGING + fi + if test ! -f "$SRC/Makefile" ; then + AC_MSG_NOTICE([create TOP/Makefile guessed from local Makefile]) + x='`' ; cat >$tmp/conftemp.sed <<_EOF +/^\$/n +x +/^\$/bS +x +/\\\\\$/{H;d;} +{H;s/.*//;x;} +bM +:S +x +/\\\\\$/{h;d;} +{h;s/.*//;x;} +:M +s/\\(\\n\\) /\\1 /g +/^ /d +/^[[ ]]*[[\\#]]/d +/^VPATH *=/d +s/^srcdir *=.*/srcdir = ./ +s/^top_srcdir *=.*/top_srcdir = ./ +/[[:=]]/!d +/^\\./d +dnl Now handle rules (i.e. lines containing ":" but not " = "). +/ = /b +/ .= /b +/:/!b +s/:.*/:/ +s/ / /g +s/ \\([[a-z]][[a-z-]]*[[a-zA-Z0-9]]\\)\\([[ :]]\\)/ \\1 \\1[]_ALL\\2/g +s/^\\([[a-z]][[a-z-]]*[[a-zA-Z0-9]]\\)\\([[ :]]\\)/\\1 \\1[]_ALL\\2/ +s/ / /g +/^all all[]_ALL[[ :]]/i\\ +all-configured : all[]_ALL +dnl dist-all exists... and would make for dist-all-all +s/ [[a-zA-Z0-9-]]*[]_ALL [[a-zA-Z0-9-]]*[]_ALL[]_ALL//g +/[]_ALL[]_ALL/d +a\\ + @ HOST="\$(HOST)\" \\\\\\ + ; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\ + ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\ + ; use=$x basename "\$\@" _ALL $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\ + ; echo "MAKE \$\$HOST : \$\$n * \$\@"; if test "\$\$n" -eq "0" ; then : \\\\\\ + ; BUILD=$x grep "^####.*|" Makefile |tail -1| sed -e 's/.*|//' $x ; fi \\\\\\ + ; test ".\$\$BUILD" = "." && BUILD="." \\\\\\ + ; test "\$\$use" = "\$\@" && BUILD=$x echo "\$\$BUILD" | tail -1 $x \\\\\\ + ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\ + ; (cd "\$\$i" && test ! -f configure && \$(MAKE) \$\$use) || exit; done +dnl special rule add-on: "dist" copies the tarball to $(PUB). (source tree) +/dist[]_ALL *:/a\\ + @ HOST="\$(HOST)\" \\\\\\ + ; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\ + ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\ + ; found=$x echo \$\$BUILD | wc -w $x \\\\\\ + ; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).tar.*" \\\\\\ + ; if test "\$\$found" -eq "0" ; then : \\\\\\ + ; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\ + ; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\ + ; for f in \$\$i/\$(PACKAGE)-\$(VERSION).tar.* \\\\\\ + ; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done +dnl special rule add-on: "dist-foo" copies all the archives to $(PUB). (source tree) +/dist-[[a-zA-Z0-9]]*[]_ALL *:/a\\ + @ HOST="\$(HOST)\" \\\\\\ + ; test ".\$\$HOST" = "." && HOST=$x sh ./config.guess $x \\\\\\ + ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\ + ; found=$x echo \$\$BUILD | wc -w $x \\\\\\ + ; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).*" \\\\\\ + ; if test "\$\$found" -eq "0" ; then : \\\\\\ + ; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\ + ; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\ + ; for f in \$\$i/\$(PACKAGE)-\$(VERSION).* \\\\\\ + ; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done +dnl special rule add-on: "distclean" removes all local builddirs completely +/distclean[]_ALL *:/a\\ + @ HOST="\$(HOST)\" \\\\\\ + ; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\ + ; BUILD=$x grep "^#### .*|" Makefile | sed -e 's/.*|//' $x \\\\\\ + ; use=$x basename "\$\@" _ALL $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\ + ; echo "MAKE \$\$HOST : \$\$n * \$\@ (all local builds)" \\\\\\ + ; test ".\$\$BUILD" = "." && BUILD="." \\\\\\ + ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\ + ; echo "# rm -r \$\$i"; done ; echo "# (sleep 3)" ; sleep 3 \\\\\\ + ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\ + ; echo "\$\$i" | grep "^/" > /dev/null && continue \\\\\\ + ; echo "\$\$i" | grep "^../" > /dev/null && continue \\\\\\ + ; echo "rm -r \$\$i"; (rm -r "\$\$i") ; done ; rm Makefile +_EOF + cp "$tmp/conftemp.sed" "$SRC/makefile.sed~" ## DEBUGGING + $SED -f $tmp/conftemp.sed Makefile >$SRC/Makefile + if test -f "$SRC/m4_ifval([$2],[$2],[END])" ; then + AC_MSG_NOTICE([extend TOP/Makefile with TOP/m4_ifval([$2],[$2],[END])]) + cat $SRC/END >>$SRC/Makefile + fi ; xxxx="####" + echo "$xxxx CONFIGURATIONS FOR TOPLEVEL MAKEFILE: " >>$SRC/Makefile + # sanity check + if grep '^; echo "MAKE ' $SRC/Makefile >/dev/null ; then + AC_MSG_NOTICE([buggy sed found - it deletes tab in "a" text parts]) + $SED -e '/^@ HOST=/s/^/ /' -e '/^; /s/^/ /' $SRC/Makefile \ + >$SRC/Makefile~ + (test -s $SRC/Makefile~ && mv $SRC/Makefile~ $SRC/Makefile) 2>/dev/null + fi + else + xxxx="\\#\\#\\#\\#" + # echo "/^$xxxx *$ax_enable_builddir_host /d" >$tmp/conftemp.sed + echo "s!^$xxxx [[^|]]* | *$SUB *\$!$xxxx ...... $SUB!" >$tmp/conftemp.sed + $SED -f "$tmp/conftemp.sed" "$SRC/Makefile" >$tmp/mkfile.tmp + cp "$tmp/conftemp.sed" "$SRC/makefiles.sed~" ## DEBUGGING + cp "$tmp/mkfile.tmp" "$SRC/makefiles.out~" ## DEBUGGING + if cmp -s "$SRC/Makefile" "$tmp/mkfile.tmp" 2>/dev/null ; then + AC_MSG_NOTICE([keeping TOP/Makefile from earlier configure]) + rm "$tmp/mkfile.tmp" + else + AC_MSG_NOTICE([reusing TOP/Makefile from earlier configure]) + mv "$tmp/mkfile.tmp" "$SRC/Makefile" + fi + fi + AC_MSG_NOTICE([build in $SUB (HOST=$ax_enable_builddir_host)]) + xxxx="####" + echo "$xxxx" "$ax_enable_builddir_host" "|$SUB" >>$SRC/Makefile + fi +popdef([END])dnl +AS_VAR_POPDEF([SED])dnl +AS_VAR_POPDEF([AUX])dnl +AS_VAR_POPDEF([SRC])dnl +AS_VAR_POPDEF([TOP])dnl +AS_VAR_POPDEF([SUB])dnl +],[dnl +ax_enable_builddir_srcdir="$srcdir" # $srcdir +ax_enable_builddir_host="$HOST" # $HOST / $host +ax_enable_builddir_version="$VERSION" # $VERSION +ax_enable_builddir_package="$PACKAGE" # $PACKAGE +ax_enable_builddir_auxdir="$ax_enable_builddir_auxdir" # $AUX +ax_enable_builddir_sed="$ax_enable_builddir_sed" # $SED +ax_enable_builddir="$ax_enable_builddir" # $SUB +])dnl +]) diff --git a/m4/ax_extend_srcdir.m4 b/m4/ax_extend_srcdir.m4 new file mode 100644 index 00000000..a308d67d --- /dev/null +++ b/m4/ax_extend_srcdir.m4 @@ -0,0 +1,86 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_extend_srcdir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_EXTEND_SRCDIR +# +# DESCRIPTION +# +# The AX_EXTEND_SRCDIR macro extends $srcdir by one path component. +# +# As an example, when working in /home/michael/i3-4.12/build and calling +# ../configure, your $srcdir is "..". After calling AX_EXTEND_SRCDIR, +# $srcdir will be set to "../../i3-4.12". +# +# The result of extending $srcdir is that filenames (e.g. in the output of +# the "backtrace" gdb command) will include one more path component of the +# absolute source path. The additional path component makes it easy for +# users to recognize which files belong to the PACKAGE, and -- provided a +# dist tarball was unpacked -- which version of PACKAGE was used. +# +# As an example, in "backtrace", you will see: +# +# #0 main (argc=1, argv=0x7fffffff1fc8) at ../../i3-4.12/src/main.c:187 +# +# instead of: +# +# #0 main (argc=1, argv=0x7fffffff1fc8) at ../src/main.c:187 +# +# In case your code uses the __FILE__ preprocessor directive to refer to +# the filename of the current source file (e.g. in debug messages), using +# the extended path might be undesirable. For this purpose, +# AX_EXTEND_SRCDIR defines the output variable AX_EXTEND_SRCDIR_CPPFLAGS, +# which can be added to AM_CPPFLAGS in Makefile.am in order to define the +# preprocessor directive STRIPPED__FILE__. As an example, when compiling +# the file "../../i3-4.12/src/main.c", STRIPPED__FILE__ evaluates to +# "main.c". +# +# There are some caveats: When $srcdir is "." (i.e. when ./configure was +# called instead of ../configure in a separate build directory), +# AX_EXTEND_SRCDIR will still extend $srcdir, but the intended effect will +# not be achieved because of the way automake specifies file paths: +# automake defines COMPILE to use "`test -f '$source' || echo +# '\$(srcdir)/'`$source" in order to prefer files in the current directory +# over specifying $srcdir explicitly. +# +# The AX_EXTEND_SRCDIR author is not aware of any way to influence this +# automake behavior. Patches very welcome. +# +# To work around this issue, you can use AX_ENABLE_BUILDDIR i.e. by adding +# the following code to configure.ac: +# +# AX_ENABLE_BUILDDIR +# dnl ... +# AX_EXTEND_SRCDIR +# +# Then also add this bit to Makefile.am (if you wish to use +# STRIPPED__FILE__ in your code): +# +# AM_CPPFLAGS = @AX_EXTEND_SRCDIR_CPPFLAGS@ +# +# LICENSE +# +# Copyright (c) 2016 Michael Stapelberg +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 3 + +AC_DEFUN([AX_EXTEND_SRCDIR], +[dnl +AS_CASE([$srcdir], + [.|.*], + [ + # pwd -P is specified in IEEE 1003.1 from 2004 + as_dir=`cd "$srcdir" && pwd -P` + as_base=`AS_BASENAME([$as_dir])` + srcdir=${srcdir}/../${as_base} + + AC_SUBST([AX_EXTEND_SRCDIR_CPPFLAGS], ["-DSTRIPPED__FILE__=AS_ESCAPE([\"$$(basename $<)\"])"]) + ]) +])dnl AX_EXTEND_SRCDIR diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 new file mode 100644 index 00000000..4c4051ea --- /dev/null +++ b/m4/ax_pthread.m4 @@ -0,0 +1,485 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 23 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; +esac + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + +ax_pthread_clang_warning=no + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + PTHREAD_CFLAGS="-pthread" + PTHREAD_LIBS= + + ax_pthread_ok=yes + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -mt,pthread) + AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) + PTHREAD_CFLAGS="-mt" + PTHREAD_LIBS="-lpthread" + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4 new file mode 100644 index 00000000..cae11112 --- /dev/null +++ b/m4/ax_require_defined.m4 @@ -0,0 +1,37 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_require_defined.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_REQUIRE_DEFINED(MACRO) +# +# DESCRIPTION +# +# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have +# been defined and thus are available for use. This avoids random issues +# where a macro isn't expanded. Instead the configure script emits a +# non-fatal: +# +# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found +# +# It's like AC_REQUIRE except it doesn't expand the required macro. +# +# Here's an example: +# +# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +# +# LICENSE +# +# Copyright (c) 2014 Mike Frysinger +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([AX_REQUIRE_DEFINED], [dnl + m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) +])dnl AX_REQUIRE_DEFINED diff --git a/m4/ax_sanitizers.m4 b/m4/ax_sanitizers.m4 new file mode 100644 index 00000000..836d4afd --- /dev/null +++ b/m4/ax_sanitizers.m4 @@ -0,0 +1,130 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_sanitizers.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_SANITIZERS([SANITIZERS], [ENABLED-BY-DEFAULT], [ACTION-SUCCESS]) +# +# DESCRIPTION +# +# Offers users to enable one or more sanitizers (see +# https://github.com/google/sanitizers) with the corresponding +# --enable--sanitizer option. +# +# SANITIZERS is a whitespace-separated list of sanitizers to offer via +# --enable--sanitizer options, e.g. "address memory" for the +# address sanitizer and the memory sanitizer. If SANITIZERS is not specified, +# all known sanitizers to AX_SANITIZERS will be offered, which at the time of +# writing are "address memory undefined". +# NOTE that SANITIZERS is expanded at autoconf time, not at configure time, +# i.e. you cannot use shell variables in SANITIZERS. +# +# ENABLED-BY-DEFAULT is a whitespace-separated list of sanitizers which +# should be enabled by default, e.g. "memory undefined". Note that not all +# sanitizers can be combined, e.g. memory sanitizer cannot be enabled when +# address sanitizer is already enabled. +# Set ENABLED-BY-DEFAULT to a single whitespace in order to disable all +# sanitizers by default. +# ENABLED-BY-DEFAULT is expanded at configure time, so you can use shell +# variables. +# +# ACTION-SUCCESS allows to specify shell commands to execute on success, i.e. +# when one of the sanitizers was successfully enabled. This is a good place +# to call AC_DEFINE for any precompiler constants you might need to make your +# code play nice with sanitizers. +# +# The variable ax_enabled_sanitizers contains a whitespace-separated list of +# all enabled sanitizers, so that you can print them at the end of configure, +# if you wish. +# +# The additional --enable-sanitizers option allows users to enable/disable +# all sanitizers, effectively overriding ENABLED-BY-DEFAULT. +# +# EXAMPLES +# +# AX_SANITIZERS([address]) +# dnl offer users to enable address sanitizer via --enable-address-sanitizer +# +# is_debug_build=… +# if test "x$is_debug_build" = "xyes"; then +# default_sanitizers="address memory" +# else +# default_sanitizers= +# fi +# AX_SANITIZERS([address memory], [$default_sanitizers]) +# dnl enable address sanitizer and memory sanitizer by default for debug +# dnl builds, e.g. when building from git instead of a dist tarball. +# +# AX_SANITIZERS(, , [ +# AC_DEFINE([SANITIZERS_ENABLED], +# [], +# [At least one sanitizer was enabled])]) +# dnl enable all sanitizers known to AX_SANITIZERS by default and set the +# dnl SANITIZERS_ENABLED precompiler constant. +# +# AX_SANITIZERS(, [ ]) +# dnl provide all sanitizers, but enable none by default. +# +# LICENSE +# +# Copyright (c) 2016 Michael Stapelberg +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. + +AC_DEFUN([AX_SANITIZERS], +[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) +AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +AC_ARG_ENABLE(sanitizers, + AS_HELP_STRING( + [--enable-sanitizers], + [enable all known sanitizers]), + [ax_sanitizers_default=$enableval], + [ax_sanitizers_default=]) +ax_enabled_sanitizers= +m4_foreach_w([mysan], m4_default($1, [address memory undefined]), [ + dnl If ax_sanitizers_default is unset, i.e. the user neither explicitly + dnl enabled nor explicitly disabled all sanitizers, we get the default value + dnl for this sanitizer based on whether it is listed in ENABLED-BY-DEFAULT. + AS_IF([test "x$ax_sanitizers_default" = "x"], [dnl + ax_sanitizer_default= + for mycheck in m4_default([$2], [address memory undefined]); do + AS_IF([test "x$mycheck" = "x[]mysan"], [ax_sanitizer_default=yes]) + done + AS_IF([test "x$ax_sanitizer_default" = "x"], [ax_sanitizer_default=no]) + ], + [ax_sanitizer_default=$ax_sanitizers_default]) + AC_ARG_ENABLE(mysan[]-sanitizer, + AS_HELP_STRING( + [--enable-[]mysan[]-sanitizer], + [enable -fsanitize=mysan]), + [ax_sanitizer_enabled=$enableval], + [ax_sanitizer_enabled=$ax_sanitizer_default]) + +AS_IF([test "x$ax_sanitizer_enabled" = "xyes"], [ +dnl Not using AX_APPEND_COMPILE_FLAGS and AX_APPEND_LINK_FLAGS because they +dnl lack the ability to specify ACTION-SUCCESS. + AX_CHECK_COMPILE_FLAG([-fsanitize=[]mysan], [ + AX_CHECK_LINK_FLAG([-fsanitize=[]mysan], [ + AX_APPEND_FLAG([-fsanitize=[]mysan], []) +dnl If and only if libtool is being used, LDFLAGS needs to contain -Wc,-fsanitize=…. +dnl See e.g. https://sources.debian.net/src/systemd/231-7/configure.ac/?hl=128#L135 +dnl TODO: how can recognize that situation and add -Wc,? + AX_APPEND_FLAG([-fsanitize=[]mysan], [LDFLAGS]) +dnl TODO: add -fPIE -pie for memory + # -fno-omit-frame-pointer results in nicer stack traces in error + # messages, see http://clang.llvm.org/docs/AddressSanitizer.html#usage + AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], [ + AX_APPEND_FLAG([-fno-omit-frame-pointer], [])]) +dnl TODO: at least for clang, we should specify exactly -O1, not -O2 or -O0, so that performance is reasonable but stacktraces are not tampered with (due to inlining), see http://clang.llvm.org/docs/AddressSanitizer.html#usage + m4_default([$3], :) + ax_enabled_sanitizers="[]mysan $ax_enabled_sanitizers" + ]) + ]) +]) +])dnl +])dnl AX_SANITIZERS diff --git a/man/Makefile b/man/Makefile deleted file mode 100644 index e4cee0cc..00000000 --- a/man/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - $(MAKE) -C .. mans - -clean: - $(MAKE) -C .. clean-mans - -.PHONY: all clean diff --git a/man/asciidoc.conf b/man/asciidoc.conf.in similarity index 87% rename from man/asciidoc.conf rename to man/asciidoc.conf.in index 11e76501..e9a9ea23 100644 --- a/man/asciidoc.conf +++ b/man/asciidoc.conf.in @@ -7,7 +7,7 @@ template::[header-declarations] {mantitle} {manvolnum} i3 -4.12 +@PACKAGE_VERSION@ i3 Manual diff --git a/man/i3-msg.man b/man/i3-msg.man index e0c70c44..e81a94ab 100644 --- a/man/i3-msg.man +++ b/man/i3-msg.man @@ -29,7 +29,7 @@ from the root window and then try /tmp/i3-ipc.sock before exiting with an error. *-t* 'type':: -Send ipc message, see below. +Send ipc message, see below. This option defaults to "command". *message*:: Send ipc message, see below. @@ -62,6 +62,9 @@ get_bar_config:: Gets the configuration (as JSON map) of the workspace bar with the given ID. If no ID is provided, an array with all configured bar IDs is returned instead. +get_binding_modes:: +Gets a list of configured binding modes. + get_version:: Gets the version of i3. The reply will be a JSON-encoded dictionary with the major, minor, patch and human-readable version. diff --git a/man/i3-sensible-editor.man b/man/i3-sensible-editor.man index 19b0f3a5..bfc5c5c6 100644 --- a/man/i3-sensible-editor.man +++ b/man/i3-sensible-editor.man @@ -20,6 +20,7 @@ It tries to start one of the following (in that order): * $VISUAL * $EDITOR * nano +* nvim * vim * vi * emacs diff --git a/man/i3-sensible-terminal.man b/man/i3-sensible-terminal.man index b231f808..b830cd09 100644 --- a/man/i3-sensible-terminal.man +++ b/man/i3-sensible-terminal.man @@ -29,6 +29,7 @@ It tries to start one of the following (in that order): * terminator * Eterm * aterm +* uxterm * xterm * gnome-terminal * roxterm @@ -38,6 +39,7 @@ It tries to start one of the following (in that order): * mate-terminal * terminology * st +* qterminal Please don’t complain about the order: If the user has any preference, they will have $TERMINAL set or modified their i3 configuration file. diff --git a/man/man.mk b/man/man.mk deleted file mode 100644 index e0df386d..00000000 --- a/man/man.mk +++ /dev/null @@ -1,42 +0,0 @@ -DISTCLEAN_TARGETS += clean-mans - -A2X = a2x -POD2MAN = pod2man - -A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f man/asciidoc.conf" $(A2X_FLAGS) $< -POD2MAN_CALL = $(V_POD2MAN)$(POD2MAN) --utf8 $< > $@ - -MANS_ASCIIDOC = \ - man/i3.1 \ - man/i3bar.1 \ - man/i3-msg.1 \ - man/i3-input.1 \ - man/i3-nagbar.1 \ - man/i3-config-wizard.1 \ - man/i3-migrate-config-to-v4.1 \ - man/i3-sensible-editor.1 \ - man/i3-sensible-pager.1 \ - man/i3-sensible-terminal.1 \ - man/i3-dump-log.1 - -MANS_POD = \ - man/i3-dmenu-desktop.1 \ - man/i3-save-tree.1 - -MANS = \ - $(MANS_ASCIIDOC) \ - $(MANS_POD) - -mans: $(MANS) - -$(MANS_ASCIIDOC): %.1: %.man man/asciidoc.conf - $(A2X_MAN_CALL) - -$(MANS_POD): man/%.1: % - $(POD2MAN_CALL) - -clean-mans: - for file in $(notdir $(MANS)); \ - do \ - rm -f man/$${file} man/$${file%.*}.html man/$${file%.*}.xml; \ - done diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index ec7fbabf..d4b3dbc6 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -53,6 +53,8 @@ state CRITERIA: ctype = 'title' -> CRITERION ctype = 'urgent' -> CRITERION ctype = 'workspace' -> CRITERION + ctype = 'tiling', 'floating' + -> call cmd_criteria_add($ctype, NULL); CRITERIA ']' -> call cmd_criteria_match_windows(); INITIAL state CRITERION: diff --git a/parser-specs/config.spec b/parser-specs/config.spec index ef3bc2e0..90296819 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -18,6 +18,7 @@ state INITIAL: error -> '#' -> IGNORE_LINE 'set' -> IGNORE_LINE + 'set_from_resource' -> IGNORE_LINE bindtype = 'bindsym', 'bindcode', 'bind' -> BINDING 'bar' -> BARBRACE 'font' -> FONT @@ -121,10 +122,10 @@ state NEW_WINDOW_PIXELS_PX: end -> call cfg_new_window($windowtype, $border, &width) -# hide_edge_borders +# hide_edge_borders # also hide_edge_borders for compatibility state HIDE_EDGE_BORDERS: - hide_borders = 'none', 'vertical', 'horizontal', 'both' + hide_borders = 'none', 'vertical', 'horizontal', 'both', 'smart' -> call cfg_hide_edge_borders($hide_borders) hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active' -> call cfg_hide_edge_borders($hide_borders) @@ -172,6 +173,8 @@ state CRITERIA: ctype = 'title' -> CRITERION ctype = 'urgent' -> CRITERION ctype = 'workspace' -> CRITERION + ctype = 'tiling', 'floating' + -> call cfg_criteria_add($ctype, NULL); CRITERIA ']' -> call cfg_criteria_pop_state() diff --git a/release.sh b/release.sh index 766f4d30..9101332c 100755 --- a/release.sh +++ b/release.sh @@ -1,8 +1,8 @@ #!/bin/zsh # This script is used to prepare a new release of i3. -export RELEASE_VERSION="4.11" -export PREVIOUS_VERSION="4.10.4" +export RELEASE_VERSION="4.12" +export PREVIOUS_VERSION="4.11" export RELEASE_BRANCH="next" if [ ! -e "../i3.github.io" ] @@ -45,7 +45,7 @@ if ! wget https://i3wm.org/downloads/i3-${PREVIOUS_VERSION}.tar.bz2; then echo "Could not download i3-${PREVIOUS_VERSION}.tar.bz2 (required for comparing files)." exit 1 fi -git clone --quiet --branch "${RELEASE_BRANCH}" file://${STARTDIR} +git clone --quiet --branch "${RELEASE_BRANCH}" https://github.com/i3/i3 cd i3 if [ ! -e "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" ]; then echo "Required file RELEASE-NOTES-${RELEASE_VERSION} not found." @@ -55,11 +55,17 @@ git checkout -b release-${RELEASE_VERSION} cp "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" "RELEASE-NOTES-${RELEASE_VERSION}" git add RELEASE-NOTES-${RELEASE_VERSION} git rm RELEASE-NOTES-${PREVIOUS_VERSION} -sed -i "s,[^<]*,${RELEASE_VERSION},g" man/asciidoc.conf +sed -i "s,RELEASE-NOTES-${PREVIOUS_VERSION},RELEASE-NOTES-${RELEASE_VERSION},g" Makefile.am +sed -i "s/AC_INIT(\[i3\], \[${PREVIOUS_VERSION}\]/AC_INIT([i3], [${RELEASE_VERSION}]/" configure.ac +echo "${RELEASE_VERSION} ($(date +%F))" > I3_VERSION +git add I3_VERSION git commit -a -m "release i3 ${RELEASE_VERSION}" git tag "${RELEASE_VERSION}" -m "release i3 ${RELEASE_VERSION}" --sign --local-user=0x4AC8EE1D -make dist +autoreconf -fi +mkdir build +(cd build && ../configure && make dist-bzip2 -j8) +cp build/i3-${RELEASE_VERSION}.tar.bz2 . echo "Differences in the release tarball file lists:" @@ -68,14 +74,13 @@ diff -u \ <(tar tf i3-${RELEASE_VERSION}.tar.bz2 | sed "s,i3-${RELEASE_VERSION}/,,g" | sort) \ | colordiff -if ! tar xf i3-${RELEASE_VERSION}.tar.bz2 --to-stdout --strip-components=1 i3-${RELEASE_VERSION}/I3_VERSION | grep -q "^${RELEASE_VERSION} " -then - echo "I3_VERSION file does not start with ${RELEASE_VERSION}" - exit 1 -fi gpg --armor -b i3-${RELEASE_VERSION}.tar.bz2 +echo "${RELEASE_VERSION}-non-git" > I3_VERSION +git add I3_VERSION +git commit -a -m "Set non-git version to ${RELEASE_VERSION}-non-git." + if [ "${RELEASE_BRANCH}" = "master" ]; then git checkout master git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'" @@ -156,7 +161,7 @@ git add downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt sed -i "s,

Documentation for i3 v[^<]*

,

Documentation for i3 v${RELEASE_VERSION}

,g" docs/index.html sed -i "s,[^<]*,${RELEASE_VERSION},g" index.html sed -i "s,The current stable version is .*$,The current stable version is ${RELEASE_VERSION}.,g" downloads/index.html -sed -i "s,,\n \n ${RELEASE_VERSION}\n i3-${RELEASE_VERSION}.tar.bz2\n $(ls -lh ../i3/i3-${RELEASE_VERSION}.tar.bz2 | awk -F " " {'print $5'} | sed 's/K$/ KiB/g')\n signature\n $(date +'%Y-%m-%d')\n release notes\n \n,g" downloads/index.html +sed -i "s,,\n \n ${RELEASE_VERSION}\n i3-${RELEASE_VERSION}.tar.bz2\n $(LC_ALL=en_US.UTF-8 ls -lh ../i3/i3-${RELEASE_VERSION}.tar.bz2 | awk -F " " {'print $5'} | sed 's/K$/ KiB/g' | sed 's/M$/ MiB/g')\n signature\n $(date +'%Y-%m-%d')\n release notes\n \n,g" downloads/index.html git commit -a -m "add ${RELEASE_VERSION} release" @@ -194,7 +199,7 @@ git config --add remote.origin.push "+refs/heads/master:refs/heads/master" cd ${TMPDIR} cat >email.txt < -To: i3-announce@i3.zekjur.net +To: i3-announce@freelists.org Subject: i3 v${RELEASE_VERSION} released Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit diff --git a/i3.applications.desktop b/share/applications/i3.desktop similarity index 83% rename from i3.applications.desktop rename to share/applications/i3.desktop index e40689dd..28445922 100644 --- a/i3.applications.desktop +++ b/share/applications/i3.desktop @@ -2,6 +2,7 @@ Type=Application Name=i3 NoDisplay=true +GenericName=A dynamic tiling window manager Comment=improved dynamic tiling window manager Exec=i3 X-GNOME-WMName=i3 diff --git a/i3-with-shmlog.xsession.desktop b/share/xsessions/i3-with-shmlog.desktop similarity index 71% rename from i3-with-shmlog.xsession.desktop rename to share/xsessions/i3-with-shmlog.desktop index 8cd9431b..3c634500 100644 --- a/i3-with-shmlog.xsession.desktop +++ b/share/xsessions/i3-with-shmlog.desktop @@ -3,3 +3,4 @@ Name=i3 (with debug log) Comment=improved dynamic tiling window manager Exec=i3-with-shmlog Type=Application +Keywords=tiling;wm;windowmanager;window;manager; diff --git a/i3.xsession.desktop b/share/xsessions/i3.desktop similarity index 75% rename from i3.xsession.desktop rename to share/xsessions/i3.desktop index d1340053..11a64e26 100644 --- a/i3.xsession.desktop +++ b/share/xsessions/i3.desktop @@ -6,3 +6,4 @@ TryExec=i3 Type=Application X-LightDM-DesktopName=i3 DesktopNames=i3 +Keywords=tiling;wm;windowmanager;window;manager; diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 6a37f56c..00000000 --- a/src/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - $(MAKE) -C .. i3 - -install: - $(MAKE) -C .. install-i3 - -clean: - $(MAKE) -C .. clean-i3 - -.PHONY: all install clean diff --git a/src/assignments.c b/src/assignments.c index 6c563357..a0f5d5a7 100644 --- a/src/assignments.c +++ b/src/assignments.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "assignments.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/bindings.c b/src/bindings.c index 351a5862..eec821b6 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -57,7 +57,7 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch const char *release, const char *border, const char *whole_window, const char *command, const char *modename, bool pango_markup) { Binding *new_binding = scalloc(1, sizeof(Binding)); - DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release); + DLOG("Binding %p bindtype %s, modifiers %s, input code %s, release %s\n", new_binding, bindtype, modifiers, input_code, release); new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS); new_binding->border = (border != NULL); new_binding->whole_window = (whole_window != NULL); @@ -95,33 +95,45 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch struct Mode *mode = mode_from_name(modename, pango_markup); TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings); + TAILQ_INIT(&(new_binding->keycodes_head)); + return new_binding; } -static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) { - if (bind->input_type != B_KEYBOARD) - return; +static bool binding_in_current_group(const Binding *bind) { + /* If no bits are set, the binding should be installed in every group. */ + if ((bind->event_state_mask >> 16) == I3_XKB_GROUP_MASK_ANY) + return true; + switch (xkb_current_group) { + case XCB_XKB_GROUP_1: + return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_1); + case XCB_XKB_GROUP_2: + return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_2); + case XCB_XKB_GROUP_3: + return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_3); + case XCB_XKB_GROUP_4: + return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_4); + default: + ELOG("BUG: xkb_current_group (= %d) outside of [XCB_XKB_GROUP_1..XCB_XKB_GROUP_4]\n", xkb_current_group); + return false; + } +} +static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) { /* Grab the key in all combinations */ #define GRAB_KEY(modifier) \ do { \ xcb_grab_key(conn, 0, root, modifier, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \ } while (0) - int mods = bind->event_state_mask; - if (((mods >> 16) & I3_XKB_GROUP_MASK_1) && xkb_current_group != XCB_XKB_GROUP_1) - return; - if (((mods >> 16) & I3_XKB_GROUP_MASK_2) && xkb_current_group != XCB_XKB_GROUP_2) - return; - if (((mods >> 16) & I3_XKB_GROUP_MASK_3) && xkb_current_group != XCB_XKB_GROUP_3) - return; - if (((mods >> 16) & I3_XKB_GROUP_MASK_4) && xkb_current_group != XCB_XKB_GROUP_4) - return; - mods &= 0xFFFF; - DLOG("Grabbing keycode %d with event state mask 0x%x (mods 0x%x)\n", - keycode, bind->event_state_mask, mods); + const int mods = (bind->event_state_mask & 0xFFFF); + DLOG("Binding %p Grabbing keycode %d with event state mask 0x%x (mods 0x%x)\n", + bind, keycode, bind->event_state_mask, mods); GRAB_KEY(mods); + /* Also bind the key with active NumLock */ GRAB_KEY(mods | xcb_numlock_mask); + /* Also bind the key with active CapsLock */ GRAB_KEY(mods | XCB_MOD_MASK_LOCK); + /* Also bind the key with active NumLock+CapsLock */ GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK); } @@ -135,14 +147,22 @@ void grab_all_keys(xcb_connection_t *conn) { if (bind->input_type != B_KEYBOARD) continue; + if (!binding_in_current_group(bind)) + continue; + /* The easy case: the user specified a keycode directly. */ if (bind->keycode > 0) { grab_keycode_for_binding(conn, bind, bind->keycode); continue; } - for (uint32_t i = 0; i < bind->number_keycodes; i++) - grab_keycode_for_binding(conn, bind, bind->translated_to[i]); + struct Binding_Keycode *binding_keycode; + TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) { + const int keycode = binding_keycode->keycode; + const int mods = (binding_keycode->modifiers & 0xFFFF); + DLOG("Binding %p Grabbing keycode %d with mods %d\n", bind, keycode, mods); + xcb_grab_key(conn, 0, root, mods, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + } } } @@ -152,7 +172,7 @@ void grab_all_keys(xcb_connection_t *conn) { * */ void regrab_all_buttons(xcb_connection_t *conn) { - bool grab_scrollwheel = bindings_should_grab_scrollwheel_buttons(); + int *buttons = bindings_get_buttons_to_grab(); xcb_grab_server(conn); Con *con; @@ -161,12 +181,24 @@ void regrab_all_buttons(xcb_connection_t *conn) { continue; xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, con->window->id, XCB_BUTTON_MASK_ANY); - xcb_grab_buttons(conn, con->window->id, grab_scrollwheel); + xcb_grab_buttons(conn, con->window->id, buttons); } + FREE(buttons); xcb_ungrab_server(conn); } +static bool modifiers_match(const uint32_t modifiers_mask, const uint32_t modifiers_state) { + /* modifiers_mask is a special case: a value of 0 does not mean “match + * all”, but rather “match exactly when no modifiers are present”. */ + if (modifiers_mask == 0) { + /* Verify no modifiers are pressed. A bitwise AND would lead to + * false positives, see issue #2002. */ + return (modifiers_state == 0); + } + return ((modifiers_state & modifiers_mask) == modifiers_mask); +} + /* * Returns a pointer to the Binding with the specified modifiers and * keycode or NULL if no such binding exists. @@ -189,45 +221,47 @@ static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_releas const uint32_t xkb_group_state = (state_filtered & 0xFFFF0000); const uint32_t modifiers_state = (state_filtered & 0x0000FFFF); TAILQ_FOREACH(bind, bindings, bindings) { - const uint32_t xkb_group_mask = (bind->event_state_mask & 0xFFFF0000); - /* modifiers_mask is a special case: a value of 0 does not mean “match all”, - * but rather “match exactly when no modifiers are present”. */ - const uint32_t modifiers_mask = (bind->event_state_mask & 0x0000FFFF); - const bool groups_match = ((xkb_group_state & xkb_group_mask) == xkb_group_mask); - bool mods_match; - if (modifiers_mask == 0) { - /* Verify no modifiers are pressed. A bitwise AND would lead to - * false positives, see issue #2002. */ - mods_match = (modifiers_state == 0); - } else { - mods_match = ((modifiers_state & modifiers_mask) == modifiers_mask); - } - const bool state_matches = (groups_match && mods_match); - - DLOG("binding groups_match = %s, mods_match = %s, state_matches = %s\n", - (groups_match ? "yes" : "no"), - (mods_match ? "yes" : "no"), - (state_matches ? "yes" : "no")); - /* First compare the state_filtered (unless this is a - * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease - * event) */ if (bind->input_type != input_type) continue; - if (!state_matches && - (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS || - !is_release)) + + const uint32_t xkb_group_mask = (bind->event_state_mask & 0xFFFF0000); + const bool groups_match = ((xkb_group_state & xkb_group_mask) == xkb_group_mask); + if (!groups_match) { + DLOG("skipping binding %p because XKB groups do not match\n", bind); continue; + } /* For keyboard bindings where a symbol was specified by the user, we * need to look in the array of translated keycodes for the event’s * keycode */ if (input_type == B_KEYBOARD && bind->symbol != NULL) { xcb_keycode_t input_keycode = (xcb_keycode_t)input_code; - if (memmem(bind->translated_to, - bind->number_keycodes * sizeof(xcb_keycode_t), - &input_keycode, sizeof(xcb_keycode_t)) == NULL) + bool found_keycode = false; + struct Binding_Keycode *binding_keycode; + TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) { + const uint32_t modifiers_mask = (binding_keycode->modifiers & 0x0000FFFF); + const bool mods_match = modifiers_match(modifiers_mask, modifiers_state); + DLOG("binding_keycode->modifiers = %d, modifiers_mask = %d, modifiers_state = %d, mods_match = %s\n", + binding_keycode->modifiers, modifiers_mask, modifiers_state, (mods_match ? "yes" : "no")); + if (binding_keycode->keycode == input_keycode && mods_match) { + found_keycode = true; + break; + } + } + if (!found_keycode) continue; } else { + const uint32_t modifiers_mask = (bind->event_state_mask & 0x0000FFFF); + const bool mods_match = modifiers_match(modifiers_mask, modifiers_state); + DLOG("binding mods_match = %s\n", (mods_match ? "yes" : "no")); + /* First compare the state_filtered (unless this is a + * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease + * event) */ + if (!mods_match && + (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS || + !is_release)) + continue; + /* This case is easier: The user specified a keycode */ if (bind->keycode != input_code) continue; @@ -237,8 +271,15 @@ static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_releas * user pressed. We therefore mark it as B_UPON_KEYRELEASE_IGNORE_MODS * for later, so that the user can release the modifiers before the * actual key or button and the release event will still be matched. */ - if (bind->release == B_UPON_KEYRELEASE && !is_release) + if (bind->release == B_UPON_KEYRELEASE && !is_release) { bind->release = B_UPON_KEYRELEASE_IGNORE_MODS; + DLOG("marked bind %p as B_UPON_KEYRELEASE_IGNORE_MODS\n", bind); + /* The correct binding has been found, so abort the search, but + * also don’t return this binding, since it should not be executed + * yet (only when the keys are released). */ + bind = TAILQ_END(bindings); + break; + } /* Check if the binding is for a press or a release event */ if ((bind->release == B_UPON_KEYPRESS && is_release) || @@ -268,9 +309,9 @@ Binding *get_binding_from_xcb_event(xcb_generic_event_t *event) { const uint16_t event_state = ((xcb_key_press_event_t *)event)->state; const uint16_t event_detail = ((xcb_key_press_event_t *)event)->detail; - /* Remove the numlock bit */ - i3_event_state_mask_t state_filtered = event_state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); - DLOG("(removed numlock, state = 0x%x)\n", state_filtered); + /* Remove the CapsLock bit */ + i3_event_state_mask_t state_filtered = event_state & ~XCB_MOD_MASK_LOCK; + DLOG("(removed capslock, state = 0x%x)\n", state_filtered); /* Transform the keyboard_group from bit 13 and bit 14 into an * i3_xkb_group_mask_t, so that get_binding() can just bitwise AND the * configured bindings against |state_filtered|. @@ -311,6 +352,12 @@ struct resolve { /* Like |xkb_state|, just without the shift modifier, if shift was specified. */ struct xkb_state *xkb_state_no_shift; + + /* Like |xkb_state|, but with NumLock. */ + struct xkb_state *xkb_state_numlock; + + /* Like |xkb_state|, but with NumLock, just without the shift modifier, if shift was specified. */ + struct xkb_state *xkb_state_numlock_no_shift; }; /* @@ -321,6 +368,7 @@ struct resolve { */ static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) { const struct resolve *resolving = data; + struct xkb_state *numlock_state = resolving->xkb_state_numlock; xkb_keysym_t sym = xkb_state_key_get_one_sym(resolving->xkb_state, key); if (sym != resolving->keysym) { /* Check if Shift was specified, and try resolving the symbol without @@ -330,16 +378,51 @@ static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key, return; if (xkb_state_key_get_level(resolving->xkb_state, key, layout) > 1) return; + /* Skip the Shift fallback for keypad keys, otherwise one cannot bind + * KP_1 independent of KP_End. */ + if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_Equal) + return; + numlock_state = resolving->xkb_state_numlock_no_shift; sym = xkb_state_key_get_one_sym(resolving->xkb_state_no_shift, key); if (sym != resolving->keysym) return; } Binding *bind = resolving->bind; - bind->number_keycodes++; - bind->translated_to = srealloc(bind->translated_to, - (sizeof(xcb_keycode_t) * - bind->number_keycodes)); - bind->translated_to[bind->number_keycodes - 1] = key; + +#define ADD_TRANSLATED_KEY(mods) \ + do { \ + struct Binding_Keycode *binding_keycode = smalloc(sizeof(struct Binding_Keycode)); \ + binding_keycode->modifiers = (mods); \ + binding_keycode->keycode = key; \ + TAILQ_INSERT_TAIL(&(bind->keycodes_head), binding_keycode, keycodes); \ + } while (0) + + ADD_TRANSLATED_KEY(bind->event_state_mask); + + /* Also bind the key with active CapsLock */ + ADD_TRANSLATED_KEY(bind->event_state_mask | XCB_MOD_MASK_LOCK); + + /* If this binding is not explicitly for NumLock, check whether we need to + * add a fallback. */ + if ((bind->event_state_mask & xcb_numlock_mask) != xcb_numlock_mask) { + /* Check whether the keycode results in the same keysym when NumLock is + * active. If so, grab the key with NumLock as well, so that users don’t + * need to duplicate every key binding with an additional Mod2 specified. + */ + xkb_keysym_t sym_numlock = xkb_state_key_get_one_sym(numlock_state, key); + if (sym_numlock == resolving->keysym) { + /* Also bind the key with active NumLock */ + ADD_TRANSLATED_KEY(bind->event_state_mask | xcb_numlock_mask); + + /* Also bind the key with active NumLock+CapsLock */ + ADD_TRANSLATED_KEY(bind->event_state_mask | xcb_numlock_mask | XCB_MOD_MASK_LOCK); + } else { + DLOG("Skipping automatic numlock fallback, key %d resolves to 0x%x with numlock\n", + key, sym_numlock); + } + } + +#undef ADD_TRANSLATED_KEY } /* @@ -359,6 +442,18 @@ void translate_keysyms(void) { return; } + struct xkb_state *dummy_state_numlock = xkb_state_new(xkb_keymap); + if (dummy_state_numlock == NULL) { + ELOG("Could not create XKB state, cannot translate keysyms.\n"); + return; + } + + struct xkb_state *dummy_state_numlock_no_shift = xkb_state_new(xkb_keymap); + if (dummy_state_numlock_no_shift == NULL) { + ELOG("Could not create XKB state, cannot translate keysyms.\n"); + return; + } + bool has_errors = false; Binding *bind; TAILQ_FOREACH(bind, bindings, bindings) { @@ -392,7 +487,9 @@ void translate_keysyms(void) { else if ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_4) group = XCB_XKB_GROUP_4; - DLOG("group = %d, event_state_mask = %d, &2 = %s, &3 = %s, &4 = %s\n", group, + DLOG("Binding %p group = %d, event_state_mask = %d, &2 = %s, &3 = %s, &4 = %s\n", + bind, + group, bind->event_state_mask, (bind->event_state_mask & I3_XKB_GROUP_MASK_2) ? "yes" : "no", (bind->event_state_mask & I3_XKB_GROUP_MASK_3) ? "yes" : "no", @@ -415,21 +512,47 @@ void translate_keysyms(void) { 0 /* xkb_layout_index_t latched_group, */, group /* xkb_layout_index_t locked_group, */); + (void)xkb_state_update_mask( + dummy_state_numlock, + (bind->event_state_mask & 0x1FFF) | xcb_numlock_mask /* xkb_mod_mask_t base_mods, */, + 0 /* xkb_mod_mask_t latched_mods, */, + 0 /* xkb_mod_mask_t locked_mods, */, + 0 /* xkb_layout_index_t base_group, */, + 0 /* xkb_layout_index_t latched_group, */, + group /* xkb_layout_index_t locked_group, */); + + (void)xkb_state_update_mask( + dummy_state_numlock_no_shift, + ((bind->event_state_mask & 0x1FFF) | xcb_numlock_mask) ^ XCB_KEY_BUT_MASK_SHIFT /* xkb_mod_mask_t base_mods, */, + 0 /* xkb_mod_mask_t latched_mods, */, + 0 /* xkb_mod_mask_t locked_mods, */, + 0 /* xkb_layout_index_t base_group, */, + 0 /* xkb_layout_index_t latched_group, */, + group /* xkb_layout_index_t locked_group, */); + struct resolve resolving = { .bind = bind, .keysym = keysym, .xkb_state = dummy_state, .xkb_state_no_shift = dummy_state_no_shift, + .xkb_state_numlock = dummy_state_numlock, + .xkb_state_numlock_no_shift = dummy_state_numlock_no_shift, }; - FREE(bind->translated_to); - bind->number_keycodes = 0; + while (!TAILQ_EMPTY(&(bind->keycodes_head))) { + struct Binding_Keycode *first = TAILQ_FIRST(&(bind->keycodes_head)); + TAILQ_REMOVE(&(bind->keycodes_head), first, keycodes); + FREE(first); + } xkb_keymap_key_for_each(xkb_keymap, add_keycode_if_matches, &resolving); char *keycodes = sstrdup(""); - for (uint32_t n = 0; n < bind->number_keycodes; n++) { + int num_keycodes = 0; + struct Binding_Keycode *binding_keycode; + TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) { char *tmp; - sasprintf(&tmp, "%s %d", keycodes, bind->translated_to[n]); + sasprintf(&tmp, "%s %d", keycodes, binding_keycode->keycode); free(keycodes); keycodes = tmp; + num_keycodes++; /* check for duplicate bindings */ Binding *check; @@ -438,8 +561,8 @@ void translate_keysyms(void) { continue; if (check->symbol != NULL) continue; - if (check->keycode != bind->translated_to[n] || - check->event_state_mask != bind->event_state_mask || + if (check->keycode != binding_keycode->keycode || + check->event_state_mask != binding_keycode->modifiers || check->release != bind->release) continue; has_errors = true; @@ -447,12 +570,14 @@ void translate_keysyms(void) { } } DLOG("state=0x%x, cfg=\"%s\", sym=0x%x → keycodes%s (%d)\n", - bind->event_state_mask, bind->symbol, keysym, keycodes, bind->number_keycodes); + bind->event_state_mask, bind->symbol, keysym, keycodes, num_keycodes); free(keycodes); } xkb_state_unref(dummy_state); xkb_state_unref(dummy_state_no_shift); + xkb_state_unref(dummy_state_numlock); + xkb_state_unref(dummy_state_numlock_no_shift); if (has_errors) { start_config_error_nagbar(current_configpath, true); @@ -615,10 +740,14 @@ static Binding *binding_copy(Binding *bind) { ret->symbol = sstrdup(bind->symbol); if (bind->command != NULL) ret->command = sstrdup(bind->command); - if (bind->translated_to != NULL) { - ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes); - memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes); + TAILQ_INIT(&(ret->keycodes_head)); + struct Binding_Keycode *binding_keycode; + TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) { + struct Binding_Keycode *ret_binding_keycode = smalloc(sizeof(struct Binding_Keycode)); + *ret_binding_keycode = *binding_keycode; + TAILQ_INSERT_TAIL(&(ret->keycodes_head), ret_binding_keycode, keycodes); } + return ret; } @@ -630,8 +759,13 @@ void binding_free(Binding *bind) { return; } + while (!TAILQ_EMPTY(&(bind->keycodes_head))) { + struct Binding_Keycode *first = TAILQ_FIRST(&(bind->keycodes_head)); + TAILQ_REMOVE(&(bind->keycodes_head), first, keycodes); + FREE(first); + } + FREE(bind->symbol); - FREE(bind->translated_to); FREE(bind->command); FREE(bind); } @@ -811,15 +945,30 @@ bool load_keymap(void) { } /* - * Returns true if the current config has any binding to a scroll wheel button - * (4 or 5) which is a whole-window binding. - * We need this to figure out whether we should grab all buttons or just 1-3 - * when managing a window. See #2049. - * + * Returns a list of buttons that should be grabbed on a window. + * This list will always contain 1–3, all higher buttons will only be returned + * if there is a whole-window binding for it on some window in the current + * config. + * The list is terminated by a 0. */ -bool bindings_should_grab_scrollwheel_buttons(void) { +int *bindings_get_buttons_to_grab(void) { + /* Let's make the reasonable assumption that there's no more than 25 + * buttons. */ + int num_max = 25; + + int buffer[num_max]; + int num = 0; + + /* We always return buttons 1 through 3. */ + buffer[num++] = 1; + buffer[num++] = 2; + buffer[num++] = 3; + Binding *bind; TAILQ_FOREACH(bind, bindings, bindings) { + if (num + 1 == num_max) + break; + /* We are only interested in whole window mouse bindings. */ if (bind->input_type != B_MOUSE || !bind->whole_window) continue; @@ -831,11 +980,18 @@ bool bindings_should_grab_scrollwheel_buttons(void) { continue; } - /* If the binding is for either scrollwheel button, we need to grab everything. */ - if (button == 4 || button == 5) { - return true; + /* Avoid duplicates. */ + for (int i = 0; i < num_max; i++) { + if (buffer[i] == button) + continue; } - } - return false; + buffer[num++] = button; + } + buffer[num++] = 0; + + int *buttons = scalloc(num, sizeof(int)); + memcpy(buttons, buffer, num * sizeof(int)); + + return buttons; } diff --git a/src/click.c b/src/click.c index a670120f..913741b4 100644 --- a/src/click.c +++ b/src/click.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "click.c" /* * vim:ts=4:sw=4:expandtab * @@ -187,11 +185,10 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod * default behavior. */ if (dest == CLICK_DECORATION || dest == CLICK_INSIDE || dest == CLICK_BORDER) { Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event); - /* clicks over a window decoration will always trigger the binding and - * clicks on the inside of the window will only trigger a binding if - * the --whole-window flag was given for the binding. */ - if (bind && ((dest == CLICK_DECORATION || bind->whole_window) || - (dest == CLICK_BORDER && bind->border))) { + + if (bind != NULL && (dest == CLICK_DECORATION || + (dest == CLICK_INSIDE && bind->whole_window) || + (dest == CLICK_BORDER && bind->border))) { CommandResult *result = run_binding(bind, con); /* ASYNC_POINTER eats the event */ diff --git a/src/commands.c b/src/commands.c index 0faf2775..b91c71a4 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "commands.c" /* * vim:ts=4:sw=4:expandtab * @@ -9,6 +7,9 @@ * commands.c: all command functions (see commands_parser.c) * */ +#include "all.h" + +#include #include #include @@ -16,7 +17,6 @@ #include #endif -#include "all.h" #include "shmlog.h" // Macros to make the YAJL API a bit easier to use. @@ -46,7 +46,7 @@ } \ } while (0) -/** If an error occured during parsing of the criteria, we want to exit instead +/** If an error occurred during parsing of the criteria, we want to exit instead * of relying on fallback behavior. See #2091. */ #define HANDLE_INVALID_MATCH \ do { \ @@ -86,17 +86,6 @@ static bool definitelyGreaterThan(float a, float b, float epsilon) { return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); } -/* - * Returns the output containing the given container. - */ -static Output *get_output_of_con(Con *con) { - Con *output_con = con_get_output(con); - Output *output = get_output_by_name(output_con->name); - assert(output != NULL); - - return output; -} - /* * Checks whether we switched to a new workspace and returns false in that case, * signaling that further workspace switching should be done by the calling function @@ -1035,7 +1024,7 @@ void cmd_move_con_to_output(I3_CMD, const char *name) { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); - Output *current_output = get_output_of_con(current->con); + Output *current_output = get_output_for_con(current->con); assert(current_output != NULL); Output *output = get_output_from_string(current_output, name); @@ -1625,7 +1614,7 @@ void cmd_open(I3_CMD) { ystr("success"); y(bool, true); ystr("id"); - y(integer, (long int)con); + y(integer, (uintptr_t)con); y(map_close); cmd_output->needs_tree_render = true; @@ -1647,7 +1636,7 @@ void cmd_focus_output(I3_CMD, const char *name) { Output *output; TAILQ_FOREACH(current, &owindows, owindows) - current_output = get_output_of_con(current->con); + current_output = get_output_for_con(current->con); assert(current_output != NULL); output = get_output_from_string(current_output, name); diff --git a/src/commands_parser.c b/src/commands_parser.c index cdd35e45..98f06659 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "commands_parser.c" /* * vim:ts=4:sw=4:expandtab * @@ -25,6 +23,8 @@ * instead of actually calling any function). * */ +#include "all.h" + #include #include #include @@ -32,8 +32,6 @@ #include #include -#include "all.h" - // Macros to make the YAJL API a bit easier to use. #define y(x, ...) (command_output.json_gen != NULL ? yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) : 0) #define ystr(str) (command_output.json_gen != NULL ? yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) : 0) diff --git a/src/con.c b/src/con.c index cd17f9e5..36225415 100644 --- a/src/con.c +++ b/src/con.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "con.c" /* * vim:ts=4:sw=4:expandtab * @@ -12,6 +10,7 @@ * */ #include "all.h" + #include "yajl_utils.h" static void con_on_remove_child(Con *con); @@ -68,7 +67,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) { */ Con *con_new(Con *parent, i3Window *window) { Con *new = con_new_skeleton(parent, window); - x_con_init(new, new->depth); + x_con_init(new); return new; } @@ -617,6 +616,7 @@ void con_mark(Con *con, const char *mark, mark_mode_t mode) { mark_t *new = scalloc(1, sizeof(mark_t)); new->name = sstrdup(mark); TAILQ_INSERT_TAIL(&(con->marks_head), new, marks); + ipc_send_window_event("mark", con); con->mark_changed = true; } @@ -645,6 +645,8 @@ void con_unmark(Con *con, const char *name) { FREE(mark->name); TAILQ_REMOVE(&(current->marks_head), mark, marks); FREE(mark); + + ipc_send_window_event("mark", current); } current->mark_changed = true; @@ -668,6 +670,8 @@ void con_unmark(Con *con, const char *name) { FREE(mark->name); TAILQ_REMOVE(&(current->marks_head), mark, marks); FREE(mark); + + ipc_send_window_event("mark", current); break; } } @@ -727,6 +731,49 @@ int con_num_children(Con *con) { return children; } +/** + * Returns the number of visible non-floating children of this container. + * For example, if the container contains a hsplit which has two children, + * this will return 2 instead of 1. + */ +int con_num_visible_children(Con *con) { + if (con == NULL) + return 0; + + int children = 0; + Con *current = NULL; + TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + /* Visible leaf nodes are a child. */ + if (!con_is_hidden(current) && con_is_leaf(current)) + children++; + /* All other containers need to be recursed. */ + else + children += con_num_visible_children(current); + } + + return children; +} + +/* + * Count the number of windows (i.e., leaf containers). + * + */ +int con_num_windows(Con *con) { + if (con == NULL) + return 0; + + if (con_has_managed_window(con)) + return 1; + + int num = 0; + Con *current = NULL; + TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + num += con_num_windows(current); + } + + return num; +} + /* * Updates the percent attribute of the children of the given container. This * function needs to be called when a window is added or removed from a @@ -1157,6 +1204,19 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool _con_move_to_con(con, target, true, fix_coordinates, dont_warp, ignore_focus); } +/* + * Moves the given container to the currently focused container on the + * visible workspace on the given output. + * + */ +void con_move_to_output(Con *con, Output *output) { + Con *ws = NULL; + GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); + assert(ws != NULL); + DLOG("Moving con %p to output %s\n", con, output->name); + con_move_to_workspace(con, ws, false, false, false); +} + /* * Returns the orientation of the given container (for stacked containers, * vertical orientation is used regardless of the actual orientation of the @@ -1411,6 +1471,12 @@ Con *con_descend_direction(Con *con, direction_t direction) { * */ Rect con_border_style_rect(Con *con) { + if (config.hide_edge_borders == HEBM_SMART && con_num_visible_children(con_get_workspace(con)) <= 1) { + if (!con_is_floating(con)) { + return (Rect){0, 0, 0, 0}; + } + } + adjacent_t borders_to_hide = ADJ_NONE; int border_width = con->current_border_width; DLOG("The border width for con is set to: %d\n", con->current_border_width); @@ -1886,6 +1952,13 @@ bool con_has_urgent_child(Con *con) { void con_update_parents_urgency(Con *con) { Con *parent = con->parent; + /* Urgency hints should not be set on any container higher up in the + * hierarchy than the workspace level. Unfortunately, since the content + * container has type == CT_CON, that’s not easy to verify in the loop + * below, so we need another condition to catch that case: */ + if (con->type == CT_WORKSPACE) + return; + bool new_urgency_value = con->urgent; while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) { if (new_urgency_value) { diff --git a/src/config.c b/src/config.c index 9028a881..d4441d5d 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "config.c" /* * vim:ts=4:sw=4:expandtab * @@ -11,6 +9,7 @@ * */ #include "all.h" + #include char *current_configpath = NULL; @@ -160,13 +159,6 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, FREE(barconfig); } -/* Clear workspace names */ -#if 0 - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) - workspace_set_name(ws, NULL); -#endif - /* Invalidate pixmap caches in case font or colors changed */ Con *con; TAILQ_FOREACH(con, &all_cons, all_cons) @@ -254,21 +246,4 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, x_deco_recurse(croot); xcb_flush(conn); } - -#if 0 - /* Set an empty name for every workspace which got no name */ - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->name != NULL) { - /* If the font was not specified when the workspace name - * was loaded, we need to predict the text width now */ - if (ws->text_width == 0) - ws->text_width = predict_text_width(global_conn, - config.font, ws->name, ws->name_len); - continue; - } - - workspace_set_name(ws, NULL); - } -#endif } diff --git a/src/config_directives.c b/src/config_directives.c index ec99321a..6b5464f1 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "config_directives.c" /* * vim:ts=4:sw=4:expandtab * @@ -9,11 +7,11 @@ * config_directives.c: all config storing functions (see config_parser.c) * */ +#include "all.h" + #include #include -#include "all.h" - /******************************************************************************* * Criteria functions. ******************************************************************************/ @@ -223,18 +221,20 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width) } CFGFUN(hide_edge_borders, const char *borders) { - if (strcmp(borders, "vertical") == 0) - config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; + if (strcmp(borders, "smart") == 0) + config.hide_edge_borders = HEBM_SMART; + else if (strcmp(borders, "vertical") == 0) + config.hide_edge_borders = HEBM_VERTICAL; else if (strcmp(borders, "horizontal") == 0) - config.hide_edge_borders = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; + config.hide_edge_borders = HEBM_HORIZONTAL; else if (strcmp(borders, "both") == 0) - config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; + config.hide_edge_borders = HEBM_BOTH; else if (strcmp(borders, "none") == 0) - config.hide_edge_borders = ADJ_NONE; + config.hide_edge_borders = HEBM_NONE; else if (eval_boolstr(borders)) - config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; + config.hide_edge_borders = HEBM_VERTICAL; else - config.hide_edge_borders = ADJ_NONE; + config.hide_edge_borders = HEBM_NONE; } CFGFUN(focus_follows_mouse, const char *value) { diff --git a/src/config_parser.c b/src/config_parser.c index e97a37e1..a8265869 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "config_parser.c" /* * vim:ts=4:sw=4:expandtab * @@ -25,6 +23,8 @@ * nearest token. * */ +#include "all.h" + #include #include #include @@ -35,13 +35,14 @@ #include #include #include - -#include "all.h" +#include // Macros to make the YAJL API a bit easier to use. #define y(x, ...) yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) #define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) +xcb_xrm_database_t *database = NULL; + #ifndef TEST_PARSER pid_t config_error_nagbar_pid = -1; static struct context *context; @@ -811,18 +812,80 @@ void start_config_error_nagbar(const char *configpath, bool has_errors) { free(pageraction); } +/* + * Inserts or updates a variable assignment depending on whether it already exists. + * + */ +static void upsert_variable(struct variables_head *variables, char *key, char *value) { + struct Variable *current; + SLIST_FOREACH(current, variables, variables) { + if (strcmp(current->key, key) != 0) { + continue; + } + + DLOG("Updated variable: %s = %s -> %s\n", key, current->value, value); + FREE(current->value); + current->value = sstrdup(value); + return; + } + + DLOG("Defined new variable: %s = %s\n", key, value); + struct Variable *new = scalloc(1, sizeof(struct Variable)); + struct Variable *test = NULL, *loc = NULL; + new->key = sstrdup(key); + 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) { + if (strlen(new->key) >= strlen(test->key)) + break; + loc = test; + } + + if (loc == NULL) { + SLIST_INSERT_HEAD(variables, new, variables); + } else { + SLIST_INSERT_AFTER(loc, new, variables); + } +} + +static char *get_resource(char *name) { + if (conn == NULL) { + return NULL; + } + + /* Load the resource database lazily. */ + if (database == NULL) { + database = xcb_xrm_database_from_default(conn); + + if (database == NULL) { + ELOG("Failed to open the resource database.\n"); + + /* Load an empty database so we don't keep trying to load the + * default database over and over again. */ + database = xcb_xrm_database_from_string(""); + + return NULL; + } + } + + char *resource; + xcb_xrm_resource_get_string(database, name, NULL, &resource); + return resource; +} + /* * Parses the given file by first replacing the variables, then calling * parse_config and possibly launching i3-nagbar. * */ bool parse_file(const char *f, bool use_nagbar) { - SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables); + struct variables_head variables = SLIST_HEAD_INITIALIZER(&variables); int fd; struct stat stbuf; char *buf; FILE *fstr; - char buffer[4096], key[512], value[512], *continuation = NULL; + char buffer[4096], key[512], value[4096], *continuation = NULL; if ((fd = open(f, O_RDONLY)) == -1) die("Could not open configuration file: %s\n", strerror(errno)); @@ -846,48 +909,86 @@ bool parse_file(const char *f, bool use_nagbar) { if (buffer[strlen(buffer) - 1] != '\n' && !feof(fstr)) { ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer)); } + + /* sscanf implicitly strips whitespace. */ + const bool skip_line = (sscanf(buffer, "%511s %4095[^\n]", key, value) < 1 || strlen(key) < 3); + const bool comment = (key[0] == '#'); + value[4095] = '\n'; + continuation = strstr(buffer, "\\\n"); if (continuation) { - continue; + if (!comment) { + continue; + } + DLOG("line continuation in comment is ignored: \"%.*s\"\n", (int)strlen(buffer) - 1, buffer); + continuation = NULL; } strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1); - /* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */ - if (sscanf(buffer, "%511s %511[^\n]", key, value) < 1 || - key[0] == '#' || strlen(key) < 3) + /* Skip comments and empty lines. */ + if (skip_line || comment) { continue; + } if (strcasecmp(key, "set") == 0) { - if (value[0] != '$') { + char v_key[512]; + char v_value[4096]; + + if (sscanf(value, "%511s %4095[^\n]", v_key, v_value) < 1) { + ELOG("Failed to parse variable specification '%s', skipping it.\n", value); + continue; + } + + if (v_key[0] != '$') { ELOG("Malformed variable assignment, name has to start with $\n"); continue; } - /* get key/value for this variable */ - char *v_key = value, *v_value; - if (strstr(value, " ") == NULL && strstr(value, "\t") == NULL) { - ELOG("Malformed variable assignment, need a value\n"); + upsert_variable(&variables, v_key, v_value); + continue; + } else if (strcasecmp(key, "set_from_resource") == 0) { + char res_name[512]; + char v_key[512]; + char fallback[4096]; + + /* Ensure that this string is terminated. For example, a user might + * want a variable to be empty if the resource can't be found and + * uses + * set_from_resource $foo i3wm.foo + * Without explicitly terminating the string first, sscanf() will + * leave it uninitialized, causing garbage in the config.*/ + fallback[0] = '\0'; + + if (sscanf(value, "%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) { + ELOG("Failed to parse resource specification '%s', skipping it.\n", value); continue; } - if (!(v_value = strstr(value, " "))) - v_value = strstr(value, "\t"); + if (v_key[0] != '$') { + ELOG("Malformed variable assignment, name has to start with $\n"); + continue; + } - *(v_value++) = '\0'; - while (*v_value == '\t' || *v_value == ' ') - v_value++; + char *res_value = get_resource(res_name); + if (res_value == NULL) { + DLOG("Could not get resource '%s', using fallback '%s'.\n", res_name, fallback); + res_value = sstrdup(fallback); + } - struct Variable *new = scalloc(1, sizeof(struct Variable)); - new->key = sstrdup(v_key); - new->value = sstrdup(v_value); - SLIST_INSERT_HEAD(&variables, new, variables); - DLOG("Got new variable %s = %s\n", v_key, v_value); + upsert_variable(&variables, v_key, res_value); + FREE(res_value); continue; } } fclose(fstr); + if (database != NULL) { + xcb_xrm_database_free(database); + /* Explicitly set the database to NULL again in case the config gets reloaded. */ + database = NULL; + } + /* For every custom variable, see how often it occurs in the file and * how much extra bytes it requires when replaced. */ struct Variable *current, *nearest; diff --git a/src/debug.c b/src/debug.c index bb880b2f..ea4ca997 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "debug.c" /* * vim:ts=4:sw=4:expandtab * @@ -10,6 +8,8 @@ * events. This code is from xcb-util. * */ +#include + #include #include diff --git a/src/display_version.c b/src/display_version.c index c82610ed..0e650e81 100644 --- a/src/display_version.c +++ b/src/display_version.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "key_press.c" /* * vim:ts=4:sw=4:expandtab * @@ -10,6 +8,8 @@ * i3 --moreversion. * */ +#include "all.h" + #include #include #include @@ -17,7 +17,6 @@ #include #include #include -#include "all.h" static bool human_readable_key, loaded_config_file_name_key; static char *human_readable_version, *loaded_config_file_name; diff --git a/src/ewmh.c b/src/ewmh.c index c4ae844e..f4bb049a 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "ewmh.c" /* * vim:ts=4:sw=4:expandtab * @@ -124,11 +122,13 @@ void ewmh_update_desktop_viewport(void) { } static void ewmh_update_wm_desktop_recursively(Con *con, const uint32_t desktop) { - /* Recursively call this to descend through the entire subtree. */ Con *child; + + /* Recursively call this to descend through the entire subtree. */ 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) { @@ -139,8 +139,6 @@ static void ewmh_update_wm_desktop_recursively(Con *con, const uint32_t desktop) if (!con_has_managed_window(con)) return; - const xcb_window_t window = con->window->id; - uint32_t wm_desktop = desktop; /* Sticky windows are only actually sticky when they are floating or inside * a floating container. This is technically still slightly wrong, since @@ -151,11 +149,20 @@ static void ewmh_update_wm_desktop_recursively(Con *con, const uint32_t desktop) wm_desktop = NET_WM_DESKTOP_ALL; } + /* If the window is on the scratchpad we assign the sticky value to it + * since showing it works on any workspace. We cannot remove the property + * as per specification. */ + Con *ws = con_get_workspace(con); + if (ws != NULL && con_is_internal(ws)) { + wm_desktop = NET_WM_DESKTOP_ALL; + } + /* If this is the cached value, we don't need to do anything. */ if (con->window->wm_desktop == wm_desktop) return; con->window->wm_desktop = wm_desktop; + const xcb_window_t window = con->window->id; if (wm_desktop != NET_WM_DESKTOP_NONE) { DLOG("Setting _NET_WM_DESKTOP = %d for window 0x%08x.\n", wm_desktop, window); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, A__NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &wm_desktop); @@ -179,11 +186,11 @@ void ewmh_update_wm_desktop(void) { TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { Con *workspace; TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) { - if (con_is_internal(workspace)) - continue; - ewmh_update_wm_desktop_recursively(workspace, desktop); - ++desktop; + + if (!con_is_internal(workspace)) { + ++desktop; + } } } } diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 3df0e246..b898ce98 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "fake_outputs.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/floating.c b/src/floating.c index 231577fd..0a8b6957 100644 --- a/src/floating.c +++ b/src/floating.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "floating.c" /* * vim:ts=4:sw=4:expandtab * @@ -11,6 +9,10 @@ */ #include "all.h" +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + /* * Calculates sum of heights and sum of widths of all currently active outputs * @@ -176,7 +178,10 @@ void floating_enable(Con *con, bool automatic) { if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) && con_num_children(con->parent) == 0) { DLOG("Old container empty after setting this child to floating, closing\n"); - tree_close_internal(con->parent, DONT_KILL_WINDOW, false, false); + Con *parent = con->parent; + /* clear the pointer before calling tree_close_internal in which the memory is freed */ + con->parent = NULL; + tree_close_internal(parent, DONT_KILL_WINDOW, false, false); } char *name; @@ -313,6 +318,7 @@ void floating_disable(Con *con, bool automatic) { const bool set_focus = (con == focused); Con *ws = con_get_workspace(con); + Con *parent = con->parent; /* 1: detach from parent container */ TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes); @@ -321,7 +327,9 @@ void floating_disable(Con *con, bool automatic) { /* 2: kill parent container */ TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows); TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused); - tree_close_internal(con->parent, DONT_KILL_WINDOW, true, false); + /* clear the pointer before calling tree_close_internal in which the memory is freed */ + con->parent = NULL; + tree_close_internal(parent, DONT_KILL_WINDOW, true, false); /* 3: re-attach to the parent of the currently focused con on the workspace * this floating con was on */ @@ -617,7 +625,7 @@ void floating_resize_window(Con *con, const bool proportional, con->scratchpad_state = SCRATCHPAD_CHANGED; } -/* As endorsed by “ASSOCIATING CUSTOM DATA WITH A WATCHER” in ev(3) */ +/* Custom data structure used to track dragging-related events. */ struct drag_x11_cb { ev_check check; @@ -639,7 +647,7 @@ struct drag_x11_cb { }; static void xcb_drag_check_cb(EV_P_ ev_check *w, int revents) { - struct drag_x11_cb *dragloop = (struct drag_x11_cb *)w; + struct drag_x11_cb *dragloop = (struct drag_x11_cb *)w->data; xcb_motion_notify_event_t *last_motion_notify = NULL; xcb_generic_event_t *event; @@ -778,16 +786,18 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ .callback = callback, .extra = extra, }; + ev_check *check = &loop.check; if (con) loop.old_rect = con->rect; - ev_check_init(&loop.check, xcb_drag_check_cb); + ev_check_init(check, xcb_drag_check_cb); + check->data = &loop; main_set_x11_cb(false); - ev_check_start(main_loop, &loop.check); + ev_check_start(main_loop, check); while (loop.result == DRAGGING) ev_run(main_loop, EVRUN_ONCE); - ev_check_stop(main_loop, &loop.check); + ev_check_stop(main_loop, check); main_set_x11_cb(true); xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME); @@ -880,80 +890,3 @@ void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) { con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height) / (int32_t)old_rect->height - (int32_t)(con->rect.height / 2); DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y); } - -#if 0 -/* - * Moves the client 10px to the specified direction. - * - */ -void floating_move(xcb_connection_t *conn, Client *currently_focused, direction_t direction) { - DLOG("floating move\n"); - - Rect destination = currently_focused->rect; - Rect *screen = &(currently_focused->workspace->output->rect); - - switch (direction) { - case D_LEFT: - destination.x -= 10; - break; - case D_RIGHT: - destination.x += 10; - break; - case D_UP: - destination.y -= 10; - break; - case D_DOWN: - destination.y += 10; - break; - /* to make static analyzers happy */ - default: - break; - } - - /* Prevent windows from vanishing completely */ - if ((int32_t)(destination.x + destination.width - 5) <= (int32_t)screen->x || - (int32_t)(destination.x + 5) >= (int32_t)(screen->x + screen->width) || - (int32_t)(destination.y + destination.height - 5) <= (int32_t)screen->y || - (int32_t)(destination.y + 5) >= (int32_t)(screen->y + screen->height)) { - DLOG("boundary check failed, not moving\n"); - return; - } - - currently_focused->rect = destination; - reposition_client(conn, currently_focused); - - /* Because reposition_client does not send a faked configure event (only resize does), - * we need to initiate that on our own */ - fake_absolute_configure_notify(conn, currently_focused); - /* fake_absolute_configure_notify flushes */ -} - -/* - * Hides all floating clients (or show them if they are currently hidden) on - * the specified workspace. - * - */ -void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) { - Client *client; - - workspace->floating_hidden = !workspace->floating_hidden; - DLOG("floating_hidden is now: %d\n", workspace->floating_hidden); - TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) { - if (workspace->floating_hidden) - client_unmap(conn, client); - else client_map(conn, client); - } - - /* If we just unmapped all floating windows we should ensure that the focus - * is set correctly, that ist, to the first non-floating client in stack */ - if (workspace->floating_hidden) - SLIST_FOREACH(client, &(workspace->focus_stack), focus_clients) { - if (client_is_floating(client)) - continue; - set_focus(conn, client, true); - return; - } - - xcb_flush(conn); -} -#endif diff --git a/src/handlers.c b/src/handlers.c index 2991d7c3..7dfacef7 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "handlers.c" /* * vim:ts=4:sw=4:expandtab * @@ -148,18 +146,15 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { enter_child = true; } - /* If not, then the user moved their cursor to the root window. In that case, we adjust c_ws */ - if (con == NULL) { + /* If we cannot find the container, the user moved their cursor to the root + * window. In this case and if they used it to a dock, we need to focus the + * workspace on the correct output. */ + if (con == NULL || con->parent->type == CT_DOCKAREA) { DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y); check_crossing_screen_boundary(event->root_x, event->root_y); return; } - if (con->parent->type == CT_DOCKAREA) { - DLOG("Ignoring, this is a dock client\n"); - return; - } - /* see if the user entered the window on a certain window decoration */ layout_t layout = (enter_child ? con->parent->layout : con->layout); if (layout == L_DEFAULT) { @@ -172,16 +167,6 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { } } -#if 0 - if (client->workspace != c_ws && client->workspace->output == c_ws->output) { - /* This can happen when a client gets assigned to a different workspace than - * the current one (see src/mainx.c:reparent_window). Shortly after it was created, - * an enter_notify will follow. */ - DLOG("enter_notify for a client on a different workspace but the same screen, ignoring\n"); - return 1; - } -#endif - if (config.disable_focus_follows_mouse) return; @@ -421,22 +406,6 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { return; } -#if 0 - -/* - * Configuration notifies are only handled because we need to set up ignore for - * the following enter notify events. - * - */ -int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) { - DLOG("configure_event, sequence %d\n", event->sequence); - /* We ignore this sequence twice because events for child and frame should be ignored */ - add_ignore_event(event->sequence); - add_ignore_event(event->sequence); - - return 1; -} -#endif /* * Gets triggered upon a RandR screen change event, that is when the user @@ -630,23 +599,6 @@ static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t return true; } -#if 0 -/* - * Updates the client’s WM_CLASS property - * - */ -static int handle_windowclass_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 1; - - window_update_class(con->window, prop, false); - - return 0; -} -#endif - /* * Expose event means we should redraw our windows (= title bar) * @@ -759,14 +711,13 @@ static void handle_client_message(xcb_client_message_event_t *event) { } Con *ws = con_get_workspace(con); - if (ws == NULL) { DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n"); return; } - if (con_is_internal(ws)) { - DLOG("Workspace is internal, ignoring _NET_ACTIVE_WINDOW\n"); + if (con_is_internal(ws) && ws != workspace_get("__i3_scratch", NULL)) { + DLOG("Workspace is internal but not scratchpad, ignoring _NET_ACTIVE_WINDOW\n"); return; } @@ -775,10 +726,19 @@ static void handle_client_message(xcb_client_message_event_t *event) { /* Always focus the con if it is from a pager, because this is most * likely from some user action */ DLOG("This request came from a pager. Focusing con = %p\n", con); - workspace_show(ws); - con_focus(con); + + if (con_is_internal(ws)) { + scratchpad_show(con); + } else { + workspace_show(ws); + con_focus(con); + } } else { /* Request is from an application. */ + if (con_is_internal(ws)) { + DLOG("Ignoring request to make con = %p active because it's on an internal workspace.\n", con); + return; + } if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) { DLOG("Focusing con = %p\n", con); @@ -1214,6 +1174,38 @@ static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t stat return true; } +/* + * 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; + } + + border_style_t motif_border_style; + window_update_motif_hints(con->window, prop, &motif_border_style); + + if (motif_border_style != con->border_style && motif_border_style != BS_NORMAL) { + DLOG("Update border style of con %p to %d\n", con, motif_border_style); + con_set_border_style(con, motif_border_style, con->current_border_width); + + x_push_changes(croot); + } + + return true; +} + /* * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients. * @@ -1315,7 +1307,8 @@ static struct property_handler_t property_handlers[] = { {0, 128, handle_windowrole_change}, {0, 128, handle_class_change}, {0, UINT_MAX, handle_strut_partial_change}, - {0, UINT_MAX, handle_window_type}}; + {0, UINT_MAX, handle_window_type}, + {0, 5 * sizeof(uint64_t), handle_motif_hints_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -1336,6 +1329,7 @@ void property_handlers_init(void) { 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; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { diff --git a/src/i3.mk b/src/i3.mk deleted file mode 100644 index ed8e3ae9..00000000 --- a/src/i3.mk +++ /dev/null @@ -1,99 +0,0 @@ -ALL_TARGETS += i3 test-tools -INSTALL_TARGETS += install-i3 -CLEAN_TARGETS += clean-i3 - -i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) -i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) -i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCB_CURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCB_CURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread - -# When using clang, we use pre-compiled headers to speed up the build. With -# gcc, this actually makes the build slower. -ifeq ($(CC),clang) -i3_HEADERS_DEP := $(i3_HEADERS) include/all.h.pch -PCH_FLAGS := -include include/all.h -else -i3_HEADERS_DEP := $(i3_HEADERS) -PCH_FLAGS := -endif - -i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o) - -# The basename/pwd calls are for canonicalizing the path: Instead -# of src/main.c, we will see something like ../i3-4.2/src/main.c in -# debugger backtraces, making it clearer which code belongs to i3 and -# which code doesn’t. -# We only do this for src/ since all the other subdirectories contain i3 in -# their name already. -canonical_path := ../$(shell basename $(shell pwd -P)) - -include/all.h.pch: $(i3_HEADERS) - echo "[i3] PCH all.h" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch - -src/version.o: src/version.c LAST_VERSION $(i3_HEADERS_DEP) - echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$< - -src/%.o: src/%.c $(i3_HEADERS_DEP) - echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$< - -test-tools: test.commands_parser test.config_parser - -test.commands_parser: src/commands_parser.c $(i3_HEADERS_DEP) i3-command-parser.stamp libi3.a - echo "[i3] Link test.commands_parser" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -g -o test.commands_parser $< $(LIBS) $(i3_LIBS) - -test.config_parser: src/config_parser.c $(i3_HEADERS_DEP) i3-config-parser.stamp libi3.a - echo "[i3] Link test.config_parser" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -g -o test.config_parser $< $(LIBS) $(i3_LIBS) - -src/commands_parser.o: src/commands_parser.c $(i3_HEADERS_DEP) i3-command-parser.stamp - echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ ${canonical_path}/$< - -src/config_parser.o: src/config_parser.c $(i3_HEADERS_DEP) i3-config-parser.stamp - echo "[i3] CC $<" - $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ ${canonical_path}/$< - -i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec - echo "[i3] Generating command parser" - (cd $(TOPDIR)/include; ../generate-command-parser.pl --input=../parser-specs/commands.spec --prefix=command) - touch $@ - -i3-config-parser.stamp: generate-command-parser.pl parser-specs/config.spec - echo "[i3] Generating config parser" - (cd $(TOPDIR)/include; ../generate-command-parser.pl --input=../parser-specs/config.spec --prefix=config) - touch $@ - -i3: libi3.a $(i3_OBJECTS) - echo "[i3] Link i3" - $(CC) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_LIBS) - -install-i3: i3 - echo "[i3] Install" - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin - $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3 - $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/include/i3 - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions - $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications - $(INSTALL) -m 0755 i3 $(DESTDIR)$(EXEC_PREFIX)/bin/ - $(LN) -sf i3 $(DESTDIR)$(EXEC_PREFIX)/bin/i3-with-shmlog - $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(EXEC_PREFIX)/bin/ - $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(EXEC_PREFIX)/bin/ - $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(EXEC_PREFIX)/bin/ - $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(EXEC_PREFIX)/bin/ - $(INSTALL) -m 0755 i3-save-tree $(DESTDIR)$(EXEC_PREFIX)/bin/ - $(INSTALL) -m 0755 i3-dmenu-desktop $(DESTDIR)$(EXEC_PREFIX)/bin/ - test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config - test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes - $(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop - $(INSTALL) -m 0644 i3-with-shmlog.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3-with-shmlog.desktop - $(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop - $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(EXEC_PREFIX)/include/i3/ - -clean-i3: - echo "[i3] Clean" - rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3-config-parser.stamp i3 test.config_parser test.commands_parser src/*.gcno src/cfgparse.* src/cmdparse.* LAST_VERSION diff --git a/src/ipc.c b/src/ipc.c index 566fe52a..c0dfb1ec 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "ipc.c" /* * vim:ts=4:sw=4:expandtab * @@ -10,8 +8,10 @@ * */ #include "all.h" + #include "yajl_utils.h" +#include #include #include #include @@ -217,7 +217,7 @@ static void dump_binding(yajl_gen gen, Binding *bind) { void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(map_open); ystr("id"); - y(integer, (long int)con); + y(integer, (uintptr_t)con); ystr("type"); switch (con->type) { @@ -293,6 +293,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focused"); y(bool, (con == focused)); + if (con->type != CT_ROOT && con->type != CT_OUTPUT) { + ystr("output"); + ystr(con_get_output(con)->name); + } + ystr("layout"); switch (con->layout) { case L_DEFAULT: @@ -444,7 +449,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("focus"); y(array_open); TAILQ_FOREACH(node, &(con->focus_head), focused) { - y(integer, (long int)node); + y(integer, (uintptr_t)node); } y(array_close); @@ -479,7 +484,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { if (match->restart_mode) continue; y(map_open); - if (match->dock != -1) { + if (match->dock != M_DONTCHECK) { ystr("dock"); y(integer, match->dock); ystr("insert_where"); @@ -957,6 +962,28 @@ IPC_HANDLER(get_bar_config) { y(free); } +/* + * Returns a list of configured binding modes + * + */ +IPC_HANDLER(get_binding_modes) { + yajl_gen gen = ygenalloc(); + + y(array_open); + struct Mode *mode; + SLIST_FOREACH(mode, &modes, modes) { + ystr(mode->name); + } + y(array_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload); + y(free); +} + /* * Callback for the YAJL parser (will be called when a string is parsed). * @@ -1033,7 +1060,7 @@ IPC_HANDLER(subscribe) { /* The index of each callback function corresponds to the numeric * value of the message type (see include/i3/ipc.h) */ -handler_t handlers[8] = { +handler_t handlers[9] = { handle_command, handle_get_workspaces, handle_subscribe, @@ -1042,6 +1069,7 @@ handler_t handlers[8] = { handle_get_marks, handle_get_bar_config, handle_get_version, + handle_get_binding_modes, }; /* diff --git a/src/key_press.c b/src/key_press.c index 6760e35b..d16174f8 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "key_press.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/load_layout.c b/src/load_layout.c index 173e573b..7004a859 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "load_layout.c" /* * vim:ts=4:sw=4:expandtab * @@ -48,6 +46,7 @@ static int json_start_map(void *ctx) { LOG("creating new swallow\n"); current_swallow = smalloc(sizeof(Match)); match_init(current_swallow); + current_swallow->dock = M_DONTCHECK; TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches); swallow_is_empty = true; } else { @@ -150,7 +149,7 @@ static int json_end_map(void *ctx) { LOG("attaching\n"); con_attach(json_node, json_node->parent, true); LOG("Creating window\n"); - x_con_init(json_node, json_node->depth); + x_con_init(json_node); json_node = json_node->parent; } diff --git a/src/log.c b/src/log.c index e8a08b53..1c33649a 100644 --- a/src/log.c +++ b/src/log.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "log.c" /* * vim:ts=4:sw=4:expandtab * @@ -9,6 +7,8 @@ * log.c: Logging functions. * */ +#include + #include #include #include @@ -20,7 +20,9 @@ #include #include #include +#if !defined(__OpenBSD__) #include +#endif #include "util.h" #include "log.h" @@ -157,11 +159,13 @@ void open_logbuffer(void) { header = (i3_shmlog_header *)logbuffer; +#if !defined(__OpenBSD__) pthread_condattr_t cond_attr; pthread_condattr_init(&cond_attr); if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0) fprintf(stderr, "pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n"); pthread_cond_init(&(header->condvar), &cond_attr); +#endif logwalk = logbuffer + sizeof(i3_shmlog_header); loglastwrap = logbuffer + logbuffer_size; @@ -277,8 +281,10 @@ static void vlog(const bool print, const char *fmt, va_list args) { store_log_markers(); +#if !defined(__OpenBSD__) /* Wake up all (i3-dump-log) processes waiting for condvar. */ pthread_cond_broadcast(&(header->condvar)); +#endif if (print) fwrite(message, len, 1, stdout); diff --git a/src/main.c b/src/main.c index b2ce17d8..4737175b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "main.c" /* * vim:ts=4:sw=4:expandtab * @@ -9,6 +7,8 @@ * main.c: Initialization, main loop * */ +#include "all.h" + #include #include #include @@ -19,7 +19,6 @@ #include #include #include -#include "all.h" #include "shmlog.h" #include "sd-daemon.h" @@ -189,7 +188,7 @@ static void handle_signal(int sig, siginfo_t *info, void *data) { int main(int argc, char *argv[]) { /* Keep a symbol pointing to the I3_VERSION string constant so that we have * it in gdb backtraces. */ - const char *_i3_version __attribute__((unused)) = i3_version; + static const char *_i3_version __attribute__((used)) = I3_VERSION; char *override_configpath = NULL; bool autostart = true; char *layout_path = NULL; @@ -404,12 +403,14 @@ int main(int argc, char *argv[]) { memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + FREE(socket_path); if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) err(EXIT_FAILURE, "Could not connect to i3"); if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)payload) == -1) err(EXIT_FAILURE, "IPC: write()"); + FREE(payload); uint32_t reply_length; uint32_t reply_type; @@ -423,6 +424,7 @@ int main(int argc, char *argv[]) { if (reply_type != I3_IPC_MESSAGE_TYPE_COMMAND) errx(EXIT_FAILURE, "IPC: received reply of type %d but expected %d (COMMAND)", reply_type, I3_IPC_MESSAGE_TYPE_COMMAND); printf("%.*s\n", reply_length, reply); + FREE(reply); return 0; } @@ -451,7 +453,7 @@ int main(int argc, char *argv[]) { memset(cwd, '\0', cwd_size); if (read(patternfd, cwd, cwd_size) > 0) /* a trailing newline is included in cwd */ - LOG("CORE DUMPS: Your core_pattern is: \"%s\".\n", cwd); + LOG("CORE DUMPS: Your core_pattern is: %s", cwd); close(patternfd); } free(cwd); @@ -503,10 +505,11 @@ int main(int argc, char *argv[]) { visual_type = get_visualtype(root_screen); } + init_dpi(); + DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_type->visual_id); - DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d, dpi = %d\n", - root_screen->height_in_pixels, root_screen->height_in_millimeters, - (int)((double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters)); + DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d\n", + root_screen->height_in_pixels, root_screen->height_in_millimeters); DLOG("One logical pixel corresponds to %d physical pixels on this display.\n", logical_px(1)); xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root); @@ -538,7 +541,11 @@ int main(int argc, char *argv[]) { xcb_void_cookie_t cookie; cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK}); - check_error(conn, cookie, "Another window manager seems to be running"); + xcb_generic_error_t *error = xcb_request_check(conn, cookie); + if (error != NULL) { + ELOG("Another window manager seems to be running (X error %d)\n", error->error_code); + return 1; + } xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL); if (greply == NULL) { @@ -623,7 +630,7 @@ int main(int argc, char *argv[]) { grab_all_keys(conn); bool needs_tree_init = true; - if (layout_path) { + if (layout_path != NULL) { LOG("Trying to restore the layout from \"%s\".\n", layout_path); needs_tree_init = !tree_restore(layout_path, greply); if (delete_layout_path) { @@ -633,7 +640,6 @@ int main(int argc, char *argv[]) { * sockets) left. */ rmdir(dir); } - free(layout_path); } if (needs_tree_init) tree_init(greply); @@ -658,6 +664,33 @@ int main(int argc, char *argv[]) { randr_init(&randr_base); } + /* We need to force disabling outputs which have been loaded from the + * layout file but are no longer active. This can happen if the output has + * been disabled in the short time between writing the restart layout file + * and restarting i3. See #2326. */ + if (layout_path != NULL && randr_base > -1) { + Con *con; + TAILQ_FOREACH(con, &(croot->nodes_head), nodes) { + Output *output; + TAILQ_FOREACH(output, &outputs, outputs) { + if (output->active || strcmp(con->name, output->name) != 0) + continue; + + /* This will correctly correlate the output with its content + * container. We need to make the connection to properly + * disable the output. */ + if (output->con == NULL) { + output_init_con(output); + output->changed = false; + } + + output->to_be_disabled = true; + randr_disable_output(output); + } + } + } + FREE(layout_path); + scratchpad_fix_resolution(); xcb_query_pointer_reply_t *pointerreply; @@ -796,7 +829,7 @@ int main(int argc, char *argv[]) { (uint32_t[]){XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS}); xcb_copy_area(conn, root->root, pixmap, gc, 0, 0, 0, 0, width, height); - xcb_change_window_attributes_checked(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap}); + xcb_change_window_attributes(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap}); xcb_flush(conn); xcb_free_gc(conn, gc); xcb_free_pixmap(conn, pixmap); diff --git a/src/manage.c b/src/manage.c index 93272f1b..81ee16fd 100644 --- a/src/manage.c +++ b/src/manage.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "manage.c" /* * vim:ts=4:sw=4:expandtab * @@ -10,6 +8,7 @@ * */ #include "all.h" + #include "yajl_utils.h" #include @@ -170,7 +169,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki cwindow->id = window; cwindow->depth = get_visual_depth(attr->visual); - xcb_grab_buttons(conn, window, bindings_should_grab_scrollwheel_buttons()); + int *buttons = bindings_get_buttons_to_grab(); + xcb_grab_buttons(conn, window, buttons); + FREE(buttons); /* update as much information as possible so far (some replies may be NULL) */ window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true); @@ -359,8 +360,16 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_FULLSCREEN)) { /* If this window is already fullscreen (after restarting!), skip * toggling fullscreen, that would drop it out of fullscreen mode. */ - if (fs != nc) + if (fs != nc) { + Output *output = get_output_with_dimensions((Rect){geom->x, geom->y, geom->width, geom->height}); + /* If the requested window geometry spans the whole area + * of an output, move the window to that output. This is + * needed e.g. for LibreOffice Impress multi-monitor + * presentations to work out of the box. */ + if (output != NULL) + con_move_to_output(nc, output); con_toggle_fullscreen(nc, CF_OUTPUT); + } fs = NULL; } @@ -420,7 +429,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY)) nc->sticky = true; - if (cwindow->wm_desktop == NET_WM_DESKTOP_ALL) { + /* We ignore the hint for an internal workspace because windows in the + * scratchpad also have this value, but upon restarting i3 we don't want + * them to become sticky windows. */ + if (cwindow->wm_desktop == NET_WM_DESKTOP_ALL && (ws == NULL || !con_is_internal(ws))) { DLOG("This window has _NET_WM_DESKTOP = 0xFFFFFFFF. Will float it and make it sticky.\n"); nc->sticky = true; want_floating = true; @@ -567,7 +579,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki /* The first window on a workspace should always be focused. We have to * compare with == 1 because the container has already been inserted at * this point. */ - if (con_num_children(ws) == 1) { + if (con_num_windows(ws) == 1) { DLOG("This is the first window on this workspace, ignoring no_focus.\n"); } else { DLOG("no_focus was set for con = %p, not setting focus.\n", nc); @@ -592,9 +604,20 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki xcb_discard_reply(conn, wm_user_time_cookie.sequence); } + if (set_focus) { + /* Even if the client doesn't want focus, we still need to focus the + * container to not break focus workflows. Our handling towards X will + * take care of not setting the input focus. However, one exception to + * this are clients using the globally active input model which we + * don't want to focus at all. */ + if (nc->window->doesnt_accept_focus && !nc->window->needs_take_focus) { + set_focus = false; + } + } + /* Defer setting focus after the 'new' event has been sent to ensure the * proper window event sequence. */ - if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) { + if (set_focus && nc->mapped) { DLOG("Now setting focus.\n"); con_focus(nc); } diff --git a/src/match.c b/src/match.c index d072b85f..4d87c560 100644 --- a/src/match.c +++ b/src/match.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "match.c" /* * vim:ts=4:sw=4:expandtab * @@ -27,8 +25,8 @@ */ void match_init(Match *match) { memset(match, 0, sizeof(Match)); - match->dock = M_DONTCHECK; match->urgent = U_DONTCHECK; + match->window_mode = WM_ANY; /* we use this as the placeholder value for "not set". */ match->window_type = UINT32_MAX; } @@ -53,8 +51,8 @@ bool match_is_empty(Match *match) { match->id == XCB_NONE && match->window_type == UINT32_MAX && match->con_id == NULL && - match->dock == -1 && - match->floating == M_ANY); + match->dock == M_NODOCK && + match->window_mode == WM_ANY); } /* @@ -244,6 +242,21 @@ bool match_matches_window(Match *match, i3Window *window) { } } + if (match->window_mode != WM_ANY) { + 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; + } + + LOG("window_mode matches\n"); + } + return true; } @@ -385,5 +398,15 @@ void match_parse_property(Match *match, const char *ctype, const char *cvalue) { return; } + if (strcmp(ctype, "tiling") == 0) { + match->window_mode = WM_TILING; + return; + } + + if (strcmp(ctype, "floating") == 0) { + match->window_mode = WM_FLOATING; + return; + } + ELOG("Unknown criterion: %s\n", ctype); } diff --git a/src/move.c b/src/move.c index 87f78ee3..70c8c788 100644 --- a/src/move.c +++ b/src/move.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "move.c" /* * vim:ts=4:sw=4:expandtab * @@ -101,8 +99,7 @@ static void attach_to_workspace(Con *con, Con *ws, direction_t direction) { */ static void move_to_output_directed(Con *con, direction_t direction) { Con *old_ws = con_get_workspace(con); - Con *current_output_con = con_get_output(con); - Output *current_output = get_output_by_name(current_output_con->name); + Output *current_output = get_output_for_con(con); Output *output = get_output_next(direction, current_output, CLOSEST_OUTPUT); if (!output) { diff --git a/src/output.c b/src/output.c index 62b146a7..0f2bd617 100644 --- a/src/output.c +++ b/src/output.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "output.c" /* * vim:ts=4:sw=4:expandtab * @@ -31,18 +29,33 @@ Con *output_get_content(Con *output) { * */ Output *get_output_from_string(Output *current_output, const char *output_str) { - Output *output; + if (strcasecmp(output_str, "current") == 0) { + return get_output_for_con(focused); + } else if (strcasecmp(output_str, "left") == 0) { + return get_output_next_wrap(D_LEFT, current_output); + } else if (strcasecmp(output_str, "right") == 0) { + return get_output_next_wrap(D_RIGHT, current_output); + } else if (strcasecmp(output_str, "up") == 0) { + return get_output_next_wrap(D_UP, current_output); + } else if (strcasecmp(output_str, "down") == 0) { + return get_output_next_wrap(D_DOWN, current_output); + } - if (strcasecmp(output_str, "left") == 0) - output = get_output_next_wrap(D_LEFT, current_output); - else if (strcasecmp(output_str, "right") == 0) - output = get_output_next_wrap(D_RIGHT, current_output); - else if (strcasecmp(output_str, "up") == 0) - output = get_output_next_wrap(D_UP, current_output); - else if (strcasecmp(output_str, "down") == 0) - output = get_output_next_wrap(D_DOWN, current_output); - else - output = get_output_by_name(output_str); + return get_output_by_name(output_str); +} + +Output *get_output_for_con(Con *con) { + Con *output_con = con_get_output(con); + if (output_con == NULL) { + ELOG("Could not get the output container for con = %p.\n", con); + return NULL; + } + + Output *output = get_output_by_name(output_con->name); + if (output == NULL) { + ELOG("Could not get output from name \"%s\".\n", output_con->name); + return NULL; + } return output; } diff --git a/src/randr.c b/src/randr.c index 6753f8a6..e5dcddfc 100644 --- a/src/randr.c +++ b/src/randr.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "randr.c" /* * vim:ts=4:sw=4:expandtab * @@ -108,6 +106,27 @@ Output *get_output_containing(unsigned int x, unsigned int y) { return NULL; } +/* + * Returns the active output which spans exactly the area specified by + * rect or NULL if there is no output like this. + * + */ +Output *get_output_with_dimensions(Rect rect) { + Output *output; + 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", + rect.x, rect.y, rect.width, rect.height, + output->rect.x, output->rect.y, output->rect.width, output->rect.height); + if (rect.x == output->rect.x && rect.width == output->rect.width && + rect.y == output->rect.y && rect.height == output->rect.height) + return output; + } + + return NULL; +} + /* * In contained_by_output, we check if any active output contains part of the container. * We do this by checking if the output rect is intersected by the Rect. @@ -420,7 +439,7 @@ void init_ws_for_output(Output *output, Con *content) { /* In case the workspace we just moved was visible but there was no * other workspace to switch to, we need to initialize the source - * output aswell */ + * output as well */ if (visible && previous == NULL) { LOG("There is no workspace left on \"%s\", re-initializing\n", workspace_out->name); @@ -601,7 +620,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id, * */ void randr_query_outputs(void) { - Output *output, *other, *first; + Output *output, *other; xcb_randr_get_output_primary_cookie_t pcookie; xcb_randr_get_screen_resources_current_cookie_t rcookie; @@ -712,84 +731,7 @@ void randr_query_outputs(void) { * because the user disabled them or because they are clones) */ TAILQ_FOREACH(output, &outputs, outputs) { if (output->to_be_disabled) { - output->active = false; - DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); - - 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, if the disappearing ws is focused */ - Con *next = NULL; - if (TAILQ_FIRST(&(croot->focus_head)) == output->con) { - DLOG("This output (%p) was focused! Getting next\n", output->con); - next = focused; - DLOG("next = %p\n", next); - } - - /* 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, 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)); - DLOG("Done, next\n"); - } - DLOG("re-attached all workspaces\n"); - - 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); - tree_close_internal(output->con, DONT_KILL_WINDOW, true, false); - DLOG("Done. Should be fine now\n"); - output->con = NULL; - } - - output->to_be_disabled = false; - output->changed = false; + randr_disable_output(output); } if (output->changed) { @@ -825,6 +767,96 @@ void randr_query_outputs(void) { FREE(primary); } +/* + * Disables the output and moves its content. + * + */ +void randr_disable_output(Output *output) { + assert(output->to_be_disabled); + + output->active = false; + DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); + + 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, if the disappearing ws is focused */ + Con *next = NULL; + if (TAILQ_FIRST(&(croot->focus_head)) == output->con) { + DLOG("This output (%p) was focused! Getting next\n", output->con); + next = focused; + DLOG("next = %p\n", next); + } + + /* 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, 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)); + } + DLOG("Done, next\n"); + } + DLOG("re-attached all workspaces\n"); + + 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); + 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, false); + DLOG("Done. Should be fine now\n"); + } + + output->to_be_disabled = false; + output->changed = false; +} + /* * We have just established a connection to the X server and need the initial * XRandR information to setup workspaces for each screen. diff --git a/src/regex.c b/src/regex.c index 24846981..296b91dd 100644 --- a/src/regex.c +++ b/src/regex.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "regex.c" /* * vim:ts=4:sw=4:expandtab * @@ -28,11 +26,9 @@ struct regex *regex_new(const char *pattern) { struct regex *re = scalloc(1, sizeof(struct regex)); re->pattern = sstrdup(pattern); int options = PCRE_UTF8; -#ifdef PCRE_HAS_UCP /* We use PCRE_UCP so that \B, \b, \D, \d, \S, \s, \W, \w and some POSIX * character classes play nicely with Unicode */ options |= PCRE_UCP; -#endif while (!(re->regex = pcre_compile2(pattern, options, &errorcode, &error, &offset, NULL))) { /* If the error is that PCRE was not compiled with UTF-8 support we * disable it and try again */ diff --git a/src/render.c b/src/render.c index 9fa40f03..85548f26 100644 --- a/src/render.c +++ b/src/render.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "render.c" /* * vim:ts=4:sw=4:expandtab * @@ -167,7 +165,7 @@ void render_con(Con *con, bool render_fullscreen) { /* By rendering the stacked container again, we handle the case * that we have a non-leaf-container inside the stack. In that * case, the children of the non-leaf-container need to be raised - * aswell. */ + * as well. */ render_con(child, false); } diff --git a/src/resize.c b/src/resize.c index 5d9eb2eb..f07fcec6 100644 --- a/src/resize.c +++ b/src/resize.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "resize.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/restore_layout.c b/src/restore_layout.c index 7e1b78ae..131196e9 100644 --- a/src/restore_layout.c +++ b/src/restore_layout.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "restore_layout.c" /* * vim:ts=4:sw=4:expandtab * @@ -240,6 +238,7 @@ static void open_placeholder_window(Con *con) { /* create temporary id swallow to match the placeholder */ Match *temp_id = smalloc(sizeof(Match)); match_init(temp_id); + temp_id->dock = M_DONTCHECK; temp_id->id = placeholder; TAILQ_INSERT_HEAD(&(con->swallow_head), temp_id, matches); } diff --git a/src/scratchpad.c b/src/scratchpad.c index 6d83a558..9018ad3f 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "scratchpad.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/sd-daemon.c b/src/sd-daemon.c index f0b5ca05..dc33b2e9 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -45,9 +45,6 @@ #include "sd-daemon.h" int sd_listen_fds(int unset_environment) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else int r, fd; const char *e; char *p = NULL; @@ -121,7 +118,6 @@ finish: } return r; -#endif } int sd_is_fifo(int fd, const char *path) { diff --git a/src/sighandler.c b/src/sighandler.c index 80d2fae2..79f90d9a 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "sighandler.c" /* * vim:ts=4:sw=4:expandtab * @@ -116,7 +114,7 @@ static int backtrace(void) { waitpid(pid_gdb, &status, 0); - /* see if the backtrace was succesful or not */ + /* see if the backtrace was successful or not */ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { DLOG("GDB did not run properly\n"); return -1; @@ -176,7 +174,7 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei static int sig_handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { uint16_t state = event->state; - /* Apparantly, after activating numlock once, the numlock modifier + /* Apparently, after activating numlock once, the numlock modifier * stays turned on (use xev(1) to verify). So, to resolve useful * keysyms, we remove the numlock flag from the event state */ state &= ~xcb_numlock_mask; diff --git a/src/startup.c b/src/startup.c index b7950c20..166842e0 100644 --- a/src/startup.c +++ b/src/startup.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "startup.c" /* * vim:ts=4:sw=4:expandtab * @@ -13,6 +11,7 @@ * */ #include "all.h" + #include "sd-daemon.h" #include @@ -103,8 +102,8 @@ static int _prune_startup_sequences(void) { */ void startup_sequence_delete(struct Startup_Sequence *sequence) { assert(sequence != NULL); - DLOG("Deleting startup sequence %s, delete_at = %ld, current_time = %ld\n", - sequence->id, sequence->delete_at, time(NULL)); + DLOG("Deleting startup sequence %s, delete_at = %lld, current_time = %lld\n", + sequence->id, (long long)sequence->delete_at, (long long)time(NULL)); /* Unref the context, will be free()d */ sn_launcher_context_unref(sequence->context); @@ -239,8 +238,8 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) { /* Mark the given sequence for deletion in 30 seconds. */ time_t current_time = time(NULL); sequence->delete_at = current_time + 30; - DLOG("Will delete startup sequence %s at timestamp %ld\n", - sequence->id, sequence->delete_at); + DLOG("Will delete startup sequence %s at timestamp %lld\n", + sequence->id, (long long)sequence->delete_at); if (_prune_startup_sequences() == 0) { DLOG("No more startup sequences running, changing root window cursor to default pointer.\n"); diff --git a/src/tree.c b/src/tree.c index 0c301209..6e128363 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "tree.c" /* * vim:ts=4:sw=4:expandtab * @@ -477,7 +475,7 @@ static void mark_unmapped(Con *con) { TAILQ_FOREACH(current, &(con->nodes_head), nodes) mark_unmapped(current); if (con->type == CT_WORKSPACE) { - /* We need to call mark_unmapped on floating nodes aswell since we can + /* 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); diff --git a/src/util.c b/src/util.c index 35ce8b19..cfe4c953 100644 --- a/src/util.c +++ b/src/util.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "util.c" /* * vim:ts=4:sw=4:expandtab * @@ -145,20 +143,6 @@ void exec_i3_utility(char *name, char *argv[]) { _exit(2); } -/* - * Checks a generic cookie for errors and quits with the given message if there - * was an error. - * - */ -void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, char *err_message) { - xcb_generic_error_t *error = xcb_request_check(conn, cookie); - if (error != NULL) { - fprintf(stderr, "ERROR: %s (X error %d)\n", err_message, error->error_code); - xcb_disconnect(conn); - exit(-1); - } -} - /* * Checks if the given path exists by calling stat(). * diff --git a/src/version.c b/src/version.c index 6e385ada..1244711a 100644 --- a/src/version.c +++ b/src/version.c @@ -8,4 +8,6 @@ * and used dynamically without recompiling every object file. * */ +#include + const char *i3_version = I3_VERSION; diff --git a/src/window.c b/src/window.c index d10811f5..db6215d0 100644 --- a/src/window.c +++ b/src/window.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "window.c" /* * vim:ts=4:sw=4:expandtab * diff --git a/src/workspace.c b/src/workspace.c index f8d15ba1..d7f2ce7c 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "workspace.c" /* * vim:ts=4:sw=4:expandtab * @@ -525,7 +523,7 @@ Con *workspace_next(void) { continue; if (!first) first = child; - if (!first_opposite && child->num != -1) + if (!first_opposite || (child->num != -1 && child->num < first_opposite->num)) first_opposite = child; if (child == current) { found_current = true; @@ -544,7 +542,7 @@ Con *workspace_next(void) { NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; - if (!first) + if (!first || (child->num != -1 && child->num < first->num)) first = child; if (!first_opposite && child->num == -1) first_opposite = child; @@ -590,13 +588,13 @@ Con *workspace_prev(void) { continue; if (!last) last = child; - if (!first_opposite && child->num != -1) + if (!first_opposite || (child->num != -1 && child->num > first_opposite->num)) first_opposite = child; if (child == current) { found_current = true; } else if (child->num == -1 && found_current) { prev = child; - goto workspace_prev_end; + return prev; } } } @@ -610,7 +608,7 @@ Con *workspace_prev(void) { NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; - if (!last) + if (!last || (child->num != -1 && last->num < child->num)) last = child; if (!first_opposite && child->num == -1) first_opposite = child; @@ -628,7 +626,6 @@ Con *workspace_prev(void) { if (!prev) prev = first_opposite ? first_opposite : last; -workspace_prev_end: return prev; } @@ -912,17 +909,12 @@ Con *workspace_encapsulate(Con *ws) { bool workspace_move_to_output(Con *ws, const char *name) { LOG("Trying to move workspace %p / %s to output \"%s\".\n", ws, ws->name, name); - Con *current_output_con = con_get_output(ws); - if (!current_output_con) { - ELOG("Could not get the output container for workspace %p / %s.\n", ws, ws->name); - return false; - } - - Output *current_output = get_output_by_name(current_output_con->name); - if (!current_output) { + Output *current_output = get_output_for_con(ws); + if (current_output == NULL) { ELOG("Cannot get current output. This is a bug in i3.\n"); return false; } + Output *output = get_output_from_string(current_output, name); if (!output) { ELOG("Could not get output from string \"%s\"\n", name); diff --git a/src/x.c b/src/x.c index f44bc37a..8d7d3dd8 100644 --- a/src/x.c +++ b/src/x.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "x.c" /* * vim:ts=4:sw=4:expandtab * @@ -12,6 +10,10 @@ */ #include "all.h" +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + xcb_window_t ewmh_window; /* Stores the X11 window ID of the currently focused window */ @@ -94,18 +96,25 @@ static con_state *state_for_frame(xcb_window_t window) { * every container from con_new(). * */ -void x_con_init(Con *con, uint16_t depth) { +void x_con_init(Con *con) { /* TODO: maybe create the window when rendering first? we could then even * get the initial geometry right */ uint32_t mask = 0; uint32_t values[5]; - /* For custom visuals, we need to create a colormap before creating - * this window. It will be freed directly after creating the window. */ - xcb_visualid_t visual = get_visualid_by_depth(depth); - xcb_colormap_t win_colormap = xcb_generate_id(conn); - xcb_create_colormap_checked(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, root, visual); + xcb_visualid_t visual = get_visualid_by_depth(con->depth); + xcb_colormap_t win_colormap; + if (con->depth != root_depth) { + /* We need to create a custom colormap. */ + win_colormap = xcb_generate_id(conn); + xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, root, visual); + con->colormap = win_colormap; + } else { + /* Use the default colormap. */ + win_colormap = colormap; + con->colormap = XCB_NONE; + } /* We explicitly set a background color and border color (even though we * don’t even have a border) because the X11 server requires us to when @@ -129,7 +138,7 @@ void x_con_init(Con *con, uint16_t depth) { values[4] = win_colormap; Rect dims = {-15, -15, 10, 10}; - xcb_window_t frame_id = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); + xcb_window_t frame_id = create_window(conn, dims, con->depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); draw_util_surface_init(conn, &(con->frame), frame_id, get_visualtype_by_id(visual), dims.width, dims.height); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, @@ -140,9 +149,6 @@ void x_con_init(Con *con, uint16_t depth) { (strlen("i3-frame") + 1) * 2, "i3-frame\0i3-frame\0"); - if (win_colormap != XCB_NONE) - xcb_free_colormap(conn, win_colormap); - struct con_state *state = scalloc(1, sizeof(struct con_state)); state->id = con->frame.id; state->mapped = false; @@ -225,6 +231,10 @@ void x_move_win(Con *src, Con *dest) { void x_con_kill(Con *con) { con_state *state; + if (con->colormap != XCB_NONE) { + xcb_free_colormap(conn, con->colormap); + } + draw_util_surface_free(conn, &(con->frame)); draw_util_surface_free(conn, &(con->frame_buffer)); xcb_destroy_window(conn, con->frame.id); @@ -323,13 +333,20 @@ static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p assert(con->parent != NULL); Rect *dr = &(con->deco_rect); - Rect br = con_border_style_rect(con); /* Redraw the right border to cut off any text that went past it. * This is necessary when the text was drawn using XCB since cutting text off * automatically does not work there. For pango rendering, this isn't necessary. */ - draw_util_rectangle(conn, &(con->parent->frame_buffer), p->color->background, - dr->x + dr->width + br.width, dr->y, -br.width, dr->height); + if (!font_is_pango()) { + /* We actually only redraw the far right two pixels as that is the + * distance we keep from the edge (not the entire border width). + * Redrawing the entire border would cause text to be cut off. */ + draw_util_rectangle(conn, &(con->parent->frame_buffer), p->color->background, + dr->x + dr->width - 2 * logical_px(1), + dr->y, + 2 * logical_px(1), + dr->height); + } /* Draw a 1px separator line before and after every tab, so that tabs can * be easily distinguished. */ @@ -453,11 +470,6 @@ void x_draw_decoration(Con *con) { borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; Rect br = con_border_style_rect(con); -#if 0 - DLOG("con->rect spans %d x %d\n", con->rect.width, con->rect.height); - DLOG("border_rect spans (%d, %d) with %d x %d\n", br.x, br.y, br.width, br.height); - DLOG("window_rect spans (%d, %d) with %d x %d\n", con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height); -#endif /* These rectangles represent the border around the child window * (left, bottom and right part). We don’t just fill the whole @@ -544,8 +556,9 @@ void x_draw_decoration(Con *con) { draw_util_text(title, &(parent->frame_buffer), p->color->text, p->color->background, - con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, - con->deco_rect.width - 2); + con->deco_rect.x + logical_px(2), + con->deco_rect.y + text_offset_y, + con->deco_rect.width - 2 * logical_px(2)); I3STRING_FREE(title); goto after_title; @@ -554,23 +567,6 @@ void x_draw_decoration(Con *con) { if (win->name == NULL) goto copy_pixmaps; - int indent_level = 0, - indent_mult = 0; - Con *il_parent = parent; - if (il_parent->layout != L_STACKED) { - while (1) { - //DLOG("il_parent = %p, layout = %d\n", il_parent, il_parent->layout); - if (il_parent->layout == L_STACKED) - indent_level++; - if (il_parent->type == CT_WORKSPACE || il_parent->type == CT_DOCKAREA || il_parent->type == CT_OUTPUT) - break; - il_parent = il_parent->parent; - indent_mult++; - } - } - //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult); - int indent_px = (indent_level * 5) * indent_mult; - int mark_width = 0; if (config.show_marks && !TAILQ_EMPTY(&(con->marks_head))) { char *formatted_mark = sstrdup(""); @@ -606,8 +602,9 @@ void x_draw_decoration(Con *con) { i3String *title = con->title_format == NULL ? win->name : con_parse_title_format(con); draw_util_text(title, &(parent->frame_buffer), p->color->text, p->color->background, - con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y, - con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2)); + con->deco_rect.x + logical_px(2), + con->deco_rect.y + text_offset_y, + con->deco_rect.width - mark_width - 2 * logical_px(2)); if (con->title_format != NULL) I3STRING_FREE(title); @@ -790,7 +787,7 @@ void x_push_node(Con *con) { int width = MAX((int32_t)rect.width, 1); int height = MAX((int32_t)rect.height, 1); - xcb_create_pixmap_checked(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height); + xcb_create_pixmap(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height); draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id, get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height); @@ -1124,7 +1121,7 @@ void x_push_changes(Con *con) { values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE); xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values); } - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp); if (focused->window != NULL) { values[0] = CHILD_EVENT_MASK; xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values); @@ -1144,7 +1141,7 @@ void x_push_changes(Con *con) { /* If we still have no window to focus, we focus the EWMH window instead. We use this rather than the * root window in order to avoid an X11 fallback mechanism causing a ghosting effect (see #1378). */ DLOG("Still no window focused, better set focus to the EWMH support window (%d)\n", ewmh_window); - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, XCB_CURRENT_TIME); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, last_timestamp); ewmh_update_active_window(XCB_WINDOW_NONE); focused_id = ewmh_window; } diff --git a/src/xcb.c b/src/xcb.c index 9d181cfa..900840a2 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "xcb.c" /* * vim:ts=4:sw=4:expandtab * @@ -346,20 +344,12 @@ release_grab: * Grab the specified buttons on a window when managing it. * */ -void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, bool bind_scrollwheel) { - uint8_t buttons[3]; - int num = 0; - - if (bind_scrollwheel) { - buttons[num++] = XCB_BUTTON_INDEX_ANY; - } else { - buttons[num++] = XCB_BUTTON_INDEX_1; - buttons[num++] = XCB_BUTTON_INDEX_2; - buttons[num++] = XCB_BUTTON_INDEX_3; - } - - for (int i = 0; i < num; i++) { +void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, int *buttons) { + int i = 0; + while (buttons[i] > 0) { xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, buttons[i], XCB_BUTTON_MASK_ANY); + + i++; } } diff --git a/src/xcursor.c b/src/xcursor.c index 1e1e23b0..cbfe808b 100644 --- a/src/xcursor.c +++ b/src/xcursor.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "xcursor.c" /* * vim:ts=4:sw=4:expandtab * @@ -9,6 +7,8 @@ * xcursor.c: xcursor support for themed cursors. * */ +#include + #include #include diff --git a/src/xinerama.c b/src/xinerama.c index fb3b8603..25bfa6b1 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "xinerama.c" /* * vim:ts=4:sw=4:expandtab * @@ -121,14 +119,4 @@ void xinerama_init(void) { FREE(reply); } - -#if 0 - Output *output; - Workspace *ws; - /* Just go through each active output and associate one workspace */ - TAILQ_FOREACH(output, &outputs, outputs) { - ws = get_first_workspace_for_output(output); - initialize_output(conn, output, ws); - } -#endif } diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl.in similarity index 88% rename from testcases/complete-run.pl rename to testcases/complete-run.pl.in index 2d61e993..d872bda1 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl.in @@ -17,19 +17,8 @@ use TAP::Parser::Aggregator; use Time::HiRes qw(time); use IO::Handle; -my $dirname; - -BEGIN { - use File::Basename; - use Cwd qw(abs_path); - - # fileparse()[1] contains the directory portion of the specified path. - # See File::Basename(3p) for more details. - $dirname = (fileparse(abs_path($0)))[1]; -} - # these are shipped with the testsuite -use lib $dirname . 'lib'; +use lib qw(@abs_top_builddir@/testcases/lib @abs_top_srcdir@/testcases/lib); use i3test::Util qw(slurp); use StartXServer; use StatusLine; @@ -49,7 +38,7 @@ binmode STDERR, ':utf8'; # subshell or situations like that. AnyEvent::Util::close_all_fds_except(0, 1, 2); -# convinience wrapper to write to the log file +# convenience wrapper to write to the log file my $log; sub Log { say $log "@_" } @@ -82,17 +71,15 @@ my $result = GetOptions( pod2usage(-verbose => 2, -exitcode => 0) if $help; -chdir $dirname or die "Could not chdir into $dirname"; - # Check for missing executables my @binaries = qw( - ../i3 - ../i3bar/i3bar - ../i3-config-wizard/i3-config-wizard - ../i3-dump-log/i3-dump-log - ../i3-input/i3-input - ../i3-msg/i3-msg - ../i3-nagbar/i3-nagbar + @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 ); foreach my $binary (@binaries) { @@ -100,15 +87,16 @@ foreach my $binary (@binaries) { die "$binary is not an executable" unless -x $binary; } -if ($options{coverage}) { - qx(command -v lcov &> /dev/null); - die "Cannot find lcov needed for coverage testing." if $?; - qx(command -v genhtml &> /dev/null); - die "Cannot find genhtml needed for coverage testing." if $?; - - # clean out the counters that may be left over from previous tests. - qx(lcov -d ../ --zerocounters &> /dev/null); -} +$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}); qx(Xephyr -help 2>&1); die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?; @@ -120,7 +108,17 @@ die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on my @testfiles = @ARGV; # if no files were passed on command line, run all tests from t/ -@testfiles = if @testfiles == 0; +if (scalar @testfiles == 0) { + @testfiles = <@abs_top_srcdir@/testcases/t/*.t> if @testfiles == 0; +} else { + @testfiles = map { + # Fully qualify each specified file if necessary + if (! -e $_) { + $_ = "@abs_top_srcdir@/testcases/$_"; + } + $_ + } @testfiles; +} my $numtests = scalar @testfiles; @@ -208,7 +206,7 @@ $single_cv->recv; $aggregator->stop(); -# print empty lines to seperate failed tests from statuslines +# print empty lines to separate failed tests from statuslines print "\n\n"; for (@done) { @@ -251,17 +249,6 @@ if ($numtests == 1) { END { cleanup() } -if ($options{coverage}) { - print("\nGenerating test coverage report...\n"); - qx(lcov -d ../ -b ../ --capture -o latest/i3-coverage.info); - qx(genhtml -o latest/i3-coverage latest/i3-coverage.info); - if ($?) { - print("Could not generate test coverage html. Did you compile i3 with test coverage support?\n"); - } else { - print("Test coverage report generated in latest/i3-coverage\n"); - } -} - # Report logfiles that match “(Leak|Address)Sanitizer:”. my @logs_with_leaks; for my $log (<$outdir/i3-log-for-*>) { diff --git a/testcases/lib/SocketActivation.pm b/testcases/lib/SocketActivation.pm index b58707a4..53dbb3b6 100644 --- a/testcases/lib/SocketActivation.pm +++ b/testcases/lib/SocketActivation.pm @@ -62,14 +62,6 @@ sub activate_i3 { mkdir $ENV{XDG_RUNTIME_DIR}; } $ENV{DISPLAY} = $args{display}; - $ENV{PATH} = join(':', - '../i3-nagbar', - '../i3-msg', - '../i3-config-wizard', - '../i3bar', - '..', - $ENV{PATH} - ); # We are about to exec, but we did not modify $^F to include $socket # when creating the socket (because the file descriptor could have a @@ -96,7 +88,7 @@ sub activate_i3 { # the interactive signalhandler to make it crash immediately instead. # Also disable logging to SHM since we redirect the logs anyways. # Force Xinerama because we use Xdmx for multi-monitor tests. - my $i3cmd = abs_path("../i3") . q| --shmlog-size=0 --disable-signalhandler --force-xinerama|; + my $i3cmd = q|i3 --shmlog-size=0 --disable-signalhandler --force-xinerama|; if (!$args{validate_config}) { # We only set logging if i3 is actually started, but not if we only # validate the config file. This is to keep logging to a minimum as diff --git a/testcases/lib/StartXServer.pm b/testcases/lib/StartXServer.pm index 86a37d92..d3042c07 100644 --- a/testcases/lib/StartXServer.pm +++ b/testcases/lib/StartXServer.pm @@ -69,15 +69,6 @@ sub start_xserver { my @displays = (); my @childpids = (); - $SIG{CHLD} = sub { - my $child = waitpid -1, POSIX::WNOHANG; - @pids = grep { $_ != $child } @pids; - return unless @pids == 0; - print STDERR "All X server processes died.\n"; - print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n"; - exit 1; - }; - # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have # _SC_NPROCESSORS_CONF. my $num_cores; @@ -101,6 +92,15 @@ sub start_xserver { say "Starting $parallel Xephyr instances, starting at :$displaynum..."; + $SIG{CHLD} = sub { + my $child = waitpid -1, POSIX::WNOHANG; + @pids = grep { $_ != $child } @pids; + return unless @pids == 0; + print STDERR "All X server processes died.\n"; + print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n"; + exit 1; + }; + my @sockets_waiting; for (1 .. $parallel) { my $socket = fork_xserver($keep_xserver_output, $displaynum, diff --git a/testcases/lib/StatusLine.pm b/testcases/lib/StatusLine.pm index f2487797..6d32104a 100644 --- a/testcases/lib/StatusLine.pm +++ b/testcases/lib/StatusLine.pm @@ -46,8 +46,8 @@ sub status_init { status_completed(0); } -# generates the status text, prints it in the appropiate line -# and returns it, so it can be used in conjuction with C +# generates the status text, prints it in the appropriate line +# and returns it, so it can be used in conjunction with C sub status { my ($display, $msg) = @_; my $status = "[$display] $msg"; diff --git a/testcases/lib/TestWorker.pm b/testcases/lib/TestWorker.pm index 140537d4..aee994f7 100644 --- a/testcases/lib/TestWorker.pm +++ b/testcases/lib/TestWorker.pm @@ -112,8 +112,9 @@ sub worker_wait { $test->failure_output(\*STDERR); $test->todo_output(\*STDOUT); - @ENV{qw(DISPLAY TESTNAME OUTDIR VALGRIND STRACE XTRACE COVERAGE RESTART)} - = ($self->{display}, + @ENV{qw(HOME DISPLAY TESTNAME OUTDIR VALGRIND STRACE XTRACE COVERAGE RESTART)} + = ($outdir, + $self->{display}, basename($file), $outdir, $options->{valgrind}, @@ -124,7 +125,7 @@ sub worker_wait { package main; local $@; - do "./$file"; + do $file; $test->ok(undef, "$@") if $@; # XXX hack, we need to trigger the read watcher once more diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm.in similarity index 99% rename from testcases/lib/i3test.pm rename to testcases/lib/i3test.pm.in index 98486122..f9f6e821 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm.in @@ -836,7 +836,7 @@ sub launch_with_config { if ($config ne '-default') { say $fh $config; } else { - open(my $conf_fh, '<', './i3-test.config') + open(my $conf_fh, '<', '@abs_top_srcdir@/testcases/i3-test.config') or $tester->BAIL_OUT("could not open default config: $!"); local $/; say $fh scalar <$conf_fh>; diff --git a/testcases/t/103-move.t b/testcases/t/103-move.t deleted file mode 100644 index 0e01d90b..00000000 --- a/testcases/t/103-move.t +++ /dev/null @@ -1,96 +0,0 @@ -#!perl -# vim:ts=4:sw=4:expandtab -# -# Please read the following documents before working on tests: -# • http://build.i3wm.org/docs/testsuite.html -# (or docs/testsuite) -# -# • http://build.i3wm.org/docs/lib-i3test.html -# (alternatively: perldoc ./testcases/lib/i3test.pm) -# -# • http://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) -# -# Beware that this test uses workspace 9 to perform some tests (it expects -# the workspace to be empty). -# TODO: skip it by default? - -use i3test tests => 8; -use X11::XCB qw(:all); -use Time::HiRes qw(sleep); - -BEGIN { - use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection'); -} - -SKIP: { - skip "Testcase not yet modified for new move concept", 7; - -my $x = X11::XCB::Connection->new; - -my $i3 = i3; - -# Switch to the nineth workspace -$i3->command('9')->recv; - -##################################################################### -# Create two windows and make sure focus switching works -##################################################################### - -my $top = i3test::open_standard_window($x); -sleep(0.25); -my $mid = i3test::open_standard_window($x); -sleep(0.25); -my $bottom = i3test::open_standard_window($x); -sleep(0.25); - -diag("top id = " . $top->id); -diag("mid id = " . $mid->id); -diag("bottom id = " . $bottom->id); - -# -# Returns the input focus after sending the given command to i3 via IPC -# end sleeping for half a second to make sure i3 reacted -# -sub focus_after { - my $msg = shift; - - $i3->command($msg)->recv; - return $x->input_focus; -} - -my $focus = $x->input_focus; -is($focus, $bottom->id, "Latest window focused"); - -$focus = focus_after("ml"); -is($focus, $bottom->id, "Right window still focused"); - -$focus = focus_after("h"); -is($focus, $mid->id, "Middle window focused"); - -##################################################################### -# Now move to the top window, move right, then move left again -# (e.g., does i3 remember the focus in the last container?) -##################################################################### - -$focus = focus_after("k"); -is($focus, $top->id, "Top window focused"); - -$focus = focus_after("l"); -is($focus, $bottom->id, "Right window focused"); - -$focus = focus_after("h"); -is($focus, $top->id, "Top window focused"); - -##################################################################### -# Move window cross-workspace -##################################################################### - -for my $cmd (qw(m12 t m13 12 13)) { - $i3->command($cmd)->recv; -} -ok(1, "Still living"); -} diff --git a/testcases/t/105-stacking.t b/testcases/t/105-stacking.t deleted file mode 100644 index 96c64975..00000000 --- a/testcases/t/105-stacking.t +++ /dev/null @@ -1,144 +0,0 @@ -#!perl -# vim:ts=4:sw=4:expandtab -# -# Please read the following documents before working on tests: -# • http://build.i3wm.org/docs/testsuite.html -# (or docs/testsuite) -# -# • http://build.i3wm.org/docs/lib-i3test.html -# (alternatively: perldoc ./testcases/lib/i3test.pm) -# -# • http://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) -# -# Beware that this test uses workspace 9 to perform some tests (it expects -# the workspace to be empty). -# TODO: skip it by default? - -use i3test tests => 22; -use X11::XCB qw(:all); -use Time::HiRes qw(sleep); - -BEGIN { - use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection'); -} - -SKIP: { - skip "stacking test not yet updated", 21; - -my $x = X11::XCB::Connection->new; - -my $i3 = i3; - -# Switch to the nineth workspace -$i3->command('9')->recv; - -##################################################################### -# Create two windows and make sure focus switching works -##################################################################### - -my $top = i3test::open_standard_window($x); -my $mid = i3test::open_standard_window($x); -my $bottom = i3test::open_standard_window($x); -sleep(0.25); - -diag("top id = " . $top->id); -diag("mid id = " . $mid->id); -diag("bottom id = " . $bottom->id); - -# -# Returns the input focus after sending the given command to i3 via IPC -# end sleeping for half a second to make sure i3 reacted -# -sub focus_after { - my $msg = shift; - - $i3->command($msg)->recv; - return $x->input_focus; -} - -my $focus = $x->input_focus; -is($focus, $bottom->id, "Latest window focused"); - -$focus = focus_after("s"); -is($focus, $bottom->id, "Last window still focused"); - -$focus = focus_after("k"); -is($focus, $mid->id, "Middle window focused"); - -$focus = focus_after("k"); -is($focus, $top->id, "Top window focused"); - -##################################################################### -# Test focus wrapping -##################################################################### - -$focus = focus_after("k"); -is($focus, $bottom->id, "Bottom window focused (wrapping to the top works)"); - -$focus = focus_after("j"); -is($focus, $top->id, "Top window focused (wrapping to the bottom works)"); - -##################################################################### -# Restore of focus after moving windows out/into the stack -##################################################################### - -$focus = focus_after("ml"); -is($focus, $top->id, "Top window still focused (focus after moving)"); - -$focus = focus_after("h"); -is($focus, $bottom->id, "Bottom window focused (focus after moving)"); - -my $new = i3test::open_standard_window($x); -sleep(0.25); - -# By now, we have this layout: -# ---------------- -# | mid | -# | bottom | top -# | new | -# ---------------- - -$focus = focus_after("l"); -is($focus, $top->id, "Got top window"); - -$focus = focus_after("mh"); -is($focus, $top->id, "Moved it into the stack"); - -$focus = focus_after("k"); -is($focus, $new->id, "Window above is new"); - -$focus = focus_after("k"); -is($focus, $bottom->id, "Window above is bottom"); - -$focus = focus_after("k"); -is($focus, $mid->id, "Window above is mid"); - -$focus = focus_after("k"); -is($focus, $top->id, "At top again"); - -$focus = focus_after("ml"); -is($focus, $top->id, "Still at top, moved out"); - -$focus = focus_after("h"); -is($focus, $mid->id, "At mid again"); - -$focus = focus_after("j"); -is($focus, $bottom->id, "At bottom again"); - -$focus = focus_after("l"); -is($focus, $top->id, "At top again"); - -$focus = focus_after("mh"); -is($focus, $top->id, "Still at top, moved into"); - -$focus = focus_after("k"); -is($focus, $bottom->id, "Window above is bottom"); - -$focus = focus_after("k"); -is($focus, $mid->id, "Window above is mid"); - -} diff --git a/testcases/t/131-stacking-order.t b/testcases/t/131-stacking-order.t index c04f1b09..8993a071 100644 --- a/testcases/t/131-stacking-order.t +++ b/testcases/t/131-stacking-order.t @@ -14,7 +14,7 @@ # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf # (unless you are already familiar with Perl) # -# Check if stacking containers can be used independantly of +# Check if stacking containers can be used independently of # the split mode (horizontal/vertical) of the underlying # container. # diff --git a/testcases/t/158-wm_take_focus.t b/testcases/t/158-wm_take_focus.t index b8dae3b1..5d481924 100644 --- a/testcases/t/158-wm_take_focus.t +++ b/testcases/t/158-wm_take_focus.t @@ -56,7 +56,8 @@ subtest 'Window without WM_TAKE_FOCUS', sub { ok(!recv_take_focus($window), 'did not receive ClientMessage'); - my $con = shift get_ws_content($ws); + my ($nodes) = get_ws_content($ws); + my $con = shift @$nodes; ok($con->{focused}, 'con is focused'); done_testing; @@ -91,7 +92,8 @@ subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub { ok(!recv_take_focus($window), 'did not receive ClientMessage'); - my $con = shift get_ws_content($ws); + my ($nodes) = get_ws_content($ws); + my $con = shift @$nodes; ok($con->{focused}, 'con is focused'); done_testing; @@ -111,7 +113,8 @@ subtest 'Window with WM_TAKE_FOCUS and unspecified InputHint', sub { ok(!recv_take_focus($window), 'did not receive ClientMessage'); - my $con = shift get_ws_content($ws); + my ($nodes) = get_ws_content($ws); + my $con = shift @$nodes; ok($con->{focused}, 'con is focused'); done_testing; diff --git a/testcases/t/159-socketpaths.t b/testcases/t/159-socketpaths.t index d21581d1..6c214d20 100644 --- a/testcases/t/159-socketpaths.t +++ b/testcases/t/159-socketpaths.t @@ -65,7 +65,7 @@ ok(-S $socketpath, "file $socketpath exists and is a socket"); exit_gracefully($pid); ##################################################################### -# configuration file case: socket gets placed whereever we specify +# configuration file case: socket gets placed wherever we specify ##################################################################### my $tmpdir = tempdir(CLEANUP => 1); diff --git a/testcases/t/165-for_window.t b/testcases/t/165-for_window.t index 025fe21c..bc3df114 100644 --- a/testcases/t/165-for_window.t +++ b/testcases/t/165-for_window.t @@ -459,6 +459,33 @@ is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'triggered' ], "mark set for workspa exit_gracefully($pid); +############################################################## +# 13: check that the tiling / floating criteria work. +############################################################## + +$config = <<"EOT"; +# i3 config file (v4) +font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +for_window [tiling] mark tiled +for_window [floating] mark floated +EOT + +$pid = launch_with_config($config); +$tmp = fresh_workspace; + +open_window; +open_floating_window; + +@nodes = @{get_ws($tmp)->{nodes}}; +cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); +is_deeply($nodes[0]->{marks}, [ 'tiled' ], "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}, [ 'floated' ], "mark set for 'floating' criterion"); + +exit_gracefully($pid); + ############################################################## done_testing; diff --git a/testcases/t/171-config-migrate.t b/testcases/t/171-config-migrate.t index d098ae58..5bd21128 100644 --- a/testcases/t/171-config-migrate.t +++ b/testcases/t/171-config-migrate.t @@ -29,7 +29,7 @@ sub migrate_config { print $fh $config; close($fh); - my $cmd = "sh -c 'exec " . abs_path("../i3-migrate-config-to-v4") . " --v3 <$tmpfile'"; + my $cmd = "sh -c 'exec i3-migrate-config-to-v4 --v3 <$tmpfile'"; return [ split /\n/, qx($cmd) ]; } diff --git a/testcases/t/175-startup-notification.t b/testcases/t/175-startup-notification.t index b27bed53..4456b1d0 100644 --- a/testcases/t/175-startup-notification.t +++ b/testcases/t/175-startup-notification.t @@ -192,7 +192,7 @@ is_num_children($third_ws, 2, 'two containers on the third workspace'); # environment variable. ###################################################################### -mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp"; +mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!"; cmd qq|exec --no-startup-id echo \$DESKTOP_STARTUP_ID >$tmp|; @@ -208,7 +208,7 @@ is($startup_id, '', 'startup_id empty'); # 4) same thing, but with double quotes in exec ###################################################################### -mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp"; +mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!"; cmd qq|exec --no-startup-id "echo \$DESKTOP_STARTUP_ID >$tmp"|; diff --git a/testcases/t/180-fd-leaks.t b/testcases/t/180-fd-leaks.t index 454bfe7d..16272d31 100644 --- a/testcases/t/180-fd-leaks.t +++ b/testcases/t/180-fd-leaks.t @@ -20,6 +20,9 @@ use i3test; use POSIX qw(mkfifo); use File::Temp qw(:POSIX tempfile); +SKIP: { +skip "Procfs not available on $^O", 1 if $^O eq 'openbsd'; + my $i3 = i3(get_socket_path()); my $tmp = tmpnam(); @@ -59,4 +62,6 @@ for my $fd (keys %fds) { is(scalar keys %fds, 0, 'No file descriptors leaked'); +} + done_testing; diff --git a/testcases/t/183-config-variables.t b/testcases/t/183-config-variables.t index 8fbbff70..65f16679 100644 --- a/testcases/t/183-config-variables.t +++ b/testcases/t/183-config-variables.t @@ -79,6 +79,22 @@ EOT is(launch_get_border($config), 'none', 'no border'); +##################################################################### +# test that longest matching variable name is substituted +##################################################################### + +$config = <<'EOT'; +# i3 config file (v4) +font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 + +set $var normal title +set $vartest special title +set $vart mundane title +for_window [title="$vartest"] border none +EOT + +is(launch_get_border($config), 'none', 'no border'); + done_testing; diff --git a/testcases/t/187-commands-parser.t b/testcases/t/187-commands-parser.t index e3481b0a..dc5b67ed 100644 --- a/testcases/t/187-commands-parser.t +++ b/testcases/t/187-commands-parser.t @@ -25,7 +25,7 @@ sub parser_calls { # TODO: use a timeout, so that we can error out if it doesn’t terminate # TODO: better way of passing arguments - my $stdout = qx(../test.commands_parser '$command' 2>&1 >&-); + my $stdout = qx(test.commands_parser '$command' 2>&1 >&-); # Filter out all debugging output. my @lines = split("\n", $stdout); @@ -144,34 +144,35 @@ is(parser_calls("\nworkspace test"), ################################################################################ is(parser_calls('unknown_literal'), - "ERROR: Expected one of these tokens: , '[', " . - "'move', " . - "'exec', " . - "'exit', " . - "'restart', " . - "'reload', " . - "'shmlog', " . - "'debuglog', " . - "'border', " . - "'layout', " . - "'append_layout', " . - "'workspace', " . - "'focus', " . - "'kill', " . - "'open', " . - "'fullscreen', " . - "'sticky', " . - "'split', " . - "'floating', " . - "'mark', " . - "'unmark', " . - "'resize', " . - "'rename', " . - "'nop', " . - "'scratchpad', " . - "'title_format', " . - "'mode', " . - "'bar'\n" . + "ERROR: Expected one of these tokens: , '[', '" . join("', '", qw( + move + exec + exit + restart + reload + shmlog + debuglog + border + layout + append_layout + workspace + focus + kill + open + fullscreen + sticky + split + floating + mark + unmark + resize + rename + nop + scratchpad + title_format + mode + bar + )) . "'\n" . "ERROR: Your command: unknown_literal\n" . "ERROR: ^^^^^^^^^^^^^^^", 'error for unknown literal ok'); diff --git a/testcases/t/195-net-active-window.t b/testcases/t/195-net-active-window.t index 032e5452..804a7add 100644 --- a/testcases/t/195-net-active-window.t +++ b/testcases/t/195-net-active-window.t @@ -39,6 +39,7 @@ sub send_net_active_window { 0; $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg); + sync_with_i3; } sub get_net_active_window { @@ -131,6 +132,23 @@ send_net_active_window($scratch->id); is($x->input_focus, $win3->id, 'window 3 still focused'); +################################################################################ +# A scratchpad window should be shown if _NET_ACTIVE_WINDOW from a pager +# is received. +################################################################################ + +my $scratch = open_window; + +is($x->input_focus, $scratch->id, 'to-scratchpad window has focus'); + +cmd 'move scratchpad'; + +is($x->input_focus, $win3->id, 'focus reverted to window 3'); + +send_net_active_window($scratch->id, 'pager'); + +is($x->input_focus, $scratch->id, 'scratchpad window is shown'); + ################################################################################ # Verify that the _NET_ACTIVE_WINDOW property is updated on the root window # correctly. diff --git a/testcases/t/196-randr-output-names.t b/testcases/t/196-randr-output-names.t index e5049eb8..7c53732b 100644 --- a/testcases/t/196-randr-output-names.t +++ b/testcases/t/196-randr-output-names.t @@ -28,7 +28,7 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 workspace 2 output DVI-I_1/digital EOT -my $output = qx(../i3 -C -c $filename); +my $output = qx(i3 -C -c $filename); unlike($output, qr/ERROR/, 'no errors in i3 -C'); close($fh); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index e8835005..6cd84b6f 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -25,7 +25,7 @@ sub parser_calls { my ($command) = @_; my $stdout; - run [ '../test.config_parser', $command ], + run [ 'test.config_parser', $command ], '>/dev/null', '2>', \$stdout; # TODO: use a timeout, so that we can error out if it doesn’t terminate @@ -286,6 +286,7 @@ hide_edge_borders none hide_edge_borders vertical hide_edge_borders horizontal hide_edge_borders both +hide_edge_borders smart EOT $expected = <<'EOT'; @@ -293,6 +294,7 @@ cfg_hide_edge_borders(none) cfg_hide_edge_borders(vertical) cfg_hide_edge_borders(horizontal) cfg_hide_edge_borders(both) +cfg_hide_edge_borders(smart) EOT is(parser_calls($config), @@ -440,9 +442,51 @@ hide_edge_border both client.focused #4c7899 #285577 #ffffff #2e9ef4 EOT -my $expected_all_tokens = <<'EOT'; -ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder' -EOT +my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: , '#', '" . join("', '", qw( + set + set_from_resource + bindsym + bindcode + bind + bar + font + mode + floating_minimum_size + floating_maximum_size + floating_modifier + default_orientation + workspace_layout + new_window + new_float + hide_edge_borders + for_window + assign + no_focus + focus_follows_mouse + mouse_warping + force_focus_wrapping + force_xinerama + force-xinerama + workspace_auto_back_and_forth + fake_outputs + fake-outputs + force_display_urgency_hint + focus_on_window_activation + show_marks + workspace + ipc_socket + ipc-socket + restart_state + popup_during_fullscreen + exec_always + exec + client.background + client.focused_inactive + client.focused + client.unfocused + client.urgent + client.placeholder + )) . "'\n"; my $expected_end = <<'EOT'; ERROR: CONFIG: (in file ) @@ -464,7 +508,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4 EOT $expected = <<'EOT'; -ERROR: CONFIG: Expected one of these tokens: 'none', 'vertical', 'horizontal', 'both', '1', 'yes', 'true', 'on', 'enable', 'active' +ERROR: CONFIG: Expected one of these tokens: 'none', 'vertical', 'horizontal', 'both', 'smart', '1', 'yes', 'true', 'on', 'enable', 'active' ERROR: CONFIG: (in file ) ERROR: CONFIG: Line 1: hide_edge_borders FOOBAR ERROR: CONFIG: ^^^^^^ diff --git a/testcases/t/207-shmlog.t b/testcases/t/207-shmlog.t index b63a7499..b6c6d776 100644 --- a/testcases/t/207-shmlog.t +++ b/testcases/t/207-shmlog.t @@ -33,7 +33,7 @@ my $pid = launch_with_config($config); my $stdout; my $stderr; -run [ '../i3-dump-log/i3-dump-log' ], +run [ 'i3-dump-log' ], '>', \$stdout, '2>', \$stderr; @@ -49,7 +49,7 @@ cmd 'shmlog on'; my $random_nop = mktemp('nop.XXXXXX'); cmd "nop $random_nop"; -run [ '../i3-dump-log/i3-dump-log' ], +run [ 'i3-dump-log' ], '>', \$stdout, '2>', \$stderr; @@ -62,7 +62,7 @@ like($stderr, qr#^$#, 'stderr empty'); cmd 'shmlog ' . (23 * 1024 * 1024); -run [ '../i3-dump-log/i3-dump-log' ], +run [ 'i3-dump-log' ], '>', \$stdout, '2>', \$stderr; @@ -75,7 +75,7 @@ like($stderr, qr#^$#, 'stderr empty'); cmd 'shmlog off'; -run [ '../i3-dump-log/i3-dump-log' ], +run [ 'i3-dump-log' ], '>', \$stdout, '2>', \$stderr; diff --git a/testcases/t/235-check-config-no-x.t b/testcases/t/235-check-config-no-x.t index ec17353e..5e87d992 100644 --- a/testcases/t/235-check-config-no-x.t +++ b/testcases/t/235-check-config-no-x.t @@ -26,7 +26,7 @@ sub check_config { my ($config) = @_; my ($fh, $tmpfile) = tempfile(UNLINK => 1); print $fh $config; - my $output = qx(DISPLAY= ../i3 -C -c $tmpfile 2>&1); + my $output = qx(DISPLAY= i3 -C -c $tmpfile 2>&1); my $retval = $?; $fh->flush; close($fh); diff --git a/testcases/t/247-config-line-continuation.t b/testcases/t/247-config-line-continuation.t index 0396d872..cb429083 100644 --- a/testcases/t/247-config-line-continuation.t +++ b/testcases/t/247-config-line-continuation.t @@ -198,4 +198,20 @@ EOT is(launch_get_border($config), 'none', 'no border'); +##################################################################### +# test ignoring of line continuation within a comment +##################################################################### + +$config = <<'EOT'; +# i3 config file (v4) +font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 + +set $vartest \"special title\" +for_window [title="$vartest"] border pixel 1 +# this line is not continued, so the following is not contained in this comment\ +for_window [title="$vartest"] border none +EOT + +is(launch_get_border($config), 'none', 'no border'); + done_testing; diff --git a/testcases/t/258-keypress-release.t b/testcases/t/258-keypress-release.t index 4c2775b6..b0d77858 100644 --- a/testcases/t/258-keypress-release.t +++ b/testcases/t/258-keypress-release.t @@ -33,6 +33,10 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 bindsym Print nop Print bindsym --release Control+Print nop Control+Print + +# see issue #2442 +bindsym Mod1+b nop Mod1+b +bindsym --release Mod1+Shift+b nop Mod1+Shift+b release EOT my $pid = launch_with_config($config); @@ -59,8 +63,32 @@ is(listen_for_binding( 'Control+Print', 'triggered the "Control+Print" keybinding'); +is(listen_for_binding( + sub { + xtest_key_press(64); # Alt_L + xtest_key_press(56); # b + xtest_key_release(56); # b + xtest_key_release(64); # Alt_L + }, + ), + 'Mod1+b', + 'triggered the "Mod1+b" keybinding'); + +is(listen_for_binding( + sub { + xtest_key_press(64); # Alt_L + xtest_key_press(50); # Shift_L + xtest_key_press(56); # b + xtest_key_release(56); # b + xtest_key_release(50); # Shift_L + xtest_key_release(64); # Alt_L + }, + ), + 'Mod1+Shift+b release', + 'triggered the "Mod1+Shift+b" release keybinding'); + sync_with_i3; -is(scalar @i3test::XTEST::binding_events, 2, 'Received exactly 2 binding events'); +is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events'); exit_gracefully($pid); diff --git a/testcases/t/263-edge-borders.t b/testcases/t/263-edge-borders.t new file mode 100644 index 00000000..0d14c65c --- /dev/null +++ b/testcases/t/263-edge-borders.t @@ -0,0 +1,159 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://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) +# +# Tests that the hide_edge_borders smart option works +# Ticket: #2188 + +use i3test i3_autostart => 0; + +#################################################################### +# 1: check that the borders are present on a floating windows +##################################################################### + +my $config = <{floating_nodes}}; +ok(@floating == 1, 'one floating container opened'); +is($floating[0]->{nodes}[0]->{current_border_width}, 2, 'floating current border width set to 2'); +is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*2, 'floating border width 2'); + +exit_gracefully($pid); + +##################################################################### +# 2: check that the borders are present on a workspace with two tiled +# windows visible +##################################################################### + +$config = <{nodes}}; +ok(@tiled == 2, 'two tiled container opened'); +is($tiled[0]->{current_border_width}, 2, 'first tiled current border width set to 2'); +is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2'); +is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2'); +is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2'); + +exit_gracefully($pid); + +##################################################################### +# 3: check that the borders are hidden on a workspace with one tiled +# window visible +##################################################################### + +$config = <{nodes}}; +ok(@tiled == 1, 'one tiled container opened'); +is($tiled[0]->{current_border_width}, 2, 'tiled current border width set to 2'); +is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*0, 'single tiled border width 0'); + +exit_gracefully($pid); + +##################################################################### +# 4: check that the borders are present on a workspace with two tiled +# windows visible, recursively +##################################################################### + +$config = < back to one container'); + +cmd 'focus parent'; +my $tilewindow3 = open_window; +ok(@{get_ws_content($tmp)} == 2, 'after split & new window, two containers'); + +$wscontent = get_ws($tmp); + +@tiled = @{$wscontent->{nodes}}; +ok(@tiled == 2, 'two tiled container opened in another container'); +is($tiled[0]->{current_border_width}, -1, 'first tiled current border width set to -1'); +is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2'); +is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2'); +is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2'); + +exit_gracefully($pid); + +done_testing; diff --git a/testcases/t/264-dock-criteria.t b/testcases/t/264-dock-criteria.t new file mode 100644 index 00000000..cbbdeb0f --- /dev/null +++ b/testcases/t/264-dock-criteria.t @@ -0,0 +1,71 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://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 that command or config criteria does not match dock clients +# Bug still in: 4.12-38-ge690e3d +use i3test i3_autostart => 0; + +my $config = < $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'), + wm_class => "x" +}); + +is(get_dock_clients, 1, "created one docked client"); +is_num_children($ws, 0, 'no container on the current workspace'); + +cmd '[class="^x$"] move workspace current'; + +does_i3_live +is(get_dock_clients, 1, "one docked client after move"); +is_num_children($ws, 0, 'no container on the current workspace'); + +cmd '[class="^x$"] fullscreen'; + +does_i3_live +is(get_dock_clients, 1, "one docked client after fullscreen"); +is_num_children($ws, 0, 'no container on the current workspace'); + +cmd '[class="^x$"] kill'; + +does_i3_live +is(get_dock_clients, 1, "one docked client after kill"); +is_num_children($ws, 0, 'no container on the current workspace'); + + +## config criteria should not match dock windows +open_window({ + window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'), + wm_class => "dock" +}); + +does_i3_live +is(get_dock_clients, 2, "created second docked client"); +is_num_children($ws, 0, 'no container on the current workspace'); + + +exit_gracefully($pid); +done_testing; diff --git a/testcases/t/264-keypress-numlock.t b/testcases/t/264-keypress-numlock.t new file mode 100644 index 00000000..45ec7e88 --- /dev/null +++ b/testcases/t/264-keypress-numlock.t @@ -0,0 +1,231 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://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 that one can bind on numpad keys in different numlock states. +# Ticket: #2346 +# Bug still in: 4.12-78-g85bb324 +use i3test i3_autostart => 0; +use i3test::XTEST; +use ExtUtils::PkgConfig; + +SKIP: { + skip "libxcb-xkb too old (need >= 1.11)", 1 unless + ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11'); + +my $config = <connect()->recv; + +$i3->subscribe({ + window => sub { + my ($event) = @_; + return unless defined $mark; + return unless $event->{change} eq 'mark'; + + $mark->send($event); + } +})->recv; + +$timer = AnyEvent->timer( + after => 0.5, + cb => sub { + $mark->send(0); + } +); + +############################################################################### +# Marking a container triggers a 'mark' event. +############################################################################### +fresh_workspace; +open_window; + +$mark = AnyEvent->condvar; +cmd 'mark x'; + +$event = $mark->recv; +ok($event, 'window::mark event has been received'); + +############################################################################### +# Unmarking a container triggers a 'mark' event. +############################################################################### +fresh_workspace; +open_window; +cmd 'mark x'; + +$mark = AnyEvent->condvar; +cmd 'unmark x'; + +$event = $mark->recv; +ok($event, 'window::mark event has been received'); + +############################################################################### + +done_testing; diff --git a/testcases/t/504-move-workspace-to-output.t b/testcases/t/504-move-workspace-to-output.t index efe0d6e7..c43b8b40 100644 --- a/testcases/t/504-move-workspace-to-output.t +++ b/testcases/t/504-move-workspace-to-output.t @@ -19,9 +19,6 @@ use List::Util qw(first); use i3test i3_autostart => 0; -# TODO: -# introduce 'move workspace 3 to output ' with synonym 'move workspace 3 to ' - # Ensure the pointer is at (0, 0) so that we really start on the first # (the left) workspace. $x->root->warp_pointer(0, 0); @@ -163,6 +160,26 @@ ok(!($empty_ws ~~ @$x0), 'empty_ws not on fake-0'); ok(!($empty_ws ~~ @$x1), 'empty_ws not on fake-1'); ok($other_output_ws ~~ @$x0, 'other_output_ws on fake-0'); -exit_gracefully($pid); +################################################################################ +# Verify that the special word 'current' can be used for the output. +################################################################################ +my $ws1 = fresh_workspace(output => 1); +open_window; +cmd 'mark marked'; + +my $ws0 = fresh_workspace(output => 0); + +($x0, $x1) = workspaces_per_screen(); +ok($ws0 ~~ @$x0, 'ws0 on fake-0'); +ok($ws1 ~~ @$x1, 'ws1 on fake-1'); + +cmd '[con_mark=marked] move workspace to output current'; + +($x0, $x1) = workspaces_per_screen(); +ok($ws1 ~~ @$x0, 'ws1 on fake-0'); + +################################################################################ + +exit_gracefully($pid); done_testing; diff --git a/testcases/t/528-workspace-next-prev-reversed.t b/testcases/t/528-workspace-next-prev-reversed.t new file mode 100644 index 00000000..00a9bbe4 --- /dev/null +++ b/testcases/t/528-workspace-next-prev-reversed.t @@ -0,0 +1,129 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://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) +# +# Tests whether 'workspace next' works correctly. +# +use List::Util qw(first); +use i3test i3_autostart => 0; + +sub assert_next { + my ($expected) = @_; + + cmd 'workspace next'; + # We need to sync after changing focus to a different output to wait for the + # EnterNotify to be processed, otherwise it will be processed at some point + # later in time and mess up our subsequent tests. + sync_with_i3; + + is(focused_ws, $expected, "workspace $expected focused"); +} + +sub assert_prev { + my ($expected) = @_; + + cmd 'workspace prev'; + # We need to sync after changing focus to a different output to wait for the + # EnterNotify to be processed, otherwise it will be processed at some point + # later in time and mess up our subsequent tests. + sync_with_i3; + + is(focused_ws, $expected, "workspace $expected focused"); +} + +my $config = <root->warp_pointer(0, 0); +sync_with_i3; + +################################################################################ +# Setup workspaces so that they stay open (with an empty container). +# open_window ensures, this +# +# numbered named +# output 1 (left) : 1, 2, 3, 6, 7, B, F, C +# output 2 (right): 4, 5, A, D, E +# +################################################################################ + +cmd 'focus output left'; +cmd 'workspace A'; open_window; +cmd 'workspace D'; open_window; +cmd 'workspace 4'; open_window; +cmd 'workspace 5'; open_window; +cmd 'workspace E'; open_window; + +cmd 'focus output right'; +cmd 'workspace 1'; open_window; +cmd 'workspace 2'; open_window; +cmd 'workspace B'; open_window; +cmd 'workspace 3'; open_window; +cmd 'workspace F'; open_window; +cmd 'workspace 6'; open_window; +cmd 'workspace C'; open_window; +cmd 'workspace 7'; open_window; + +################################################################################ +# Use workspace next and verify the correct order. +# numbered -> numerical sort +# named -> sort by creation time +################################################################################ +cmd 'workspace 1'; +is(focused_ws, '1', 'back on workspace 1'); + +assert_next('2'); +assert_next('3'); +assert_next('4'); +assert_next('5'); +assert_next('6'); +assert_next('7'); + +assert_next('B'); +assert_next('F'); +assert_next('C'); +assert_next('A'); +assert_next('D'); +assert_next('E'); +assert_next('1'); + +cmd 'workspace 1'; +is(focused_ws, '1', 'back on workspace 1'); + +assert_prev('E'); +assert_prev('D'); +assert_prev('A'); +assert_prev('C'); +assert_prev('F'); +assert_prev('B'); + +assert_prev('7'); +assert_prev('6'); +assert_prev('5'); +assert_prev('4'); +assert_prev('3'); +assert_prev('2'); +assert_prev('1'); + + +exit_gracefully($pid); + +done_testing; diff --git a/testcases/t/528-workspace-next-prev.t b/testcases/t/528-workspace-next-prev.t index 79e83a07..1a83de23 100644 --- a/testcases/t/528-workspace-next-prev.t +++ b/testcases/t/528-workspace-next-prev.t @@ -31,6 +31,17 @@ sub assert_next { is(focused_ws, $expected, "workspace $expected focused"); } +sub assert_prev { + my ($expected) = @_; + + cmd 'workspace prev'; + # We need to sync after changing focus to a different output to wait for the + # EnterNotify to be processed, otherwise it will be processed at some point + # later in time and mess up our subsequent tests. + sync_with_i3; + + is(focused_ws, $expected, "workspace $expected focused"); +} my $config = <root->warp_pointer(0, 0); sync_with_i3; -cmd 'workspace A'; -# ensure workspace A stays open -open_window; - -cmd 'workspace B'; -# ensure workspace B stays open -open_window; - -cmd 'workspace D'; -# ensure workspace D stays open -open_window; - -cmd 'workspace E'; -# ensure workspace E stays open -open_window; +################################################################################ +# Setup workspaces so that they stay open (with an empty container). +# open_window ensures, this +# +# numbered named +# output 1 (left) : 1, 2, 3, 6, 7, B, F, C +# output 2 (right): 4, 5, A, D, E +# +################################################################################ cmd 'focus output right'; +cmd 'workspace A'; open_window; +cmd 'workspace D'; open_window; +cmd 'workspace 4'; open_window; +cmd 'workspace 5'; open_window; +cmd 'workspace E'; open_window; -cmd 'workspace 1'; -# ensure workspace 1 stays open -open_window; - -cmd 'workspace 2'; -# ensure workspace 2 stays open -open_window; - -cmd 'workspace 3'; -# ensure workspace 3 stays open -open_window; - -cmd 'workspace 4'; -# ensure workspace 4 stays open -open_window; - -cmd 'workspace 5'; -# ensure workspace 5 stays open -open_window; - -cmd 'workspace C'; -# ensure workspace C stays open -open_window; - -cmd 'workspace F'; -# ensure workspace F stays open -open_window; - -cmd 'focus output right'; +cmd 'focus output left'; +cmd 'workspace 1'; open_window; +cmd 'workspace 2'; open_window; +cmd 'workspace B'; open_window; +cmd 'workspace 3'; open_window; +cmd 'workspace F'; open_window; +cmd 'workspace 6'; open_window; +cmd 'workspace C'; open_window; +cmd 'workspace 7'; open_window; ################################################################################ # Use workspace next and verify the correct order. +# numbered -> numerical sort +# named -> sort by creation time ################################################################################ +cmd 'workspace 1'; +is(focused_ws, '1', 'back on workspace 1'); -# The current order should be: -# output 1: A, B, D, E -# output 2: 1, 2, 3, 4, 5, C, F - -cmd 'workspace A'; -is(focused_ws, 'A', 'back on workspace A'); - -assert_next('B'); -assert_next('D'); -assert_next('E'); -assert_next('C'); -assert_next('F'); -assert_next('1'); assert_next('2'); assert_next('3'); assert_next('4'); assert_next('5'); -assert_next('A'); +assert_next('6'); +assert_next('7'); + assert_next('B'); +assert_next('F'); +assert_next('C'); +assert_next('A'); +assert_next('D'); +assert_next('E'); +assert_next('1'); + +cmd 'workspace 1'; +is(focused_ws, '1', 'back on workspace 1'); + +assert_prev('E'); +assert_prev('D'); +assert_prev('A'); +assert_prev('C'); +assert_prev('F'); +assert_prev('B'); + +assert_prev('7'); +assert_prev('6'); +assert_prev('5'); +assert_prev('4'); +assert_prev('3'); +assert_prev('2'); +assert_prev('1'); + exit_gracefully($pid); diff --git a/testcases/t/529-net-wm-desktop.t b/testcases/t/529-net-wm-desktop.t index f6a3b218..8f2df735 100644 --- a/testcases/t/529-net-wm-desktop.t +++ b/testcases/t/529-net-wm-desktop.t @@ -345,6 +345,26 @@ is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); kill_windows; exit_gracefully($pid); +############################################################################### +# _NET_WM_DESKTOP is updated when a window is moved to the scratchpad. +############################################################################### + +$pid = launch_with_config($config); + +cmd 'workspace 0'; +$con = open_window; +cmd 'floating enable'; +is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); + +cmd 'move scratchpad'; +is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated'); + +cmd 'scratchpad show'; +is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)'); + +kill_windows; +exit_gracefully($pid); + ############################################################################### done_testing; diff --git a/testcases/t/531-fullscreen-on-given-output.t b/testcases/t/531-fullscreen-on-given-output.t new file mode 100644 index 00000000..fd328653 --- /dev/null +++ b/testcases/t/531-fullscreen-on-given-output.t @@ -0,0 +1,75 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://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) +# +# Tests that fullscreen windows appear on the output indicated by +# their geometry +use i3test i3_autostart => 0; +use List::Util qw(first); + +my $config = <fullscreen(1); +} + +sub find_window { + my ($nodes, $id) = @_; + + foreach (@{$nodes}) { + return $_ if ($_->{window} // 0) == $id; + my $node = find_window($_->{nodes}, $id); + return $node if $node; + }; + return undef; +} + +# Create two fullscreen windows, each on different output +my $orig_rect1 = X11::XCB::Rect->new(x => 0, y => 0, width => 1024, height => 768); +my $orig_rect2 = X11::XCB::Rect->new(x => 1024, y => 0, width => 1024, height => 768); + +my $win_on_first_output = open_window(rect => $orig_rect1, + before_map => \&fullscreen); + +my $win_on_second_output = open_window(rect => $orig_rect2, + before_map => \&fullscreen); + +sync_with_i3; + +# Check that the windows are on the correct output +is_deeply(scalar $win_on_first_output->rect, $orig_rect1, "first window spans the first output"); +is_deeply(scalar $win_on_second_output->rect, $orig_rect2, "second window spans the sencond output"); + +# Check that both windows remained fullscreen +my $tree = i3(get_socket_path())->get_tree->recv; + +my $node1 = find_window($tree->{nodes}, $win_on_first_output->{id}); +my $node2 = find_window($tree->{nodes}, $win_on_second_output->{id}); + +is($node1->{fullscreen_mode}, 1, "first window is fullscreen"); +is($node2->{fullscreen_mode}, 1, "second window is fullscreen"); + +exit_gracefully($pid); + +done_testing; diff --git a/testcases/t/532-xresources.t b/testcases/t/532-xresources.t new file mode 100644 index 00000000..438fad20 --- /dev/null +++ b/testcases/t/532-xresources.t @@ -0,0 +1,64 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://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) +# +# Tests for using X resources in the config. +# Ticket: #2130 +use i3test i3_autostart => 0; +use X11::XCB qw(PROP_MODE_REPLACE); + +sub get_marks { + return i3(get_socket_path())->get_marks->recv; +} + +my $config = <change_property( + PROP_MODE_REPLACE, + $x->get_root_window(), + $x->atom(name => 'RESOURCE_MANAGER')->id, + $x->atom(name => 'STRING')->id, + 32, + length('*mark: works'), + '*mark: works'); +$x->flush; + +my $pid = launch_with_config($config); + +open_window(wm_class => 'worksforme'); +sync_with_i3; +is_deeply(get_marks(), [ 'works' ], 'the resource has loaded correctly'); + +cmd 'kill'; + +open_window(wm_class => 'doesnotworkforme'); +sync_with_i3; +is_deeply(get_marks(), [ 'none' ], 'the resource fallback was used'); + +exit_gracefully($pid); + +done_testing; diff --git a/travis/bintray-autobuild-debian.json b/travis/bintray-autobuild-debian.json new file mode 100644 index 00000000..23fe6050 --- /dev/null +++ b/travis/bintray-autobuild-debian.json @@ -0,0 +1,36 @@ +{ + "package": { + "name": "i3-wm", + "repo": "i3-autobuild", + "subject": "i3" + }, + + "version": { + "name": "%version%", + "desc": "TODO", + "gpgSign": false + }, + + "files": [ + { + "includePattern": "build/deb/debian-amd64/(.*\\.deb)$", + "matrixParams": { + "deb_distribution": "sid", + "deb_component": "main", + "deb_architecture": "amd64" + }, + "uploadPattern": "$1" + }, + { + "includePattern": "build/deb/debian-i386/(.*\\.deb)$", + "matrixParams": { + "deb_distribution": "sid", + "deb_component": "main", + "deb_architecture": "i386" + }, + "uploadPattern": "$1" + } + ], + + "publish": true +} diff --git a/travis/bintray-autobuild-ubuntu.json b/travis/bintray-autobuild-ubuntu.json new file mode 100644 index 00000000..948f3dc4 --- /dev/null +++ b/travis/bintray-autobuild-ubuntu.json @@ -0,0 +1,36 @@ +{ + "package": { + "name": "i3-wm", + "repo": "i3-autobuild-ubuntu", + "subject": "i3" + }, + + "version": { + "name": "%version%", + "desc": "TODO", + "gpgSign": false + }, + + "files": [ + { + "includePattern": "build/deb/ubuntu-amd64/(.*\\.deb)$", + "matrixParams": { + "deb_distribution": "xenial", + "deb_component": "main", + "deb_architecture": "amd64" + }, + "uploadPattern": "$1" + }, + { + "includePattern": "build/deb/ubuntu-i386/(.*\\.deb)$", + "matrixParams": { + "deb_distribution": "xenial", + "deb_component": "main", + "deb_architecture": "i386" + }, + "uploadPattern": "$1" + } + ], + + "publish": true +} diff --git a/travis/check-formatting.sh b/travis/check-formatting.sh index abdb1168..ead25157 100755 --- a/travis/check-formatting.sh +++ b/travis/check-formatting.sh @@ -1,2 +1,6 @@ #!/bin/sh + +set -e +set -x + clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false) diff --git a/travis/check-spelling.pl b/travis/check-spelling.pl index 71feec31..6d070136 100755 --- a/travis/check-spelling.pl +++ b/travis/check-spelling.pl @@ -29,13 +29,13 @@ my $binary_spelling_exceptions = { 'betwen' => 1, # asan_flags.inc contains this spelling error. }; my @binaries = qw( - i3 - i3-config-wizard/i3-config-wizard - i3-dump-log/i3-dump-log - i3-input/i3-input - i3-msg/i3-msg - i3-nagbar/i3-nagbar - i3bar/i3bar + 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 ); for my $binary (@binaries) { check_spelling(slurp($binary), $binary_spelling_exceptions, sub { @@ -50,7 +50,7 @@ for my $binary (@binaries) { my $manpage_spelling_exceptions = { }; -for my $name (glob('man/*.1')) { +for my $name (glob('build/man/*.1')) { for my $line (split(/\n/, slurp($name))) { next if $line =~ /^\.\\\"/o; check_spelling($line, $manpage_spelling_exceptions, sub { diff --git a/travis/clang-analyze.sh b/travis/clang-analyze.sh new file mode 100755 index 00000000..5ef390d3 --- /dev/null +++ b/travis/clang-analyze.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e +set -x + +mkdir -p deb/DIST-clang/build +tar xf build/*.tar.bz2 -C deb/DIST-clang --strip-components=1 +(cd deb/DIST-clang/build && scan-build -o ../../CLANG ../configure && scan-build -o ../../CLANG --html-title="Analysis of i3 v$(git describe --tags)" make -j8) +mv deb/CLANG/*/* deb/CLANG diff --git a/travis/cleanup-bintray.pl b/travis/cleanup-bintray.pl new file mode 100755 index 00000000..e89efb1b --- /dev/null +++ b/travis/cleanup-bintray.pl @@ -0,0 +1,39 @@ +#!/usr/bin/env perl +# vim:ts=4:sw=4:expandtab + +use strict; +use warnings; +use Data::Dumper; +use HTTP::Tiny; # in core since v5.13.9 +use JSON::PP; # in core since v5.13.9 +use MIME::Base64; # in core since v5.7 +use v5.13; + +my $repo = shift; + +my $auth = $ENV{'BINTRAY_USER'} . ':' . $ENV{'BINTRAY_KEY'}; +die "BINTRAY_USER and/or BINTRAY_KEY environment variables not set" if $auth eq ':'; +# TODO(stapelberg): switch to putting $auth into the URL once perl-modules ≥ +# 5.20 is available on travis (Ubuntu Wily or newer). +my $auth_header = 'Basic ' . MIME::Base64::encode_base64($auth, ""); +my $apiurl = 'https://api.bintray.com/packages/i3/' . $repo . '/i3-wm'; +my $client = HTTP::Tiny->new( + verify_SSL => 1, + default_headers => { + 'authorization' => $auth_header, + }); +my $resp = $client->get($apiurl); +die "Getting versions failed: HTTP status $resp->{status} (content: $resp->{content})" unless $resp->{success}; +my $decoded = decode_json($resp->{content}); +my @versions = reverse sort { + (system("/usr/bin/dpkg", "--compare-versions", "$a", "gt", "$b") == 0) ? 1 : -1 +} @{$decoded->{versions}}; + +# Keep the most recent 5 versions. +splice(@versions, 0, 5); + +for my $version (@versions) { + say "Deleting old version $version"; + $resp = $client->request('DELETE', "$apiurl/versions/$version"); + die "Deletion of version $version failed: HTTP status $resp->{status} (content: $resp->{content})" unless $resp->{success}; +} diff --git a/travis/debian-build.sh b/travis/debian-build.sh new file mode 100755 index 00000000..9ce5a5af --- /dev/null +++ b/travis/debian-build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -e +set -x + +DEST=$1 + +mkdir -p build +cd build +../configure +make echo-version > ../I3_VERSION +make dist-bzip2 +# unpack dist tarball +mkdir -p "${DEST}" +tar xf *.tar.bz2 -C "${DEST}" --strip-components=1 +cp -r ../debian "${DEST}" +sed -i '/^\s*libxcb-xrm-dev/d' deb/ubuntu-*/DIST/debian/control || true +cd "${DEST}" +debchange -m -l+g$(git describe --tags) 'Automatically built' +dpkg-buildpackage -b diff --git a/travis/deploy-github-pages.sh b/travis/deploy-github-pages.sh new file mode 100755 index 00000000..f86fbf22 --- /dev/null +++ b/travis/deploy-github-pages.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -e +set -x + +GITVERSION=$(git describe --tags) + +mkdir build.i3wm.org +cp -r deb/CLANG build.i3wm.org/clang-analyze +cp -r deb/COPY-DOCS build.i3wm.org/docs +cd build.i3wm.org +echo build.i3wm.org > CNAME +git init + +git config user.name "Travis CI" +git config user.email "i3bot@i3wm.org" +git add . +git commit -m "Publish docs/static analysis for github.com/i3/i3 v${GITVERSION}" + +# Hide stdout/stderr because it might contain sensitive info. +set +x +echo "git push" +git push --force --quiet "https://${GH_TOKEN}@github.com/i3/build.i3wm.org.git" master:gh-pages >/dev/null 2>&1 diff --git a/travis/docker-build-and-push.sh b/travis/docker-build-and-push.sh index 7dfd3392..9b654a84 100755 --- a/travis/docker-build-and-push.sh +++ b/travis/docker-build-and-push.sh @@ -2,10 +2,19 @@ set -e +BASENAME=$1 +DOCKERFILE=$2 + # .dockerignore is created on demand so that release.sh and other scripts are # not influenced by our travis setup. echo .git > .dockerignore -docker build --pull --no-cache --rm -t=${BASENAME} -f travis-build.Dockerfile . -docker login -e ${DOCKER_EMAIL} -u ${DOCKER_USER} -p ${DOCKER_PASS} -docker push ${BASENAME} +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 +# the login+push step when the variable isn’t set. +if [ -n "${DOCKER_PASS}" ] +then + docker login -e ${DOCKER_EMAIL} -u ${DOCKER_USER} -p ${DOCKER_PASS} + docker push ${BASENAME} +fi diff --git a/travis/docs.sh b/travis/docs.sh new file mode 100755 index 00000000..d452e9b6 --- /dev/null +++ b/travis/docs.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e +set -x + +for f in $(grep '\.html$' debian/i3-wm.docs | grep -v 'docs/refcard.html' | grep -v 'docs/lib-i3test') +do + asciidoc -a linkcss -a stylesdir=http://i3wm.org/css -a scriptsdir=http://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) +do + [ -f "$file" ] && asciidoc -a linkcss -a stylesdir=http://i3wm.org/css -a scriptsdir=http://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/ diff --git a/travis/ha.sh b/travis/ha.sh index 688755c3..ed70320d 100755 --- a/travis/ha.sh +++ b/travis/ha.sh @@ -4,4 +4,4 @@ # installed in the container, so that any changes in what needs to be installed # will result in a cache invalidation. -cat debian/control travis-build.Dockerfile | sha256sum | dd bs=1 count=8 status=none +cat debian/control "$1" | sha256sum | dd bs=1 count=8 status=none diff --git a/travis/prep-bintray.sh b/travis/prep-bintray.sh new file mode 100755 index 00000000..4ea56dcd --- /dev/null +++ b/travis/prep-bintray.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +set -e +set -x + +sed -i "s,%version%,$(git describe --tags),g" travis/bintray-autobuild-*.json diff --git a/travis/run-tests.sh b/travis/run-tests.sh index 87c5dbb7..44df81d2 100755 --- a/travis/run-tests.sh +++ b/travis/run-tests.sh @@ -1,8 +1,32 @@ #!/bin/sh -cd testcases + +set -e +set -x + +cd build + +# TODO: remove this workaround once https://bugs.debian.org/836723 is fixed +# Found at https://llvm.org/bugs/show_bug.cgi?id=27310#c8: +if [ "$CC" = "clang" ] +then + cat >fixasan.c < /etc/dpkg/dpkg.cfg.d/docker-apt-speedup +# Paper over occasional network flakiness of some mirrors. +RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry + +# NOTE: I tried exclusively using gce_debian_mirror.storage.googleapis.com +# instead of httpredir.debian.org, but the results (Fetched 123 MB in 36s (3357 +# kB/s)) are not any better than httpredir.debian.org (Fetched 123 MB in 34s +# (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-3.5 (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-3.5 \ + lintian && \ + rm -rf /var/lib/apt/lists/* + +# Install i3 build dependencies. +COPY debian/control /usr/src/i3-debian-packaging/control +RUN linux32 apt-get update && \ + DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \ + rm -rf /var/lib/apt/lists/* diff --git a/travis/travis-base-ubuntu-386.Dockerfile b/travis/travis-base-ubuntu-386.Dockerfile new file mode 100644 index 00000000..65f123c8 --- /dev/null +++ b/travis/travis-base-ubuntu-386.Dockerfile @@ -0,0 +1,33 @@ +# vim:ft=Dockerfile +# Same as travis-base.Dockerfile, but without the test suite dependencies since +# we only build Debian packages on Ubuntu i386, we don’t run the tests. +FROM i386/ubuntu:xenial + +RUN echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup +# Paper over occasional network flakiness of some mirrors. +RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry + +# NOTE: I tried exclusively using gce_debian_mirror.storage.googleapis.com +# instead of httpredir.debian.org, but the results (Fetched 123 MB in 36s (3357 +# kB/s)) are not any better than httpredir.debian.org (Fetched 123 MB in 34s +# (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-3.5 (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-3.5 \ + lintian && \ + rm -rf /var/lib/apt/lists/* + +# Install i3 build dependencies. +COPY debian/control /usr/src/i3-debian-packaging/control +RUN echo 'deb http://dl.bintray.com/i3/i3-autobuild-ubuntu xenial main' > /etc/apt/sources.list.d/i3-autobuild.list && \ + linux32 apt-get update && \ + linux32 apt-get --allow-unauthenticated install i3-autobuild-keyring && \ + rm -f /var/lib/apt/lists/dl.bintray.com_i3_i3-autobuild-ubuntu_* && \ + linux32 apt-get update && \ + DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \ + rm -rf /var/lib/apt/lists/* diff --git a/travis/travis-base-ubuntu.Dockerfile b/travis/travis-base-ubuntu.Dockerfile new file mode 100644 index 00000000..ab474691 --- /dev/null +++ b/travis/travis-base-ubuntu.Dockerfile @@ -0,0 +1,34 @@ +# vim:ft=Dockerfile +# Same as travis-base.Dockerfile, but without the test suite dependencies since +# we only build Debian packages on Ubuntu, we don’t run the tests. +FROM ubuntu:xenial + +RUN echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup +# Paper over occasional network flakiness of some mirrors. +RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry + +# NOTE: I tried exclusively using gce_debian_mirror.storage.googleapis.com +# instead of httpredir.debian.org, but the results (Fetched 123 MB in 36s (3357 +# kB/s)) are not any better than httpredir.debian.org (Fetched 123 MB in 34s +# (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-3.5 (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-3.5 \ + lintian && \ + rm -rf /var/lib/apt/lists/* + +# Install i3 build dependencies. +COPY debian/control /usr/src/i3-debian-packaging/control +RUN echo 'deb http://dl.bintray.com/i3/i3-autobuild-ubuntu xenial main' > /etc/apt/sources.list.d/i3-autobuild.list && \ + apt-get update && \ + apt-get --allow-unauthenticated install i3-autobuild-keyring && \ + rm -f /var/lib/apt/lists/dl.bintray.com_i3_i3-autobuild-ubuntu_* && \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \ + rm -rf /var/lib/apt/lists/* diff --git a/travis-build.Dockerfile b/travis/travis-base.Dockerfile similarity index 83% rename from travis-build.Dockerfile rename to travis/travis-base.Dockerfile index 0861b26a..85fa2752 100644 --- a/travis-build.Dockerfile +++ b/travis/travis-base.Dockerfile @@ -19,7 +19,7 @@ RUN apt-get update && \ dpkg-dev devscripts git equivs \ clang clang-format-3.5 \ lintian \ - libanyevent-perl libanyevent-i3-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-modules 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 libanyevent-i3-perl && \ + libanyevent-perl libanyevent-i3-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 libanyevent-i3-perl && \ rm -rf /var/lib/apt/lists/* # Install i3 build dependencies.