diff options
author | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2023-08-17 00:07:28 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-08-17 00:07:28 +0000 |
commit | 43fb25f235b333bf436c0b790dfa12b5b11725c1 (patch) | |
tree | 71181f84a33360ed77e4342ccd550aa6b78cffa0 | |
parent | 28e86b61c69b3b42385b0c535c2044e84657cffb (diff) | |
parent | 8e0233feec80c9fc25dab874723009612ee64563 (diff) | |
download | wayland-43fb25f235b333bf436c0b790dfa12b5b11725c1.tar.gz |
Merge changes I50045de0,I91b450f1 into main am: 8e0233feec
Original change: https://android-review.googlesource.com/c/platform/external/wayland/+/2699957
Change-Id: Ie0881cff0d49f699110bf032c0210e312ad85b28
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
84 files changed, 4311 insertions, 2788 deletions
@@ -1,48 +1,8 @@ *.announce -*.deps -*.jpg -*.la -*.lo -*.o -*.pc *.sig -*.so *.swp -*.3 -*.7 *.log -*.trs *.tar.xz *~ -*-test -.libs -.dirstamp cscope.out ctags -/aclocal.m4 -/wayland-scanner.m4 -/autom4te.cache -/compile -/config.guess -/config.h -/config.h.in -/config.log -/config.mk -/config.status -/config.sub -/configure -/depcomp -/install-sh -/libtool -/ltmain.sh -/missing -/stamp-h1 -/test-driver -/tests/output/ -Makefile -Makefile.in -exec-fd-leak-checker -fixed-benchmark -/wayland-egl-abi-check -/wayland-scanner -protocol/*.[ch] diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b2e174f..f7f74ec 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,90 +1,352 @@ -.templates_sha: &template_sha bd8010dd0123d3f0dda4ef691078566af2842613 # see https://docs.gitlab.com/ee/ci/yaml/#includefile +# This file uses the freedesktop ci-templates to build Wayland and run our +# tests in CI. +# +# ci-templates uses a multi-stage build process. First, the base container +# image is built which contains the core distribution, the toolchain, and +# all our build dependencies. This container is aggressively cached; if a +# container image matching $FDO_DISTRIBUTION_TAG is found in either the +# upstream repo (wayland/weston) or the user's downstream repo, it is +# reused for the build. This gives us predictability of build and far +# quicker runtimes, however it means that any changes to the base container +# must also change $FDO_DISTRIBUTION_TAG. When changing this, please use +# the current date as well as a unique build identifier. +# +# After the container is either rebuilt (tag mismatch) or reused (tag +# previously used), the build stage executes within this container. +# +# The final stage is used to expose documentation and coverage information, +# including publishing documentation to the public site when built on the +# main branch. +# +# Apart from the 'variables', 'include', and 'stages' top-level anchors, +# everything not beginning with a dot ('.') is the name of a job which will +# be executed as part of CI, unless the rules specify that it should not be +# run. +# +# Variables prefixed with CI_ are generally provided by GitLab itself; +# variables prefixed with FDO_ and templates prefixed by .fdo are provided +# by the ci-templates. +# +# For more information on GitLab CI, including the YAML syntax, see: +# https://docs.gitlab.com/ee/ci/yaml/README.html +# +# Note that freedesktop.org uses the 'Community Edition' of GitLab, so features +# marked as 'premium' or 'ultimate' are not available to us. +# +# For more information on ci-templates, see: +# - documentation at https://freedesktop.pages.freedesktop.org/ci-templates/ +# - repo at https://gitlab.freedesktop.org/freedesktop/ci-templates/ +# Here we use a fixed ref in order to isolate ourselves from ci-templates +# API changes. If you need new features from ci-templates you must bump +# this to the current SHA you require from the ci-templates repo, however +# be aware that you may need to account for API changes when doing so. +.templates_sha: &template_sha d5aa3941aa03c2f716595116354fb81eb8012acb # see https://docs.gitlab.com/ee/ci/yaml/#includefile include: - # Debian container builder template - project: 'freedesktop/ci-templates' ref: *template_sha - file: '/templates/debian.yml' + file: + - '/templates/debian.yml' + - '/templates/freebsd.yml' + - '/templates/ci-fairy.yml' + +variables: + FDO_UPSTREAM_REPO: wayland/wayland + FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH" +# Define the build stages. These are used for UI grouping as well as +# dependencies. stages: - - prep - - build + - "Merge request checks" + - "Base container" + - "Build and test" + - "Other build configurations" +.ci-rules: + rules: + - when: on_success -variables: - DEBIAN_PACKAGES: 'build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev doxygen graphviz xmlto xsltproc docbook-xsl python3-pip python3-setuptools ninja-build' - DEBIAN_EXEC: 'pip3 install meson==0.52.1' - # these tags should be updated each time the list of packages is updated - # changing these will force rebuilding the associated image - # Note: these tags have no meaning and are not tied to a particular - # wayland version - DEBIAN_TAG: '2020-06-05.1' - FDO_UPSTREAM_REPO: wayland/wayland +# Base variables used for anything using a Debian environment +.os-debian: + variables: + BUILD_OS: debian + FDO_DISTRIBUTION_VERSION: bullseye + FDO_DISTRIBUTION_PACKAGES: 'build-essential pkg-config libexpat1-dev libffi-dev libxml2-dev doxygen graphviz xmlto xsltproc docbook-xsl python3-pip python3-setuptools ninja-build' + FDO_DISTRIBUTION_EXEC: 'pip3 install meson==0.56.0' + # bump this tag every time you change something which requires rebuilding the + # base image + FDO_DISTRIBUTION_TAG: "2022-08-08.0" + +.debian-x86_64: + extends: + - .os-debian + variables: + BUILD_ARCH: "x86-64" + +.debian-aarch64: + extends: + - .os-debian + variables: + BUILD_ARCH: "aarch64" + +.debian-armv7: + extends: + - .os-debian + variables: + BUILD_ARCH: "armv7" + + +# Does not inherit .ci-rules as we only want it to run in MR context. +check-commit: + extends: + - .fdo.ci-fairy + stage: "Merge request checks" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: always + - when: never + script: + - ci-fairy check-commits --signed-off-by --junit-xml=results.xml + variables: + GIT_DEPTH: 100 + artifacts: + reports: + junit: results.xml -.debian.buster: +# Build our base container image, which contains the core distribution, the +# toolchain, and all our build dependencies. This will be reused in the build +# stage. +x86_64-debian-container_prep: + extends: + - .ci-rules + - .debian-x86_64 + - .fdo.container-build@debian + stage: "Base container" variables: - FDO_DISTRIBUTION_PACKAGES: $DEBIAN_PACKAGES - FDO_DISTRIBUTION_TAG: $DEBIAN_TAG - FDO_DISTRIBUTION_VERSION: 'buster' - FDO_DISTRIBUTION_EXEC: $DEBIAN_EXEC + GIT_STRATEGY: none +aarch64-debian-container_prep: + extends: + - .ci-rules + - .debian-aarch64 + - .fdo.container-build@debian + tags: + - aarch64 + stage: "Base container" + variables: + GIT_STRATEGY: none -debian:buster@container-prep: +armv7-debian-container_prep: extends: - - .debian.buster + - .ci-rules + - .debian-armv7 - .fdo.container-build@debian - stage: prep + tags: + - aarch64 + stage: "Base container" variables: GIT_STRATEGY: none + FDO_BASE_IMAGE: "arm32v7/debian:$FDO_DISTRIBUTION_VERSION" + + +# Core build environment. +.build-env: + variables: + MESON_BUILD_TYPE: "-Dbuildtype=debug -Doptimization=0 -Db_sanitize=address,undefined" + # See https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/154 + ASAN_OPTIONS: "detect_odr_violation=0" + before_script: + - export BUILD_ID="wayland-$CI_JOB_NAME" + - export PREFIX="${CI_PROJECT_DIR}/prefix-${BUILD_ID}" + - export BUILDDIR="${CI_PROJECT_DIR}/build-${BUILD_ID}" + - mkdir "$BUILDDIR" "$PREFIX" + +# Build variants to be stacked on as required. +.build-release: + stage: "Other build configurations" + variables: + MESON_BUILD_TYPE: "-Dbuildtype=release" + + +# OS/architecture-specific variants +.build-env-debian-x86_64: + extends: + - .fdo.suffixed-image@debian + - .debian-x86_64 + - .build-env + needs: + - job: x86_64-debian-container_prep + artifacts: false -build-native-autotools: +.build-env-debian-aarch64: extends: - - .debian.buster - - .fdo.distribution-image@debian - stage: build + - .fdo.suffixed-image@debian + - .debian-aarch64 + - .build-env + variables: + # At least with the versions we have, the LSan runtime makes fork unusably + # slow on AArch64, which is bad news since the test suite decides to fork + # for every single subtest. For now, in order to get AArch64 builds and + # tests into CI, just assume that we're not going to leak any more on + # AArch64 than we would on ARMv7 or x86-64. + ASAN_OPTIONS: "detect_leaks=0,detect_odr_violation=0" + tags: + - aarch64 + needs: + - job: aarch64-debian-container_prep + artifacts: false + +.build-env-debian-armv7: + extends: + - .fdo.suffixed-image@debian + - .debian-armv7 + - .build-env + tags: + - aarch64 + needs: + - job: armv7-debian-container_prep + artifacts: false + + +# Full build and test. +.do-build: + extends: + - .ci-rules + stage: "Build and test" script: - - export BUILD_ID="wayland-$CI_JOB_NAME_$CI_COMMIT_SHA-$CI_JOB_ID" - - export PREFIX="$(pwd)/prefix-$BUILD_ID" - - export BUILDDIR="$(pwd)/build-$BUILD_ID" - - export MAKEFLAGS="-j4" - - mkdir "$BUILDDIR" "$PREFIX" - - cd "$BUILDDIR" - - ../autogen.sh --prefix="$PREFIX" --with-icondir=/usr/share/X11/icons - - make all - - make check - - make install - - make distcheck + - cd "$BUILDDIR" + - meson --prefix="$PREFIX" -Dicon_directory=/usr/share/X11/icons -Dwerror=true ${MESON_BUILD_TYPE} .. + - ninja -k0 -j${FDO_CI_CONCURRENT:-4} + - meson test --num-processes ${FDO_CI_CONCURRENT:-4} + - ninja clean artifacts: - name: wayland-$CI_COMMIT_SHA-$CI_JOB_ID + name: wayland-$CI_JOB_NAME when: always paths: - - build-*/wayland-*.tar.xz - - build-*/wayland*/_build/sub/*.log - - build-*/*.log - - prefix-* - + - build-*/meson-logs + - prefix-* + reports: + junit: build-*/meson-logs/testlog.junit.xml -build-native-meson: +# Full build and test. +.do-build-qemu: extends: - - .debian.buster - - .fdo.distribution-image@debian - stage: build + - .ci-rules + stage: "Build and test" script: - - export BUILD_ID="wayland-$CI_JOB_NAME_$CI_COMMIT_SHA-$CI_JOB_ID" - - export PREFIX="$(pwd)/prefix-$BUILD_ID" - - export BUILDDIR="$(pwd)/build-$BUILD_ID" - - mkdir "$BUILDDIR" "$PREFIX" - - cd "$BUILDDIR" - - meson --prefix="$PREFIX" -Dicon_directory=/usr/share/X11/icons .. - - ninja -k0 test - - ninja clean + # Start the VM and copy our workspace to the VM + - /app/vmctl start + - scp -r $PWD "vm:" + # The `set +e is needed to ensure that we always copy the meson logs back to + # the workspace to see details about the failed tests. + - | + set +e + /app/vmctl exec "pkg info; cd $CI_PROJECT_NAME ; meson $BUILDDIR --prefix=$PREFIX $MESON_BUILD_TYPE $MESON_ARGS && ninja -C $BUILDDIR -j${FDO_CI_CONCURRENT:-4}" + /app/vmctl exec "meson test --print-errorlogs -C $BUILDDIR --num-processes ${FDO_CI_CONCURRENT:-4}" && touch .tests-successful + set -ex + scp -r vm:$BUILDDIR/meson-logs . + /app/vmctl exec "ninja -C $BUILDDIR install" + mkdir -p $PREFIX && scp -r vm:$PREFIX/ $PREFIX/ + # Finally, shut down the VM. + - /app/vmctl stop + - test -f .tests-successful || exit 1 artifacts: - name: wayland-meson-$CI_COMMIT_SHA-$CI_JOB_ID + name: wayland-$CI_JOB_NAME when: always paths: - - build-meson/meson-logs - - prefix-* + - meson-logs + - prefix-* + reports: + junit: meson-logs/testlog.junit.xml + +# Full build and test. +x86_64-debian-build: + extends: + - .build-env-debian-x86_64 + - .do-build + +x86_64-release-debian-build: + extends: + - .build-env-debian-x86_64 + - .do-build + - .build-release + +aarch64-debian-build: + extends: + - .build-env-debian-aarch64 + - .do-build + +aarch64-release-debian-build: + extends: + - .build-env-debian-aarch64 + - .do-build + - .build-release + +armv7-debian-build: + extends: + - .build-env-debian-armv7 + - .do-build + +armv7-release-debian-build: + extends: + - .build-env-debian-armv7 + - .do-build + - .build-release + +# Base variables used for anything using a FreeBSD environment +.os-freebsd: + variables: + BUILD_OS: freebsd + FDO_DISTRIBUTION_VERSION: "13.1" + FDO_DISTRIBUTION_PACKAGES: 'libxslt meson ninja pkgconf expat libffi libepoll-shim libxml2' + # bump this tag every time you change something which requires rebuilding the + # base image + FDO_DISTRIBUTION_TAG: "2022-09-08.0" + # Don't build documentation since installing the required tools massively + # increases the VM image (and therefore container) size. + MESON_ARGS: "-Ddocumentation=false" + +.freebsd-x86_64: + extends: + - .os-freebsd + variables: + BUILD_ARCH: "x86_64" + +x86_64-freebsd-container_prep: + extends: + - .ci-rules + - .freebsd-x86_64 + - .fdo.qemu-build@freebsd@x86_64 + stage: "Base container" + variables: + GIT_STRATEGY: none + +.build-env-freebsd-x86_64: + variables: + # Compiling with ASan+UBSan appears to trigger an infinite loop in the + # compiler shipped with FreeBSD 13.0, so we only use UBSan here. + # Additionally, sanitizers can't be used with b_lundef on FreeBSD. + MESON_BUILD_TYPE: "-Dbuildtype=debug -Db_sanitize=undefined -Db_lundef=false" + extends: + - .fdo.suffixed-image@freebsd + - .freebsd-x86_64 + - .build-env + needs: + - job: x86_64-freebsd-container_prep + artifacts: false + +# Full build and test. +x86_64-freebsd-build: + extends: + - .build-env-freebsd-x86_64 + - .do-build-qemu + +x86_64-release-freebsd-build: + extends: + - .build-env-freebsd-x86_64 + - .do-build-qemu + - .build-release diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..bdb791e --- /dev/null +++ b/.mailmap @@ -0,0 +1,3 @@ +Faith Ekstrand <faith@gfxstrand.net> <jason@jlekstrand.net> +Faith Ekstrand <faith@gfxstrand.net> <jason.ekstrand@intel.com> +Faith Ekstrand <faith@gfxstrand.net> <jason.ekstrand@collabora.com> @@ -2,6 +2,7 @@ Copyright © 2008-2012 Kristian Høgsberg Copyright © 2010-2012 Intel Corporation Copyright © 2011 Benjamin Franzke Copyright © 2012 Collabora, Ltd. +Copyright 2022 Google LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -16,7 +16,7 @@ third_party { type: GIT value: "https://gitlab.freedesktop.org/wayland/wayland.git" } - version: "1.19.0" - last_upgrade_date { year: 2021 month: 2 day: 9 } + version: "1.22.0" + last_upgrade_date { year: 2023 month: 8 day: 8 } license_type: NOTICE } diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index b9438b7..0000000 --- a/Makefile.am +++ /dev/null @@ -1,341 +0,0 @@ -if BUILD_DOCS -SUBDIRS = doc -endif - -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} - -AM_CPPFLAGS = \ - -I$(top_builddir)/src \ - -I$(top_srcdir)/src \ - -I$(top_builddir)/protocol - -AM_CFLAGS = $(GCC_CFLAGS) - -aclocaldir = $(datadir)/aclocal -dist_aclocal_DATA = wayland-scanner.m4 - -dist_pkgdata_DATA = \ - wayland-scanner.mk \ - protocol/wayland.xml \ - protocol/wayland.dtd - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = - -bin_PROGRAMS = wayland-scanner -wayland_scanner_SOURCES = src/scanner.c -wayland_scanner_CPPFLAGS = $(AM_CPPFLAGS) -include config.h -wayland_scanner_CFLAGS = $(EXPAT_CFLAGS) $(LIBXML_CFLAGS) $(AM_CFLAGS) -wayland_scanner_LDADD = $(EXPAT_LIBS) $(LIBXML_LIBS) libwayland-util.la -pkgconfig_DATA += src/wayland-scanner.pc - -if DTD_VALIDATION -wayland_scanner_SOURCES += src/dtddata.S -endif -src/dtddata.o: protocol/wayland.dtd - -if USE_HOST_SCANNER -wayland_scanner = wayland-scanner -else -$(BUILT_SOURCES) : wayland-scanner -wayland_scanner = $(top_builddir)/wayland-scanner -endif - -libwayland_util_la_CFLAGS = $(AM_CFLAGS) -libwayland_util_la_SOURCES = \ - src/wayland-util.c \ - src/wayland-util.h - -noinst_LTLIBRARIES = libwayland-util.la - -if ENABLE_LIBRARIES -noinst_LTLIBRARIES += libwayland-private.la -lib_LTLIBRARIES = libwayland-server.la libwayland-client.la - -libwayland_private_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -libwayland_private_la_SOURCES = \ - src/connection.c \ - src/wayland-os.c \ - src/wayland-os.h \ - src/wayland-private.h \ - src/wayland-server-private.h - -include_HEADERS = \ - src/wayland-util.h \ - src/wayland-server.h \ - src/wayland-server-core.h \ - src/wayland-client.h \ - src/wayland-client-core.h \ - src/wayland-version.h - -nodist_include_HEADERS = \ - protocol/wayland-server-protocol.h \ - protocol/wayland-client-protocol.h - -libwayland_server_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -pthread -libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la $(RT_LIBS) -lm -libwayland_server_la_LDFLAGS = -version-info 1:0:1 -libwayland_server_la_SOURCES = \ - src/wayland-server.c \ - src/wayland-shm.c \ - src/event-loop.c - -nodist_libwayland_server_la_SOURCES = \ - protocol/wayland-server-protocol.h \ - protocol/wayland-protocol.c - -libwayland_client_la_CFLAGS = $(FFI_CFLAGS) $(AM_CFLAGS) -pthread -libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-private.la libwayland-util.la $(RT_LIBS) -lm -libwayland_client_la_LDFLAGS = -version-info 3:0:3 -libwayland_client_la_SOURCES = \ - src/wayland-client.c - -nodist_libwayland_client_la_SOURCES = \ - protocol/wayland-client-protocol.h \ - protocol/wayland-protocol.c - -pkgconfig_DATA += src/wayland-client.pc src/wayland-server.pc - -protocol/%-protocol.c : $(top_srcdir)/protocol/%.xml -if USE_HOST_SCANNER - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s code $< $@ -else - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s public-code $< $@ -endif - -protocol/%-server-protocol.h : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s server-header $< $@ - -protocol/%-client-protocol.h : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s client-header $< $@ - -protocol/%-server-protocol-core.h : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s server-header -c < $< > $@ - -protocol/%-client-protocol-core.h : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) -s client-header -c < $< > $@ - -BUILT_SOURCES = \ - $(nodist_libwayland_server_la_SOURCES) \ - $(nodist_libwayland_client_la_SOURCES) \ - $(nodist_headers_test_SOURCES) \ - $(nodist_display_test_SOURCES) - -CLEANFILES = $(BUILT_SOURCES) doc/doxygen/doxygen_sqlite3.db -DISTCLEANFILES = src/wayland-version.h -EXTRA_DIST = CONTRIBUTING.md - - - -lib_LTLIBRARIES += libwayland-cursor.la - -include_HEADERS += cursor/wayland-cursor.h - -libwayland_cursor_la_SOURCES = \ - cursor/wayland-cursor.c \ - cursor/os-compatibility.c \ - cursor/os-compatibility.h \ - cursor/cursor-data.h \ - cursor/xcursor.c \ - cursor/xcursor.h -libwayland_cursor_la_LIBADD = libwayland-client.la - -pkgconfig_DATA += cursor/wayland-cursor.pc - -libwayland_cursor_la_CFLAGS = \ - $(AM_CFLAGS) \ - -I$(top_builddir)/src \ - -I$(top_srcdir)/src \ - -DICONDIR=\"$(ICONDIR)\" - -lib_LTLIBRARIES += libwayland-egl.la - -include_HEADERS += egl/wayland-egl.h -include_HEADERS += egl/wayland-egl-core.h - -libwayland_egl_la_SOURCES = egl/wayland-egl.c -libwayland_egl_la_LDFLAGS = -version-info 1 - -pkgconfig_DATA += egl/wayland-egl.pc - -## XXX: backend interface -include_HEADERS += egl/wayland-egl-backend.h -pkgconfig_DATA += egl/wayland-egl-backend.pc - -built_test_programs = \ - array-test \ - client-test \ - display-test \ - connection-test \ - event-loop-test \ - fixed-test \ - interface-test \ - list-test \ - map-test \ - os-wrappers-test \ - sanity-test \ - socket-test \ - queue-test \ - proxy-test \ - signal-test \ - newsignal-test \ - resources-test \ - message-test \ - headers-test \ - compositor-introspection-test \ - protocol-logger-test \ - wayland-egl-abi-check - -EXTRA_DIST += egl/wayland-egl-symbols-check - -check_PROGRAMS = wayland-egl-abi-check -wayland_egl_abi_check_SOURCES = egl/wayland-egl-abi-check.c - -if ENABLE_CPP_TEST -built_test_programs += cpp-compile-test -endif - -AM_TESTS_ENVIRONMENT = \ - export WAYLAND_SCANNER='$(top_builddir)/wayland-scanner' \ - TEST_DATA_DIR='$(top_srcdir)/tests/data' \ - TEST_OUTPUT_DIR='$(top_builddir)/tests/output' \ - WAYLAND_EGL_LIB='$(top_builddir)/.libs/libwayland-egl.so' \ - SED=$(SED) \ - NM='$(NM)' \ - ; - -TESTS = $(built_test_programs) \ - egl/wayland-egl-symbols-check \ - tests/scanner-test.sh - -noinst_PROGRAMS = \ - $(built_test_programs) \ - exec-fd-leak-checker \ - fixed-benchmark - -noinst_LTLIBRARIES += \ - libtest-runner.la \ - libtest-helpers.la - -libtest_helpers_la_SOURCES = tests/test-helpers.c - -libtest_runner_la_SOURCES = \ - tests/test-runner.c \ - tests/test-runner.h \ - tests/test-compositor.h \ - tests/test-compositor.c -libtest_runner_la_LIBADD = \ - libwayland-private.la \ - libwayland-util.la \ - libwayland-client.la \ - libwayland-server.la \ - libtest-helpers.la \ - $(RT_LIBS) $(DL_LIBS) $(FFI_LIBS) - -array_test_SOURCES = tests/array-test.c -array_test_LDADD = libtest-runner.la -client_test_SOURCES = tests/client-test.c -client_test_LDADD = libtest-runner.la -display_test_CFLAGS = -pthread -display_test_SOURCES = tests/display-test.c -display_test_LDADD = libtest-runner.la -nodist_display_test_SOURCES = \ - protocol/tests-server-protocol.h \ - protocol/tests-client-protocol.h \ - protocol/tests-protocol.c -connection_test_SOURCES = tests/connection-test.c -connection_test_LDADD = libtest-runner.la -event_loop_test_SOURCES = tests/event-loop-test.c -event_loop_test_LDADD = libtest-runner.la -fixed_test_SOURCES = tests/fixed-test.c -fixed_test_LDADD = libtest-runner.la -interface_test_SOURCES = tests/interface-test.c -interface_test_LDADD = libtest-runner.la -list_test_SOURCES = tests/list-test.c -list_test_LDADD = libtest-runner.la -map_test_SOURCES = tests/map-test.c -map_test_LDADD = libtest-runner.la -sanity_test_SOURCES = tests/sanity-test.c -sanity_test_LDADD = libtest-runner.la -socket_test_SOURCES = tests/socket-test.c -socket_test_LDADD = libtest-runner.la -queue_test_SOURCES = tests/queue-test.c -queue_test_LDADD = libtest-runner.la -proxy_test_SOURCES = tests/proxy-test.c -proxy_test_LDADD = libtest-runner.la -signal_test_SOURCES = tests/signal-test.c -signal_test_LDADD = libtest-runner.la -# wayland-server.c is needed here to access wl_priv_* functions -newsignal_test_SOURCES = tests/newsignal-test.c src/wayland-server.c -newsignal_test_LDADD = libtest-runner.la -resources_test_SOURCES = tests/resources-test.c -resources_test_LDADD = libtest-runner.la -message_test_SOURCES = tests/message-test.c -message_test_LDADD = libtest-runner.la -compositor_introspection_test_SOURCES = tests/compositor-introspection-test.c -compositor_introspection_test_LDADD = libtest-runner.la -protocol_logger_test_SOURCES = tests/protocol-logger-test.c -protocol_logger_test_LDADD = libtest-runner.la -headers_test_SOURCES = tests/headers-test.c \ - tests/headers-protocol-test.c \ - tests/headers-protocol-core-test.c -nodist_headers_test_SOURCES = \ - protocol/wayland-server-protocol-core.h \ - protocol/wayland-client-protocol-core.h - -if ENABLE_CPP_TEST -cpp_compile_test_SOURCES = tests/cpp-compile-test.cpp -endif - -fixed_benchmark_SOURCES = tests/fixed-benchmark.c -fixed_benchmark_LDADD = $(RT_LIBS) - -os_wrappers_test_SOURCES = tests/os-wrappers-test.c -os_wrappers_test_LDADD = libtest-runner.la - -exec_fd_leak_checker_SOURCES = tests/exec-fd-leak-checker.c -exec_fd_leak_checker_LDADD = libtest-helpers.la - -EXTRA_DIST += tests/scanner-test.sh \ - protocol/tests.xml \ - tests/data/bad-identifier-arg.xml \ - tests/data/bad-identifier-entry.xml \ - tests/data/bad-identifier-enum.xml \ - tests/data/bad-identifier-event.xml \ - tests/data/bad-identifier-interface.xml \ - tests/data/bad-identifier-protocol.xml \ - tests/data/bad-identifier-request.xml \ - tests/data/example.xml \ - tests/data/example-client.h \ - tests/data/example-server.h \ - tests/data/example-code.c \ - tests/data/small.xml \ - tests/data/small-code.c \ - tests/data/small-client.h \ - tests/data/small-server.h \ - tests/data/small-code-core.c \ - tests/data/small-client-core.h \ - tests/data/small-server-core.h \ - tests/data/small-private-code.c \ - meson.build \ - meson_options.txt \ - cursor/meson.build \ - doc/meson.build \ - doc/doxygen/mainpage.dox \ - doc/doxygen/meson.build \ - doc/doxygen/gen-doxygen.py \ - doc/doxygen/xml/meson.build \ - doc/doxygen/xml/Client/meson.build \ - doc/doxygen/xml/Server/meson.build \ - doc/publican/meson.build \ - doc/publican/sources/meson.build \ - egl/meson.build \ - src/meson.build \ - tests/meson.build - -tests/scanner-test.sh: $(top_builddir)/wayland-scanner - -clean-local: - -rm -rf tests/output - -endif #ENABLE_LIBRARIES @@ -1,4 +1,4 @@ -What is Wayland? +# Wayland Wayland is a project to define a protocol for a compositor to talk to its clients as well as a library implementation of the protocol. The @@ -17,10 +17,6 @@ protocol does not handle rendering, which is one of the features that makes wayland so simple. All clients are expected to handle rendering themselves, typically through cairo or OpenGL. -The weston compositor is a reference implementation of a wayland -compositor and the weston repository also includes a few example -clients. - Building the wayland libraries is fairly simple, aside from libffi, they don't have many dependencies: @@ -29,6 +25,6 @@ they don't have many dependencies: $ meson build/ --prefix=PREFIX $ ninja -C build/ install -where PREFIX is where you want to install the libraries. See -https://wayland.freedesktop.org for more complete build instructions -for wayland, weston, xwayland and various toolkits. +where PREFIX is where you want to install the libraries. + +See https://wayland.freedesktop.org for documentation. diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 916169a..0000000 --- a/autogen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -test -n "$srcdir" || srcdir=`dirname "$0"` -test -n "$srcdir" || srcdir=. -( - cd "$srcdir" && - autoreconf --force -v --install -) || exit -test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 7f2f393..0000000 --- a/configure.ac +++ /dev/null @@ -1,202 +0,0 @@ -AC_PREREQ([2.64]) - -m4_define([wayland_major_version], [1]) -m4_define([wayland_minor_version], [19]) -m4_define([wayland_micro_version], [0]) -m4_define([wayland_version], - [wayland_major_version.wayland_minor_version.wayland_micro_version]) - -AC_INIT([wayland], - [wayland_version], - [https://gitlab.freedesktop.org/wayland/wayland/issues/], - [wayland], - [https://wayland.freedesktop.org/]) - -AC_SUBST([WAYLAND_VERSION_MAJOR], [wayland_major_version]) -AC_SUBST([WAYLAND_VERSION_MINOR], [wayland_minor_version]) -AC_SUBST([WAYLAND_VERSION_MICRO], [wayland_micro_version]) -AC_SUBST([WAYLAND_VERSION], [wayland_version]) - -AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_MACRO_DIR([m4]) - -AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects]) - -AM_SILENT_RULES([yes]) - -# Check for programs -AC_PROG_CC -AC_PROG_CXX -AC_PROG_GREP -AM_PROG_AS -AC_PROG_NM - -# check if we have C++ compiler. This is hacky workaround, -# for a reason why it is this way see -# http://lists.gnu.org/archive/html/bug-autoconf/2010-05/msg00001.html -have_cpp_compiler=yes - -if ! which "$CXX" &>/dev/null; then - have_cpp_compiler=no -fi - -AM_CONDITIONAL(ENABLE_CPP_TEST, test "x$have_cpp_compiler" = "xyes") - -# Initialize libtool -LT_PREREQ([2.2]) -LT_INIT([disable-static]) - -PKG_PROG_PKG_CONFIG() - -AC_ARG_ENABLE([fatal-warnings], - AC_HELP_STRING([--enable-fatal-warnings], - [Build with -Werror]), - [enable_fatal_warnings=$enableval], - [enable_fatal_warnings=no]) -AS_IF([test x"$enable_fatal_warnings" != "xno"], [ - WERROR_CFLAGS="-Werror" -]) - -if test "x$GCC" = "xyes"; then - GCC_CFLAGS="$WERROR_CFLAGS -Wall -Wextra -Wno-unused-parameter -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden" -fi -AC_SUBST(GCC_CFLAGS) - -AC_CHECK_HEADERS([sys/prctl.h]) -AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl memfd_create strndup]) - -# *BSD don't have libdl, but they have its functions in libc -WESTON_SEARCH_LIBS([DL], [dl], [dlsym]) - -# OpenBSD doesn't have librt, but it has its functions in libc -WESTON_SEARCH_LIBS([RT], [rt], [clock_gettime]) - -AC_ARG_ENABLE([libraries], - [AC_HELP_STRING([--disable-libraries], - [Disable compilation of wayland libraries])], - [], - [enable_libraries=yes]) - -AC_ARG_WITH([host-scanner], - [AC_HELP_STRING([--with-host-scanner], - [Use installed wayland-scanner from host PATH during build])], - [], - [with_host_scanner=no]) - -AC_ARG_ENABLE([documentation], - [AC_HELP_STRING([--disable-documentation], - [Disable building the documentation])], - [], - [enable_documentation=yes]) - -AC_ARG_ENABLE([dtd-validation], - [AC_HELP_STRING([--disable-dtd-validation], - [Disable DTD validation of the protocol])], - [], - [enable_dtd_validation=yes]) - -AM_CONDITIONAL(USE_HOST_SCANNER, test "x$with_host_scanner" = xyes) - -AM_CONDITIONAL(ENABLE_LIBRARIES, test "x$enable_libraries" = xyes) - -AC_ARG_WITH(icondir, [ --with-icondir=<dir> Look for cursor icons here], - [ ICONDIR=$withval], - [ ICONDIR=${datadir}/icons]) -AC_SUBST([ICONDIR]) - -if test "x$enable_libraries" = "xyes"; then - PKG_CHECK_MODULES(FFI, [libffi]) - AC_CHECK_DECL(SFD_CLOEXEC,[], - [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile wayland libraries")], - [[#include <sys/signalfd.h>]]) - AC_CHECK_DECL(TFD_CLOEXEC,[], - [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile wayland libraries")], - [[#include <sys/timerfd.h>]]) - AC_CHECK_DECL(CLOCK_MONOTONIC,[], - [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile wayland libraries")], - [[#include <time.h>]]) -fi - -PKG_CHECK_MODULES(EXPAT, [expat]) - -AM_CONDITIONAL([DTD_VALIDATION], [test "x$enable_dtd_validation" = "xyes"]) -if test "x$enable_dtd_validation" = "xyes"; then - PKG_CHECK_MODULES(LIBXML, [libxml-2.0]) - AC_DEFINE(HAVE_LIBXML, 1, [libxml-2.0 is available]) - AC_CONFIG_LINKS([src/wayland.dtd.embed:protocol/wayland.dtd]) -fi - -AC_PATH_PROG(XSLTPROC, xsltproc) -AM_CONDITIONAL([HAVE_XSLTPROC], [test "x$XSLTPROC" != "x"]) - - -AM_CONDITIONAL(BUILD_DOCS, [test x$enable_documentation = xyes]) -if test "x$enable_documentation" = "xyes"; then - AC_PATH_PROG(DOXYGEN, doxygen) - - if test "x$DOXYGEN" = "x"; then - AC_MSG_ERROR([Documentation build requested but doxygen not found. Install doxygen or disable the documentation using --disable-documentation]) - fi - - AC_MSG_CHECKING([for compatible doxygen version]) - doxygen_version=`$DOXYGEN --version` - AS_VERSION_COMPARE([$doxygen_version], [1.6.0], - [AC_MSG_RESULT([no]) - AC_MSG_ERROR([Doxygen $doxygen_version too old. Doxygen 1.6+ required for documentation build. Install required doxygen version or disable the documentation using --disable-documentation])], - [AC_MSG_RESULT([yes])], - [AC_MSG_RESULT([yes])]) - - AC_PATH_PROG(XMLTO, xmlto) - - if test "x$XMLTO" = "x"; then - AC_MSG_ERROR([Documentation build requested but xmlto not found. Install xmlto or disable the documentation using --disable-documentation]) - fi - - AC_PATH_PROG(DOT, dot) - if test "x$DOT" = "x"; then - AC_MSG_ERROR([Documentation build requested but graphviz's dot not found. Install graphviz or disable the documentation using --disable-documentation]) - fi - AC_MSG_CHECKING([for compatible dot version]) - dot_version=`$DOT -V 2>&1|$GREP -o ['[0-9]*\.[0-9]*\.[0-9]*']` - AS_VERSION_COMPARE([$dot_version], [2.26.0], - [AC_MSG_RESULT([no]) - AC_MSG_ERROR([Graphviz dot $dot_version too old. Graphviz 2.26+ required for documentation build. Install required graphviz version or disable the documentation using --disable-documentation])], - [AC_MSG_RESULT([yes])], - [AC_MSG_RESULT([yes])]) - - AC_MSG_CHECKING([for docbook stylesheets]) - DOCS_STYLESHEET=http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl - AC_PATH_PROGS_FEATURE_CHECK([XSLTPROC_TMP], [xsltproc], - AS_IF([`"$ac_path_XSLTPROC_TMP" --nonet "$DOCS_STYLESHEET" > /dev/null 2>&1`], - [HAVE_DOCS_STYLESHEET=yes])) - if test "x$HAVE_DOCS_STYLESHEET" != "xyes"; then - AC_MSG_RESULT([no]) - AC_MSG_ERROR([Documentation build requested but docbook-xsl stylesheets are not found. Install the docbook-xsl package or disable the documentation using --disable-documentation]) - fi - - AC_MSG_RESULT([yes]) - AC_SUBST(DOCS_STYLESHEET) - - AC_CONFIG_FILES([ - doc/doxygen/wayland.doxygen - ]) - -fi -AM_CONDITIONAL([HAVE_XMLTO], [test "x$XMLTO" != "x"]) - -AC_CONFIG_FILES([Makefile - cursor/wayland-cursor.pc - cursor/wayland-cursor-uninstalled.pc - doc/Makefile - doc/publican/Makefile - doc/doxygen/Makefile - egl/wayland-egl.pc - egl/wayland-egl-backend.pc - src/wayland-server-uninstalled.pc - src/wayland-client-uninstalled.pc - src/wayland-scanner-uninstalled.pc - src/wayland-server.pc - src/wayland-client.pc - src/wayland-scanner.pc - src/wayland-version.h]) -AC_OUTPUT diff --git a/cursor/meson.build b/cursor/meson.build index ae85ed9..f7d82e4 100644 --- a/cursor/meson.build +++ b/cursor/meson.build @@ -3,6 +3,15 @@ if icondir == '' icondir = join_paths(get_option('prefix'), get_option('datadir'), 'icons') endif +if wayland_version[0] != '1' + # The versioning used for the shared libraries assumes that the major + # version of Wayland as a whole will increase to 2 if and only if there + # is an ABI break, at which point we should probably bump the SONAME of + # all libraries to .so.2. For more details see + # https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/177 + error('We probably need to bump the SONAME of libwayland-cursor') +endif + wayland_cursor = library( 'wayland-cursor', sources: [ @@ -10,7 +19,9 @@ wayland_cursor = library( 'os-compatibility.c', 'xcursor.c', ], - version: '0.0.0', + # To avoid an unnecessary SONAME bump, wayland 1.x.y produces + # libwayland-cursor.so.0.x.y. + version: '.'.join(['0', wayland_version[1], wayland_version[2]]), dependencies: [ wayland_client_dep ], c_args: [ '-DICONDIR="@0@"'.format(icondir) ], install: true, @@ -25,3 +36,12 @@ pkgconfig.generate( libraries: wayland_cursor, filebase: 'wayland-cursor', ) + +wayland_cursor_dep = declare_dependency( + link_with: wayland_cursor, + include_directories: [ root_inc, include_directories('.') ], +) + +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('wayland-cursor', wayland_cursor_dep) +endif diff --git a/cursor/os-compatibility.c b/cursor/os-compatibility.c index 8d51e52..f2445ce 100644 --- a/cursor/os-compatibility.c +++ b/cursor/os-compatibility.c @@ -31,7 +31,9 @@ #include <unistd.h> #include <fcntl.h> #include <errno.h> +#include <signal.h> #include <string.h> +#include <stdio.h> #include <stdlib.h> #ifdef HAVE_MEMFD_CREATE @@ -118,6 +120,7 @@ os_create_anonymous_file(off_t size) static const char template[] = "/wayland-cursor-shared-XXXXXX"; const char *path; char *name; + size_t name_size; int fd; #ifdef HAVE_MEMFD_CREATE @@ -134,17 +137,17 @@ os_create_anonymous_file(off_t size) #endif { path = getenv("XDG_RUNTIME_DIR"); - if (!path) { + if (!path || path[0] != '/') { errno = ENOENT; return -1; } - name = malloc(strlen(path) + sizeof(template)); + name_size = strlen(path) + sizeof(template); + name = malloc(name_size); if (!name) return -1; - strcpy(name, path); - strcat(name, template); + snprintf(name, name_size, "%s%s", path, template); fd = create_tmpfile_cloexec(name); @@ -166,11 +169,28 @@ int os_resize_anonymous_file(int fd, off_t size) { #ifdef HAVE_POSIX_FALLOCATE - /* - * Filesystems that do support fallocate will return EINVAL or + sigset_t mask; + sigset_t old_mask; + + /* + * posix_fallocate() might be interrupted, so we need to check + * for EINTR and retry in that case. + * However, in the presence of an alarm, the interrupt may trigger + * repeatedly and prevent a large posix_fallocate() to ever complete + * successfully, so we need to first block SIGALRM to prevent + * this. + */ + sigemptyset(&mask); + sigaddset(&mask, SIGALRM); + sigprocmask(SIG_BLOCK, &mask, &old_mask); + /* + * Filesystems that do not support fallocate will return EINVAL or * EOPNOTSUPP. In this case we need to fall back to ftruncate */ - errno = posix_fallocate(fd, 0, size); + do { + errno = posix_fallocate(fd, 0, size); + } while (errno == EINTR); + sigprocmask(SIG_SETMASK, &old_mask, NULL); if (errno == 0) return 0; else if (errno != EINVAL && errno != EOPNOTSUPP) diff --git a/cursor/wayland-cursor-uninstalled.pc.in b/cursor/wayland-cursor-uninstalled.pc.in deleted file mode 100644 index f52b113..0000000 --- a/cursor/wayland-cursor-uninstalled.pc.in +++ /dev/null @@ -1,8 +0,0 @@ -libdir=@abs_builddir@/.libs -includedir=@abs_srcdir@ - -Name: Wayland Cursor -Description: Wayland cursor helper library (not installed) -Version: @WAYLAND_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -lwayland-cursor diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index 4e2dc50..156f0a8 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -92,7 +92,7 @@ shm_pool_resize(struct shm_pool *pool, int size) pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, pool->fd, 0); - if (pool->data == (void *)-1) + if (pool->data == MAP_FAILED) return 0; pool->size = size; @@ -129,7 +129,6 @@ struct wl_cursor_theme { struct wl_cursor **cursors; struct wl_shm *shm; struct shm_pool *pool; - char *name; int size; }; @@ -152,32 +151,32 @@ struct cursor { * the returned buffer. */ WL_EXPORT struct wl_buffer * -wl_cursor_image_get_buffer(struct wl_cursor_image *_img) +wl_cursor_image_get_buffer(struct wl_cursor_image *image) { - struct cursor_image *image = (struct cursor_image *) _img; - struct wl_cursor_theme *theme = image->theme; + struct cursor_image *img = (struct cursor_image *) image; + struct wl_cursor_theme *theme = img->theme; - if (!image->buffer) { - image->buffer = + if (!img->buffer) { + img->buffer = wl_shm_pool_create_buffer(theme->pool->pool, - image->offset, - _img->width, _img->height, - _img->width * 4, + img->offset, + image->width, image->height, + image->width * 4, WL_SHM_FORMAT_ARGB8888); }; - return image->buffer; + return img->buffer; } static void -wl_cursor_image_destroy(struct wl_cursor_image *_img) +wl_cursor_image_destroy(struct wl_cursor_image *image) { - struct cursor_image *image = (struct cursor_image *) _img; + struct cursor_image *img = (struct cursor_image *) image; - if (image->buffer) - wl_buffer_destroy(image->buffer); + if (img->buffer) + wl_buffer_destroy(img->buffer); - free(image); + free(img); } static void @@ -252,13 +251,10 @@ err_free_cursor: } static void -load_default_theme(struct wl_cursor_theme *theme) +load_fallback_theme(struct wl_cursor_theme *theme) { uint32_t i; - free(theme->name); - theme->name = strdup("default"); - theme->cursor_count = ARRAY_LENGTH(cursor_metadata); theme->cursors = malloc(theme->cursor_count * sizeof(*theme->cursors)); @@ -278,7 +274,7 @@ load_default_theme(struct wl_cursor_theme *theme) } static struct wl_cursor * -wl_cursor_create_from_xcursor_images(XcursorImages *images, +wl_cursor_create_from_xcursor_images(struct xcursor_images *images, struct wl_cursor_theme *theme) { struct cursor *cursor; @@ -339,13 +335,13 @@ wl_cursor_create_from_xcursor_images(XcursorImages *images, } static void -load_callback(XcursorImages *images, void *data) +load_callback(struct xcursor_images *images, void *data) { struct wl_cursor_theme *theme = data; struct wl_cursor *cursor; if (wl_cursor_theme_get_cursor(theme, images->name)) { - XcursorImagesDestroy(images); + xcursor_images_destroy(images); return; } @@ -365,7 +361,7 @@ load_callback(XcursorImages *images, void *data) } } - XcursorImagesDestroy(images); + xcursor_images_destroy(images); } /** Load a cursor theme to memory shared with the compositor @@ -391,9 +387,6 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) if (!name) name = "default"; - theme->name = strdup(name); - if (!theme->name) - goto out_error_name; theme->size = size; theme->cursor_count = 0; theme->cursors = NULL; @@ -405,13 +398,14 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) xcursor_load_theme(name, size, load_callback, theme); if (theme->cursor_count == 0) - load_default_theme(theme); + xcursor_load_theme(NULL, size, load_callback, theme); + + if (theme->cursor_count == 0) + load_fallback_theme(theme); return theme; out_error_pool: - free(theme->name); -out_error_name: free(theme); return NULL; } @@ -430,7 +424,6 @@ wl_cursor_theme_destroy(struct wl_cursor_theme *theme) shm_pool_destroy(theme->pool); - free(theme->name); free(theme->cursors); free(theme); } @@ -468,21 +461,21 @@ wl_cursor_theme_get_cursor(struct wl_cursor_theme *theme, * given time in the cursor animation. */ WL_EXPORT int -wl_cursor_frame_and_duration(struct wl_cursor *_cursor, uint32_t time, +wl_cursor_frame_and_duration(struct wl_cursor *cursor, uint32_t time, uint32_t *duration) { - struct cursor *cursor = (struct cursor *) _cursor; + struct cursor *cur = (struct cursor *) cursor; uint32_t t; int i; - if (cursor->cursor.image_count == 1) { + if (cur->cursor.image_count == 1 || cur->total_delay == 0) { if (duration) *duration = 0; return 0; } i = 0; - t = time % cursor->total_delay; + t = time % cur->total_delay; /* If there is a 0 delay in the image set then this * loop breaks on it and we display that cursor until @@ -491,8 +484,8 @@ wl_cursor_frame_and_duration(struct wl_cursor *_cursor, uint32_t time, * seen one in a cursor file, we haven't bothered to * "fix" this. */ - while (t - cursor->cursor.images[i]->delay < t) - t -= cursor->cursor.images[i++]->delay; + while (t - cur->cursor.images[i]->delay < t) + t -= cur->cursor.images[i++]->delay; if (!duration) return i; @@ -500,10 +493,10 @@ wl_cursor_frame_and_duration(struct wl_cursor *_cursor, uint32_t time, /* Make sure we don't accidentally tell the caller this is * a static cursor image. */ - if (t >= cursor->cursor.images[i]->delay) + if (t >= cur->cursor.images[i]->delay) *duration = 1; else - *duration = cursor->cursor.images[i]->delay - t; + *duration = cur->cursor.images[i]->delay - t; return i; } @@ -517,7 +510,7 @@ wl_cursor_frame_and_duration(struct wl_cursor *_cursor, uint32_t time, * given time in the cursor animation. */ WL_EXPORT int -wl_cursor_frame(struct wl_cursor *_cursor, uint32_t time) +wl_cursor_frame(struct wl_cursor *cursor, uint32_t time) { - return wl_cursor_frame_and_duration(_cursor, time, NULL); + return wl_cursor_frame_and_duration(cursor, time, NULL); } diff --git a/cursor/wayland-cursor.pc.in b/cursor/wayland-cursor.pc.in deleted file mode 100644 index fbbf5ff..0000000 --- a/cursor/wayland-cursor.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Wayland Cursor -Description: Wayland cursor helper library -Version: @WAYLAND_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -lwayland-cursor diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 1f1360f..43a5292 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -23,20 +23,15 @@ * SOFTWARE. */ +#define _GNU_SOURCE #include "xcursor.h" +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> /* - * From libXcursor/include/X11/extensions/Xcursor.h - */ - -#define XcursorTrue 1 -#define XcursorFalse 0 - -/* * Cursor files start with a header. The header * contains a magic number, a version number and a * table of contents which has type and offset information @@ -67,43 +62,31 @@ * CARD32 position absolute file position */ -#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ - -/* - * Current Xcursor version number. Will be substituted by configure - * from the version in the libXcursor configure.ac file. - */ - -#define XCURSOR_LIB_MAJOR 1 -#define XCURSOR_LIB_MINOR 1 -#define XCURSOR_LIB_REVISION 13 -#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \ - (XCURSOR_LIB_MINOR * 100) + \ - (XCURSOR_LIB_REVISION)) +#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ /* * This version number is stored in cursor files; changes to the * file format require updating this version number */ -#define XCURSOR_FILE_MAJOR 1 -#define XCURSOR_FILE_MINOR 0 -#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) -#define XCURSOR_FILE_HEADER_LEN (4 * 4) -#define XCURSOR_FILE_TOC_LEN (3 * 4) - -typedef struct _XcursorFileToc { - XcursorUInt type; /* chunk type */ - XcursorUInt subtype; /* subtype (size for images) */ - XcursorUInt position; /* absolute position in file */ -} XcursorFileToc; - -typedef struct _XcursorFileHeader { - XcursorUInt magic; /* magic number */ - XcursorUInt header; /* byte length of header */ - XcursorUInt version; /* file version number */ - XcursorUInt ntoc; /* number of toc entries */ - XcursorFileToc *tocs; /* table of contents */ -} XcursorFileHeader; +#define XCURSOR_FILE_MAJOR 1 +#define XCURSOR_FILE_MINOR 0 +#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) +#define XCURSOR_FILE_HEADER_LEN (4 * 4) +#define XCURSOR_FILE_TOC_LEN (3 * 4) + +struct xcursor_file_toc { + uint32_t type; /* chunk type */ + uint32_t subtype; /* subtype (size for images) */ + uint32_t position; /* absolute position in file */ +}; + +struct xcursor_file_header { + uint32_t magic; /* magic number */ + uint32_t header; /* byte length of header */ + uint32_t version; /* file version number */ + uint32_t ntoc; /* number of toc entries */ + struct xcursor_file_toc *tocs; /* table of contents */ +}; /* * The rest of the file is a list of chunks, each tagged by type @@ -121,42 +104,14 @@ typedef struct _XcursorFileHeader { * CARD32 version chunk type version */ -#define XCURSOR_CHUNK_HEADER_LEN (4 * 4) +#define XCURSOR_CHUNK_HEADER_LEN (4 * 4) -typedef struct _XcursorChunkHeader { - XcursorUInt header; /* bytes in chunk header */ - XcursorUInt type; /* chunk type */ - XcursorUInt subtype; /* chunk subtype (size for images) */ - XcursorUInt version; /* version of this type */ -} XcursorChunkHeader; - -/* - * Here's a list of the known chunk types - */ - -/* - * Comments consist of a 4-byte length field followed by - * UTF-8 encoded text - * - * Comment: - * ChunkHeader header chunk header - * CARD32 length bytes in text - * LISTofCARD8 text UTF-8 encoded text - */ - -#define XCURSOR_COMMENT_TYPE 0xfffe0001 -#define XCURSOR_COMMENT_VERSION 1 -#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4)) -#define XCURSOR_COMMENT_COPYRIGHT 1 -#define XCURSOR_COMMENT_LICENSE 2 -#define XCURSOR_COMMENT_OTHER 3 -#define XCURSOR_COMMENT_MAX_LEN 0x100000 - -typedef struct _XcursorComment { - XcursorUInt version; - XcursorUInt comment_type; - char *comment; -} XcursorComment; +struct xcursor_chunk_header { + uint32_t header; /* bytes in chunk header */ + uint32_t type; /* chunk type */ + uint32_t subtype; /* chunk subtype (size for images) */ + uint32_t version; /* version of this type */ +}; /* * Each cursor image occupies a separate image chunk. @@ -174,439 +129,354 @@ typedef struct _XcursorComment { * LISTofCARD32 pixels ARGB pixels */ -#define XCURSOR_IMAGE_TYPE 0xfffd0002 -#define XCURSOR_IMAGE_VERSION 1 -#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) -#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ - -typedef struct _XcursorFile XcursorFile; - -struct _XcursorFile { - void *closure; - int (*read) (XcursorFile *file, unsigned char *buf, int len); - int (*write) (XcursorFile *file, unsigned char *buf, int len); - int (*seek) (XcursorFile *file, long offset, int whence); -}; - -typedef struct _XcursorComments { - int ncomment; /* number of comments */ - XcursorComment **comments; /* array of XcursorComment pointers */ -} XcursorComments; +#define XCURSOR_IMAGE_TYPE 0xfffd0002 +#define XCURSOR_IMAGE_VERSION 1 +#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) +#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ /* * From libXcursor/src/file.c */ -static XcursorImage * -XcursorImageCreate (int width, int height) +static struct xcursor_image * +xcursor_image_create(int width, int height) { - XcursorImage *image; - - if (width < 0 || height < 0) - return NULL; - if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) - return NULL; - - image = malloc (sizeof (XcursorImage) + - width * height * sizeof (XcursorPixel)); - if (!image) - return NULL; - image->version = XCURSOR_IMAGE_VERSION; - image->pixels = (XcursorPixel *) (image + 1); - image->size = width > height ? width : height; - image->width = width; - image->height = height; - image->delay = 0; - return image; + struct xcursor_image *image; + + if (width < 0 || height < 0) + return NULL; + if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) + return NULL; + + image = malloc(sizeof(struct xcursor_image) + + width * height * sizeof(uint32_t)); + if (!image) + return NULL; + image->version = XCURSOR_IMAGE_VERSION; + image->pixels = (uint32_t *) (image + 1); + image->size = width > height ? width : height; + image->width = width; + image->height = height; + image->delay = 0; + return image; } static void -XcursorImageDestroy (XcursorImage *image) +xcursor_image_destroy(struct xcursor_image *image) { - free (image); + free(image); } -static XcursorImages * -XcursorImagesCreate (int size) +static struct xcursor_images * +xcursor_images_create(int size) { - XcursorImages *images; - - images = malloc (sizeof (XcursorImages) + - size * sizeof (XcursorImage *)); - if (!images) - return NULL; - images->nimage = 0; - images->images = (XcursorImage **) (images + 1); - images->name = NULL; - return images; + struct xcursor_images *images; + + images = malloc(sizeof(struct xcursor_images) + + size * sizeof(struct xcursor_image *)); + if (!images) + return NULL; + images->nimage = 0; + images->images = (struct xcursor_image **) (images + 1); + images->name = NULL; + return images; } void -XcursorImagesDestroy (XcursorImages *images) -{ - int n; - - if (!images) - return; - - for (n = 0; n < images->nimage; n++) - XcursorImageDestroy (images->images[n]); - if (images->name) - free (images->name); - free (images); -} - -static void -XcursorImagesSetName (XcursorImages *images, const char *name) +xcursor_images_destroy(struct xcursor_images *images) { - char *new; + int n; - if (!images || !name) - return; - - new = malloc (strlen (name) + 1); - - if (!new) - return; + if (!images) + return; - strcpy (new, name); - if (images->name) - free (images->name); - images->name = new; + for (n = 0; n < images->nimage; n++) + xcursor_image_destroy(images->images[n]); + free(images->name); + free(images); } -static XcursorBool -_XcursorReadUInt (XcursorFile *file, XcursorUInt *u) +static bool +xcursor_read_uint(FILE *file, uint32_t *u) { - unsigned char bytes[4]; + unsigned char bytes[4]; - if (!file || !u) - return XcursorFalse; + if (!file || !u) + return false; - if ((*file->read) (file, bytes, 4) != 4) - return XcursorFalse; + if (fread(bytes, 1, 4, file) != 4) + return false; - *u = ((XcursorUInt)(bytes[0]) << 0) | - ((XcursorUInt)(bytes[1]) << 8) | - ((XcursorUInt)(bytes[2]) << 16) | - ((XcursorUInt)(bytes[3]) << 24); - return XcursorTrue; + *u = ((uint32_t)(bytes[0]) << 0) | + ((uint32_t)(bytes[1]) << 8) | + ((uint32_t)(bytes[2]) << 16) | + ((uint32_t)(bytes[3]) << 24); + return true; } static void -_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) +xcursor_file_header_destroy(struct xcursor_file_header *file_header) { - free (fileHeader); + free(file_header); } -static XcursorFileHeader * -_XcursorFileHeaderCreate (int ntoc) +static struct xcursor_file_header * +xcursor_file_header_create(uint32_t ntoc) { - XcursorFileHeader *fileHeader; - - if (ntoc > 0x10000) - return NULL; - fileHeader = malloc (sizeof (XcursorFileHeader) + - ntoc * sizeof (XcursorFileToc)); - if (!fileHeader) - return NULL; - fileHeader->magic = XCURSOR_MAGIC; - fileHeader->header = XCURSOR_FILE_HEADER_LEN; - fileHeader->version = XCURSOR_FILE_VERSION; - fileHeader->ntoc = ntoc; - fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); - return fileHeader; + struct xcursor_file_header *file_header; + + if (ntoc > 0x10000) + return NULL; + file_header = malloc(sizeof(struct xcursor_file_header) + + ntoc * sizeof(struct xcursor_file_toc)); + if (!file_header) + return NULL; + file_header->magic = XCURSOR_MAGIC; + file_header->header = XCURSOR_FILE_HEADER_LEN; + file_header->version = XCURSOR_FILE_VERSION; + file_header->ntoc = ntoc; + file_header->tocs = (struct xcursor_file_toc *) (file_header + 1); + return file_header; } -static XcursorFileHeader * -_XcursorReadFileHeader (XcursorFile *file) +static struct xcursor_file_header * +xcursor_read_file_header(FILE *file) { - XcursorFileHeader head, *fileHeader; - XcursorUInt skip; - unsigned int n; - - if (!file) - return NULL; - - if (!_XcursorReadUInt (file, &head.magic)) - return NULL; - if (head.magic != XCURSOR_MAGIC) - return NULL; - if (!_XcursorReadUInt (file, &head.header)) - return NULL; - if (!_XcursorReadUInt (file, &head.version)) - return NULL; - if (!_XcursorReadUInt (file, &head.ntoc)) - return NULL; - skip = head.header - XCURSOR_FILE_HEADER_LEN; - if (skip) - if ((*file->seek) (file, skip, SEEK_CUR) == EOF) - return NULL; - fileHeader = _XcursorFileHeaderCreate (head.ntoc); - if (!fileHeader) - return NULL; - fileHeader->magic = head.magic; - fileHeader->header = head.header; - fileHeader->version = head.version; - fileHeader->ntoc = head.ntoc; - for (n = 0; n < fileHeader->ntoc; n++) - { - if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) - break; - if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) - break; - if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) - break; - } - if (n != fileHeader->ntoc) - { - _XcursorFileHeaderDestroy (fileHeader); - return NULL; - } - return fileHeader; + struct xcursor_file_header head, *file_header; + uint32_t skip; + unsigned int n; + + if (!file) + return NULL; + + if (!xcursor_read_uint(file, &head.magic)) + return NULL; + if (head.magic != XCURSOR_MAGIC) + return NULL; + if (!xcursor_read_uint(file, &head.header)) + return NULL; + if (!xcursor_read_uint(file, &head.version)) + return NULL; + if (!xcursor_read_uint(file, &head.ntoc)) + return NULL; + skip = head.header - XCURSOR_FILE_HEADER_LEN; + if (skip) + if (fseek(file, skip, SEEK_CUR) == EOF) + return NULL; + file_header = xcursor_file_header_create(head.ntoc); + if (!file_header) + return NULL; + file_header->magic = head.magic; + file_header->header = head.header; + file_header->version = head.version; + file_header->ntoc = head.ntoc; + for (n = 0; n < file_header->ntoc; n++) { + if (!xcursor_read_uint(file, &file_header->tocs[n].type)) + break; + if (!xcursor_read_uint(file, &file_header->tocs[n].subtype)) + break; + if (!xcursor_read_uint(file, &file_header->tocs[n].position)) + break; + } + if (n != file_header->ntoc) { + xcursor_file_header_destroy(file_header); + return NULL; + } + return file_header; } -static XcursorBool -_XcursorSeekToToc (XcursorFile *file, - XcursorFileHeader *fileHeader, - int toc) +static bool +xcursor_seek_to_toc(FILE *file, + struct xcursor_file_header *file_header, + int toc) { - if (!file || !fileHeader || \ - (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) - return XcursorFalse; - return XcursorTrue; + if (!file || !file_header || + fseek(file, file_header->tocs[toc].position, SEEK_SET) == EOF) + return false; + return true; } -static XcursorBool -_XcursorFileReadChunkHeader (XcursorFile *file, - XcursorFileHeader *fileHeader, - int toc, - XcursorChunkHeader *chunkHeader) +static bool +xcursor_file_read_chunk_header(FILE *file, + struct xcursor_file_header *file_header, + int toc, + struct xcursor_chunk_header *chunk_header) { - if (!file || !fileHeader || !chunkHeader) - return XcursorFalse; - if (!_XcursorSeekToToc (file, fileHeader, toc)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->header)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->type)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->subtype)) - return XcursorFalse; - if (!_XcursorReadUInt (file, &chunkHeader->version)) - return XcursorFalse; - /* sanity check */ - if (chunkHeader->type != fileHeader->tocs[toc].type || - chunkHeader->subtype != fileHeader->tocs[toc].subtype) - return XcursorFalse; - return XcursorTrue; + if (!file || !file_header || !chunk_header) + return false; + if (!xcursor_seek_to_toc(file, file_header, toc)) + return false; + if (!xcursor_read_uint(file, &chunk_header->header)) + return false; + if (!xcursor_read_uint(file, &chunk_header->type)) + return false; + if (!xcursor_read_uint(file, &chunk_header->subtype)) + return false; + if (!xcursor_read_uint(file, &chunk_header->version)) + return false; + /* sanity check */ + if (chunk_header->type != file_header->tocs[toc].type || + chunk_header->subtype != file_header->tocs[toc].subtype) + return false; + return true; } -#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) - -static XcursorDim -_XcursorFindBestSize (XcursorFileHeader *fileHeader, - XcursorDim size, - int *nsizesp) +static uint32_t +dist(uint32_t a, uint32_t b) { - unsigned int n; - int nsizes = 0; - XcursorDim bestSize = 0; - XcursorDim thisSize; - - if (!fileHeader || !nsizesp) - return 0; - - for (n = 0; n < fileHeader->ntoc; n++) - { - if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) - continue; - thisSize = fileHeader->tocs[n].subtype; - if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) - { - bestSize = thisSize; - nsizes = 1; - } - else if (thisSize == bestSize) - nsizes++; - } - *nsizesp = nsizes; - return bestSize; + return a > b ? a - b : b - a; } -static int -_XcursorFindImageToc (XcursorFileHeader *fileHeader, - XcursorDim size, - int count) +static uint32_t +xcursor_file_best_size(struct xcursor_file_header *file_header, + uint32_t size, int *nsizesp) { - unsigned int toc; - XcursorDim thisSize; - - if (!fileHeader) - return 0; - - for (toc = 0; toc < fileHeader->ntoc; toc++) - { - if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) - continue; - thisSize = fileHeader->tocs[toc].subtype; - if (thisSize != size) - continue; - if (!count) - break; - count--; - } - if (toc == fileHeader->ntoc) - return -1; - return toc; -} + unsigned int n; + int nsizes = 0; + uint32_t best_size = 0; + uint32_t this_size; -static XcursorImage * -_XcursorReadImage (XcursorFile *file, - XcursorFileHeader *fileHeader, - int toc) -{ - XcursorChunkHeader chunkHeader; - XcursorImage head; - XcursorImage *image; - int n; - XcursorPixel *p; - - if (!file || !fileHeader) - return NULL; - - if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) - return NULL; - if (!_XcursorReadUInt (file, &head.width)) - return NULL; - if (!_XcursorReadUInt (file, &head.height)) - return NULL; - if (!_XcursorReadUInt (file, &head.xhot)) - return NULL; - if (!_XcursorReadUInt (file, &head.yhot)) - return NULL; - if (!_XcursorReadUInt (file, &head.delay)) - return NULL; - /* sanity check data */ - if (head.width > XCURSOR_IMAGE_MAX_SIZE || - head.height > XCURSOR_IMAGE_MAX_SIZE) - return NULL; - if (head.width == 0 || head.height == 0) - return NULL; - if (head.xhot > head.width || head.yhot > head.height) - return NULL; - - /* Create the image and initialize it */ - image = XcursorImageCreate (head.width, head.height); - if (image == NULL) - return NULL; - if (chunkHeader.version < image->version) - image->version = chunkHeader.version; - image->size = chunkHeader.subtype; - image->xhot = head.xhot; - image->yhot = head.yhot; - image->delay = head.delay; - n = image->width * image->height; - p = image->pixels; - while (n--) - { - if (!_XcursorReadUInt (file, p)) - { - XcursorImageDestroy (image); - return NULL; - } - p++; - } - return image; -} + if (!file_header || !nsizesp) + return 0; -static XcursorImages * -XcursorXcFileLoadImages (XcursorFile *file, int size) -{ - XcursorFileHeader *fileHeader; - XcursorDim bestSize; - int nsize; - XcursorImages *images; - int n; - int toc; - - if (!file || size < 0) - return NULL; - fileHeader = _XcursorReadFileHeader (file); - if (!fileHeader) - return NULL; - bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); - if (!bestSize) - { - _XcursorFileHeaderDestroy (fileHeader); - return NULL; - } - images = XcursorImagesCreate (nsize); - if (!images) - { - _XcursorFileHeaderDestroy (fileHeader); - return NULL; - } - for (n = 0; n < nsize; n++) - { - toc = _XcursorFindImageToc (fileHeader, bestSize, n); - if (toc < 0) - break; - images->images[images->nimage] = _XcursorReadImage (file, fileHeader, - toc); - if (!images->images[images->nimage]) - break; - images->nimage++; - } - _XcursorFileHeaderDestroy (fileHeader); - if (images->nimage != nsize) - { - XcursorImagesDestroy (images); - images = NULL; - } - return images; + for (n = 0; n < file_header->ntoc; n++) { + if (file_header->tocs[n].type != XCURSOR_IMAGE_TYPE) + continue; + this_size = file_header->tocs[n].subtype; + if (!best_size || dist(this_size, size) < dist(best_size, size)) { + best_size = this_size; + nsizes = 1; + } else if (this_size == best_size) { + nsizes++; + } + } + *nsizesp = nsizes; + return best_size; } static int -_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) +xcursor_find_image_toc(struct xcursor_file_header *file_header, + uint32_t size, int count) { - FILE *f = file->closure; - return fread (buf, 1, len, f); -} + unsigned int toc; + uint32_t this_size; -static int -_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) -{ - FILE *f = file->closure; - return fwrite (buf, 1, len, f); -} + if (!file_header) + return 0; -static int -_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) -{ - FILE *f = file->closure; - return fseek (f, offset, whence); + for (toc = 0; toc < file_header->ntoc; toc++) { + if (file_header->tocs[toc].type != XCURSOR_IMAGE_TYPE) + continue; + this_size = file_header->tocs[toc].subtype; + if (this_size != size) + continue; + if (!count) + break; + count--; + } + if (toc == file_header->ntoc) + return -1; + return toc; } -static void -_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file) +static struct xcursor_image * +xcursor_read_image(FILE *file, + struct xcursor_file_header *file_header, + int toc) { - file->closure = stdfile; - file->read = _XcursorStdioFileRead; - file->write = _XcursorStdioFileWrite; - file->seek = _XcursorStdioFileSeek; + struct xcursor_chunk_header chunk_header; + struct xcursor_image head; + struct xcursor_image *image; + int n; + uint32_t *p; + + if (!file || !file_header) + return NULL; + + if (!xcursor_file_read_chunk_header(file, file_header, toc, &chunk_header)) + return NULL; + if (!xcursor_read_uint(file, &head.width)) + return NULL; + if (!xcursor_read_uint(file, &head.height)) + return NULL; + if (!xcursor_read_uint(file, &head.xhot)) + return NULL; + if (!xcursor_read_uint(file, &head.yhot)) + return NULL; + if (!xcursor_read_uint(file, &head.delay)) + return NULL; + /* sanity check data */ + if (head.width > XCURSOR_IMAGE_MAX_SIZE || + head.height > XCURSOR_IMAGE_MAX_SIZE) + return NULL; + if (head.width == 0 || head.height == 0) + return NULL; + if (head.xhot > head.width || head.yhot > head.height) + return NULL; + + /* Create the image and initialize it */ + image = xcursor_image_create(head.width, head.height); + if (image == NULL) + return NULL; + if (chunk_header.version < image->version) + image->version = chunk_header.version; + image->size = chunk_header.subtype; + image->xhot = head.xhot; + image->yhot = head.yhot; + image->delay = head.delay; + n = image->width * image->height; + p = image->pixels; + while (n--) { + if (!xcursor_read_uint(file, p)) { + xcursor_image_destroy(image); + return NULL; + } + p++; + } + return image; } -static XcursorImages * -XcursorFileLoadImages (FILE *file, int size) +static struct xcursor_images * +xcursor_xc_file_load_images(FILE *file, int size) { - XcursorFile f; - - if (!file) - return NULL; - - _XcursorStdioFileInitialize (file, &f); - return XcursorXcFileLoadImages (&f, size); + struct xcursor_file_header *file_header; + uint32_t best_size; + int nsize; + struct xcursor_images *images; + int n; + int toc; + + if (!file || size < 0) + return NULL; + file_header = xcursor_read_file_header(file); + if (!file_header) + return NULL; + best_size = xcursor_file_best_size(file_header, (uint32_t) size, &nsize); + if (!best_size) { + xcursor_file_header_destroy(file_header); + return NULL; + } + images = xcursor_images_create(nsize); + if (!images) { + xcursor_file_header_destroy(file_header); + return NULL; + } + for (n = 0; n < nsize; n++) { + toc = xcursor_find_image_toc(file_header, best_size, n); + if (toc < 0) + break; + images->images[images->nimage] = xcursor_read_image(file, file_header, + toc); + if (!images->images[images->nimage]) + break; + images->nimage++; + } + xcursor_file_header_destroy(file_header); + if (images->nimage != nsize) { + xcursor_images_destroy(images); + images = NULL; + } + return images; } /* @@ -621,275 +491,213 @@ XcursorFileLoadImages (FILE *file, int size) #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR #endif -static const char * -XcursorLibraryPath (void) +#define XDG_DATA_HOME_FALLBACK "~/.local/share" +#define CURSORDIR "/icons" + +/** Get search path for cursor themes + * + * This function builds the list of directories to look for cursor + * themes in. The format is PATH-like: directories are separated by + * colons. + * + * The memory block returned by this function is allocated on the heap + * and must be freed by the caller. + */ +static char * +xcursor_library_path(void) { - static const char *path; + const char *env_var, *suffix; + char *path; + size_t path_size; - if (!path) - { - path = getenv ("XCURSOR_PATH"); - if (!path) - path = XCURSORPATH; - } - return path; -} + env_var = getenv("XCURSOR_PATH"); + if (env_var) + return strdup(env_var); -static void -_XcursorAddPathElt (char *path, const char *elt, int len) -{ - int pathlen = strlen (path); - - /* append / if the path doesn't currently have one */ - if (path[0] == '\0' || path[pathlen - 1] != '/') - { - strcat (path, "/"); - pathlen++; - } - if (len == -1) - len = strlen (elt); - /* strip leading slashes */ - while (len && elt[0] == '/') - { - elt++; - len--; - } - strncpy (path + pathlen, elt, len); - path[pathlen + len] = '\0'; + env_var = getenv("XDG_DATA_HOME"); + if (!env_var || env_var[0] != '/') + env_var = XDG_DATA_HOME_FALLBACK; + + suffix = CURSORDIR ":" XCURSORPATH; + path_size = strlen(env_var) + strlen(suffix) + 1; + path = malloc(path_size); + if (!path) + return NULL; + snprintf(path, path_size, "%s%s", env_var, suffix); + return path; } static char * -_XcursorBuildThemeDir (const char *dir, const char *theme) +xcursor_build_theme_dir(const char *dir, const char *theme) { - const char *colon; - const char *tcolon; - char *full; - char *home; - int dirlen; - int homelen; - int themelen; - int len; - - if (!dir || !theme) - return NULL; - - colon = strchr (dir, ':'); - if (!colon) - colon = dir + strlen (dir); - - dirlen = colon - dir; - - tcolon = strchr (theme, ':'); - if (!tcolon) - tcolon = theme + strlen (theme); - - themelen = tcolon - theme; - - home = NULL; - homelen = 0; - if (*dir == '~') - { - home = getenv ("HOME"); - if (!home) - return NULL; - homelen = strlen (home); - dir++; - dirlen--; - } - - /* - * add space for any needed directory separators, one per component, - * and one for the trailing null - */ - len = 1 + homelen + 1 + dirlen + 1 + themelen + 1; - - full = malloc (len); - if (!full) - return NULL; - full[0] = '\0'; - - if (home) - _XcursorAddPathElt (full, home, -1); - _XcursorAddPathElt (full, dir, dirlen); - _XcursorAddPathElt (full, theme, themelen); - return full; + const char *colon; + const char *tcolon; + char *full; + const char *home, *homesep; + int dirlen; + int homelen; + int themelen; + size_t full_size; + + if (!dir || !theme) + return NULL; + + colon = strchr(dir, ':'); + if (!colon) + colon = dir + strlen(dir); + + dirlen = colon - dir; + + tcolon = strchr(theme, ':'); + if (!tcolon) + tcolon = theme + strlen(theme); + + themelen = tcolon - theme; + + home = ""; + homelen = 0; + homesep = ""; + if (*dir == '~') { + home = getenv("HOME"); + if (!home) + return NULL; + homelen = strlen(home); + homesep = "/"; + dir++; + dirlen--; + } + + /* + * add space for any needed directory separators, one per component, + * and one for the trailing null + */ + full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1; + full = malloc(full_size); + if (!full) + return NULL; + snprintf(full, full_size, "%s%s%.*s/%.*s", home, homesep, + dirlen, dir, themelen, theme); + return full; } static char * -_XcursorBuildFullname (const char *dir, const char *subdir, const char *file) +xcursor_build_fullname(const char *dir, const char *subdir, const char *file) { - char *full; - - if (!dir || !subdir || !file) - return NULL; - - full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1); - if (!full) - return NULL; - full[0] = '\0'; - _XcursorAddPathElt (full, dir, -1); - _XcursorAddPathElt (full, subdir, -1); - _XcursorAddPathElt (full, file, -1); - return full; + char *full; + size_t full_size; + + if (!dir || !subdir || !file) + return NULL; + + full_size = strlen(dir) + 1 + strlen(subdir) + 1 + strlen(file) + 1; + full = malloc(full_size); + if (!full) + return NULL; + snprintf(full, full_size, "%s/%s/%s", dir, subdir, file); + return full; } static const char * -_XcursorNextPath (const char *path) +xcursor_next_path(const char *path) { - char *colon = strchr (path, ':'); + char *colon = strchr(path, ':'); - if (!colon) - return NULL; - return colon + 1; + if (!colon) + return NULL; + return colon + 1; } -#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') -#define XcursorSep(c) ((c) == ';' || (c) == ',') +static bool +xcursor_white(char c) +{ + return c == ' ' || c == '\t' || c == '\n'; +} + +static bool +xcursor_sep(char c) +{ + return c == ';' || c == ','; +} static char * -_XcursorThemeInherits (const char *full) +xcursor_theme_inherits(const char *full) { - char line[8192]; - char *result = NULL; - FILE *f; - - if (!full) - return NULL; - - f = fopen (full, "r"); - if (f) - { - while (fgets (line, sizeof (line), f)) - { - if (!strncmp (line, "Inherits", 8)) - { - char *l = line + 8; - char *r; - while (*l == ' ') l++; - if (*l != '=') continue; + char *line = NULL; + size_t line_size = 0; + char *result = NULL; + FILE *f; + + if (!full) + return NULL; + + f = fopen(full, "r"); + if (!f) + return NULL; + + while (getline(&line, &line_size, f) >= 0) { + const char *l; + char *r; + + if (strncmp(line, "Inherits", 8)) + continue; + + l = line + 8; + while (*l == ' ') + l++; + if (*l != '=') + continue; l++; - while (*l == ' ') l++; - result = malloc (strlen (l) + 1); - if (result) - { - r = result; - while (*l) - { - while (XcursorSep(*l) || XcursorWhite (*l)) l++; + while (*l == ' ') + l++; + result = malloc(strlen(l) + 1); + if (!result) + break; + + r = result; + while (*l) { + while (xcursor_sep(*l) || xcursor_white(*l)) + l++; if (!*l) - break; + break; if (r != result) - *r++ = ':'; - while (*l && !XcursorWhite(*l) && - !XcursorSep(*l)) - *r++ = *l++; - } - *r++ = '\0'; + *r++ = ':'; + while (*l && !xcursor_white(*l) && !xcursor_sep(*l)) + *r++ = *l++; } + *r++ = '\0'; + break; - } } - fclose (f); - } - return result; -} -static FILE * -XcursorScanTheme (const char *theme, const char *name) -{ - FILE *f = NULL; - char *full; - char *dir; - const char *path; - char *inherits = NULL; - const char *i; - - if (!theme || !name) - return NULL; - - /* - * Scan this theme - */ - for (path = XcursorLibraryPath (); - path && f == NULL; - path = _XcursorNextPath (path)) - { - dir = _XcursorBuildThemeDir (path, theme); - if (dir) - { - full = _XcursorBuildFullname (dir, "cursors", name); - if (full) - { - f = fopen (full, "r"); - free (full); - } - if (!f && !inherits) - { - full = _XcursorBuildFullname (dir, "", "index.theme"); - if (full) - { - inherits = _XcursorThemeInherits (full); - free (full); - } - } - free (dir); - } - } - /* - * Recurse to scan inherited themes - */ - for (i = inherits; i && f == NULL; i = _XcursorNextPath (i)) - f = XcursorScanTheme (i, name); - if (inherits != NULL) - free (inherits); - return f; -} + fclose(f); + free(line); -XcursorImages * -XcursorLibraryLoadImages (const char *file, const char *theme, int size) -{ - FILE *f = NULL; - XcursorImages *images = NULL; - - if (!file) - return NULL; - - if (theme) - f = XcursorScanTheme (theme, file); - if (!f) - f = XcursorScanTheme ("default", file); - if (f) - { - images = XcursorFileLoadImages (f, size); - if (images) - XcursorImagesSetName (images, file); - fclose (f); - } - return images; + return result; } static void load_all_cursors_from_dir(const char *path, int size, - void (*load_callback)(XcursorImages *, void *), + void (*load_callback)(struct xcursor_images *, void *), void *user_data) { FILE *f; DIR *dir = opendir(path); struct dirent *ent; char *full; - XcursorImages *images; + struct xcursor_images *images; if (!dir) return; - for(ent = readdir(dir); ent; ent = readdir(dir)) { + for (ent = readdir(dir); ent; ent = readdir(dir)) { #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && - (ent->d_type != DT_REG && ent->d_type != DT_LNK)) + ent->d_type != DT_REG && + ent->d_type != DT_LNK) continue; #endif - full = _XcursorBuildFullname(path, "", ent->d_name); + full = xcursor_build_fullname(path, "", ent->d_name); if (!full) continue; @@ -899,14 +707,14 @@ load_all_cursors_from_dir(const char *path, int size, continue; } - images = XcursorFileLoadImages(f, size); + images = xcursor_xc_file_load_images(f, size); if (images) { - XcursorImagesSetName(images, ent->d_name); + images->name = strdup(ent->d_name); load_callback(images, user_data); } - fclose (f); + fclose(f); free(full); } @@ -916,63 +724,60 @@ load_all_cursors_from_dir(const char *path, int size, /** Load all the cursor of a theme * * This function loads all the cursor images of a given theme and its - * inherited themes. Each cursor is loaded into an XcursorImages object + * inherited themes. Each cursor is loaded into an struct xcursor_images object * which is passed to the caller's load callback. If a cursor appears * more than once across all the inherited themes, the load callback - * will be called multiple times, with possibly different XcursorImages + * will be called multiple times, with possibly different struct xcursor_images * object which have the same name. The user is expected to destroy the - * XcursorImages objects passed to the callback with - * XcursorImagesDestroy(). + * struct xcursor_images objects passed to the callback with + * xcursor_images_destroy(). * * \param theme The name of theme that should be loaded * \param size The desired size of the cursor images * \param load_callback A callback function that will be called - * for each cursor loaded. The first parameter is the XcursorImages + * for each cursor loaded. The first parameter is the struct xcursor_images * object representing the loaded cursor and the second is a pointer * to data provided by the user. * \param user_data The data that should be passed to the load callback */ void xcursor_load_theme(const char *theme, int size, - void (*load_callback)(XcursorImages *, void *), - void *user_data) + void (*load_callback)(struct xcursor_images *, void *), + void *user_data) { char *full, *dir; char *inherits = NULL; const char *path, *i; + char *xcursor_path; if (!theme) theme = "default"; - for (path = XcursorLibraryPath(); + xcursor_path = xcursor_library_path(); + for (path = xcursor_path; path; - path = _XcursorNextPath(path)) { - dir = _XcursorBuildThemeDir(path, theme); + path = xcursor_next_path(path)) { + dir = xcursor_build_theme_dir(path, theme); if (!dir) continue; - full = _XcursorBuildFullname(dir, "cursors", ""); - - if (full) { - load_all_cursors_from_dir(full, size, load_callback, - user_data); - free(full); - } + full = xcursor_build_fullname(dir, "cursors", ""); + load_all_cursors_from_dir(full, size, load_callback, + user_data); + free(full); if (!inherits) { - full = _XcursorBuildFullname(dir, "", "index.theme"); - if (full) { - inherits = _XcursorThemeInherits(full); - free(full); - } + full = xcursor_build_fullname(dir, "", "index.theme"); + inherits = xcursor_theme_inherits(full); + free(full); } free(dir); } - for (i = inherits; i; i = _XcursorNextPath(i)) + for (i = inherits; i; i = xcursor_next_path(i)) xcursor_load_theme(i, size, load_callback, user_data); - if (inherits) - free(inherits); + free(inherits); + free(xcursor_path); } diff --git a/cursor/xcursor.h b/cursor/xcursor.h index c1ca12c..459f816 100644 --- a/cursor/xcursor.h +++ b/cursor/xcursor.h @@ -28,40 +28,31 @@ #include <stdint.h> -typedef int XcursorBool; -typedef uint32_t XcursorUInt; - -typedef XcursorUInt XcursorDim; -typedef XcursorUInt XcursorPixel; - -typedef struct _XcursorImage { - XcursorUInt version; /* version of the image data */ - XcursorDim size; /* nominal size for matching */ - XcursorDim width; /* actual width */ - XcursorDim height; /* actual height */ - XcursorDim xhot; /* hot spot x (must be inside image) */ - XcursorDim yhot; /* hot spot y (must be inside image) */ - XcursorUInt delay; /* animation delay to next frame (ms) */ - XcursorPixel *pixels; /* pointer to pixels */ -} XcursorImage; +struct xcursor_image { + uint32_t version; /* version of the image data */ + uint32_t size; /* nominal size for matching */ + uint32_t width; /* actual width */ + uint32_t height; /* actual height */ + uint32_t xhot; /* hot spot x (must be inside image) */ + uint32_t yhot; /* hot spot y (must be inside image) */ + uint32_t delay; /* animation delay to next frame (ms) */ + uint32_t *pixels; /* pointer to pixels */ +}; /* * Other data structures exposed by the library API */ -typedef struct _XcursorImages { - int nimage; /* number of images */ - XcursorImage **images; /* array of XcursorImage pointers */ - char *name; /* name used to load images */ -} XcursorImages; - -XcursorImages * -XcursorLibraryLoadImages (const char *file, const char *theme, int size); +struct xcursor_images { + int nimage; /* number of images */ + struct xcursor_image **images; /* array of XcursorImage pointers */ + char *name; /* name used to load images */ +}; void -XcursorImagesDestroy (XcursorImages *images); +xcursor_images_destroy(struct xcursor_images *images); void xcursor_load_theme(const char *theme, int size, - void (*load_callback)(XcursorImages *, void *), - void *user_data); + void (*load_callback)(struct xcursor_images *, void *), + void *user_data); #endif diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index 0b1c4f2..0000000 --- a/doc/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = doxygen publican diff --git a/doc/doxygen/Makefile.am b/doc/doxygen/Makefile.am deleted file mode 100644 index 86fd8bf..0000000 --- a/doc/doxygen/Makefile.am +++ /dev/null @@ -1,113 +0,0 @@ - -.SUFFIXES = .gv .png .map - -noinst_DATA = \ - xml/Client/index.xml \ - xml/Cursor/index.xml \ - xml/Server/index.xml \ - html/Client/index.html \ - html/Cursor/index.html \ - html/Server/index.html -dist_noinst_DATA = wayland.doxygen.in - -scanned_src_files_shared = \ - $(top_srcdir)/src/wayland-util.h - -scanned_src_files_Client = \ - $(scanned_src_files_shared) \ - $(top_srcdir)/src/wayland-client.c \ - $(top_srcdir)/src/wayland-client.h \ - $(top_srcdir)/src/wayland-client-core.h - -scanned_src_files_Cursor = \ - $(top_srcdir)/cursor/wayland-cursor.c \ - $(top_srcdir)/cursor/wayland-cursor.h - -scanned_src_files_Server = \ - $(scanned_src_files_shared) \ - $(top_srcdir)/src/event-loop.c \ - $(top_srcdir)/src/wayland-server.c \ - $(top_srcdir)/src/wayland-server.h \ - $(top_srcdir)/src/wayland-server-core.h \ - $(top_srcdir)/src/wayland-shm.c - -scanned_src_files_man = \ - $(scanned_src_files_Server) \ - $(top_srcdir)/src/wayland-client.c \ - $(top_srcdir)/src/wayland-client.h \ - $(top_srcdir)/src/wayland-client-core.h - -extra_doxygen = \ - mainpage.dox - -extra_doxygen_Server = \ - $(top_builddir)/protocol/wayland-server-protocol.h \ - $(extra_doxygen) - -extra_doxygen_Client = \ - $(top_builddir)/protocol/wayland-client-protocol.h \ - $(extra_doxygen) - -extra_doxygen_Cursor = \ - $(extra_doxygen) - -diagramsdir := $(srcdir)/dot -diagramssrc := $(wildcard $(diagramsdir)/*.gv) -diagrams := $(patsubst $(diagramsdir)/%,xml/%,$(diagramssrc:.gv=.png)) -diagram_maps := $(patsubst $(diagramsdir)/%,xml/%,$(diagramssrc:.gv=.map)) - -# find all man/man3/wl_foo.3 pages -# for this to work, we need to create them before the man target (hence -# all-local below) -dist_man3_MANS = $(shell test -d man && find man/man3 -name "wl_*.3" -printf "man/man3/%P\n") - -# Listing various directories that might need to be created. -alldirsrel := xml xml/Client xml/Server xml/Cursor man/man3 html/Client html/Server html/Cursor -alldirs := $(patsubst %,$(CURDIR)/%,$(alldirsrel)) - -$(diagrams): $(diagramssrc) - -$(diagram_maps): $(diagramssrc) - -xml/%/index.xml: $(top_srcdir)/src/scanner.c $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | $(CURDIR)/xml/% - $(AM_V_GEN)(cat wayland.doxygen; \ - echo "GENERATE_XML=YES"; \ - echo "XML_OUTPUT=xml/$*"; \ - echo "INPUT= $(scanned_src_files_$*)"; \ - ) | $(DOXYGEN) - - -html/%/index.html: $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | $(CURDIR)/html/% - $(AM_V_GEN)(cat wayland.doxygen; \ - echo "PROJECT_NAME=\"Wayland $* API\""; \ - echo "GENERATE_HTML=YES"; \ - echo "HTML_OUTPUT=html/$*"; \ - echo "INPUT= $(scanned_src_files_$*) $(extra_doxygen_$*)"; \ - ) | $(DOXYGEN) - - -man/man3/wl_display.3: $(top_srcdir)/src/scanner.c $(scanned_src_files_man) wayland.doxygen | $(CURDIR)/man/man3 - $(AM_V_GEN)(cat wayland.doxygen; \ - echo "GENERATE_MAN=YES"; \ - echo "MAN_OUTPUT=man"; \ - echo "JAVADOC_AUTOBRIEF=NO"; \ - echo "INPUT= $(scanned_src_files_man)"; \ - ) | $(DOXYGEN) - - -xml/%.png: $(diagramsdir)/%.gv | $(CURDIR)/xml - $(AM_V_GEN)$(DOT) -Tpng -o$@ $< - -xml/%.map: $(diagramsdir)/%.gv | $(CURDIR)/xml - $(AM_V_GEN)$(DOT) -Tcmapx_np -o$@ $< - -# general rule to create one of the listed directories. -$(alldirs): - $(AM_V_GEN)$(MKDIR_P) $@ - -# there is no man-local -all-local: man/man3/wl_display.3 - -clean-local: - rm -rf xml/ - rm -rf html/ - rm -rf man/ - -EXTRA_DIST = $(diagramssrc) diff --git a/doc/doxygen/meson.build b/doc/doxygen/meson.build index f2bee14..6112620 100644 --- a/doc/doxygen/meson.build +++ b/doc/doxygen/meson.build @@ -13,7 +13,7 @@ dot_map = [] doxygen_conf = configuration_data() doxygen_conf.set('VERSION', meson.project_version()) -doxygen_conf.set('top_builddir', meson.build_root()) +doxygen_conf.set('top_builddir', meson.project_build_root()) wayland_doxygen = configure_file( input: 'wayland.doxygen.in', output: 'wayland.doxygen', @@ -76,6 +76,7 @@ foreach f_name, sections: formats # We do not really need an output file, but Meson # will complain if one is not set, so we use a # dummy 'stamp' file + stamp = join_paths(meson.current_build_dir(), '@0@.stamp'.format(t_name)) custom_target( t_name, command: [ @@ -84,7 +85,7 @@ foreach f_name, sections: formats '--builddir=@OUTDIR@', '--section=@0@'.format(s_name), '--output-format=@0@'.format(f_name), - '--stamp=doc/doxygen/@0@.stamp'.format(t_name), + '--stamp=@0@'.format(stamp), wayland_doxygen, '@INPUT@', ], @@ -97,13 +98,14 @@ foreach f_name, sections: formats endforeach man_files = shared_files + server_files + client_files + cursor_files +stamp = join_paths(meson.current_build_dir(), 'man3.stamp') custom_target( 'man-pages-3', command: [ gen_doxygen, '--builddir=@OUTDIR@', '--output-format=man3', - '--stamp=doc/doxygen/man3.stamp', + '--stamp=@0@'.format(stamp), wayland_doxygen, '@INPUT@', ], diff --git a/doc/meson.build b/doc/meson.build index f74b6b1..44fda2a 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -1,3 +1,7 @@ +if not get_option('libraries') + error('-Ddocumentation=true requires -Dlibraries=true') +endif + dot = find_program('dot') doxygen = find_program('doxygen') xsltproc = find_program('xsltproc') @@ -18,7 +22,7 @@ if vers.version_compare('< 2.26.0') endif manpage_xsl = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl' -cmd = run_command(xsltproc, '--nonet', manpage_xsl) +cmd = run_command(xsltproc, '--nonet', manpage_xsl, check: false) if cmd.returncode() != 0 error('The style sheet for man pages providing "@0@" was not found.'.format(manpage_xsl)) endif diff --git a/doc/publican/Makefile.am b/doc/publican/Makefile.am deleted file mode 100644 index e861fe6..0000000 --- a/doc/publican/Makefile.am +++ /dev/null @@ -1,169 +0,0 @@ -# Documentation is built with xmlto, but some of the recipes in here are -# leftovers from building with Publican (https://fedorahosted.org/publican/) -# -# How this build works: -# * the main target is Wayland, documentation ends up in $(builddir)/Wayland/ -# * hand-written chapters and CSS files are located in sources. These are -# copied into $(builddir)/en-US/ -# * ProtocolSpec.xml is generated from $(top_srcdir)/protocol/wayland.xml, -# changed into docbook via XSLT and saved in $(builddir)/en-US/ -# * ProtocolInterfaces.xml, same as above, uses a different XSLT -# * *API.xml is generated from the doxygen output and saved in -# $(builddir)/en-US -# * run xmlto on $(builddir)/en-US, output to $(builddir)/Wayland/en-US - -doxydir := $(top_builddir)/doc/doxygen -html_destdir := $(builddir)/Wayland/en-US/html - -publican_sources = \ - $(srcdir)/sources/Wayland.ent \ - $(srcdir)/sources/Wayland.xml \ - $(srcdir)/sources/Book_Info.xml \ - $(srcdir)/sources/Author_Group.xml \ - $(srcdir)/sources/Foreword.xml \ - $(srcdir)/sources/Preface.xml \ - $(srcdir)/sources/Revision_History.xml \ - $(srcdir)/sources/Protocol.xml \ - $(srcdir)/sources/Xwayland.xml \ - $(srcdir)/sources/Compositors.xml \ - $(srcdir)/sources/images/icon.svg \ - $(srcdir)/sources/images/wayland.png \ - $(srcdir)/sources/images/xwayland-architecture.png \ - $(srcdir)/sources/Client.xml \ - $(srcdir)/sources/Server.xml - -processed_sources := \ - $(srcdir)/sources/Architecture.xml \ - $(srcdir)/sources/Introduction.xml - -css_sources = \ - $(srcdir)/sources/css/brand.css \ - $(srcdir)/sources/css/common.css \ - $(srcdir)/sources/css/default.css \ - $(srcdir)/sources/css/epub.css \ - $(srcdir)/sources/css/print.css - -img_sources = \ - $(srcdir)/sources/images/icon.svg \ - $(srcdir)/sources/images/wayland.png \ - $(srcdir)/sources/images/xwayland-architecture.png - -doxygen_img_sources := \ - $(doxydir)/xml/wayland-architecture.png \ - $(doxydir)/xml/x-architecture.png - -map_sources := \ - $(doxydir)/xml/x-architecture.map \ - $(doxydir)/xml/wayland-architecture.map - -if HAVE_XMLTO -if HAVE_XSLTPROC -noinst_DATA = $(builddir)/Wayland $(publican_targets) -XMLTO_PARAM = \ - --skip-validation \ - --stringparam chunk.section.depth=0 \ - --stringparam toc.section.depth=1 \ - --stringparam html.stylesheet=css/default.css - -# Listing various directories that might need to be created. -alldirs := $(builddir)/en-US $(builddir)/en-US/images $(html_destdir) $(html_destdir)/css $(html_destdir)/images - - -html_css_targets = $(addprefix $(html_destdir)/css/,$(notdir $(css_sources))) -html_img_targets = $(addprefix $(html_destdir)/images/,$(notdir $(img_sources))) -doxygen_img_targets := $(doxygen_img_sources:$(doxydir)/xml/%=$(html_destdir)/images/%) -map_targets := $(map_sources:$(doxydir)/xml/%=$(builddir)/en-US/images/%) -processed_targets := $(processed_sources:$(srcdir)/sources/%=$(builddir)/en-US/%) - -$(builddir)/Wayland: $(publican_targets) $(html_css_targets) $(html_img_targets) $(processed_targets) $(doxygen_img_targets) | $(builddir)/en-US - $(AM_V_GEN)$(XMLTO) $(XMLTO_PARAM) html $(builddir)/en-US/Wayland.xml -o $(html_destdir) - @touch $@ - -$(html_destdir)/css/%: $(srcdir)/sources/css/% | $(html_destdir)/css - $(AM_V_GEN)cp -f $< $@ - -$(html_destdir)/images/%: $(srcdir)/sources/images/% | $(html_destdir)/images - $(AM_V_GEN)cp -f $< $@ - -$(html_destdir)/images/%: $(doxydir)/xml/% | $(html_destdir)/images - $(AM_V_GEN)cp -f $< $@ - -pubdir = $(docdir)/Wayland/en-US - -publican_targets = $(publican_sources:$(srcdir)/sources/%=$(builddir)/en-US/%) \ - $(builddir)/en-US/ProtocolSpec.xml \ - $(builddir)/en-US/ProtocolInterfaces.xml \ - $(builddir)/en-US/ClientAPI.xml \ - $(builddir)/en-US/ServerAPI.xml - -# The Protocol.xml is purely generated and required before running publican -$(builddir)/en-US/ProtocolSpec.xml: $(top_srcdir)/protocol/wayland.xml $(srcdir)/protocol-to-docbook.xsl | $(builddir)/en-US - $(AM_V_GEN)$(XSLTPROC) $(srcdir)/protocol-to-docbook.xsl \ - $(top_srcdir)/protocol/wayland.xml > $@ - -$(builddir)/en-US/ProtocolInterfaces.xml: $(top_srcdir)/protocol/wayland.xml $(srcdir)/protocol-interfaces-to-docbook.xsl | $(builddir)/en-US - $(AM_V_GEN)$(XSLTPROC) $(srcdir)/protocol-interfaces-to-docbook.xsl \ - $(top_srcdir)/protocol/wayland.xml > $@ - -# * use doxygen's combine.xslt to merge the xml files into one single file -# * pipe that through the doxygen-to-publican stylesheet -$(builddir)/en-US/%API.xml: $(doxydir)/xml/%/index.xml $(srcdir)/doxygen-to-publican.xsl | $(builddir)/en-US - $(AM_V_GEN)$(XSLTPROC) $(doxydir)/xml/$*/combine.xslt \ - $(doxydir)/xml/$*/index.xml | \ - $(XSLTPROC) --stringparam which $* \ - $(srcdir)/doxygen-to-publican.xsl - > $@ - -# Copy the sources source files into en-US destination -# This is required for out-of-source-tree build as publican does not allow us -# to specify the location of the source code. -$(builddir)/en-US/%: $(srcdir)/sources/% $(publican_sources) | $(builddir)/en-US/images - $(AM_V_GEN)cp -f $< $@ - $(AM_V_at)chmod a+w $@ - -$(builddir)/en-US/images/%: $(doxydir)/xml/% | $(builddir)/en-US/images - $(AM_V_GEN)cp -f $< $@ - $(AM_V_at)chmod a+w $@ - -# More specific rule to override explicitly listed targets and perform xslt -# modifications on them. -# Note that we can't use $< as all targets must be there -$(processed_targets): $(processed_sources) $(map_targets) $(srcdir)/merge-mapcoords.xsl | $(builddir)/en-US/images - $(AM_V_GEN)$(XSLTPROC) --stringparam basedir $(builddir)/en-US \ - $(srcdir)/merge-mapcoords.xsl $(addprefix $(srcdir)/sources/,$(notdir $@)) > $@ - -# general rule to create one of the listed directories. -$(alldirs): - $(AM_V_GEN)$(MKDIR_P) $@ - -CLEANFILES = $(publican_targets) - -clean-local: - $(AM_V_at)rm -fr $(builddir)/en-US - $(AM_V_at)rm -fr $(builddir)/Wayland - -install-data-local: - test -z "$(pubdir)/html/css" || $(mkdir_p) "$(DESTDIR)$(pubdir)/html/css" - test -z "$(pubdir)/html/images" || $(mkdir_p) "$(DESTDIR)$(pubdir)/html/images" - list=`find $(builddir)/Wayland/en-US -type f`; \ - for p in $$list; do \ - echo " $(INSTALL_DATA) '$$p' '$(DESTDIR)$(docdir)/$$p'"; \ - $(INSTALL_DATA) "$$p" "$(DESTDIR)$(docdir)/$$p"; \ - done; - -uninstall-local: - @if test -n $(DESTDIR)$(docdir); then \ - if test -d $(DESTDIR)$(docdir); then \ - echo " rm -fr $(DESTDIR)$(docdir)/Wayland;"; \ - rm -fr $(DESTDIR)$(docdir)/Wayland; \ - fi; \ - fi; - -endif -endif - -EXTRA_DIST = \ - $(publican_sources) $(processed_sources) $(css_sources) $(img_sources) \ - protocol-to-docbook.xsl \ - protocol-interfaces-to-docbook.xsl \ - doxygen-to-publican.xsl \ - merge-mapcoords.xsl diff --git a/doc/publican/meson.build b/doc/publican/meson.build index 60305f4..eac3e9b 100644 --- a/doc/publican/meson.build +++ b/doc/publican/meson.build @@ -7,6 +7,7 @@ custom_target( command: [ xmlto, '--skip-validation', + '--stringparam', 'chunker.output.encoding=UTF-8', '--stringparam', 'chunk.section.depth=0', '--stringparam', 'toc.section.depth=1', '--stringparam', 'generate.consistent.ids=1', diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml index 97e9ba2..57d8835 100644 --- a/doc/publican/sources/Protocol.xml +++ b/doc/publican/sources/Protocol.xml @@ -149,9 +149,10 @@ <term>string</term> <listitem> <para> - Starts with an unsigned 32-bit length, followed by the - string contents, including terminating null byte, then padding - to a 32-bit boundary. + Starts with an unsigned 32-bit length (including null terminator), + followed by the string contents, including terminating null byte, + then padding to a 32-bit boundary. A null value is represented + with a length of 0. </para> </listitem> </varlistentry> @@ -159,7 +160,7 @@ <term>object</term> <listitem> <para> - 32-bit object ID. + 32-bit object ID. A null value is represented with an ID of 0. </para> </listitem> </varlistentry> @@ -194,6 +195,21 @@ </varlistentry> </variablelist> </para> + <para> + The protocol does not specify the exact position of the ancillary data + in the stream, except that the order of file descriptors is the same as + the order of messages and <code>fd</code> arguments within messages on + the wire. + </para> + <para> + In particular, it means that any byte of the stream, even the message + header, may carry the ancillary data with file descriptors. + </para> + <para> + Clients and compositors should queue incoming data until they have + whole messages to process, as file descriptors may arrive earlier + or later than the corresponding data bytes. + </para> </section> <xi:include href="ProtocolInterfaces.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/> <section id="sect-Protocol-Versioning"> diff --git a/egl/meson.build b/egl/meson.build index dee9b1d..b3cbdf3 100644 --- a/egl/meson.build +++ b/egl/meson.build @@ -5,13 +5,13 @@ wayland_egl = library( wayland_client_protocol_h ], include_directories: src_inc, - version: '1.0.0', + version: meson.project_version(), install: true ) executable('wayland-egl-abi-check', 'wayland-egl-abi-check.c') -nm_path = find_program('nm').path() +nm_path = find_program('nm').full_path() test( 'wayland-egl symbols check', @@ -41,3 +41,12 @@ pkgconfig.generate( description: 'Backend wayland-egl interface', version: '3' ) + +wayland_egl_dep = declare_dependency( + link_with: wayland_egl, + include_directories: [ root_inc, include_directories('.') ], +) + +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('wayland-egl', wayland_egl_dep) +endif diff --git a/egl/wayland-egl-backend.h b/egl/wayland-egl-backend.h index 869c86f..e5287b7 100644 --- a/egl/wayland-egl-backend.h +++ b/egl/wayland-egl-backend.h @@ -35,8 +35,8 @@ extern "C" { #endif /* - * NOTE: This version must be kept in sync with the Version field in the - * wayland-egl-backend.pc.in file. + * NOTE: This version must be kept in sync with the version field in the + * wayland-egl-backend pkgconfig file generated in meson.build. */ #define WL_EGL_WINDOW_VERSION 3 diff --git a/egl/wayland-egl-backend.pc.in b/egl/wayland-egl-backend.pc.in deleted file mode 100644 index 6cf0ed4..0000000 --- a/egl/wayland-egl-backend.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -includedir=@includedir@ - -Name: wayland-egl-backend -Description: Backend wayland-egl interface -Version: 3 -Libs: -Cflags: -I${includedir} diff --git a/egl/wayland-egl.c b/egl/wayland-egl.c index a60f899..36a3471 100644 --- a/egl/wayland-egl.c +++ b/egl/wayland-egl.c @@ -35,6 +35,20 @@ #include "wayland-util.h" +/** Resize the EGL window + * + * \param egl_window A pointer to a struct wl_egl_window + * \param width The new width + * \param height The new height + * \param dx Offset on the X axis + * \param dy Offset on the Y axis + * + * Note that applications should prefer using the wl_surface.offset request if + * the associated wl_surface has the interface version 5 or higher. + * + * If the wl_surface.offset request is used, applications MUST pass 0 to both + * dx and dy. + */ WL_EXPORT void wl_egl_window_resize(struct wl_egl_window *egl_window, int width, int height, diff --git a/egl/wayland-egl.pc.in b/egl/wayland-egl.pc.in deleted file mode 100644 index 2e2d4c4..0000000 --- a/egl/wayland-egl.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: wayland-egl -Description: Frontend wayland-egl library -Version: 18.1.0 -Requires: wayland-client -Libs: -L${libdir} -lwayland-egl -Cflags: -I${includedir} diff --git a/m4/.gitignore b/m4/.gitignore deleted file mode 100644 index 38066dd..0000000 --- a/m4/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -libtool.m4 -ltoptions.m4 -ltsugar.m4 -ltversion.m4 -lt~obsolete.m4 diff --git a/m4/weston.m4 b/m4/weston.m4 deleted file mode 100644 index 636f9fb..0000000 --- a/m4/weston.m4 +++ /dev/null @@ -1,37 +0,0 @@ -dnl -dnl Copyright © 2016 Quentin “Sardem FF7†Glidic -dnl -dnl Permission is hereby granted, free of charge, to any person obtaining a -dnl copy of this software and associated documentation files (the "Software"), -dnl to deal in the Software without restriction, including without limitation -dnl the rights to use, copy, modify, merge, publish, distribute, sublicense, -dnl and/or sell copies of the Software, and to permit persons to whom the -dnl Software is furnished to do so, subject to the following conditions: -dnl -dnl The above copyright notice and this permission notice (including the next -dnl paragraph) shall be included in all copies or substantial portions of the -dnl Software. -dnl -dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -dnl IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -dnl FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -dnl THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -dnl LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -dnl DEALINGS IN THE SOFTWARE. -dnl - -dnl WESTON_SEARCH_LIBS(PREFIX, search-libs, function, [action-if-found], [action-if-not-found], [other-libraries]) -dnl WESTON_SEARCH_LIBS is a wrapper around AC_SEARCH_LIBS with a little difference: -dnl action-if-found is called even if no library is required -AC_DEFUN([WESTON_SEARCH_LIBS], [ - weston_save_LIBS=${LIBS} - AC_SEARCH_LIBS([$3], [$2], [$4], [$5], [$6]) - AS_CASE([${ac_cv_search_][$3][}], - ['none required'], [$4], - [no], [], - [$1][_LIBS=${ac_cv_search_][$3][}] - ) - AC_SUBST([$1][_LIBS]) - LIBS=${weston_save_LIBS} -]) diff --git a/meson.build b/meson.build index 11c35fa..558c7ac 100644 --- a/meson.build +++ b/meson.build @@ -1,18 +1,26 @@ project( - 'wayland', 'c', 'cpp', - version: '1.19.0', + 'wayland', 'c', + version: '1.22.0', license: 'MIT', - meson_version: '>= 0.52.1', + meson_version: '>= 0.56.0', default_options: [ 'warning_level=2', - 'buildtype=debugoptimized' + 'buildtype=debugoptimized', + 'c_std=c99', ] ) +wayland_version = meson.project_version().split('.') config_h = configuration_data() config_h.set_quoted('PACKAGE', meson.project_name()) config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) +cc_args = [] +if host_machine.system() != 'freebsd' + cc_args += ['-D_POSIX_C_SOURCE=200809L'] +endif +add_project_arguments(cc_args, language: 'c') + compiler_flags = [ '-Wno-unused-parameter', '-Wstrict-prototypes', @@ -26,7 +34,7 @@ add_project_arguments( language: 'c' ) -foreach h: [ 'sys/prctl.h' ] +foreach h: [ 'sys/prctl.h', 'sys/procctl.h', 'sys/ucred.h' ] config_h.set('HAVE_' + h.underscorify().to_upper(), cc.has_header(h)) endforeach @@ -36,13 +44,39 @@ have_funcs = [ 'posix_fallocate', 'prctl', 'memfd_create', + 'mremap', 'strndup', ] foreach f: have_funcs config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f)) endforeach +config_h.set10('HAVE_XUCRED_CR_PID', cc.has_member('struct xucred', 'cr_pid', prefix : '#include <sys/ucred.h>')) +have_broken_msg_cmsg_cloexec = false +if host_machine.system() == 'freebsd' + have_broken_msg_cmsg_cloexec = not cc.compiles(''' +#include <sys/param.h> /* To get __FreeBSD_version. */ +#if __FreeBSD_version < 1300502 || \ + (__FreeBSD_version >= 1400000 && __FreeBSD_version < 1400006) +/* + * FreeBSD had a broken implementation of MSG_CMSG_CLOEXEC between 2015 and + * 2021. Check if we are compiling against a version that includes the fix + * (https://cgit.freebsd.org/src/commit/?id=6ceacebdf52211). + */ +#error "Broken MSG_CMSG_CLOEXEC" +#endif +''', name : 'MSG_CMSG_CLOEXEC works correctly') +endif +config_h.set10('HAVE_BROKEN_MSG_CMSG_CLOEXEC', have_broken_msg_cmsg_cloexec) if get_option('libraries') + if host_machine.system() == 'freebsd' + # When building for FreeBSD, epoll(7) is provided by a userspace + # wrapper around kqueue(2). + epoll_dep = dependency('epoll-shim') + else + # Otherwise, assume that epoll(7) is supported natively. + epoll_dep = [] + endif ffi_dep = dependency('libffi') decls = [ @@ -52,7 +86,7 @@ if get_option('libraries') ] foreach d: decls - if not cc.has_header_symbol(d['header'], d['symbol']) + if not cc.has_header_symbol(d['header'], d['symbol'], dependencies: epoll_dep, args: cc_args) error('@0@ is needed to compile Wayland libraries'.format(d['symbol'])) endif endforeach @@ -60,19 +94,12 @@ if get_option('libraries') rt_dep = [] if not cc.has_function('clock_gettime', prefix: '#include <time.h>') rt_dep = cc.find_library('rt') - if not cc.has_function('clock_gettime', prefix: '#include <time.h>', dependencies: rt_dep) + if not cc.has_function('clock_gettime', prefix: '#include <time.h>', dependencies: rt_dep, args: cc_args) error('clock_gettime not found') endif endif endif -scanner_deps = [ dependency('expat') ] - -if get_option('dtd_validation') - scanner_deps += dependency('libxml-2.0') - config_h.set('HAVE_LIBXML', 1) -endif - configure_file( output: 'config.h', configuration: config_h, @@ -91,10 +118,12 @@ subdir('src') if get_option('libraries') subdir('cursor') subdir('egl') +endif +if get_option('tests') subdir('tests') - if get_option('documentation') - subdir('doc') - endif +endif +if get_option('documentation') + subdir('doc') endif if get_option('scanner') diff --git a/meson_options.txt b/meson_options.txt index de588d1..b8e2ec6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,19 +1,23 @@ option('libraries', description: 'Compile Wayland libraries', type: 'boolean', - value: 'true') + value: true) option('scanner', description: 'Compile wayland-scanner binary', type: 'boolean', - value: 'true') + value: true) +option('tests', + description: 'Compile Wayland tests', + type: 'boolean', + value: true) option('documentation', description: 'Build the documentation (requires Doxygen, dot, xmlto, xsltproc)', type: 'boolean', - value: 'true') + value: true) option('dtd_validation', description: 'Validate the protocol DTD (requires libxml2)', type: 'boolean', - value: 'true') + value: true) option('icon_directory', description: 'Location used to look for cursors (defaults to ${datadir}/icons if unset)', type: 'string', diff --git a/protocol/wayland.dtd b/protocol/wayland.dtd index 15f20ab..ee062ee 100644 --- a/protocol/wayland.dtd +++ b/protocol/wayland.dtd @@ -10,6 +10,7 @@ <!ATTLIST request since CDATA #IMPLIED> <!ELEMENT event (description?,arg*)> <!ATTLIST event name CDATA #REQUIRED> + <!ATTLIST event type CDATA #IMPLIED> <!ATTLIST event since CDATA #IMPLIED> <!ELEMENT enum (description?,entry*)> <!ATTLIST enum name CDATA #REQUIRED> diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 471daf6..10e039d 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -177,9 +177,12 @@ <description summary="callback object"> Clients can handle the 'done' event to get notified when the related request is done. + + Note, because wl_callback objects are created from multiple independent + factory interfaces, the wl_callback interface is frozen at version 1. </description> - <event name="done"> + <event name="done" type="destructor"> <description summary="done event"> Notify the client when the related request is done. </description> @@ -187,7 +190,7 @@ </event> </interface> - <interface name="wl_compositor" version="4"> + <interface name="wl_compositor" version="6"> <description summary="the compositor singleton"> A compositor. This object is a singleton global. The compositor is in charge of combining the contents of multiple @@ -258,6 +261,12 @@ for the pool from the file descriptor passed when the pool was created, but using the new size. This request can only be used to make the pool bigger. + + This request only changes the amount of bytes that are mmapped + by the server and does not touch the file corresponding to the + file descriptor passed at creation time. It is the client's + responsibility to ensure that the file is at least as big as + the new pool size. </description> <arg name="size" type="int" summary="new size of the pool, in bytes"/> </request> @@ -271,8 +280,8 @@ Clients can create wl_shm_pool objects using the create_pool request. - At connection setup time, the wl_shm object emits one or more - format events to inform clients about the valid pixel formats + On binding the wl_shm object one or more format events + are emitted to inform clients about the valid pixel formats that can be used for buffers. </description> @@ -296,6 +305,9 @@ The drm format codes match the macros defined in drm_fourcc.h, except argb8888 and xrgb8888. The formats actually supported by the compositor will be reported by the format event. + + For all wl_shm formats and unless specified in another protocol + extension, pre-multiplied alpha is used for pixel values. </description> <!-- Note to protocol writers: don't update this list manually, instead run the automated script that keeps it in sync with drm_fourcc.h. --> @@ -403,6 +415,10 @@ <entry name="nv15" value="0x3531564e" summary="2x2 subsampled Cr:Cb plane"/> <entry name="q410" value="0x30313451"/> <entry name="q401" value="0x31303451"/> + <entry name="xrgb16161616" value="0x38345258" summary="[63:0] x:R:G:B 16:16:16:16 little endian"/> + <entry name="xbgr16161616" value="0x38344258" summary="[63:0] x:B:G:R 16:16:16:16 little endian"/> + <entry name="argb16161616" value="0x38345241" summary="[63:0] A:R:G:B 16:16:16:16 little endian"/> + <entry name="abgr16161616" value="0x38344241" summary="[63:0] A:B:G:R 16:16:16:16 little endian"/> </enum> <request name="create_pool"> @@ -431,10 +447,18 @@ <interface name="wl_buffer" version="1"> <description summary="content for a wl_surface"> A buffer provides the content for a wl_surface. Buffers are - created through factory interfaces such as wl_drm, wl_shm or - similar. It has a width and a height and can be attached to a - wl_surface, but the mechanism by which a client provides and - updates the contents is defined by the buffer factory interface. + created through factory interfaces such as wl_shm, wp_linux_buffer_params + (from the linux-dmabuf protocol extension) or similar. It has a width and + a height and can be attached to a wl_surface, but the mechanism by which a + client provides and updates the contents is defined by the buffer factory + interface. + + If the buffer uses a format that has an alpha channel, the alpha channel + is assumed to be premultiplied in the color channels unless otherwise + specified. + + Note, because wl_buffer objects are created from multiple independent + factory interfaces, the wl_buffer interface is frozen at version 1. </description> <request name="destroy" type="destructor"> @@ -606,8 +630,9 @@ <event name="source_actions" since="3"> <description summary="notify the source-side available actions"> This event indicates the actions offered by the data source. It - will be sent right after wl_data_device.enter, or anytime the source - side changes its offered actions through wl_data_source.set_actions. + will be sent immediately after creating the wl_data_offer object, + or anytime the source side changes its offered actions through + wl_data_source.set_actions. </description> <arg name="source_actions" type="uint" summary="actions offered by the data source" enum="wl_data_device_manager.dnd_action"/> @@ -849,11 +874,8 @@ a drag-and-drop icon. If the icon surface already has another role, it raises a protocol error. - The current and pending input regions of the icon wl_surface are - cleared, and wl_surface.set_input_region is ignored until the - wl_surface is no longer used as the icon surface. When the use - as an icon ends, the current and pending input regions become - undefined, and the wl_surface is unmapped. + The input region is ignored for wl_surfaces with the role of a + drag-and-drop icon. </description> <arg name="source" type="object" interface="wl_data_source" allow-null="true" summary="data source for the eventual transfer"/> <arg name="origin" type="object" interface="wl_surface" summary="surface where the drag originates"/> @@ -878,7 +900,7 @@ which will subsequently be used in either the data_device.enter event (for drag-and-drop) or the data_device.selection event (for selections). Immediately - following the data_device_data_offer event, the new data_offer + following the data_device.data_offer event, the new data_offer object will send out data_offer.offer events to describe the mime types it offers. </description> @@ -948,9 +970,10 @@ immediately before receiving keyboard focus and when a new selection is set while the client has keyboard focus. The data_offer is valid until a new data_offer or NULL is received - or until the client loses keyboard focus. The client must - destroy the previous selection data_offer, if any, upon receiving - this event. + or until the client loses keyboard focus. Switching surface with + keyboard focus within the same client doesn't mean a new selection + will be sent. The client must destroy the previous selection + data_offer, if any, upon receiving this event. </description> <arg name="id" type="object" interface="wl_data_offer" allow-null="true" summary="selection data_offer object"/> @@ -1038,7 +1061,8 @@ a basic surface. Note! This protocol is deprecated and not intended for production use. - For desktop-style user interfaces, use xdg_shell. + For desktop-style user interfaces, use xdg_shell. Compositors and clients + should not implement this interface. </description> <enum name="error"> @@ -1332,7 +1356,7 @@ </event> </interface> - <interface name="wl_surface" version="4"> + <interface name="wl_surface" version="6"> <description summary="an onscreen surface"> A surface is a rectangular area that may be displayed on zero or more outputs, and shown any number of times at the compositor's @@ -1364,8 +1388,9 @@ that this request gives a role to a wl_surface. Often, this request also creates a new protocol object that represents the role and adds additional functionality to wl_surface. When a - client wants to destroy a wl_surface, they must destroy this 'role - object' before the wl_surface. + client wants to destroy a wl_surface, they must destroy this role + object before the wl_surface, otherwise a defunct_role_object error is + sent. Destroying the role object does not remove the role from the wl_surface, but it may stop the wl_surface from "playing the role". @@ -1384,6 +1409,9 @@ <entry name="invalid_scale" value="0" summary="buffer scale value is invalid"/> <entry name="invalid_transform" value="1" summary="buffer transform value is invalid"/> <entry name="invalid_size" value="2" summary="buffer size is invalid"/> + <entry name="invalid_offset" value="3" summary="buffer offset is invalid"/> + <entry name="defunct_role_object" value="4" + summary="surface was destroyed before its role object"/> </enum> <request name="destroy" type="destructor"> @@ -1406,7 +1434,15 @@ buffer's upper left corner, relative to the current buffer's upper left corner, in surface-local coordinates. In other words, the x and y, combined with the new surface size define in which - directions the surface's size changes. + directions the surface's size changes. Setting anything other than 0 + as x and y arguments is discouraged, and should instead be replaced + with using the separate wl_surface.offset request. + + When the bound wl_surface version is 5 or higher, passing any + non-zero x or y is a protocol violation, and will result in an + 'invalid_offset' error being raised. The x and y arguments are ignored + and do not change the pending state. To achieve equivalent semantics, + use wl_surface.offset. Surface contents are double-buffered state, see wl_surface.commit. @@ -1434,9 +1470,12 @@ from the same backing storage or use wp_linux_buffer_release. Destroying the wl_buffer after wl_buffer.release does not change - the surface contents. However, if the client destroys the - wl_buffer before receiving the wl_buffer.release event, the surface - contents become undefined immediately. + the surface contents. Destroying the wl_buffer before wl_buffer.release + is allowed as long as the underlying buffer storage isn't re-used (this + can happen e.g. on client process termination). However, if the client + destroys the wl_buffer before receiving the wl_buffer.release event and + mutates the underlying buffer storage, the surface contents become + undefined immediately. If wl_surface.attach is sent with a NULL wl_buffer, the following wl_surface.commit will remove the surface content. @@ -1734,9 +1773,58 @@ <arg name="width" type="int" summary="width of damage rectangle"/> <arg name="height" type="int" summary="height of damage rectangle"/> </request> + + <!-- Version 5 additions --> + + <request name="offset" since="5"> + <description summary="set the surface contents offset"> + The x and y arguments specify the location of the new pending + buffer's upper left corner, relative to the current buffer's upper + left corner, in surface-local coordinates. In other words, the + x and y, combined with the new surface size define in which + directions the surface's size changes. + + Surface location offset is double-buffered state, see + wl_surface.commit. + + This request is semantically equivalent to and the replaces the x and y + arguments in the wl_surface.attach request in wl_surface versions prior + to 5. See wl_surface.attach for details. + </description> + <arg name="x" type="int" summary="surface-local x coordinate"/> + <arg name="y" type="int" summary="surface-local y coordinate"/> + </request> + + <!-- Version 6 additions --> + + <event name="preferred_buffer_scale" since="6"> + <description summary="preferred buffer scale for the surface"> + This event indicates the preferred buffer scale for this surface. It is + sent whenever the compositor's preference changes. + + It is intended that scaling aware clients use this event to scale their + content and use wl_surface.set_buffer_scale to indicate the scale they + have rendered with. This allows clients to supply a higher detail + buffer. + </description> + <arg name="factor" type="int" summary="preferred scaling factor"/> + </event> + + <event name="preferred_buffer_transform" since="6"> + <description summary="preferred buffer transform for the surface"> + This event indicates the preferred buffer transform for this surface. + It is sent whenever the compositor's preference changes. + + It is intended that transform aware clients use this event to apply the + transform to their content and use wl_surface.set_buffer_transform to + indicate the transform they have rendered with. + </description> + <arg name="transform" type="uint" enum="wl_output.transform" + summary="preferred transform"/> + </event> </interface> - <interface name="wl_seat" version="7"> + <interface name="wl_seat" version="9"> <description summary="group of input devices"> A seat is a group of keyboards, pointer and touch devices. This object is published as a global during start up, or when such a @@ -1838,9 +1926,22 @@ <event name="name" since="2"> <description summary="unique identifier for this seat"> - In a multiseat configuration this can be used by the client to help - identify which physical devices the seat represents. Based on - the seat configuration used by the compositor. + In a multi-seat configuration the seat name can be used by clients to + help identify which physical devices the seat represents. + + The seat name is a UTF-8 string with no convention defined for its + contents. Each name is unique among all wl_seat globals. The name is + only guaranteed to be unique for the current compositor instance. + + The same seat names are used for all clients. Thus, the name can be + shared across processes to refer to a specific wl_seat global. + + The name event is sent after binding to the seat global. This event is + only sent once per seat object, and the name does not change over the + lifetime of the wl_seat global. + + Compositors may re-use the same seat name if the wl_seat global is + destroyed and re-created later. </description> <arg name="name" type="string" summary="seat identifier"/> </event> @@ -1856,7 +1957,7 @@ </interface> - <interface name="wl_pointer" version="7"> + <interface name="wl_pointer" version="9"> <description summary="pointer input device"> The wl_pointer interface represents one or more input devices, such as mice, which control the pointer location and pointer_focus @@ -1900,11 +2001,13 @@ pointer surface to this request with new values for hotspot_x and hotspot_y. - The current and pending input regions of the wl_surface are - cleared, and wl_surface.set_input_region is ignored until the - wl_surface is no longer used as the cursor. When the use as a - cursor ends, the current and pending input regions become - undefined, and the wl_surface is unmapped. + The input region is ignored for wl_surfaces with the role of + a cursor. When the use as a cursor ends, the wl_surface is + unmapped. + + The serial parameter must match the latest wl_pointer.enter + serial number sent to the client. Otherwise the request will be + ignored. </description> <arg name="serial" type="uint" summary="serial number of the enter event"/> <arg name="surface" type="object" interface="wl_surface" allow-null="true" @@ -2152,6 +2255,9 @@ This event carries the axis value of the wl_pointer.axis event in discrete steps (e.g. mouse wheel clicks). + This event is deprecated with wl_pointer version 8 - this event is not + sent to clients supporting version 8 or later. + This event does not occur on its own, it is coupled with a wl_pointer.axis event that represents this axis value on a continuous scale. The protocol guarantees that each axis_discrete @@ -2159,7 +2265,8 @@ axis number within the same wl_pointer.frame. Note that the protocol allows for other events to occur between the axis_discrete and its coupled axis event, including other axis_discrete or axis - events. + events. A wl_pointer.frame must not contain more than one axis_discrete + event per axis type. This event is optional; continuous scrolling devices like two-finger scrolling on touchpads do not have discrete @@ -2177,9 +2284,93 @@ <arg name="axis" type="uint" enum="axis" summary="axis type"/> <arg name="discrete" type="int" summary="number of steps"/> </event> + + <event name="axis_value120" since="8"> + <description summary="axis high-resolution scroll event"> + Discrete high-resolution scroll information. + + This event carries high-resolution wheel scroll information, + with each multiple of 120 representing one logical scroll step + (a wheel detent). For example, an axis_value120 of 30 is one quarter of + a logical scroll step in the positive direction, a value120 of + -240 are two logical scroll steps in the negative direction within the + same hardware event. + Clients that rely on discrete scrolling should accumulate the + value120 to multiples of 120 before processing the event. + + The value120 must not be zero. + + This event replaces the wl_pointer.axis_discrete event in clients + supporting wl_pointer version 8 or later. + + Where a wl_pointer.axis_source event occurs in the same + wl_pointer.frame, the axis source applies to this event. + + The order of wl_pointer.axis_value120 and wl_pointer.axis_source is + not guaranteed. + </description> + <arg name="axis" type="uint" enum="axis" summary="axis type"/> + <arg name="value120" type="int" summary="scroll distance as fraction of 120"/> + </event> + + <!-- Version 9 additions --> + + <enum name="axis_relative_direction"> + <description summary="axis relative direction"> + This specifies the direction of the physical motion that caused a + wl_pointer.axis event, relative to the wl_pointer.axis direction. + </description> + <entry name="identical" value="0" + summary="physical motion matches axis direction"/> + <entry name="inverted" value="1" + summary="physical motion is the inverse of the axis direction"/> + </enum> + + <event name="axis_relative_direction" since="9"> + <description summary="axis relative physical direction event"> + Relative directional information of the entity causing the axis + motion. + + For a wl_pointer.axis event, the wl_pointer.axis_relative_direction + event specifies the movement direction of the entity causing the + wl_pointer.axis event. For example: + - if a user's fingers on a touchpad move down and this + causes a wl_pointer.axis vertical_scroll down event, the physical + direction is 'identical' + - if a user's fingers on a touchpad move down and this causes a + wl_pointer.axis vertical_scroll up scroll up event ('natural + scrolling'), the physical direction is 'inverted'. + + A client may use this information to adjust scroll motion of + components. Specifically, enabling natural scrolling causes the + content to change direction compared to traditional scrolling. + Some widgets like volume control sliders should usually match the + physical direction regardless of whether natural scrolling is + active. This event enables clients to match the scroll direction of + a widget to the physical direction. + + This event does not occur on its own, it is coupled with a + wl_pointer.axis event that represents this axis value. + The protocol guarantees that each axis_relative_direction event is + always followed by exactly one axis event with the same + axis number within the same wl_pointer.frame. Note that the protocol + allows for other events to occur between the axis_relative_direction + and its coupled axis event. + + The axis number is identical to the axis number in the associated + axis event. + + The order of wl_pointer.axis_relative_direction, + wl_pointer.axis_discrete and wl_pointer.axis_source is not + guaranteed. + </description> + <arg name="axis" type="uint" enum="axis" summary="axis type"/> + <arg name="direction" type="uint" enum="axis_relative_direction" + summary="physical direction relative to axis motion"/> + </event> </interface> - <interface name="wl_keyboard" version="7"> + <interface name="wl_keyboard" version="9"> <description summary="keyboard input device"> The wl_keyboard interface represents one or more keyboards associated with a seat. @@ -2193,13 +2384,14 @@ <entry name="no_keymap" value="0" summary="no keymap; client must understand how to interpret the raw keycode"/> <entry name="xkb_v1" value="1" - summary="libxkbcommon compatible; to determine the xkb keycode, clients must add 8 to the key event keycode"/> + summary="libxkbcommon compatible, null-terminated string; to determine the xkb keycode, clients must add 8 to the key event keycode"/> </enum> <event name="keymap"> <description summary="keyboard mapping"> This event provides a file descriptor to the client which can be - memory-mapped to provide a keyboard mapping description. + memory-mapped in read-only mode to provide a keyboard mapping + description. From version 7 onwards, the fd must be mapped with MAP_PRIVATE by the recipient, as MAP_SHARED may fail. @@ -2305,7 +2497,7 @@ </event> </interface> - <interface name="wl_touch" version="7"> + <interface name="wl_touch" version="9"> <description summary="touchscreen input device"> The wl_touch interface represents a touchscreen associated with a seat. @@ -2449,7 +2641,7 @@ </event> </interface> - <interface name="wl_output" version="3"> + <interface name="wl_output" version="4"> <description summary="compositor output region"> An output describes part of the compositor geometry. The compositor works in the 'compositor coordinate system' and an @@ -2505,12 +2697,15 @@ The physical size can be set to zero if it doesn't make sense for this output (e.g. for projectors or virtual outputs). + The geometry event will be followed by a done event (starting from + version 2). + Note: wl_output only advertises partial information about the output position and identification. Some compositors, for instance those not implementing a desktop-style output layout or those exposing virtual outputs, might fake this information. Instead of using x and y, clients should use xdg_output.logical_position. Instead of using make and model, - clients should use xdg_output.name and xdg_output.description. + clients should use name and description. </description> <arg name="x" type="int" summary="x position within the global compositor space"/> @@ -2566,6 +2761,9 @@ The vertical refresh rate can be set to zero if it doesn't make sense for this output (e.g. for virtual outputs). + The mode event will be followed by a done event (starting from + version 2). + Clients should not use the refresh rate to schedule frames. Instead, they should use the wl_surface.frame event or the presentation-time protocol. @@ -2612,6 +2810,8 @@ the scale of the output. That way the compositor can avoid scaling the surface, and the client can supply a higher detail image. + + The scale event will be followed by a done event. </description> <arg name="factor" type="int" summary="scaling factor of output"/> </event> @@ -2624,6 +2824,62 @@ use the output object anymore. </description> </request> + + <!-- Version 4 additions --> + + <event name="name" since="4"> + <description summary="name of this output"> + Many compositors will assign user-friendly names to their outputs, show + them to the user, allow the user to refer to an output, etc. The client + may wish to know this name as well to offer the user similar behaviors. + + The name is a UTF-8 string with no convention defined for its contents. + Each name is unique among all wl_output globals. The name is only + guaranteed to be unique for the compositor instance. + + The same output name is used for all clients for a given wl_output + global. Thus, the name can be shared across processes to refer to a + specific wl_output global. + + The name is not guaranteed to be persistent across sessions, thus cannot + be used to reliably identify an output in e.g. configuration files. + + Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do + not assume that the name is a reflection of an underlying DRM connector, + X11 connection, etc. + + The name event is sent after binding the output object. This event is + only sent once per output object, and the name does not change over the + lifetime of the wl_output global. + + Compositors may re-use the same output name if the wl_output global is + destroyed and re-created later. Compositors should avoid re-using the + same name if possible. + + The name event will be followed by a done event. + </description> + <arg name="name" type="string" summary="output name"/> + </event> + + <event name="description" since="4"> + <description summary="human-readable description of this output"> + Many compositors can produce human-readable descriptions of their + outputs. The client may wish to know this description as well, e.g. for + output selection purposes. + + The description is a UTF-8 string with no convention defined for its + contents. The description is not guaranteed to be unique among all + wl_output globals. Examples might include 'Foocorp 11" Display' or + 'Virtual X11 output via :1'. + + The description event is sent after binding the output object and + whenever the description changes. The description is optional, and may + not be sent at all. + + The description event will be followed by a done event. + </description> + <arg name="description" type="string" summary="output description"/> + </event> </interface> <interface name="wl_region" version="1"> @@ -2695,6 +2951,8 @@ <enum name="error"> <entry name="bad_surface" value="0" summary="the to-be sub-surface is invalid"/> + <entry name="bad_parent" value="1" + summary="the to-be sub-surface parent is invalid"/> </enum> <request name="get_subsurface"> @@ -2704,14 +2962,18 @@ plain wl_surface into a sub-surface. The to-be sub-surface must not already have another role, and it - must not have an existing wl_subsurface object. Otherwise a protocol - error is raised. + must not have an existing wl_subsurface object. Otherwise the + bad_surface protocol error is raised. Adding sub-surfaces to a parent is a double-buffered operation on the parent (see wl_surface.commit). The effect of adding a sub-surface becomes visible on the next time the state of the parent surface is applied. + The parent surface must not be one of the child surface's descendants, + and the parent must be different from the child surface, otherwise the + bad_parent protocol error is raised. + This request modifies the behaviour of wl_surface.commit request on the sub-surface, see the documentation on wl_subsurface interface. </description> @@ -2766,12 +3028,10 @@ synchronized mode, and then assume that all its child and grand-child sub-surfaces are synchronized, too, without explicitly setting them. - If the wl_surface associated with the wl_subsurface is destroyed, the - wl_subsurface object becomes inert. Note, that destroying either object - takes effect immediately. If you need to synchronize the removal - of a sub-surface to the parent surface update, unmap the sub-surface - first by attaching a NULL wl_buffer, update parent, and then destroy - the sub-surface. + Destroying a sub-surface takes effect immediately. If you need to + synchronize the removal of a sub-surface to the parent surface update, + unmap the sub-surface first by attaching a NULL wl_buffer, update parent, + and then destroy the sub-surface. If the parent wl_surface object is destroyed, the sub-surface is unmapped. @@ -2782,8 +3042,7 @@ The sub-surface interface is removed from the wl_surface object that was turned into a sub-surface with a wl_subcompositor.get_subsurface request. The wl_surface's association - to the parent is deleted, and the wl_surface loses its role as - a sub-surface. The wl_surface is unmapped immediately. + to the parent is deleted. The wl_surface is unmapped immediately. </description> </request> diff --git a/publish-doc b/publish-doc deleted file mode 100755 index 80fc22a..0000000 --- a/publish-doc +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -set -e - -[ -e doc ] || (echo "Run this from the project root" && exit 1) - -make - -DOC_HTML=./doc/publican/Wayland/en-US/html/ - -[ -e "${DOC_HTML}" ] || (echo "HTML documentation failed to build at ${DOC_HTML}" && exit 1) - -chmod -R g+x ${DOC_HTML} - -rsync --delete -avz ${DOC_HTML} freedesktop.org:/srv/wayland.freedesktop.org/www/docs/html/ diff --git a/release.sh b/release.sh new file mode 100755 index 0000000..8e843f5 --- /dev/null +++ b/release.sh @@ -0,0 +1,79 @@ +#!/bin/sh -eu + +build_dir=build-release + +if ! type glab >/dev/null; then + echo "glab is needed to create a release" + exit 1 +fi + +case "$(git rev-parse --abbrev-ref HEAD)" in +main | [0-9]*.[0-9]*) + ;; +*) + echo "Not on the main or a stable branch" + exit 1 +esac + +if [ -n "$(git log @{upstream}..)" ]; then + echo "The main branch has unpushed commits" + exit 1 +fi + +meson_options="" +if [ -e "$build_dir" ]; then + meson_options="$meson_options --wipe" +fi +meson setup "$build_dir" $meson_options + +prev_version="$(git describe --tags --abbrev=0)" +version="$(meson introspect "$build_dir" --projectinfo | jq -r .version)" +if [ "$version" = "$prev_version" ]; then + echo "Version not bumped" + exit 1 +fi + +name="$(meson introspect "$build_dir" --projectinfo | jq -r .descriptive_name)" +if [ "$name" = "" ]; then + echo "Cannot determine project name" + exit 1 +fi + +ninja -C "$build_dir" dist + +archive_name="$name-$version.tar.xz" +archive_path="$build_dir/meson-dist/$archive_name" +gpg --detach-sig "$archive_path" + +sha256="$(cd $build_dir/meson-dist && sha256sum $archive_name)" +sha512="$(cd $build_dir/meson-dist && sha512sum $archive_name)" +archive_url="https://gitlab.freedesktop.org/wayland/$name/-/releases/$version/downloads/$archive_name" +announce_path="$build_dir/meson-dist/$name-$version-announce.eml" +current_branch=$(git branch --show-current) +remote_name=$(git config --get branch.${current_branch}.remote) + +cat >"$announce_path" <<EOF +To: <wayland-devel@lists.freedesktop.org> +Subject: [ANNOUNCE] $name $version + +`git shortlog --no-merges "$prev_version.."` + +git tag: $version + +$archive_url +SHA256: $sha256 +SHA512: $sha512 +PGP: $archive_url.sig +EOF + +echo "Release announcement written to $announce_path" + +echo -n "Release $name $version? [y/N] " +read answer +if [ "$answer" != "y" ]; then + exit 1 +fi + +git tag -s -m "$version" "$version" +git push "$remote_name" "$version" +glab release create "$version" "$archive_path"* --notes "" diff --git a/releasing.txt b/releasing.txt index 1481f7c..e93f53d 100644 --- a/releasing.txt +++ b/releasing.txt @@ -3,41 +3,25 @@ To make a release of Wayland, follow these steps. 0. Verify the test suites and codebase checks pass. All of the tests should either pass or skip. - $ make check + $ ninja -C build/ test - 1. Update the first stanza of configure.ac to the intended version. + 1. Update the first stanza of meson.build to the intended version. Then commit your changes: $ export RELEASE_NUMBER="x.y.z" $ export RELEASE_NAME="[alpha|beta|RC1|RC2|official|point]" $ git status - $ git commit configure.ac -m "configure.ac: bump to version $RELEASE_NUMBER for the $RELEASE_NAME release" + $ git commit meson.build -m "build: bump to version $RELEASE_NUMBER for the $RELEASE_NAME release" $ git push 2. Run the release.sh script to generate the tarballs, sign and upload them, and generate a release announcement template. - This script can be obtained from X.org's modular package: - http://cgit.freedesktop.org/xorg/util/modular/tree/release.sh - - The script supports a --dry-run option to test it without actually - doing a release. If the script fails on the distcheck step due to - a testsuite error that can't be fixed for some reason, you can - skip testsuite by specifying the --dist argument. Pass --help to - see other supported options. - - $ release.sh . - - For Wayland official and point releases, also publish the publican - documentation to wayland.freedesktop.org: - - $ ./publish-doc - - 3. Compose the release announcements. The script will generate - *.x.y.z.announce files with a list of changes and tags. Prepend - it with a human-readable listing of the most notable changes. - For x.y.0 releases, indicate the schedule for the x.y+1.0 + 3. Compose the release announcements. The script will generate a + wayland-x.y.z-announce.eml file with a list of changes and tags. + Prepend it with a human-readable listing of the most notable + changes. For x.y.0 releases, indicate the schedule for the x.y+1.0 release. 4. PGP sign the release announcements and send them to @@ -69,9 +53,9 @@ development early). $ git branch x.y [sha] $ git push origin x.y -The master branch's configure.ac version should always be (at least) +The master branch's meson.build version should always be (at least) x.y.90, with x.y being the most recent stable branch. The stable -branch's configure.ac version is just whatever was most recently +branch's meson.build version is just whatever was most recently released from that branch. For stable branches, we commit fixes to master first, then cherry-pick diff --git a/src/connection.c b/src/connection.c index d0c7d9f..110b614 100644 --- a/src/connection.c +++ b/src/connection.c @@ -54,7 +54,7 @@ div_roundup(uint32_t n, size_t a) return (uint32_t) (((uint64_t) n + (a - 1)) / a); } -struct wl_buffer { +struct wl_ring_buffer { char data[4096]; uint32_t head, tail; }; @@ -65,14 +65,14 @@ struct wl_buffer { #define CLEN (CMSG_LEN(MAX_FDS_OUT * sizeof(int32_t))) struct wl_connection { - struct wl_buffer in, out; - struct wl_buffer fds_in, fds_out; + struct wl_ring_buffer in, out; + struct wl_ring_buffer fds_in, fds_out; int fd; int want_flush; }; static int -wl_buffer_put(struct wl_buffer *b, const void *data, size_t count) +ring_buffer_put(struct wl_ring_buffer *b, const void *data, size_t count) { uint32_t head, size; @@ -98,7 +98,7 @@ wl_buffer_put(struct wl_buffer *b, const void *data, size_t count) } static void -wl_buffer_put_iov(struct wl_buffer *b, struct iovec *iov, int *count) +ring_buffer_put_iov(struct wl_ring_buffer *b, struct iovec *iov, int *count) { uint32_t head, tail; @@ -122,7 +122,7 @@ wl_buffer_put_iov(struct wl_buffer *b, struct iovec *iov, int *count) } static void -wl_buffer_get_iov(struct wl_buffer *b, struct iovec *iov, int *count) +ring_buffer_get_iov(struct wl_ring_buffer *b, struct iovec *iov, int *count) { uint32_t head, tail; @@ -146,7 +146,7 @@ wl_buffer_get_iov(struct wl_buffer *b, struct iovec *iov, int *count) } static void -wl_buffer_copy(struct wl_buffer *b, void *data, size_t count) +ring_buffer_copy(struct wl_ring_buffer *b, void *data, size_t count) { uint32_t tail, size; @@ -161,7 +161,7 @@ wl_buffer_copy(struct wl_buffer *b, void *data, size_t count) } static uint32_t -wl_buffer_size(struct wl_buffer *b) +ring_buffer_size(struct wl_ring_buffer *b) { return b->head - b->tail; } @@ -181,16 +181,16 @@ wl_connection_create(int fd) } static void -close_fds(struct wl_buffer *buffer, int max) +close_fds(struct wl_ring_buffer *buffer, int max) { int32_t fds[sizeof(buffer->data) / sizeof(int32_t)], i, count; size_t size; - size = wl_buffer_size(buffer); + size = ring_buffer_size(buffer); if (size == 0) return; - wl_buffer_copy(buffer, fds, size); + ring_buffer_copy(buffer, fds, size); count = size / sizeof fds[0]; if (max > 0 && max < count) count = max; @@ -221,7 +221,7 @@ wl_connection_destroy(struct wl_connection *connection) void wl_connection_copy(struct wl_connection *connection, void *data, size_t size) { - wl_buffer_copy(&connection->in, data, size); + ring_buffer_copy(&connection->in, data, size); } void @@ -231,12 +231,12 @@ wl_connection_consume(struct wl_connection *connection, size_t size) } static void -build_cmsg(struct wl_buffer *buffer, char *data, int *clen) +build_cmsg(struct wl_ring_buffer *buffer, char *data, size_t *clen) { struct cmsghdr *cmsg; size_t size; - size = wl_buffer_size(buffer); + size = ring_buffer_size(buffer); if (size > MAX_FDS_OUT * sizeof(int32_t)) size = MAX_FDS_OUT * sizeof(int32_t); @@ -245,7 +245,7 @@ build_cmsg(struct wl_buffer *buffer, char *data, int *clen) cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(size); - wl_buffer_copy(buffer, CMSG_DATA(cmsg), size); + ring_buffer_copy(buffer, CMSG_DATA(cmsg), size); *clen = cmsg->cmsg_len; } else { *clen = 0; @@ -253,7 +253,7 @@ build_cmsg(struct wl_buffer *buffer, char *data, int *clen) } static int -decode_cmsg(struct wl_buffer *buffer, struct msghdr *msg) +decode_cmsg(struct wl_ring_buffer *buffer, struct msghdr *msg) { struct cmsghdr *cmsg; size_t size, max, i; @@ -266,13 +266,13 @@ decode_cmsg(struct wl_buffer *buffer, struct msghdr *msg) continue; size = cmsg->cmsg_len - CMSG_LEN(0); - max = sizeof(buffer->data) - wl_buffer_size(buffer); + max = sizeof(buffer->data) - ring_buffer_size(buffer); if (size > max || overflow) { overflow = 1; size /= sizeof(int32_t); for (i = 0; i < size; i++) close(((int*)CMSG_DATA(cmsg))[i]); - } else if (wl_buffer_put(buffer, CMSG_DATA(cmsg), size) < 0) { + } else if (ring_buffer_put(buffer, CMSG_DATA(cmsg), size) < 0) { return -1; } } @@ -289,9 +289,10 @@ int wl_connection_flush(struct wl_connection *connection) { struct iovec iov[2]; - struct msghdr msg; + struct msghdr msg = {0}; char cmsg[CLEN]; - int len = 0, count, clen; + int len = 0, count; + size_t clen; uint32_t tail; if (!connection->want_flush) @@ -299,17 +300,14 @@ wl_connection_flush(struct wl_connection *connection) tail = connection->out.tail; while (connection->out.head - connection->out.tail > 0) { - wl_buffer_get_iov(&connection->out, iov, &count); + ring_buffer_get_iov(&connection->out, iov, &count); build_cmsg(&connection->fds_out, cmsg, &clen); - msg.msg_name = NULL; - msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = count; msg.msg_control = (clen > 0) ? cmsg : NULL; msg.msg_controllen = clen; - msg.msg_flags = 0; do { len = sendmsg(connection->fd, &msg, @@ -332,7 +330,7 @@ wl_connection_flush(struct wl_connection *connection) uint32_t wl_connection_pending_input(struct wl_connection *connection) { - return wl_buffer_size(&connection->in); + return ring_buffer_size(&connection->in); } int @@ -343,12 +341,12 @@ wl_connection_read(struct wl_connection *connection) char cmsg[CLEN]; int len, count, ret; - if (wl_buffer_size(&connection->in) >= sizeof(connection->in.data)) { + if (ring_buffer_size(&connection->in) >= sizeof(connection->in.data)) { errno = EOVERFLOW; return -1; } - wl_buffer_put_iov(&connection->in, iov, &count); + ring_buffer_put_iov(&connection->in, iov, &count); msg.msg_name = NULL; msg.msg_namelen = 0; @@ -385,7 +383,7 @@ wl_connection_write(struct wl_connection *connection, return -1; } - if (wl_buffer_put(&connection->out, data, count) < 0) + if (ring_buffer_put(&connection->out, data, count) < 0) return -1; connection->want_flush = 1; @@ -404,7 +402,7 @@ wl_connection_queue(struct wl_connection *connection, return -1; } - return wl_buffer_put(&connection->out, data, count); + return ring_buffer_put(&connection->out, data, count); } int @@ -429,13 +427,13 @@ wl_connection_get_fd(struct wl_connection *connection) static int wl_connection_put_fd(struct wl_connection *connection, int32_t fd) { - if (wl_buffer_size(&connection->fds_out) == MAX_FDS_OUT * sizeof fd) { + if (ring_buffer_size(&connection->fds_out) == MAX_FDS_OUT * sizeof fd) { connection->want_flush = 1; if (wl_connection_flush(connection) < 0) return -1; } - return wl_buffer_put(&connection->fds_out, &fd, sizeof fd); + return ring_buffer_put(&connection->fds_out, &fd, sizeof fd); } const char * @@ -568,10 +566,10 @@ wl_closure_init(const struct wl_message *message, uint32_t size, if (size) { *num_arrays = wl_message_count_arrays(message); - closure = malloc(sizeof *closure + size + + closure = zalloc(sizeof *closure + size + *num_arrays * sizeof(struct wl_array)); } else { - closure = malloc(sizeof *closure); + closure = zalloc(sizeof *closure); } if (!closure) { @@ -632,13 +630,13 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, break; case 'n': object = args[i].o; - if (!arg.nullable && object == NULL) + if (object == NULL) goto err_null; closure->args[i].n = object ? object->id : 0; break; case 'a': - if (!arg.nullable && args[i].a == NULL) + if (args[i].a == NULL) goto err_null; break; case 'h': @@ -749,6 +747,13 @@ wl_connection_demarshal(struct wl_connection *connection, case 's': length = *p++; + if (length == 0 && !arg.nullable) { + wl_log("NULL string received on non-nullable " + "type, message %s(%s)\n", message->name, + message->signature); + errno = EINVAL; + goto err; + } if (length == 0) { closure->args[i].s = NULL; break; @@ -794,7 +799,7 @@ wl_connection_demarshal(struct wl_connection *connection, id = *p++; closure->args[i].n = id; - if (id == 0 && !arg.nullable) { + if (id == 0) { wl_log("NULL new ID received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); @@ -803,10 +808,12 @@ wl_connection_demarshal(struct wl_connection *connection, } if (wl_map_reserve_new(objects, id) < 0) { - wl_log("not a valid new object id (%u), " - "message %s(%s)\n", - id, message->name, message->signature); - errno = EINVAL; + if (errno == EINVAL) { + wl_log("not a valid new object id (%u), " + "message %s(%s)\n", id, + message->name, + message->signature); + } goto err; } @@ -842,7 +849,7 @@ wl_connection_demarshal(struct wl_connection *connection, goto err; } - wl_buffer_copy(&connection->fds_in, &fd, sizeof fd); + ring_buffer_copy(&connection->fds_in, &fd, sizeof fd); connection->fds_in.tail += sizeof fd; closure->args[i].h = fd; break; @@ -1049,7 +1056,7 @@ copy_fds_to_connection(struct wl_closure *closure, fd = closure->args[i].h; if (wl_connection_put_fd(connection, fd)) { wl_log("request could not be marshaled: " - "can't send file descriptor"); + "can't send file descriptor\n"); return -1; } closure->args[i].h = -1; @@ -1256,19 +1263,31 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) } void -wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send) +wl_closure_print(struct wl_closure *closure, struct wl_object *target, + bool send, const char *discarded_reason) { int i; struct argument_details arg; const char *signature = closure->message->signature; struct timespec tp; unsigned int time; + uint32_t nval; + FILE *f; + char *buffer; + size_t buffer_length; + + f = open_memstream(&buffer, &buffer_length); + if (f == NULL) + return; clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - fprintf(stderr, "[%10.3f] %s%s@%u.%s(", - time / 1000.0, + fprintf(f, "[%7u.%03u] %s%s%s%s%s@%u.%s(", + time / 1000, time % 1000, + (discarded_reason != NULL) ? "discarded[" : "", + (discarded_reason != NULL) ? discarded_reason : "", + (discarded_reason != NULL) ? "] " : "", send ? " -> " : "", target->interface->name, target->id, closure->message->name); @@ -1276,53 +1295,69 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send) for (i = 0; i < closure->count; i++) { signature = get_next_argument(signature, &arg); if (i > 0) - fprintf(stderr, ", "); + fprintf(f, ", "); switch (arg.type) { case 'u': - fprintf(stderr, "%u", closure->args[i].u); + fprintf(f, "%u", closure->args[i].u); break; case 'i': - fprintf(stderr, "%d", closure->args[i].i); + fprintf(f, "%d", closure->args[i].i); break; case 'f': - fprintf(stderr, "%f", - wl_fixed_to_double(closure->args[i].f)); + /* The magic number 390625 is 1e8 / 256 */ + if (closure->args[i].f >= 0) { + fprintf(f, "%d.%08d", + closure->args[i].f / 256, + 390625 * (closure->args[i].f % 256)); + } else { + + fprintf(f, "-%d.%08d", + closure->args[i].f / -256, + -390625 * (closure->args[i].f % 256)); + } break; case 's': if (closure->args[i].s) - fprintf(stderr, "\"%s\"", closure->args[i].s); + fprintf(f, "\"%s\"", closure->args[i].s); else - fprintf(stderr, "nil"); + fprintf(f, "nil"); break; case 'o': if (closure->args[i].o) - fprintf(stderr, "%s@%u", + fprintf(f, "%s@%u", closure->args[i].o->interface->name, closure->args[i].o->id); else - fprintf(stderr, "nil"); + fprintf(f, "nil"); break; case 'n': - fprintf(stderr, "new id %s@", + nval = closure->args[i].n; + + fprintf(f, "new id %s@", (closure->message->types[i]) ? closure->message->types[i]->name : "[unknown]"); - if (closure->args[i].n != 0) - fprintf(stderr, "%u", closure->args[i].n); + if (nval != 0) + fprintf(f, "%u", nval); else - fprintf(stderr, "nil"); + fprintf(f, "nil"); break; case 'a': - fprintf(stderr, "array"); + fprintf(f, "array[%zu]", closure->args[i].a->size); break; case 'h': - fprintf(stderr, "fd %d", closure->args[i].h); + fprintf(f, "fd %d", closure->args[i].h); break; } } - fprintf(stderr, ")\n"); + fprintf(f, ")\n"); + + if (fclose(f) == 0) { + fprintf(stderr, "%s", buffer); + free(buffer); + } } static int diff --git a/src/dtddata.S b/src/dtddata.S deleted file mode 100644 index 2405066..0000000 --- a/src/dtddata.S +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2015 Collabora, Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Avoid executable stack. - * from: https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart - */ -#if defined(__linux__) && defined(__ELF__) -.section .note.GNU-stack,"",%progbits -#endif - -/* from: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967#comment-348129 */ - -.macro binfile name file - .p2align 2 - .globl \name\()_begin -\name\()_begin: - .incbin "\file" -\name\()_end: - .byte 0 - .p2align 2 - .globl \name\()_len -\name\()_len: - .int (\name\()_end - \name\()_begin) -.endm - -.section .rodata -binfile DTD_DATA src/wayland.dtd.embed diff --git a/src/embed.py b/src/embed.py new file mode 100644 index 0000000..abebb15 --- /dev/null +++ b/src/embed.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +""" +Simple C data embedder + +License: MIT + +Copyright (c) 2020 Simon Ser + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import sys + +if len(sys.argv) != 3: + print('usage: ' + sys.argv[0] + ' <filename> <ident>', file=sys.stderr) + sys.exit(1) + +filename = sys.argv[1] +ident = sys.argv[2] + +with open(filename, 'rb') as f: + buf = f.read() + +print('static const char ' + ident + '[] = {\n\t', end='') +for i in range(len(buf)): + ch = buf[i:i+1] + print('0x' + ch.hex() + ', ', end='') +print('\n};') diff --git a/src/event-loop.c b/src/event-loop.c index 339ff19..37cf95d 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -179,7 +179,7 @@ wl_event_loop_add_fd(struct wl_event_loop *loop, { struct wl_event_source_fd *source; - source = malloc(sizeof *source); + source = zalloc(sizeof *source); if (source == NULL) return NULL; @@ -340,7 +340,7 @@ wl_timer_heap_reserve(struct wl_timer_heap *timers) new_space = timers->space >= 8 ? timers->space * 2 : 8; n = realloc(timers->data, (size_t)new_space * sizeof(*n)); if (!n) { - wl_log("Allocation failure when expanding timer list"); + wl_log("Allocation failure when expanding timer list\n"); return -1; } timers->data = n; @@ -361,7 +361,7 @@ wl_timer_heap_unreserve(struct wl_timer_heap *timers) if (timers->space >= 16 && timers->space >= 4 * timers->count) { n = realloc(timers->data, (size_t)timers->space / 2 * sizeof(*n)); if (!n) { - wl_log("Reallocation failure when shrinking timer list"); + wl_log("Reallocation failure when shrinking timer list\n"); return; } timers->data = n; @@ -568,7 +568,7 @@ wl_event_loop_add_timer(struct wl_event_loop *loop, if (wl_timer_heap_ensure_timerfd(&loop->timers) < 0) return NULL; - source = malloc(sizeof *source); + source = zalloc(sizeof *source); if (source == NULL) return NULL; @@ -718,7 +718,7 @@ wl_event_loop_add_signal(struct wl_event_loop *loop, struct wl_event_source_signal *source; sigset_t mask; - source = malloc(sizeof *source); + source = zalloc(sizeof *source); if (source == NULL) return NULL; @@ -775,7 +775,7 @@ wl_event_loop_add_idle(struct wl_event_loop *loop, { struct wl_event_source_idle *source; - source = malloc(sizeof *source); + source = zalloc(sizeof *source); if (source == NULL) return NULL; @@ -885,7 +885,7 @@ wl_event_loop_create(void) { struct wl_event_loop *loop; - loop = malloc(sizeof *loop); + loop = zalloc(sizeof *loop); if (loop == NULL) return NULL; @@ -942,8 +942,8 @@ post_dispatch_check(struct wl_event_loop *loop) dispatch_result = source->interface->dispatch(source, &ep); if (dispatch_result < 0) { - wl_log("Source dispatch function returned negative value!"); - wl_log("This would previously accidentally suppress a follow-up dispatch"); + wl_log("Source dispatch function returned negative value!\n"); + wl_log("This would previously accidentally suppress a follow-up dispatch\n"); } needs_recheck |= dispatch_result != 0; } diff --git a/src/meson.build b/src/meson.build index d91c503..a8a1d2b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,7 @@ -wayland_version = meson.project_version().split('.') +if not (get_option('scanner') or get_option('libraries')) + error('Either -Dscanner=true or -Dlibraries=true is required') +endif + wayland_version_h = configuration_data() wayland_version_h.set('WAYLAND_VERSION', meson.project_version()) wayland_version_h.set('WAYLAND_VERSION_MAJOR', wayland_version[0].to_int()) @@ -26,19 +29,31 @@ wayland_util_dep = declare_dependency( if get_option('scanner') # wayland-scanner - configure_file( + scanner_deps = [ dependency('expat') ] + scanner_args = [ '-include', 'config.h' ] + + if get_option('dtd_validation') + scanner_deps += dependency('libxml-2.0') + scanner_args += '-DHAVE_LIBXML=1' + endif + + prog_embed = find_program('embed.py', native: true) + + embed_dtd = custom_target( + 'wayland.dtd.h', input: '../protocol/wayland.dtd', - output: 'wayland.dtd.embed', - copy: true + output: 'wayland.dtd.h', + command: [ prog_embed, '@INPUT@', 'wayland_dtd' ], + capture: true ) - wayland_scanner_sources = [ 'scanner.c', 'dtddata.S' ] + wayland_scanner_sources = [ 'scanner.c', embed_dtd ] wayland_scanner_includes = [ root_inc, protocol_inc ] wayland_scanner = executable( 'wayland-scanner', wayland_scanner_sources, - c_args: [ '-include', 'config.h' ], + c_args: scanner_args, include_directories: wayland_scanner_includes, dependencies: [ scanner_deps, wayland_util_dep, ], install: true @@ -56,11 +71,15 @@ if get_option('scanner') ], filebase: 'wayland-scanner' ) + + if meson.can_run_host_binaries() + meson.override_find_program('wayland-scanner', wayland_scanner) + endif endif if meson.is_cross_build() or not get_option('scanner') scanner_dep = dependency('wayland-scanner', native: true, version: meson.project_version()) - wayland_scanner_for_build = find_program(scanner_dep.get_pkgconfig_variable('wayland_scanner')) + wayland_scanner_for_build = find_program(scanner_dep.get_variable(pkgconfig: 'wayland_scanner')) else wayland_scanner_for_build = wayland_scanner endif @@ -77,7 +96,7 @@ if get_option('libraries') 'connection.c', 'wayland-os.c' ], - dependencies: [ ffi_dep, rt_dep ] + dependencies: [ epoll_dep, ffi_dep, rt_dep ] ) wayland_private_dep = declare_dependency( @@ -139,6 +158,15 @@ if get_option('libraries') output: 'wayland-protocol.c' ) + if wayland_version[0] != '1' + # The versioning used for the shared libraries assumes that the major + # version of Wayland as a whole will increase to 2 if and only if there + # is an ABI break, at which point we should probably bump the SONAME of + # all libraries to .so.2. For more details see + # https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/177 + error('We probably need to bump the SONAME of libwayland-server and -client') + endif + wayland_server = library( 'wayland-server', sources: [ @@ -149,8 +177,11 @@ if get_option('libraries') 'wayland-shm.c', 'event-loop.c' ], - version: '0.1.0', + # To avoid an unnecessary SONAME bump, wayland 1.x.y produces + # libwayland-server.so.0.x.y. + version: '.'.join(['0', wayland_version[1], wayland_version[2]]), dependencies: [ + epoll_dep, ffi_dep, wayland_private_dep, wayland_util_dep, @@ -165,7 +196,7 @@ if get_option('libraries') wayland_server_dep = declare_dependency( link_with: wayland_server, include_directories: [ root_inc, include_directories('.') ], - dependencies: [ ffi_dep, mathlib_dep, threads_dep ], + dependencies: [ epoll_dep, ffi_dep, mathlib_dep, threads_dep ], sources: [ wayland_server_protocol_core_h, wayland_server_protocol_h @@ -184,6 +215,10 @@ if get_option('libraries') ] ) + if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('wayland-server', wayland_server_dep) + endif + wayland_client = library( 'wayland-client', sources: [ @@ -192,8 +227,11 @@ if get_option('libraries') wayland_protocol_c, 'wayland-client.c' ], - version: '0.3.0', + # To avoid an unnecessary SONAME bump, wayland 1.x.y produces + # libwayland-client.so.0.x.y. + version: '.'.join(['0', wayland_version[1], wayland_version[2]]), dependencies: [ + epoll_dep, ffi_dep, wayland_private_dep, wayland_util_dep, @@ -225,6 +263,10 @@ if get_option('libraries') ] ) + if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('wayland-client', wayland_client_dep) + endif + install_headers([ 'wayland-util.h', 'wayland-server.h', diff --git a/src/scanner.c b/src/scanner.c index 36ac905..c512d23 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -41,9 +41,9 @@ #if HAVE_LIBXML #include <libxml/parser.h> -/* Embedded wayland.dtd file, see dtddata.S */ -extern char DTD_DATA_begin; -extern int DTD_DATA_len; +/* Embedded wayland.dtd file */ +/* static const char wayland_dtd[]; wayland.dtd */ +#include "wayland.dtd.h" #endif /* Expat must be included after libxml as both want to declare XMLCALL; see @@ -112,8 +112,8 @@ is_dtd_valid(FILE *input, const char *filename) if (!ctx || !dtdctx) abort(); - buffer = xmlParserInputBufferCreateMem(&DTD_DATA_begin, - DTD_DATA_len, + buffer = xmlParserInputBufferCreateMem(wayland_dtd, + sizeof(wayland_dtd), XML_CHAR_ENCODING_UTF8); if (!buffer) { fprintf(stderr, "Failed to init buffer for DTD.\n"); @@ -236,6 +236,7 @@ struct entry { char *summary; int since; struct wl_list link; + struct description *description; }; struct parse_context { @@ -245,6 +246,7 @@ struct parse_context { struct interface *interface; struct message *message; struct enumeration *enumeration; + struct entry *entry; struct description *description; char character_data[8192]; unsigned int character_data_length; @@ -409,11 +411,9 @@ static bool is_nullable_type(struct arg *arg) { switch (arg->type) { - /* Strings, objects, and arrays are possibly nullable */ + /* Strings and objects are possibly nullable */ case STRING: case OBJECT: - case NEW_ID: - case ARRAY: return true; default: return false; @@ -542,6 +542,7 @@ free_entry(struct entry *entry) free(entry->uppercase_name); free(entry->value); free(entry->summary); + free_description(entry->description); free(entry); } @@ -884,6 +885,7 @@ start_element(void *data, const char *element_name, const char **atts) entry->summary = NULL; wl_list_insert(ctx->enumeration->entry_list.prev, &entry->link); + ctx->entry = entry; } else if (strcmp(element_name, "description") == 0) { if (summary == NULL) fail(&ctx->loc, "description without summary"); @@ -893,6 +895,8 @@ start_element(void *data, const char *element_name, const char **atts) if (ctx->message) ctx->message->description = description; + else if (ctx->entry) + ctx->entry->description = description; else if (ctx->enumeration) ctx->enumeration->description = description; else if (ctx->interface) @@ -1008,6 +1012,8 @@ end_element(void *data, const XML_Char *name) ctx->enumeration->name); } ctx->enumeration = NULL; + } else if (strcmp(name, "entry") == 0) { + ctx->entry = NULL; } else if (strcmp(name, "protocol") == 0) { struct interface *i; @@ -1230,38 +1236,40 @@ emit_stubs(struct wl_list *message_list, struct interface *interface) printf(")\n" "{\n"); - if (ret && ret->interface_name == NULL) { - /* an arg has type ="new_id" but interface is not - * provided, such as in wl_registry.bind */ - printf("\tstruct wl_proxy *%s;\n\n" - "\t%s = wl_proxy_marshal_constructor_versioned(" - "(struct wl_proxy *) %s,\n" - "\t\t\t %s_%s, interface, version", - ret->name, ret->name, - interface->name, - interface->uppercase_name, - m->uppercase_name); - } else if (ret) { - /* Normal factory case, an arg has type="new_id" and - * an interface is provided */ - printf("\tstruct wl_proxy *%s;\n\n" - "\t%s = wl_proxy_marshal_constructor(" - "(struct wl_proxy *) %s,\n" - "\t\t\t %s_%s, &%s_interface", - ret->name, ret->name, - interface->name, - interface->uppercase_name, - m->uppercase_name, - ret->interface_name); + printf("\t"); + if (ret) { + printf("struct wl_proxy *%s;\n\n" + "\t%s = ", ret->name, ret->name); + } + printf("wl_proxy_marshal_flags(" + "(struct wl_proxy *) %s,\n" + "\t\t\t %s_%s", + interface->name, + interface->uppercase_name, + m->uppercase_name); + + if (ret) { + if (ret->interface_name) { + /* Normal factory case, an arg has type="new_id" and + * an interface is provided */ + printf(", &%s_interface", ret->interface_name); + } else { + /* an arg has type ="new_id" but interface is not + * provided, such as in wl_registry.bind */ + printf(", interface"); + } } else { /* No args have type="new_id" */ - printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n" - "\t\t\t %s_%s", - interface->name, - interface->uppercase_name, - m->uppercase_name); + printf(", NULL"); } + if (ret && ret->interface_name == NULL) + printf(", version"); + else + printf(", wl_proxy_get_version((struct wl_proxy *) %s)", + interface->name); + printf(", %s", m->destructor ? "WL_MARSHAL_FLAG_DESTROY" : "0"); + wl_list_for_each(a, &m->arg_list, link) { if (a->type == NEW_ID) { if (a->interface_name == NULL) @@ -1273,11 +1281,6 @@ emit_stubs(struct wl_list *message_list, struct interface *interface) } printf(");\n"); - if (m->destructor) - printf("\n\twl_proxy_destroy(" - "(struct wl_proxy *) %s);\n", - interface->name); - if (ret && ret->interface_name == NULL) printf("\n\treturn (void *) %s;\n", ret->name); else if (ret) @@ -1364,10 +1367,17 @@ emit_enumerations(struct interface *interface) } printf("enum %s_%s {\n", interface->name, e->name); wl_list_for_each(entry, &e->entry_list, link) { - if (entry->summary || entry->since > 1) { + desc = entry->description; + if (entry->summary || entry->since > 1 || desc) { printf("\t/**\n"); if (entry->summary) printf("\t * %s\n", entry->summary); + if (desc) { + printf("\t * %s\n", desc->summary); + printf("\t *\n"); + if (desc->text) + desc_dump(desc->text, "\t * "); + } if (entry->since > 1) printf("\t * @since %d\n", entry->since); printf("\t */\n"); @@ -1624,7 +1634,9 @@ emit_header(struct protocol *protocol, enum side side) *p = i->name; } - qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names); + if (types.size > 0) + qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names); + prev = NULL; wl_array_for_each(p, &types) { if (prev && strcmp(*p, prev) == 0) @@ -1834,7 +1846,10 @@ emit_code(struct protocol *protocol, enum visibility vis) emit_types_forward_declarations(protocol, &i->request_list, &types); emit_types_forward_declarations(protocol, &i->event_list, &types); } - qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names); + + if (types.size > 0) + qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names); + prev = NULL; wl_array_for_each(p, &types) { if (prev && strcmp(*p, prev) == 0) diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index 94c25e3..af7c184 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -119,9 +119,27 @@ struct wl_display; */ struct wl_event_queue; +/** Destroy proxy after marshalling + * @ingroup wl_proxy + */ +#define WL_MARSHAL_FLAG_DESTROY (1 << 0) + void wl_event_queue_destroy(struct wl_event_queue *queue); +struct wl_proxy * +wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode, + const struct wl_interface *interface, + uint32_t version, + uint32_t flags, ...); + +struct wl_proxy * +wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, + const struct wl_interface *interface, + uint32_t version, + uint32_t flags, + union wl_argument *args); + void wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...); @@ -270,31 +288,106 @@ wl_display_read_events(struct wl_display *display); void wl_log_set_handler_client(wl_log_func_t handler); -enum wl_protocol_logger_client_type { - WL_PROTOCOL_LOGGER_CLIENT_REQUEST, - WL_PROTOCOL_LOGGER_CLIENT_EVENT, +/** + * The message type. + */ +enum wl_client_message_type { + /** The message is a request */ + WL_CLIENT_MESSAGE_REQUEST, + + /** The message is an event */ + WL_CLIENT_MESSAGE_EVENT, +}; + +/** + * The message discard reason codes. + */ +enum wl_client_message_discarded_reason { + /** The message was handled normally, and not discarded. */ + WL_CLIENT_MESSAGE_NOT_DISCARDED = 0, + + /** The target was not alive at dispatch time */ + WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, + + /** The target had no listener or dispatcher */ + WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH, + + /** The target was not valid when the event was demarshalled */ + WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL, }; -struct wl_protocol_logger_client_message { +/** + * The structure used to communicate details about an observed message to the + * registered observers. + */ +struct wl_client_observed_message { + /** The target for the message */ struct wl_proxy *proxy; + + /** The message opcode */ int message_opcode; + + /** The protocol message structure */ const struct wl_message *message; + + /** The count of arguments to the message */ int arguments_count; + + /** The argument array for the messagge */ const union wl_argument *arguments; + + /** The discard reason code */ + enum wl_client_message_discarded_reason discarded_reason; + + /** + * The discard reason string, or NULL if the event was not discarded. + * + * This string is only for convenience for a observer that does + * logging. The string values should not be considered stable, and + * are not localized. + */ + const char *discarded_reason_str; }; -typedef void (*wl_protocol_logger_client_func_t)( - void *user_data, - enum wl_protocol_logger_client_type direction, - const struct wl_protocol_logger_client_message *message); +/** + * The signature for a client message observer function, as registered with + * wl_display_add_client_observer(). + * + * \param user_data \c user_data pointer given when the observer was + * registered with \c wl_display_create_client_observer + * \param type type of message + * \param message details for the message + */ +typedef void (*wl_client_message_observer_func_t)( + void *user_data, enum wl_client_message_type type, + const struct wl_client_observed_message *message); + +/** \class wl_client_observer + * + * \brief Represents a client message observer + * + * A client observer allows the client to observe all request and event + * message traffic to and from the client. For events, the observer is + * also given a discard reason if the event wasn't handled. + * + * The typical use for the observer is to allow the client implementation to + * do its own debug logging, as the default when setting WAYLAND_DEBUG is to + * log to stderr. + * + * With this runtime call, the client can also enable and disable the observer + * at any time. + * + * The protocol-logger-test.c file has an example of a logger implementation. + */ +struct wl_client_observer; -struct wl_protocol_logger_client * -wl_display_add_protocol_logger_client(struct wl_display *display, - wl_protocol_logger_client_func_t, - void *user_data); +struct wl_client_observer * +wl_display_create_client_observer(struct wl_display *display, + wl_client_message_observer_func_t observer, + void *user_data); void -wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger); +wl_client_observer_destroy(struct wl_client_observer *observer); #ifdef __cplusplus } diff --git a/src/wayland-client-uninstalled.pc.in b/src/wayland-client-uninstalled.pc.in deleted file mode 100644 index 6fd0ce6..0000000 --- a/src/wayland-client-uninstalled.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -libdir=@abs_builddir@/.libs -includedir=@abs_srcdir@ -protocoldir=@abs_top_builddir@/protocol - -Name: Wayland Client -Description: Wayland client side library (not installed) -Version: @PACKAGE_VERSION@ -Cflags: -I${includedir} -I${protocoldir} -Libs: -L${libdir} -lwayland-client diff --git a/src/wayland-client.c b/src/wayland-client.c index 74d4861..d54e715 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -70,10 +70,12 @@ struct wl_proxy { wl_dispatcher_func_t dispatcher; uint32_t version; const char * const *tag; + struct wl_list queue_link; /**< in struct wl_event_queue::proxy_list */ }; struct wl_event_queue { struct wl_list event_list; + struct wl_list proxy_list; /**< struct wl_proxy::queue_link */ struct wl_display *display; }; @@ -108,47 +110,183 @@ struct wl_display { uint32_t read_serial; pthread_cond_t reader_cond; - struct wl_list protocol_loggers; + struct wl_list observers; }; /** \endcond */ -struct wl_protocol_logger_client { +struct wl_client_observer { struct wl_list link; - wl_protocol_logger_client_func_t func; + struct wl_display *display; + wl_client_message_observer_func_t func; void *user_data; }; static int debug_client = 0; +/** + * This helper function adjusts the closure arguments before they are logged. + * On the client, after the call to create_proxies(), NEW_ID arguments will + * point to a wl_proxy accessible via arg.o instead of being an int32 + * accessible by arg.n, which is what wl_closure_print() attempts to print. + * This helper transforms the argument back into an id, so wl_closure_print() + * doesn't need to handle that as a special case. + * + * \param closure closure to adjust + * \param send if this is closure is for a request + * + */ static void -log_closure(struct wl_closure *closure, struct wl_proxy* proxy, int send) +adjust_closure_args_for_logging(struct wl_closure *closure, bool send) +{ + int i; + struct argument_details arg; + const struct wl_proxy *proxy; + const char *signature = closure->message->signature; + + // No adjustment needed for a send. + if (send) + return; + + for (i = 0; i < closure->count; i++) { + signature = get_next_argument(signature, &arg); + + switch (arg.type) { + case 'n': + proxy = (struct wl_proxy *)closure->args[i].o; + closure->args[i].n = proxy ? proxy->object.id : 0; + break; + } + } +} + +/** + * Maps the \c discard_reason to a string suitable for logging. + * + * \param discarded_reason reason for discard + * \return A string describing the reason, or NULL. + * + */ +static const char * +get_discarded_reason_str( + enum wl_client_message_discarded_reason discarded_reason) +{ + switch (discarded_reason) { + case WL_CLIENT_MESSAGE_NOT_DISCARDED: + return NULL; + case WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH: + return "dead proxy on dispatch"; + case WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH: + return "no listener on dispatch"; + case WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL: + return "unknown id on demarshal"; + } + return NULL; +} + +/** + * This function helps log closures from the client, assuming logging is + * enabled. + * + * \param closure closure for the message + * \param proxy proxy for the message + * \param send true if this is closure is for a request + * \param discarded_reason reason if the message is being discarded, or + * WL_CLIENT_MESSAGE_NOT_DISCARDED + */ +static void +closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send, + enum wl_client_message_discarded_reason discarded_reason) { struct wl_display *display = proxy->display; - struct wl_protocol_logger_client *protocol_logger; - struct wl_protocol_logger_client_message message; + const char *discarded_reason_str; + struct wl_closure adjusted_closure = { 0 }; + + if (!debug_client && wl_list_empty(&display->observers)) + return; + + // Note: The real closure has extra data (referenced by its args + // immediately following the structure in memory, but we don't + // need to duplicate that. + memcpy(&adjusted_closure, closure, sizeof(struct wl_closure)); + + // Adjust the closure arguments. + adjust_closure_args_for_logging(&adjusted_closure, send); + + discarded_reason_str = get_discarded_reason_str(discarded_reason); if (debug_client) - wl_closure_print(closure, &proxy->object, send); + wl_closure_print(&adjusted_closure, &proxy->object, send, + discarded_reason_str); + + if (!wl_list_empty(&display->observers)) { + enum wl_client_message_type type = + send ? WL_CLIENT_MESSAGE_REQUEST + : WL_CLIENT_MESSAGE_EVENT; + struct wl_client_observer *observer; + struct wl_client_observed_message message; - if (!wl_list_empty(&display->protocol_loggers)) { message.proxy = proxy; - message.message_opcode = closure->opcode; - message.message = closure->message; - message.arguments_count = closure->count; - message.arguments = closure->args; - wl_list_for_each(protocol_logger, &display->protocol_loggers, - link) { - protocol_logger->func( - protocol_logger->user_data, - send ? WL_PROTOCOL_LOGGER_CLIENT_REQUEST : - WL_PROTOCOL_LOGGER_CLIENT_EVENT, - &message); + message.message_opcode = adjusted_closure.opcode; + message.message = adjusted_closure.message; + message.arguments_count = adjusted_closure.count; + message.arguments = adjusted_closure.args; + message.discarded_reason = discarded_reason; + message.discarded_reason_str = discarded_reason_str; + wl_list_for_each(observer, &display->observers, link) { + observer->func(observer->user_data, type, &message); } } } /** + * This function helps log unknown messages on the client, when logging is + * enabled. + * + * \param display current display + * \param zombie true if there was a zombie for the message target + * \param id id of the proxy this message was meant for + * \param opcode opcode from the message + * \param num_fds number of fd arguments for this message + * \param num_bytes byte size of this message + */ +static void +log_unknown_message(struct wl_display *display, bool zombie, uint32_t id, + int opcode, int num_fds, int num_bytes) +{ + char event_detail[100]; + struct wl_interface unknown_interface = { 0 }; + struct wl_proxy unknown_proxy = { 0 }; + struct wl_message unknown_message = { 0 }; + struct wl_closure unknown_closure = { 0 }; + + if (!debug_client && wl_list_empty(&display->observers)) + return; + + snprintf(event_detail, sizeof event_detail, + "[event %d, %d fds, %d bytes]", opcode, num_fds, num_bytes); + + unknown_interface.name = zombie ? "[zombie]" : "[unknown]"; + + unknown_proxy.object.interface = &unknown_interface; + unknown_proxy.object.id = id; + unknown_proxy.display = display; + unknown_proxy.refcount = -1; + unknown_proxy.flags = WL_PROXY_FLAG_WRAPPER; + + unknown_message.name = event_detail; + unknown_message.signature = ""; + unknown_message.types = NULL; + + unknown_closure.message = &unknown_message; + unknown_closure.opcode = opcode; + unknown_closure.proxy = &unknown_proxy; + + closure_log(&unknown_closure, &unknown_proxy, false, + WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL); +} + +/** * This helper function wakes up all threads that are * waiting for display->reader_cond (i. e. when reading is done, * canceled, or an error occurred) @@ -256,6 +394,7 @@ static void wl_event_queue_init(struct wl_event_queue *queue, struct wl_display *display) { wl_list_init(&queue->event_list); + wl_list_init(&queue->proxy_list); queue->display = display; } @@ -333,6 +472,27 @@ wl_event_queue_release(struct wl_event_queue *queue) { struct wl_closure *closure; + if (!wl_list_empty(&queue->proxy_list)) { + struct wl_proxy *proxy, *tmp; + + if (queue != &queue->display->default_queue) { + wl_log("warning: queue %p destroyed while proxies " + "still attached:\n", queue); + } + + wl_list_for_each_safe(proxy, tmp, &queue->proxy_list, + queue_link) { + if (queue != &queue->display->default_queue) { + wl_log(" %s@%u still attached\n", + proxy->object.interface->name, + proxy->object.id); + } + proxy->queue = NULL; + wl_list_remove(&proxy->queue_link); + wl_list_init(&proxy->queue_link); + } + } + while (!wl_list_empty(&queue->event_list)) { closure = wl_container_of(queue->event_list.next, closure, link); @@ -378,7 +538,7 @@ wl_display_create_queue(struct wl_display *display) { struct wl_event_queue *queue; - queue = malloc(sizeof *queue); + queue = zalloc(sizeof *queue); if (queue == NULL) return NULL; @@ -465,6 +625,12 @@ proxy_create(struct wl_proxy *factory, const struct wl_interface *interface, proxy->version = version; proxy->object.id = wl_map_insert_new(&display->objects, 0, proxy); + if (proxy->object.id == 0) { + free(proxy); + return NULL; + } + + wl_list_insert(&proxy->queue->proxy_list, &proxy->queue_link); return proxy; } @@ -520,7 +686,12 @@ wl_proxy_create_for_id(struct wl_proxy *factory, proxy->refcount = 1; proxy->version = factory->version; - wl_map_insert_at(&display->objects, 0, id, proxy); + if (wl_map_insert_at(&display->objects, 0, id, proxy) == -1) { + free(proxy); + return NULL; + } + + wl_list_insert(&proxy->queue->proxy_list, &proxy->queue_link); return proxy; } @@ -546,15 +717,33 @@ proxy_destroy(struct wl_proxy *proxy) proxy->flags |= WL_PROXY_FLAG_DESTROYED; + proxy->queue = NULL; + wl_list_remove(&proxy->queue_link); + wl_list_init(&proxy->queue_link); + wl_proxy_unref(proxy); } +static void +wl_proxy_destroy_caller_locks(struct wl_proxy *proxy) +{ + if (proxy->flags & WL_PROXY_FLAG_WRAPPER) + wl_abort("Tried to destroy wrapper with wl_proxy_destroy()\n"); + + proxy_destroy(proxy); +} + /** Destroy a proxy object * * \param proxy The proxy to be destroyed * * \c proxy must not be a proxy wrapper. * + * \note This function will abort in response to egregious + * errors, and will do so with the display lock held. This means + * SIGABRT handlers must not perform any actions that would + * attempt to take that lock, or a deadlock would occur. + * * \memberof wl_proxy */ WL_EXPORT void @@ -562,11 +751,10 @@ wl_proxy_destroy(struct wl_proxy *proxy) { struct wl_display *display = proxy->display; - if (proxy->flags & WL_PROXY_FLAG_WRAPPER) - wl_abort("Tried to destroy wrapper with wl_proxy_destroy()\n"); - pthread_mutex_lock(&display->mutex); - proxy_destroy(proxy); + + wl_proxy_destroy_caller_locks(proxy); + pthread_mutex_unlock(&display->mutex); } @@ -760,11 +948,93 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy, const struct wl_interface *interface, uint32_t version) { + return wl_proxy_marshal_array_flags(proxy, opcode, interface, version, 0, args); +} + +/** Prepare a request to be sent to the compositor + * + * \param proxy The proxy object + * \param opcode Opcode of the request to be sent + * \param interface The interface to use for the new proxy + * \param version The protocol object version of the new proxy + * \param flags Flags that modify marshalling behaviour + * \param ... Extra arguments for the given request + * \return A new wl_proxy for the new_id argument or NULL on error + * + * Translates the request given by opcode and the extra arguments into the + * wire format and write it to the connection buffer. + * + * For new-id arguments, this function will allocate a new wl_proxy + * and send the ID to the server. The new wl_proxy will be returned + * on success or NULL on error with errno set accordingly. The newly + * created proxy will have the version specified. + * + * The flag WL_MARSHAL_FLAG_DESTROY may be passed to ensure the proxy + * is destroyed atomically with the marshalling in order to prevent + * races that can occur if the display lock is dropped between the + * marshal and destroy operations. + * + * \note This should not normally be used by non-generated code. + * + * \memberof wl_proxy + */ +WL_EXPORT struct wl_proxy * +wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode, + const struct wl_interface *interface, uint32_t version, + uint32_t flags, ...) +{ + union wl_argument args[WL_CLOSURE_MAX_ARGS]; + va_list ap; + + va_start(ap, flags); + wl_argument_from_va_list(proxy->object.interface->methods[opcode].signature, + args, WL_CLOSURE_MAX_ARGS, ap); + va_end(ap); + + return wl_proxy_marshal_array_flags(proxy, opcode, interface, version, flags, args); +} + +/** Prepare a request to be sent to the compositor + * + * \param proxy The proxy object + * \param opcode Opcode of the request to be sent + * \param interface The interface to use for the new proxy + * \param version The protocol object version for the new proxy + * \param flags Flags that modify marshalling behaviour + * \param args Extra arguments for the given request + * + * Translates the request given by opcode and the extra arguments into the + * wire format and write it to the connection buffer. This version takes an + * array of the union type wl_argument. + * + * For new-id arguments, this function will allocate a new wl_proxy + * and send the ID to the server. The new wl_proxy will be returned + * on success or NULL on error with errno set accordingly. The newly + * created proxy will have the version specified. + * + * The flag WL_MARSHAL_FLAG_DESTROY may be passed to ensure the proxy + * is destroyed atomically with the marshalling in order to prevent + * races that can occur if the display lock is dropped between the + * marshal and destroy operations. + * + * \note This is intended to be used by language bindings and not in + * non-generated code. + * + * \sa wl_proxy_marshal_flags() + * + * \memberof wl_proxy + */ +WL_EXPORT struct wl_proxy * +wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, + const struct wl_interface *interface, uint32_t version, + uint32_t flags, union wl_argument *args) +{ struct wl_closure *closure; struct wl_proxy *new_proxy = NULL; const struct wl_message *message; + struct wl_display *disp = proxy->display; - pthread_mutex_lock(&proxy->display->mutex); + pthread_mutex_lock(&disp->mutex); message = &proxy->object.interface->methods[opcode]; if (interface) { @@ -786,7 +1056,7 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy, goto err_unlock; } - log_closure(closure, proxy, true); + closure_log(closure, proxy, true, WL_CLIENT_MESSAGE_NOT_DISCARDED); if (wl_closure_send(closure, proxy->display->connection)) { wl_log("Error sending request: %s\n", strerror(errno)); @@ -796,7 +1066,10 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy, wl_closure_destroy(closure); err_unlock: - pthread_mutex_unlock(&proxy->display->mutex); + if (flags & WL_MARSHAL_FLAG_DESTROY) + wl_proxy_destroy_caller_locks(proxy); + + pthread_mutex_unlock(&disp->mutex); return new_proxy; } @@ -1005,8 +1278,8 @@ connect_to_socket(const char *name) path_is_absolute = name[0] == '/'; runtime_dir = getenv("XDG_RUNTIME_DIR"); - if (!runtime_dir && !path_is_absolute) { - wl_log("error: XDG_RUNTIME_DIR not set in the environment.\n"); + if (((!runtime_dir || runtime_dir[0] != '/') && !path_is_absolute)) { + wl_log("error: XDG_RUNTIME_DIR is invalid or not set in the environment.\n"); /* to prevent programs reporting * "failed to create display: Success" */ errno = ENOENT; @@ -1090,13 +1363,18 @@ wl_display_connect_to_fd(int fd) pthread_mutex_init(&display->mutex, NULL); pthread_cond_init(&display->reader_cond, NULL); display->reader_count = 0; - wl_list_init(&display->protocol_loggers); + wl_list_init(&display->observers); - wl_map_insert_new(&display->objects, 0, NULL); + if (wl_map_insert_at(&display->objects, 0, 0, NULL) == -1) + goto err_connection; - display->proxy.object.interface = &wl_display_interface; display->proxy.object.id = wl_map_insert_new(&display->objects, 0, display); + + if (display->proxy.object.id == 0) + goto err_connection; + + display->proxy.object.interface = &wl_display_interface; display->proxy.display = display; display->proxy.object.implementation = (void(**)(void)) &display_listener; display->proxy.user_data = display; @@ -1183,7 +1461,9 @@ wl_display_connect(const char *name) errno = prev_errno; flags = fcntl(fd, F_GETFD); - if (flags != -1) + if (flags == -1 && errno == EBADF) + return NULL; + else if (flags != -1) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); unsetenv("WAYLAND_SOCKET"); } else { @@ -1199,8 +1479,9 @@ wl_display_connect(const char *name) * * \param display The display context object * - * Close the connection to \c display and free all resources associated - * with it. + * Close the connection to \c display. The \ref wl_proxy and + * \ref wl_event_queue objects need to be manually destroyed by the caller + * before disconnecting. * * \memberof wl_display */ @@ -1212,7 +1493,7 @@ wl_display_disconnect(struct wl_display *display) wl_map_release(&display->objects); wl_event_queue_release(&display->default_queue); wl_event_queue_release(&display->display_queue); - wl_list_remove(&display->protocol_loggers); + wl_list_remove(&display->observers); pthread_mutex_destroy(&display->mutex); pthread_cond_destroy(&display->reader_cond); close(display->fd); @@ -1394,6 +1675,7 @@ queue_event(struct wl_display *display, int len) struct wl_closure *closure; const struct wl_message *message; struct wl_event_queue *queue; + int num_zombie_fds; wl_connection_copy(display->connection, p, sizeof p); id = p[0]; @@ -1407,10 +1689,15 @@ queue_event(struct wl_display *display, int len) proxy = wl_map_lookup(&display->objects, id); if (!proxy || wl_object_is_zombie(&display->objects, id)) { struct wl_zombie *zombie = wl_map_lookup(&display->objects, id); + num_zombie_fds = (zombie && opcode < zombie->event_count) ? + zombie->fd_count[opcode] : 0; + + log_unknown_message(display, !!zombie, id, opcode, + num_zombie_fds, size); - if (zombie && zombie->fd_count[opcode]) + if (num_zombie_fds > 0) wl_connection_close_fds_in(display->connection, - zombie->fd_count[opcode]); + num_zombie_fds); wl_connection_consume(display->connection, size); return size; @@ -1446,6 +1733,9 @@ queue_event(struct wl_display *display, int len) else queue = proxy->queue; + if (!queue) + wl_abort("Tried to add event to destroyed queue\n"); + wl_list_insert(queue->event_list.prev, &closure->link); return size; @@ -1469,24 +1759,29 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue) proxy = closure->proxy; proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED); if (proxy_destroyed) { - destroy_queued_closure(closure); - return; - } - - pthread_mutex_unlock(&display->mutex); + closure_log(closure, proxy, false, + WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH); + } else if (proxy->dispatcher) { + closure_log(closure, proxy, false, + WL_CLIENT_MESSAGE_NOT_DISCARDED); - log_closure(closure, proxy, false); - - if (proxy->dispatcher) { + pthread_mutex_unlock(&display->mutex); wl_closure_dispatch(closure, proxy->dispatcher, &proxy->object, opcode); + pthread_mutex_lock(&display->mutex); } else if (proxy->object.implementation) { + closure_log(closure, proxy, false, + WL_CLIENT_MESSAGE_NOT_DISCARDED); + + pthread_mutex_unlock(&display->mutex); wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT, &proxy->object, opcode, proxy->user_data); + pthread_mutex_lock(&display->mutex); + } else { + closure_log(closure, proxy, false, + WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH); } - pthread_mutex_lock(&display->mutex); - destroy_queued_closure(closure); } @@ -2200,6 +2495,19 @@ wl_proxy_get_class(struct wl_proxy *proxy) * queued in \c queue from now. If queue is NULL, then the display's * default queue is set to the proxy. * + * In order to guarantee proper handing of all events which were queued + * before the queue change takes effect, it is required to dispatch the + * proxy's old event queue after setting a new event queue. + * + * This is particularly important for multi-threaded setups, where it is + * possible for events to be queued to the proxy's old queue from a + * different thread during the invocation of this function. + * + * To ensure that all events for a newly created proxy are dispatched + * on a particular queue, it is necessary to use a proxy wrapper if + * events are read and dispatched on more than one thread. See + * wl_proxy_create_wrapper() for more details. + * * \note By default, the queue set in proxy is the one inherited from parent. * * \sa wl_display_dispatch_queue() @@ -2209,10 +2517,20 @@ wl_proxy_get_class(struct wl_proxy *proxy) WL_EXPORT void wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue) { - if (queue) + pthread_mutex_lock(&proxy->display->mutex); + + wl_list_remove(&proxy->queue_link); + + if (queue) { + assert(proxy->display == queue->display); proxy->queue = queue; - else + } else { proxy->queue = &proxy->display->default_queue; + } + + wl_list_insert(&proxy->queue->proxy_list, &proxy->queue_link); + + pthread_mutex_unlock(&proxy->display->mutex); } /** Create a proxy wrapper for making queue assignments thread-safe @@ -2283,6 +2601,8 @@ wl_proxy_create_wrapper(void *proxy) wrapper->flags = WL_PROXY_FLAG_WRAPPER; wrapper->refcount = 1; + wl_list_insert(&wrapper->queue->proxy_list, &wrapper->queue_link); + pthread_mutex_unlock(&wrapped_proxy->display->mutex); return wrapper; @@ -2304,13 +2624,19 @@ wl_proxy_wrapper_destroy(void *proxy_wrapper) assert(wrapper->refcount == 1); + pthread_mutex_lock(&wrapper->display->mutex); + + wl_list_remove(&wrapper->queue_link); + + pthread_mutex_unlock(&wrapper->display->mutex); + free(wrapper); } /** Safely converts an object into its corresponding proxy * - * \param object The object to convert - * \return A corresponding proxy, or NULL on failure + * \param object object to get the proxy for + * \return A corresponding proxy, or NULL on failure. * * Safely converts an object into its corresponding proxy. * @@ -2335,59 +2661,63 @@ wl_log_set_handler_client(wl_log_func_t handler) wl_log_handler = handler; } -/** Adds a new protocol client logger. - * - * When a new protocol message arrives or is sent from the client - * all the protocol logger functions will be called, carrying the - * \a user_data pointer, the type of the message (request or - * event) and the actual message. - * The lifetime of the messages passed to the logger function ends - * when they return so the messages cannot be stored and accessed - * later. +/** Creates an client message observer. * - * \a errno is set on error. + * Note that the observer can potentially start receiving traffic immediately + * after being created, and even before this call returns. * - * \param display The display object - * \param func The function to call to log a new protocol message - * \param user_data The user data pointer to pass to \a func + * \param display client display to register with + * \param func function to call when client messages are observed + * \param user_data \c user_data pointer to pass to the observer * - * \return The protocol logger object on success, NULL on failure. + * \return The created observer, or NULL. * - * \sa wl_protocol_logger_client_destroy + * \sa wl_client_observer_destroy * * \memberof wl_display */ -WL_EXPORT struct wl_protocol_logger_client * -wl_display_add_protocol_logger_client(struct wl_display *display, - wl_protocol_logger_client_func_t func, - void *user_data) + +WL_EXPORT struct wl_client_observer * +wl_display_create_client_observer(struct wl_display *display, + wl_client_message_observer_func_t func, + void *user_data) { - struct wl_protocol_logger_client *logger; + struct wl_client_observer *observer; - logger = malloc(sizeof *logger); - if (!logger) + observer = malloc(sizeof *observer); + if (!observer) return NULL; - logger->func = func; - logger->user_data = user_data; - wl_list_insert(&display->protocol_loggers, &logger->link); + observer->display = display; + observer->func = func; + observer->user_data = user_data; + + pthread_mutex_lock(&display->mutex); + + wl_list_insert(&display->observers, &observer->link); + + pthread_mutex_unlock(&display->mutex); - return logger; + return observer; } -/** Destroys a protocol client logger. +/** Destroys a client message obsever. * - * This function destroys a protocol client logger and removes it from the - * display it was added to with \a wl_display_add_protocol_logger_client. - * The \a logger object becomes invalid after calling this function. + * This function destroys a client message observer, and removes it from the + * display it was added to with \c wl_display_create_client_observer. * - * \sa wl_display_add_protocol_logger_client + * \param observer observer to destroy. * - * \memberof wl_protocol_logger_client + * \memberof wl_client_observer */ WL_EXPORT void -wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger) +wl_client_observer_destroy(struct wl_client_observer *observer) { - wl_list_remove(&logger->link); - free(logger); + pthread_mutex_lock(&observer->display->mutex); + + wl_list_remove(&observer->link); + + pthread_mutex_unlock(&observer->display->mutex); + + free(observer); } diff --git a/src/wayland-client.pc.in b/src/wayland-client.pc.in deleted file mode 100644 index eef61da..0000000 --- a/src/wayland-client.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -datarootdir=@datarootdir@ -pkgdatadir=@datadir@/@PACKAGE@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Wayland Client -Description: Wayland client side library -Version: @WAYLAND_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -lwayland-client diff --git a/src/wayland-os.c b/src/wayland-os.c index 93b6f5f..a9066ca 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -25,14 +25,21 @@ #define _GNU_SOURCE +#include "../config.h" + #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> +#include <string.h> #include <sys/epoll.h> +#include <sys/mman.h> +#include <sys/un.h> +#ifdef HAVE_SYS_UCRED_H +#include <sys/ucred.h> +#endif -#include "../config.h" #include "wayland-os.h" static int @@ -72,8 +79,48 @@ wl_os_socket_cloexec(int domain, int type, int protocol) return set_cloexec_or_close(fd); } +#if defined(__FreeBSD__) +int +wl_os_socket_peercred(int sockfd, uid_t *uid, gid_t *gid, pid_t *pid) +{ + socklen_t len; + struct xucred ucred; + + len = sizeof(ucred); + if (getsockopt(sockfd, SOL_LOCAL, LOCAL_PEERCRED, &ucred, &len) < 0 || + ucred.cr_version != XUCRED_VERSION) + return -1; + *uid = ucred.cr_uid; + *gid = ucred.cr_gid; +#if HAVE_XUCRED_CR_PID + /* Since https://cgit.freebsd.org/src/commit/?id=c5afec6e895a */ + *pid = ucred.cr_pid; +#else + *pid = 0; +#endif + return 0; +} +#elif defined(SO_PEERCRED) +int +wl_os_socket_peercred(int sockfd, uid_t *uid, gid_t *gid, pid_t *pid) +{ + socklen_t len; + struct ucred ucred; + + len = sizeof(ucred); + if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) + return -1; + *uid = ucred.uid; + *gid = ucred.gid; + *pid = ucred.pid; + return 0; +} +#else +#error "Don't know how to read ucred on this platform" +#endif + int -wl_os_dupfd_cloexec(int fd, long minfd) +wl_os_dupfd_cloexec(int fd, int minfd) { int newfd; @@ -121,6 +168,15 @@ recvmsg_cloexec_fallback(int sockfd, struct msghdr *msg, int flags) ssize_t wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) { +#if HAVE_BROKEN_MSG_CMSG_CLOEXEC + /* + * FreeBSD had a broken implementation of MSG_CMSG_CLOEXEC between 2015 + * and 2021, so we have to use the non-MSG_CMSG_CLOEXEC fallback + * directly when compiling against a version that does not include the + * fix (https://cgit.freebsd.org/src/commit/?id=6ceacebdf52211). + */ +#pragma message("Using fallback directly since MSG_CMSG_CLOEXEC is broken.") +#else ssize_t len; len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC); @@ -128,7 +184,7 @@ wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) return len; if (errno != EINVAL) return -1; - +#endif return recvmsg_cloexec_fallback(sockfd, msg, flags); } @@ -165,3 +221,32 @@ wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) fd = accept(sockfd, addr, addrlen); return set_cloexec_or_close(fd); } + +/* + * Fallback function for operating systems that don't implement + * mremap(MREMAP_MAYMOVE). + */ +void * +wl_os_mremap_maymove(int fd, void *old_data, ssize_t *old_size, + ssize_t new_size, int prot, int flags) +{ + void *result; + + /* Make sure any pending write is flushed. */ + if (msync(old_data, *old_size, MS_SYNC) != 0) + return MAP_FAILED; + + /* We could try mapping a new block immediately after the current one + * with MAP_FIXED, however that is not guaranteed to work and breaks + * on CHERI-enabled architectures since the data pointer will still + * have the bounds of the previous allocation. + */ + result = mmap(NULL, new_size, prot, flags, fd, 0); + if (result == MAP_FAILED) + return MAP_FAILED; + + if (munmap(old_data, *old_size) == 0) + *old_size = 0; + + return result; +} diff --git a/src/wayland-os.h b/src/wayland-os.h index f51efaa..068fd2f 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -26,11 +26,17 @@ #ifndef WAYLAND_OS_H #define WAYLAND_OS_H +#include <sys/types.h> +#include <sys/socket.h> + int wl_os_socket_cloexec(int domain, int type, int protocol); int -wl_os_dupfd_cloexec(int fd, long minfd); +wl_os_socket_peercred(int sockfd, uid_t *uid, gid_t *gid, pid_t *pid); + +int +wl_os_dupfd_cloexec(int fd, int minfd); ssize_t wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); @@ -41,6 +47,10 @@ wl_os_epoll_create_cloexec(void); int wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +void * +wl_os_mremap_maymove(int fd, void *old_data, ssize_t *old_size, + ssize_t new_size, int prot, int flags); + /* * The following are for wayland-os.c and the unit tests. diff --git a/src/wayland-private.h b/src/wayland-private.h index 9bf8cb7..66fc78f 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -45,6 +45,7 @@ #define WL_MAP_SERVER_SIDE 0 #define WL_MAP_CLIENT_SIDE 1 #define WL_SERVER_ID_START 0xff000000 +#define WL_MAP_MAX_OBJECTS 0x00f00000 #define WL_CLOSURE_MAX_ARGS 20 struct wl_object { @@ -210,8 +211,8 @@ int wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection); void -wl_closure_print(struct wl_closure *closure, - struct wl_object *target, int send); +wl_closure_print(struct wl_closure *closure, struct wl_object *target, + bool send, const char *discarded_reason); void wl_closure_destroy(struct wl_closure *closure); diff --git a/src/wayland-scanner-uninstalled.pc.in b/src/wayland-scanner-uninstalled.pc.in deleted file mode 100644 index 4559799..0000000 --- a/src/wayland-scanner-uninstalled.pc.in +++ /dev/null @@ -1,6 +0,0 @@ -pkgdatadir=@abs_top_srcdir@ -wayland_scanner=@abs_top_builddir@/wayland-scanner - -Name: Wayland Scanner -Description: Wayland scanner (not installed) -Version: @PACKAGE_VERSION@ diff --git a/src/wayland-scanner.pc.in b/src/wayland-scanner.pc.in deleted file mode 100644 index 7b2a4c9..0000000 --- a/src/wayland-scanner.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -datarootdir=@datarootdir@ -pkgdatadir=@datadir@/@PACKAGE@ -wayland_scanner=@bindir@/wayland-scanner - -Name: Wayland Scanner -Description: Wayland scanner -Version: @WAYLAND_VERSION@ diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index e5f4e43..63c6a62 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -279,6 +279,16 @@ wl_display_set_global_filter(struct wl_display *display, const struct wl_interface * wl_global_get_interface(const struct wl_global *global); +uint32_t +wl_global_get_name(const struct wl_global *global, + const struct wl_client *client); + +uint32_t +wl_global_get_version(const struct wl_global *global); + +struct wl_display * +wl_global_get_display(const struct wl_global *global); + void * wl_global_get_user_data(const struct wl_global *global); @@ -324,6 +334,14 @@ struct wl_listener * wl_client_get_destroy_listener(struct wl_client *client, wl_notify_func_t notify); +void +wl_client_add_destroy_late_listener(struct wl_client *client, + struct wl_listener *listener); + +struct wl_listener * +wl_client_get_destroy_late_listener(struct wl_client *client, + wl_notify_func_t notify); + struct wl_resource * wl_client_get_object(struct wl_client *client, uint32_t id); @@ -478,6 +496,9 @@ wl_signal_emit(struct wl_signal *signal, void *data) l->notify(l, data); } +void +wl_signal_emit_mutable(struct wl_signal *signal, void *data); + typedef void (*wl_resource_destroy_func_t)(struct wl_resource *resource); /* diff --git a/src/wayland-server-uninstalled.pc.in b/src/wayland-server-uninstalled.pc.in deleted file mode 100644 index 6b6e603..0000000 --- a/src/wayland-server-uninstalled.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -libdir=@abs_builddir@/.libs -includedir=@abs_srcdir@ -protocoldir=@abs_top_builddir@/protocol - -Name: Wayland Server -Description: Server side implementation of the Wayland protocol (not installed) -Version: @PACKAGE_VERSION@ -Cflags: -I${includedir} -I${protocoldir} -Libs: -L${libdir} -lwayland-server diff --git a/src/wayland-server.c b/src/wayland-server.c index ca0d98d..468322d 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -31,7 +31,6 @@ #include <stddef.h> #include <stdio.h> #include <stdarg.h> -#include <stdbool.h> #include <errno.h> #include <string.h> #include <unistd.h> @@ -41,6 +40,7 @@ #include <assert.h> #include <sys/time.h> #include <fcntl.h> +#include <sys/eventfd.h> #include <sys/file.h> #include <sys/stat.h> @@ -79,7 +79,10 @@ struct wl_client { struct wl_list link; struct wl_map objects; struct wl_priv_signal destroy_signal; - struct ucred ucred; + struct wl_priv_signal destroy_late_signal; + pid_t pid; + uid_t uid; + gid_t gid; int error; struct wl_priv_signal resource_created_signal; }; @@ -88,7 +91,7 @@ struct wl_display { struct wl_event_loop *loop; int run; - uint32_t id; + uint32_t next_global_name; uint32_t serial; struct wl_list registry_resource_list; @@ -104,6 +107,9 @@ struct wl_display { wl_display_global_filter_func_t global_filter; void *global_filter_data; + + int terminate_efd; + struct wl_event_source *term_source; }; struct wl_global { @@ -151,7 +157,7 @@ log_closure(struct wl_resource *resource, struct wl_protocol_logger_message message; if (debug_server) - wl_closure_print(closure, object, send); + wl_closure_print(closure, object, send, NULL); if (!wl_list_empty(&display->protocol_loggers)) { message.resource = resource; @@ -315,7 +321,7 @@ wl_resource_post_error(struct wl_resource *resource, static void destroy_client_with_error(struct wl_client *client, const char *reason) { - wl_log("%s (pid %u)\n", reason, client->ucred.pid); + wl_log("%s (pid %u)\n", reason, client->pid); wl_client_destroy(client); } @@ -514,7 +520,6 @@ WL_EXPORT struct wl_client * wl_client_create(struct wl_display *display, int fd) { struct wl_client *client; - socklen_t len; client = zalloc(sizeof *client); if (client == NULL) @@ -529,9 +534,8 @@ wl_client_create(struct wl_display *display, int fd) if (!client->source) goto err_client; - len = sizeof client->ucred; - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, - &client->ucred, &len) < 0) + if (wl_os_socket_peercred(fd, &client->uid, &client->gid, + &client->pid) != 0) goto err_source; client->connection = wl_connection_create(fd); @@ -544,6 +548,7 @@ wl_client_create(struct wl_display *display, int fd) goto err_map; wl_priv_signal_init(&client->destroy_signal); + wl_priv_signal_init(&client->destroy_late_signal); if (bind_display(client, display) < 0) goto err_map; @@ -575,6 +580,9 @@ err_client: * SO_PEERCRED, on the client socket fd. All the pointers can be * NULL, if the caller is not interested in a particular ID. * + * Note, process IDs are subject to race conditions and are not a reliable way + * to identify a client. + * * Be aware that for clients that a compositor forks and execs and * then connects using socketpair(), this function will return the * credentials for the compositor. The credentials for the socketpair @@ -587,11 +595,11 @@ wl_client_get_credentials(struct wl_client *client, pid_t *pid, uid_t *uid, gid_t *gid) { if (pid) - *pid = client->ucred.pid; + *pid = client->pid; if (uid) - *uid = client->ucred.uid; + *uid = client->uid; if (gid) - *gid = client->ucred.gid; + *gid = client->gid; } /** Get the file descriptor for the client @@ -860,7 +868,7 @@ wl_resource_get_class(struct wl_resource *resource) /** Safely converts an object into its corresponding resource * - * \param object The object to convert + * \param object object to get the resource for * \return A corresponding resource, or NULL on failure * * Safely converts an object into its corresponding resource. @@ -880,6 +888,17 @@ wl_resource_from_object(struct wl_object *object) return wl_container_of(object, resource, object); } +/** + * Add a listener to be called at the beginning of wl_client destruction + * + * The listener provided will be called when wl_client destroy has begun, + * before any of that client's resources have been destroyed. + * + * There is no requirement to remove the link of the wl_listener when the + * signal is emitted. + * + * \memberof wl_client + */ WL_EXPORT void wl_client_add_destroy_listener(struct wl_client *client, struct wl_listener *listener) @@ -894,6 +913,32 @@ wl_client_get_destroy_listener(struct wl_client *client, return wl_priv_signal_get(&client->destroy_signal, notify); } +/** + * Add a listener to be called at the end of wl_client destruction + * + * The listener provided will be called when wl_client destroy is nearly + * complete, after all of that client's resources have been destroyed. + * + * There is no requirement to remove the link of the wl_listener when the + * signal is emitted. + * + * \memberof wl_client + * \since 1.22.0 + */ +WL_EXPORT void +wl_client_add_destroy_late_listener(struct wl_client *client, + struct wl_listener *listener) +{ + wl_priv_signal_add(&client->destroy_late_signal, listener); +} + +WL_EXPORT struct wl_listener * +wl_client_get_destroy_late_listener(struct wl_client *client, + wl_notify_func_t notify) +{ + return wl_priv_signal_get(&client->destroy_late_signal, notify); +} + WL_EXPORT void wl_client_destroy(struct wl_client *client) { @@ -906,6 +951,9 @@ wl_client_destroy(struct wl_client *client) wl_map_release(&client->objects); wl_event_source_remove(client->source); close(wl_connection_destroy(client->connection)); + + wl_priv_signal_final_emit(&client->destroy_late_signal, client); + wl_list_remove(&client->link); wl_list_remove(&client->resource_created_signal.listener_list); free(client); @@ -1053,6 +1101,16 @@ bind_display(struct wl_client *client, struct wl_display *display) return 0; } +static int +handle_display_terminate(int fd, uint32_t mask, void *data) { + uint64_t term_event; + + if (read(fd, &term_event, sizeof(term_event)) < 0 && errno != EAGAIN) + return -1; + + return 0; +} + /** Create Wayland display object. * * \return The Wayland display object. Null if failed to create @@ -1071,7 +1129,7 @@ wl_display_create(void) if (debug && (strstr(debug, "server") || strstr(debug, "1"))) debug_server = 1; - display = malloc(sizeof *display); + display = zalloc(sizeof *display); if (display == NULL) return NULL; @@ -1081,6 +1139,19 @@ wl_display_create(void) return NULL; } + display->terminate_efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (display->terminate_efd < 0) + goto err_eventfd; + + display->term_source = wl_event_loop_add_fd(display->loop, + display->terminate_efd, + WL_EVENT_READABLE, + handle_display_terminate, + NULL); + + if (display->term_source == NULL) + goto err_term_source; + wl_list_init(&display->global_list); wl_list_init(&display->socket_list); wl_list_init(&display->client_list); @@ -1090,7 +1161,7 @@ wl_display_create(void) wl_priv_signal_init(&display->destroy_signal); wl_priv_signal_init(&display->create_client_signal); - display->id = 1; + display->next_global_name = 1; display->serial = 0; display->global_filter = NULL; @@ -1099,6 +1170,13 @@ wl_display_create(void) wl_array_init(&display->additional_shm_formats); return display; + +err_term_source: + close(display->terminate_efd); +err_eventfd: + wl_event_loop_destroy(display->loop); + free(display); + return NULL; } static void @@ -1136,7 +1214,6 @@ wl_socket_alloc(void) /** Destroy Wayland display object. * * \param display The Wayland display object which should be destroyed. - * \return None. * * This function emits the wl_display destroy signal, releases * all the sockets added to this display, free's all the globals associated @@ -1158,6 +1235,10 @@ wl_display_destroy(struct wl_display *display) wl_list_for_each_safe(s, next, &display->socket_list, link) { wl_socket_destroy(s); } + + close(display->terminate_efd); + wl_event_source_remove(display->term_source); + wl_event_loop_destroy(display->loop); wl_list_for_each_safe(global, gnext, &display->global_list, link) @@ -1175,7 +1256,6 @@ wl_display_destroy(struct wl_display *display) * \param display The Wayland display object. * \param filter The global filter function. * \param data User data to be associated with the global filter. - * \return None. * * Set a filter for the wl_display to advertise or hide global objects * to clients. @@ -1190,6 +1270,10 @@ wl_display_destroy(struct wl_display *display) * Setting the filter NULL will result in all globals being * advertised to all clients. The default is no filter. * + * The filter should be installed before any client connects and should always + * take the same decision given a client and a global. Not doing so will result + * in inconsistent filtering and broken wl_registry event sequences. + * * \memberof wl_display */ WL_EXPORT void @@ -1223,12 +1307,17 @@ wl_global_create(struct wl_display *display, return NULL; } - global = malloc(sizeof *global); + if (display->next_global_name >= UINT32_MAX) { + wl_log("wl_global_create: ran out of global names\n"); + return NULL; + } + + global = zalloc(sizeof *global); if (global == NULL) return NULL; global->display = display; - global->name = display->id++; + global->name = display->next_global_name++; global->interface = interface; global->version = version; global->data = data; @@ -1237,11 +1326,12 @@ wl_global_create(struct wl_display *display, wl_list_insert(display->global_list.prev, &global->link); wl_list_for_each(resource, &display->registry_resource_list, link) - wl_resource_post_event(resource, - WL_REGISTRY_GLOBAL, - global->name, - global->interface->name, - global->version); + if (wl_global_is_visible(resource->client, global)) + wl_resource_post_event(resource, + WL_REGISTRY_GLOBAL, + global->name, + global->interface->name, + global->version); return global; } @@ -1279,8 +1369,9 @@ wl_global_remove(struct wl_global *global) global->name); wl_list_for_each(resource, &display->registry_resource_list, link) - wl_resource_post_event(resource, WL_REGISTRY_GLOBAL_REMOVE, - global->name); + if (wl_global_is_visible(resource->client, global)) + wl_resource_post_event(resource, WL_REGISTRY_GLOBAL_REMOVE, + global->name); global->removed = true; } @@ -1300,6 +1391,51 @@ wl_global_get_interface(const struct wl_global *global) return global->interface; } +/** Get the name of the global. + * + * \param global The global object. + * \param client Client for which to look up the global. + * \return The name of the global, or 0 if the global is not visible to the + * client. + * + * \memberof wl_global + * \since 1.22 + */ +WL_EXPORT uint32_t +wl_global_get_name(const struct wl_global *global, + const struct wl_client *client) +{ + return wl_global_is_visible(client, global) ? global->name : 0; +} + +/** Get the version of the given global. + * + * \param global The global object. + * \return The version advertised by the global. + * + * \memberof wl_global + * \since 1.21 + */ +WL_EXPORT uint32_t +wl_global_get_version(const struct wl_global *global) +{ + return global->version; +} + +/** Get the display object for the given global + * + * \param global The global object + * \return The display object the global is associated with. + * + * \memberof wl_global + * \since 1.20 + */ +WL_EXPORT struct wl_display * +wl_global_get_display(const struct wl_global *global) +{ + return global->display; +} + WL_EXPORT void * wl_global_get_user_data(const struct wl_global *global) { @@ -1360,7 +1496,13 @@ wl_display_get_event_loop(struct wl_display *display) WL_EXPORT void wl_display_terminate(struct wl_display *display) { + int ret; + uint64_t terminate = 1; + display->run = 0; + + ret = write(display->terminate_efd, &terminate, sizeof(terminate)); + assert (ret >= 0 || errno == EAGAIN); } WL_EXPORT void @@ -1508,8 +1650,9 @@ wl_socket_init_for_display_name(struct wl_socket *s, const char *name) if (name[0] != '/') { runtime_dir = getenv("XDG_RUNTIME_DIR"); - if (!runtime_dir) { - wl_log("error: XDG_RUNTIME_DIR not set in the environment\n"); + if (!runtime_dir || runtime_dir[0] != '/') { + wl_log("error: XDG_RUNTIME_DIR is invalid or not set in" + " the environment\n"); /* to prevent programs reporting * "failed to add socket: Success" */ @@ -1523,8 +1666,6 @@ wl_socket_init_for_display_name(struct wl_socket *s, const char *name) name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s%s%s", runtime_dir, separator, name) + 1; - s->display_name = (s->addr.sun_path + name_size - 1) - strlen(name); - assert(name_size > 0); if (name_size > (int)sizeof s->addr.sun_path) { wl_log("error: socket path \"%s%s%s\" plus null terminator" @@ -1536,6 +1677,8 @@ wl_socket_init_for_display_name(struct wl_socket *s, const char *name) return -1; } + s->display_name = (s->addr.sun_path + name_size - 1) - strlen(name); + return 0; } @@ -1576,7 +1719,7 @@ wl_display_add_socket_auto(struct wl_display *display) { struct wl_socket *s; int displayno = 0; - char display_name[16] = ""; + char display_name[20] = ""; /* A reasonable number of maximum default sockets. If * you need more than this, use the explicit add_socket API. */ @@ -1669,7 +1812,7 @@ wl_display_add_socket_fd(struct wl_display *display, int sock_fd) * * If the socket name is a relative path, the Unix socket will be created in * the directory pointed to by environment variable XDG_RUNTIME_DIR. If - * XDG_RUNTIME_DIR is not set, then this function fails and returns -1. + * XDG_RUNTIME_DIR is invalid or not set, then this function fails and returns -1. * * If the socket name is an absolute path, then it is used as-is for the * the Unix socket. @@ -1787,12 +1930,17 @@ wl_resource_create(struct wl_client *client, { struct wl_resource *resource; - resource = malloc(sizeof *resource); + resource = zalloc(sizeof *resource); if (resource == NULL) return NULL; - if (id == 0) + if (id == 0) { id = wl_map_insert_new(&client->objects, 0, NULL); + if (id == 0) { + free(resource); + return NULL; + } + } resource->object.id = id; resource->object.interface = interface; @@ -1808,9 +1956,11 @@ wl_resource_create(struct wl_client *client, resource->dispatcher = NULL; if (wl_map_insert_at(&client->objects, 0, id, resource) < 0) { - wl_resource_post_error(client->display_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "invalid new id %d", id); + if (errno == EINVAL) { + wl_resource_post_error(client->display_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid new id %d", id); + } free(resource); return NULL; } @@ -1853,7 +2003,7 @@ wl_display_add_protocol_logger(struct wl_display *display, { struct wl_protocol_logger *logger; - logger = malloc(sizeof *logger); + logger = zalloc(sizeof *logger); if (!logger) return NULL; @@ -2054,6 +2204,69 @@ wl_client_for_each_resource(struct wl_client *client, wl_map_for_each(&client->objects, resource_iterator_helper, &context); } +static void +handle_noop(struct wl_listener *listener, void *data) +{ + /* Do nothing */ +} + +/** Emits this signal, notifying all registered listeners. + * + * A safer version of wl_signal_emit() which can gracefully handle additions + * and deletions of any signal listener from within listener notification + * callbacks. + * + * Listeners deleted during a signal emission and which have not already been + * notified at the time of deletion are not notified by that emission. + * + * Listeners added (or readded) during signal emission are ignored by that + * emission. + * + * Note that repurposing a listener without explicitly removing it and readding + * it is not supported and can lead to unexpected behavior. + * + * \param signal The signal object that will emit the signal + * \param data The data that will be emitted with the signal + * + * \memberof wl_signal + * \since 1.20.90 + */ +WL_EXPORT void +wl_signal_emit_mutable(struct wl_signal *signal, void *data) +{ + struct wl_listener cursor; + struct wl_listener end; + + /* Add two special markers: one cursor and one end marker. This way, we + * know that we've already called listeners on the left of the cursor + * and that we don't want to call listeners on the right of the end + * marker. The 'it' function can remove any element it wants from the + * list without troubles. + * + * There was a previous attempt that used to steal the whole list of + * listeners but then that broke wl_signal_get(). + * + * wl_list_for_each_safe tries to be safe but it fails: it works fine + * if the current item is removed, but not if the next one is. */ + wl_list_insert(&signal->listener_list, &cursor.link); + cursor.notify = handle_noop; + wl_list_insert(signal->listener_list.prev, &end.link); + end.notify = handle_noop; + + while (cursor.link.next != &end.link) { + struct wl_list *pos = cursor.link.next; + struct wl_listener *l = wl_container_of(pos, l, link); + + wl_list_remove(&cursor.link); + wl_list_insert(pos, &cursor.link); + + l->notify(l, data); + } + + wl_list_remove(&cursor.link); + wl_list_remove(&end.link); +} + /** \cond INTERNAL */ /** Initialize a wl_priv_signal object @@ -2203,12 +2416,16 @@ wl_client_add_resource(struct wl_client *client, resource->object.id = wl_map_insert_new(&client->objects, WL_MAP_ENTRY_LEGACY, resource); + if (resource->object.id == 0) + return 0; } else if (wl_map_insert_at(&client->objects, WL_MAP_ENTRY_LEGACY, resource->object.id, resource) < 0) { - wl_resource_post_error(client->display_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "invalid new id %d", - resource->object.id); + if (errno == EINVAL) { + wl_resource_post_error(client->display_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid new id %d", + resource->object.id); + } return 0; } diff --git a/src/wayland-server.pc.in b/src/wayland-server.pc.in deleted file mode 100644 index 50dff53..0000000 --- a/src/wayland-server.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -datarootdir=@datarootdir@ -pkgdatadir=@datadir@/@PACKAGE@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Wayland Server -Description: Server side implementation of the Wayland protocol -Version: @WAYLAND_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -lwayland-server diff --git a/src/wayland-shm.c b/src/wayland-shm.c index b85e5a7..8fb657a 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -38,6 +38,7 @@ #include <stdint.h> #include <string.h> #include <sys/mman.h> +#include <sys/stat.h> #include <unistd.h> #include <assert.h> #include <signal.h> @@ -45,6 +46,7 @@ #include <errno.h> #include <fcntl.h> +#include "wayland-os.h" #include "wayland-util.h" #include "wayland-private.h" #include "wayland-server.h" @@ -61,11 +63,26 @@ struct wl_shm_pool { int internal_refcount; int external_refcount; char *data; - int32_t size; - int32_t new_size; + ssize_t size; + ssize_t new_size; +#ifndef MREMAP_MAYMOVE + /* The following three fields are needed for mremap() emulation. */ + int mmap_fd; + int mmap_flags; + int mmap_prot; +#endif bool sigbus_is_impossible; }; +/** \class wl_shm_buffer + * + * \brief A SHM buffer + * + * wl_shm_buffer provides a helper for accessing the contents of a wl_buffer + * resource created via the wl_shm interface. + * + * A wl_shm_buffer becomes invalid as soon as its #wl_resource is destroyed. + */ struct wl_shm_buffer { struct wl_resource *resource; int32_t width, height; @@ -81,6 +98,26 @@ struct wl_shm_sigbus_data { int fallback_mapping_used; }; +static void * +shm_pool_grow_mapping(struct wl_shm_pool *pool) +{ + void *data; + +#ifdef MREMAP_MAYMOVE + data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE); +#else + data = wl_os_mremap_maymove(pool->mmap_fd, pool->data, &pool->size, + pool->new_size, pool->mmap_prot, + pool->mmap_flags); + if (pool->size != 0 && pool->resource != NULL) { + wl_resource_post_error(pool->resource, + WL_SHM_ERROR_INVALID_FD, + "leaked old mapping"); + } +#endif + return data; +} + static void shm_pool_finish_resize(struct wl_shm_pool *pool) { @@ -89,11 +126,12 @@ shm_pool_finish_resize(struct wl_shm_pool *pool) if (pool->size == pool->new_size) return; - data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE); + data = shm_pool_grow_mapping(pool); if (data == MAP_FAILED) { - wl_resource_post_error(pool->resource, - WL_SHM_ERROR_INVALID_FD, - "failed mremap"); + if (pool->resource != NULL) + wl_resource_post_error(pool->resource, + WL_SHM_ERROR_INVALID_FD, + "failed mremap"); return; } @@ -106,16 +144,21 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) { if (external) { pool->external_refcount--; + assert(pool->external_refcount >= 0); if (pool->external_refcount == 0) shm_pool_finish_resize(pool); } else { pool->internal_refcount--; + assert(pool->internal_refcount >= 0); } - if (pool->internal_refcount + pool->external_refcount) + if (pool->internal_refcount + pool->external_refcount > 0) return; munmap(pool->data, pool->size); +#ifndef MREMAP_MAYMOVE + close(pool->mmap_fd); +#endif free(pool); } @@ -124,8 +167,7 @@ destroy_buffer(struct wl_resource *resource) { struct wl_shm_buffer *buffer = wl_resource_get_user_data(resource); - if (buffer->pool) - shm_pool_unref(buffer->pool, false); + shm_pool_unref(buffer->pool, false); free(buffer); } @@ -177,7 +219,7 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, } if (offset < 0 || width <= 0 || height <= 0 || stride < width || - INT32_MAX / stride <= height || + INT32_MAX / stride < height || offset > pool->size - stride * height) { wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE, @@ -186,7 +228,7 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, return; } - buffer = malloc(sizeof *buffer); + buffer = zalloc(sizeof *buffer); if (buffer == NULL) { wl_client_post_no_memory(client); return; @@ -219,6 +261,7 @@ destroy_pool(struct wl_resource *resource) { struct wl_shm_pool *pool = wl_resource_get_user_data(resource); + pool->resource = NULL; shm_pool_unref(pool, false); } @@ -263,7 +306,10 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, uint32_t id, int fd, int32_t size) { struct wl_shm_pool *pool; + struct stat statbuf; int seals; + int prot; + int flags; if (size <= 0) { wl_resource_post_error(resource, @@ -272,7 +318,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, goto err_close; } - pool = malloc(sizeof *pool); + pool = zalloc(sizeof *pool); if (pool == NULL) { wl_client_post_no_memory(client); goto err_close; @@ -282,7 +328,11 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, seals = fcntl(fd, F_GET_SEALS); if (seals == -1) seals = 0; - pool->sigbus_is_impossible = (seals & F_SEAL_SHRINK) ? true : false; + + if ((seals & F_SEAL_SHRINK) && fstat(fd, &statbuf) >= 0) + pool->sigbus_is_impossible = statbuf.st_size >= size; + else + pool->sigbus_is_impossible = false; #else pool->sigbus_is_impossible = false; #endif @@ -291,17 +341,23 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, pool->external_refcount = 0; pool->size = size; pool->new_size = size; - pool->data = mmap(NULL, size, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + prot = PROT_READ | PROT_WRITE; + flags = MAP_SHARED; + pool->data = mmap(NULL, size, prot, flags, fd, 0); if (pool->data == MAP_FAILED) { - wl_resource_post_error(resource, - WL_SHM_ERROR_INVALID_FD, + wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FD, "failed mmap fd %d: %s", fd, strerror(errno)); goto err_free; } +#ifndef MREMAP_MAYMOVE + /* We may need to keep the fd, prot and flags to emulate mremap(). */ + pool->mmap_fd = fd; + pool->mmap_prot = prot; + pool->mmap_flags = flags; +#else close(fd); - +#endif pool->resource = wl_resource_create(client, &wl_shm_pool_interface, 1, id); if (!pool->resource) { @@ -400,11 +456,6 @@ wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer) WL_EXPORT void * wl_shm_buffer_get_data(struct wl_shm_buffer *buffer) { - assert(buffer->pool); - - if (!buffer->pool) - return NULL; - if (buffer->pool->external_refcount && (buffer->pool->size != buffer->pool->new_size)) wl_log("Buffer address requested when its parent pool " @@ -511,10 +562,8 @@ sigbus_handler(int signum, siginfo_t *info, void *context) sigbus_data->fallback_mapping_used = 1; /* This should replace the previous mapping */ - if (mmap(pool->data, pool->size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, - 0, 0) == (void *) -1) { + if (mmap(pool->data, pool->size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, 0, 0) == MAP_FAILED) { reraise_sigbus(); return; } diff --git a/src/wayland-util.c b/src/wayland-util.c index d5973bf..bb2a183 100644 --- a/src/wayland-util.c +++ b/src/wayland-util.c @@ -24,6 +24,7 @@ * SOFTWARE. */ +#include <errno.h> #include <stdlib.h> #include <stdint.h> #include <stdio.h> @@ -147,7 +148,9 @@ wl_array_copy(struct wl_array *array, struct wl_array *source) array->size = source->size; } - memcpy(array->data, source->data, source->size); + if (source->size > 0) + memcpy(array->data, source->data, source->size); + return 0; } @@ -195,6 +198,7 @@ wl_map_insert_new(struct wl_map *map, uint32_t flags, void *data) union map_entry *start, *entry; struct wl_array *entries; uint32_t base; + uint32_t count; if (map->side == WL_MAP_CLIENT_SIDE) { entries = &map->client_entries; @@ -215,10 +219,26 @@ wl_map_insert_new(struct wl_map *map, uint32_t flags, void *data) start = entries->data; } + /* wl_array only grows, so if we have too many objects at + * this point there's no way to clean up. We could be more + * pro-active about trying to avoid this allocation, but + * it doesn't really matter because at this point there is + * nothing to be done but disconnect the client and delete + * the whole array either way. + */ + count = entry - start; + if (count > WL_MAP_MAX_OBJECTS) { + /* entry->data is freshly malloced garbage, so we'd + * better make it a NULL so wl_map_for_each doesn't + * dereference it later. */ + entry->data = NULL; + errno = ENOSPC; + return 0; + } entry->data = data; entry->next |= (flags & 0x1) << 1; - return (entry - start) + base; + return count + base; } int @@ -235,12 +255,21 @@ wl_map_insert_at(struct wl_map *map, uint32_t flags, uint32_t i, void *data) i -= WL_SERVER_ID_START; } + if (i > WL_MAP_MAX_OBJECTS) { + errno = ENOSPC; + return -1; + } + count = entries->size / sizeof *start; - if (count < i) + if (count < i) { + errno = EINVAL; return -1; + } - if (count == i) - wl_array_add(entries, sizeof *start); + if (count == i) { + if (!wl_array_add(entries, sizeof *start)) + return -1; + } start = entries->data; start[i].data = data; @@ -257,30 +286,43 @@ wl_map_reserve_new(struct wl_map *map, uint32_t i) struct wl_array *entries; if (i < WL_SERVER_ID_START) { - if (map->side == WL_MAP_CLIENT_SIDE) + if (map->side == WL_MAP_CLIENT_SIDE) { + errno = EINVAL; return -1; + } entries = &map->client_entries; } else { - if (map->side == WL_MAP_SERVER_SIDE) + if (map->side == WL_MAP_SERVER_SIDE) { + errno = EINVAL; return -1; + } entries = &map->server_entries; i -= WL_SERVER_ID_START; } - count = entries->size / sizeof *start; + if (i > WL_MAP_MAX_OBJECTS) { + errno = ENOSPC; + return -1; + } - if (count < i) + count = entries->size / sizeof *start; + if (count < i) { + errno = EINVAL; return -1; + } if (count == i) { - wl_array_add(entries, sizeof *start); + if (!wl_array_add(entries, sizeof *start)) + return -1; + start = entries->data; start[i].data = NULL; } else { start = entries->data; if (start[i].data != NULL) { + errno = EINVAL; return -1; } } @@ -361,18 +403,21 @@ wl_map_lookup_flags(struct wl_map *map, uint32_t i) static enum wl_iterator_result for_each_helper(struct wl_array *entries, wl_iterator_func_t func, void *data) { - union map_entry *start, *end, *p; enum wl_iterator_result ret = WL_ITERATOR_CONTINUE; + union map_entry entry, *start; + size_t count; - start = entries->data; - end = (union map_entry *) ((char *) entries->data + entries->size); + start = (union map_entry *) entries->data; + count = entries->size / sizeof(union map_entry); - for (p = start; p < end; p++) - if (p->data && !map_entry_is_free(*p)) { - ret = func(map_entry_get_data(*p), data, map_entry_get_flags(*p)); + for (size_t idx = 0; idx < count; idx++) { + entry = start[idx]; + if (entry.data && !map_entry_is_free(entry)) { + ret = func(map_entry_get_data(entry), data, map_entry_get_flags(entry)); if (ret != WL_ITERATOR_CONTINUE) break; } + } return ret; } diff --git a/src/wayland-util.h b/src/wayland-util.h index 7997778..b4cdcfa 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -118,7 +118,7 @@ struct wl_object; * * `n`: new_id * * `a`: array * * `h`: fd - * * `?`: following argument is nullable + * * `?`: following argument (`o` or `s`) is nullable * * While demarshaling primitive arguments is straightforward, when demarshaling * messages containing `object` or `new_id` arguments, the protocol @@ -182,7 +182,7 @@ struct wl_message { * For example, consider a protocol interface `foo`, marked as version `1`, with * two requests and one event. * - * \code + * \code{.xml} * <interface name="foo" version="1"> * <request name="a"></request> * <request name="b"></request> @@ -707,17 +707,17 @@ union wl_argument { * corresponding to the callback. The final argument is an array of arguments * received from the other process via the wire protocol. * - * \param "const void *" Dispatcher-specific implementation data - * \param "void *" Callback invocation target (wl_proxy or `wl_resource`) - * \param uint32_t Callback opcode - * \param "const struct wl_message *" Callback message signature - * \param "union wl_argument *" Array of received arguments + * \param user_data Dispatcher-specific implementation data + * \param target Callback invocation target (wl_proxy or `wl_resource`) + * \param opcode Callback opcode + * \param msg Callback message signature + * \param args Array of received arguments * * \return 0 on success, or -1 on failure */ -typedef int (*wl_dispatcher_func_t)(const void *, void *, uint32_t, - const struct wl_message *, - union wl_argument *); +typedef int (*wl_dispatcher_func_t)(const void *user_data, void *target, + uint32_t opcode, const struct wl_message *msg, + union wl_argument *args); /** * Log function type alias @@ -736,14 +736,14 @@ typedef int (*wl_dispatcher_func_t)(const void *, void *, uint32_t, * \note Take care to not confuse this with `wl_protocol_logger_func_t`, which * is a specific server-side logger for requests and events. * - * \param "const char *" String to write to the log, containing optional format - * specifiers - * \param "va_list" Variable argument list + * \param fmt String to write to the log, containing optional format + * specifiers + * \param args Variable argument list * * \sa wl_log_set_handler_client * \sa wl_log_set_handler_server */ -typedef void (*wl_log_func_t)(const char *, va_list) WL_PRINTF(1, 0); +typedef void (*wl_log_func_t)(const char *fmt, va_list args) WL_PRINTF(1, 0); /** * Return value of an iterator function diff --git a/src/wayland-version.h b/src/wayland-version.h index eb33462..34e860e 100644 --- a/src/wayland-version.h +++ b/src/wayland-version.h @@ -27,8 +27,8 @@ #define WAYLAND_VERSION_H #define WAYLAND_VERSION_MAJOR 1 -#define WAYLAND_VERSION_MINOR 19 +#define WAYLAND_VERSION_MINOR 22 #define WAYLAND_VERSION_MICRO 0 -#define WAYLAND_VERSION "1.19.0" +#define WAYLAND_VERSION "1.22.0" #endif diff --git a/tests/client-test.c b/tests/client-test.c index 960cfa9..47be83f 100644 --- a/tests/client-test.c +++ b/tests/client-test.c @@ -40,7 +40,11 @@ struct client_destroy_listener { struct wl_listener listener; - int done; + bool done; + struct wl_listener late_listener; + bool late_done; + struct wl_listener resource_listener; + bool resource_done; }; static void @@ -49,13 +53,38 @@ client_destroy_notify(struct wl_listener *l, void *data) struct client_destroy_listener *listener = wl_container_of(l, listener, listener); - listener->done = 1; + listener->done = true; + assert(!listener->resource_done); + assert(!listener->late_done); +} + +static void +client_resource_destroy_notify(struct wl_listener *l, void *data) +{ + struct client_destroy_listener *listener = + wl_container_of(l, listener, resource_listener); + + assert(listener->done); + listener->resource_done = true; + assert(!listener->late_done); +} + +static void +client_late_destroy_notify(struct wl_listener *l, void *data) +{ + struct client_destroy_listener *listener = + wl_container_of(l, listener, late_listener); + + assert(listener->done); + assert(listener->resource_done); + listener->late_done = true; } TEST(client_destroy_listener) { struct wl_display *display; struct wl_client *client; + struct wl_resource *resource; struct client_destroy_listener a, b; int s[2]; @@ -65,23 +94,48 @@ TEST(client_destroy_listener) client = wl_client_create(display, s[0]); assert(client); + resource = wl_resource_create(client, &wl_callback_interface, 1, 0); + assert(resource); + a.listener.notify = client_destroy_notify; - a.done = 0; + a.done = false; + a.resource_listener.notify = client_resource_destroy_notify; + a.resource_done = false; + a.late_listener.notify = client_late_destroy_notify; + a.late_done = false; wl_client_add_destroy_listener(client, &a.listener); + wl_resource_add_destroy_listener(resource, &a.resource_listener); + wl_client_add_destroy_late_listener(client, &a.late_listener); assert(wl_client_get_destroy_listener(client, client_destroy_notify) == &a.listener); + assert(wl_resource_get_destroy_listener(resource, client_resource_destroy_notify) == + &a.resource_listener); + assert(wl_client_get_destroy_late_listener(client, client_late_destroy_notify) == + &a.late_listener); b.listener.notify = client_destroy_notify; - b.done = 0; + b.done = false; + b.resource_listener.notify = client_resource_destroy_notify; + b.resource_done = false; + b.late_listener.notify = client_late_destroy_notify; + b.late_done = false; wl_client_add_destroy_listener(client, &b.listener); + wl_resource_add_destroy_listener(resource, &b.resource_listener); + wl_client_add_destroy_late_listener(client, &b.late_listener); wl_list_remove(&a.listener.link); + wl_list_remove(&a.resource_listener.link); + wl_list_remove(&a.late_listener.link); wl_client_destroy(client); assert(!a.done); + assert(!a.resource_done); + assert(!a.late_done); assert(b.done); + assert(b.resource_done); + assert(b.late_done); close(s[0]); close(s[1]); diff --git a/tests/compositor-introspection-test.c b/tests/compositor-introspection-test.c index 83194ce..064d253 100644 --- a/tests/compositor-introspection-test.c +++ b/tests/compositor-introspection-test.c @@ -40,7 +40,7 @@ static const char * require_xdg_runtime_dir(void) { char *val = getenv("XDG_RUNTIME_DIR"); - assert(val && "set $XDG_RUNTIME_DIR to run this test"); + assert(val && val[0] == '/' && "set $XDG_RUNTIME_DIR to run this test"); return val; } diff --git a/tests/connection-test.c b/tests/connection-test.c index c04845b..9762e0d 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -245,9 +245,6 @@ TEST(connection_marshal) marshal(&data, "n", 12, &object); assert(data.buffer[2] == object.id); - marshal(&data, "?n", 12, NULL); - assert(data.buffer[2] == 0); - array.data = (void *) text; array.size = sizeof text; marshal(&data, "a", 20, &array); @@ -305,7 +302,6 @@ TEST(connection_marshal_nullables) { struct marshal_data data; struct wl_object object; - struct wl_array array; const char text[] = "curry"; setup_marshal_data(&data); @@ -317,9 +313,6 @@ TEST(connection_marshal_nullables) marshal(&data, "?o", 12, NULL); assert(data.buffer[2] == 0); - marshal(&data, "?a", 12, NULL); - assert(data.buffer[2] == 0); - marshal(&data, "?s", 12, NULL); assert(data.buffer[2] == 0); @@ -327,12 +320,6 @@ TEST(connection_marshal_nullables) marshal(&data, "?o", 12, &object); assert(data.buffer[2] == object.id); - array.data = (void *) text; - array.size = sizeof text; - marshal(&data, "?a", 20, &array); - assert(data.buffer[2] == array.size); - assert(memcmp(&data.buffer[3], text, array.size) == 0); - marshal(&data, "?s", 20, text); assert(data.buffer[2] == sizeof text); assert(strcmp((char *) &data.buffer[3], text) == 0); @@ -394,7 +381,7 @@ demarshal(struct marshal_data *data, const char *format, struct wl_closure *closure; struct wl_map objects; struct wl_object object = { NULL, &func, 0 }; - int size = msg[1]; + int size = msg[1] >> 16; assert(write(data->s[1], msg, size) == size); assert(wl_connection_read(data->read_connection) == size); @@ -417,39 +404,41 @@ TEST(connection_demarshal) data.value.u = 8000; msg[0] = 400200; /* object id */ - msg[1] = 12; /* size = 12, opcode = 0 */ + msg[1] = 12 << 16; /* size = 12, opcode = 0 */ msg[2] = data.value.u; demarshal(&data, "u", msg, (void *) validate_demarshal_u); data.value.i = -557799; msg[0] = 400200; - msg[1] = 12; + msg[1] = 12 << 16; msg[2] = data.value.i; demarshal(&data, "i", msg, (void *) validate_demarshal_i); data.value.s = "superdude"; msg[0] = 400200; - msg[1] = 24; + msg[1] = 24 << 16; msg[2] = 10; + msg[3 + msg[2]/4] = 0; memcpy(&msg[3], data.value.s, msg[2]); demarshal(&data, "s", msg, (void *) validate_demarshal_s); data.value.s = "superdude"; msg[0] = 400200; - msg[1] = 24; + msg[1] = 24 << 16; msg[2] = 10; + msg[3 + msg[2]/4] = 0; memcpy(&msg[3], data.value.s, msg[2]); demarshal(&data, "?s", msg, (void *) validate_demarshal_s); data.value.i = wl_fixed_from_double(-90000.2390); msg[0] = 400200; - msg[1] = 12; + msg[1] = 12 << 16; msg[2] = data.value.i; demarshal(&data, "f", msg, (void *) validate_demarshal_f); data.value.s = NULL; msg[0] = 400200; - msg[1] = 12; + msg[1] = 12 << 16; msg[2] = 0; demarshal(&data, "?s", msg, (void *) validate_demarshal_s); @@ -553,6 +542,24 @@ expected_fail_demarshal(struct marshal_data *data, const char *format, assert(errno == expected_error); } +TEST(connection_demarshal_null_strings) +{ + struct marshal_data data; + uint32_t msg[3]; + + setup_marshal_data(&data); + + data.value.s = NULL; + msg[0] = 400200; /* object id */ + msg[1] = 12 << 16; /* size = 12, opcode = 0 */ + msg[2] = 0; /* string length = 0 */ + demarshal(&data, "?s", msg, (void *) validate_demarshal_s); + + expected_fail_demarshal(&data, "s", msg, EINVAL); + + release_marshal_data(&data); +} + /* These tests are verifying that the demarshaling code will gracefully handle * clients lying about string and array lengths and giving values near * UINT32_MAX. Before fixes f7fdface and f5b9e3b9 this test would crash on diff --git a/tests/data/example-client.h b/tests/data/example-client.h index d421af9..a14b5e0 100644 --- a/tests/data/example-client.h +++ b/tests/data/example-client.h @@ -1011,8 +1011,8 @@ wl_display_sync(struct wl_display *wl_display) { struct wl_proxy *callback; - callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display, - WL_DISPLAY_SYNC, &wl_callback_interface, NULL); + callback = wl_proxy_marshal_flags((struct wl_proxy *) wl_display, + WL_DISPLAY_SYNC, &wl_callback_interface, wl_proxy_get_version((struct wl_proxy *) wl_display), 0, NULL); return (struct wl_callback *) callback; } @@ -1029,8 +1029,8 @@ wl_display_get_registry(struct wl_display *wl_display) { struct wl_proxy *registry; - registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display, - WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL); + registry = wl_proxy_marshal_flags((struct wl_proxy *) wl_display, + WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, wl_proxy_get_version((struct wl_proxy *) wl_display), 0, NULL); return (struct wl_registry *) registry; } @@ -1142,8 +1142,8 @@ wl_registry_bind(struct wl_registry *wl_registry, uint32_t name, const struct wl { struct wl_proxy *id; - id = wl_proxy_marshal_constructor_versioned((struct wl_proxy *) wl_registry, - WL_REGISTRY_BIND, interface, version, name, interface->name, version, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_registry, + WL_REGISTRY_BIND, interface, version, 0, name, interface->name, version, NULL); return (void *) id; } @@ -1258,8 +1258,8 @@ wl_compositor_create_surface(struct wl_compositor *wl_compositor) { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor, - WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_compositor, + WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_compositor), 0, NULL); return (struct wl_surface *) id; } @@ -1274,8 +1274,8 @@ wl_compositor_create_region(struct wl_compositor *wl_compositor) { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor, - WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_compositor, + WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, wl_proxy_get_version((struct wl_proxy *) wl_compositor), 0, NULL); return (struct wl_region *) id; } @@ -1338,8 +1338,8 @@ wl_shm_pool_create_buffer(struct wl_shm_pool *wl_shm_pool, int32_t offset, int32 { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm_pool, - WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, NULL, offset, width, height, stride, format); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool, + WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), 0, NULL, offset, width, height, stride, format); return (struct wl_buffer *) id; } @@ -1356,10 +1356,8 @@ wl_shm_pool_create_buffer(struct wl_shm_pool *wl_shm_pool, int32_t offset, int32 static inline void wl_shm_pool_destroy(struct wl_shm_pool *wl_shm_pool) { - wl_proxy_marshal((struct wl_proxy *) wl_shm_pool, - WL_SHM_POOL_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_shm_pool); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool, + WL_SHM_POOL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), WL_MARSHAL_FLAG_DESTROY); } /** @@ -1373,8 +1371,8 @@ wl_shm_pool_destroy(struct wl_shm_pool *wl_shm_pool) static inline void wl_shm_pool_resize(struct wl_shm_pool *wl_shm_pool, int32_t size) { - wl_proxy_marshal((struct wl_proxy *) wl_shm_pool, - WL_SHM_POOL_RESIZE, size); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool, + WL_SHM_POOL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), 0, size); } #ifndef WL_SHM_ERROR_ENUM @@ -1734,8 +1732,8 @@ wl_shm_create_pool(struct wl_shm *wl_shm, int32_t fd, int32_t size) { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm, - WL_SHM_CREATE_POOL, &wl_shm_pool_interface, NULL, fd, size); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shm, + WL_SHM_CREATE_POOL, &wl_shm_pool_interface, wl_proxy_get_version((struct wl_proxy *) wl_shm), 0, NULL, fd, size); return (struct wl_shm_pool *) id; } @@ -1819,10 +1817,8 @@ wl_buffer_get_version(struct wl_buffer *wl_buffer) static inline void wl_buffer_destroy(struct wl_buffer *wl_buffer) { - wl_proxy_marshal((struct wl_proxy *) wl_buffer, - WL_BUFFER_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_buffer); + wl_proxy_marshal_flags((struct wl_proxy *) wl_buffer, + WL_BUFFER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_buffer), WL_MARSHAL_FLAG_DESTROY); } #ifndef WL_DATA_OFFER_ERROR_ENUM @@ -2015,8 +2011,8 @@ wl_data_offer_get_version(struct wl_data_offer *wl_data_offer) static inline void wl_data_offer_accept(struct wl_data_offer *wl_data_offer, uint32_t serial, const char *mime_type) { - wl_proxy_marshal((struct wl_proxy *) wl_data_offer, - WL_DATA_OFFER_ACCEPT, serial, mime_type); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer, + WL_DATA_OFFER_ACCEPT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, serial, mime_type); } /** @@ -2041,8 +2037,8 @@ wl_data_offer_accept(struct wl_data_offer *wl_data_offer, uint32_t serial, const static inline void wl_data_offer_receive(struct wl_data_offer *wl_data_offer, const char *mime_type, int32_t fd) { - wl_proxy_marshal((struct wl_proxy *) wl_data_offer, - WL_DATA_OFFER_RECEIVE, mime_type, fd); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer, + WL_DATA_OFFER_RECEIVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, mime_type, fd); } /** @@ -2053,10 +2049,8 @@ wl_data_offer_receive(struct wl_data_offer *wl_data_offer, const char *mime_type static inline void wl_data_offer_destroy(struct wl_data_offer *wl_data_offer) { - wl_proxy_marshal((struct wl_proxy *) wl_data_offer, - WL_DATA_OFFER_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_data_offer); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer, + WL_DATA_OFFER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), WL_MARSHAL_FLAG_DESTROY); } /** @@ -2077,8 +2071,8 @@ wl_data_offer_destroy(struct wl_data_offer *wl_data_offer) static inline void wl_data_offer_finish(struct wl_data_offer *wl_data_offer) { - wl_proxy_marshal((struct wl_proxy *) wl_data_offer, - WL_DATA_OFFER_FINISH); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer, + WL_DATA_OFFER_FINISH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0); } /** @@ -2119,8 +2113,8 @@ wl_data_offer_finish(struct wl_data_offer *wl_data_offer) static inline void wl_data_offer_set_actions(struct wl_data_offer *wl_data_offer, uint32_t dnd_actions, uint32_t preferred_action) { - wl_proxy_marshal((struct wl_proxy *) wl_data_offer, - WL_DATA_OFFER_SET_ACTIONS, dnd_actions, preferred_action); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer, + WL_DATA_OFFER_SET_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, dnd_actions, preferred_action); } #ifndef WL_DATA_SOURCE_ERROR_ENUM @@ -2344,8 +2338,8 @@ wl_data_source_get_version(struct wl_data_source *wl_data_source) static inline void wl_data_source_offer(struct wl_data_source *wl_data_source, const char *mime_type) { - wl_proxy_marshal((struct wl_proxy *) wl_data_source, - WL_DATA_SOURCE_OFFER, mime_type); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source, + WL_DATA_SOURCE_OFFER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), 0, mime_type); } /** @@ -2356,10 +2350,8 @@ wl_data_source_offer(struct wl_data_source *wl_data_source, const char *mime_typ static inline void wl_data_source_destroy(struct wl_data_source *wl_data_source) { - wl_proxy_marshal((struct wl_proxy *) wl_data_source, - WL_DATA_SOURCE_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_data_source); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source, + WL_DATA_SOURCE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), WL_MARSHAL_FLAG_DESTROY); } /** @@ -2382,8 +2374,8 @@ wl_data_source_destroy(struct wl_data_source *wl_data_source) static inline void wl_data_source_set_actions(struct wl_data_source *wl_data_source, uint32_t dnd_actions) { - wl_proxy_marshal((struct wl_proxy *) wl_data_source, - WL_DATA_SOURCE_SET_ACTIONS, dnd_actions); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source, + WL_DATA_SOURCE_SET_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), 0, dnd_actions); } #ifndef WL_DATA_DEVICE_ERROR_ENUM @@ -2408,7 +2400,7 @@ struct wl_data_device_listener { * which will subsequently be used in either the data_device.enter * event (for drag-and-drop) or the data_device.selection event * (for selections). Immediately following the - * data_device_data_offer event, the new data_offer object will + * data_device.data_offer event, the new data_offer object will * send out data_offer.offer events to describe the mime types it * offers. * @param id the new data_offer object @@ -2614,8 +2606,8 @@ wl_data_device_destroy(struct wl_data_device *wl_data_device) static inline void wl_data_device_start_drag(struct wl_data_device *wl_data_device, struct wl_data_source *source, struct wl_surface *origin, struct wl_surface *icon, uint32_t serial) { - wl_proxy_marshal((struct wl_proxy *) wl_data_device, - WL_DATA_DEVICE_START_DRAG, source, origin, icon, serial); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device, + WL_DATA_DEVICE_START_DRAG, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), 0, source, origin, icon, serial); } /** @@ -2629,8 +2621,8 @@ wl_data_device_start_drag(struct wl_data_device *wl_data_device, struct wl_data_ static inline void wl_data_device_set_selection(struct wl_data_device *wl_data_device, struct wl_data_source *source, uint32_t serial) { - wl_proxy_marshal((struct wl_proxy *) wl_data_device, - WL_DATA_DEVICE_SET_SELECTION, source, serial); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device, + WL_DATA_DEVICE_SET_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), 0, source, serial); } /** @@ -2641,10 +2633,8 @@ wl_data_device_set_selection(struct wl_data_device *wl_data_device, struct wl_da static inline void wl_data_device_release(struct wl_data_device *wl_data_device) { - wl_proxy_marshal((struct wl_proxy *) wl_data_device, - WL_DATA_DEVICE_RELEASE); - - wl_proxy_destroy((struct wl_proxy *) wl_data_device); + wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device, + WL_DATA_DEVICE_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), WL_MARSHAL_FLAG_DESTROY); } #ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM @@ -2747,8 +2737,8 @@ wl_data_device_manager_create_data_source(struct wl_data_device_manager *wl_data { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_data_device_manager, - WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE, &wl_data_source_interface, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device_manager, + WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE, &wl_data_source_interface, wl_proxy_get_version((struct wl_proxy *) wl_data_device_manager), 0, NULL); return (struct wl_data_source *) id; } @@ -2763,8 +2753,8 @@ wl_data_device_manager_get_data_device(struct wl_data_device_manager *wl_data_de { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_data_device_manager, - WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE, &wl_data_device_interface, NULL, seat); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device_manager, + WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE, &wl_data_device_interface, wl_proxy_get_version((struct wl_proxy *) wl_data_device_manager), 0, NULL, seat); return (struct wl_data_device *) id; } @@ -2828,8 +2818,8 @@ wl_shell_get_shell_surface(struct wl_shell *wl_shell, struct wl_surface *surface { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shell, - WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, NULL, surface); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shell, + WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_shell), 0, NULL, surface); return (struct wl_shell_surface *) id; } @@ -3099,8 +3089,8 @@ wl_shell_surface_destroy(struct wl_shell_surface *wl_shell_surface) static inline void wl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_PONG, serial); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, serial); } /** @@ -3115,8 +3105,8 @@ wl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial static inline void wl_shell_surface_move(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_MOVE, seat, serial); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial); } /** @@ -3131,8 +3121,8 @@ wl_shell_surface_move(struct wl_shell_surface *wl_shell_surface, struct wl_seat static inline void wl_shell_surface_resize(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, uint32_t edges) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_RESIZE, seat, serial, edges); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial, edges); } /** @@ -3145,8 +3135,8 @@ wl_shell_surface_resize(struct wl_shell_surface *wl_shell_surface, struct wl_sea static inline void wl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_TOPLEVEL); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_TOPLEVEL, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0); } /** @@ -3163,8 +3153,8 @@ wl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface) static inline void wl_shell_surface_set_transient(struct wl_shell_surface *wl_shell_surface, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_TRANSIENT, parent, x, y, flags); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_TRANSIENT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, parent, x, y, flags); } /** @@ -3207,8 +3197,8 @@ wl_shell_surface_set_transient(struct wl_shell_surface *wl_shell_surface, struct static inline void wl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint32_t method, uint32_t framerate, struct wl_output *output) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_FULLSCREEN, method, framerate, output); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, method, framerate, output); } /** @@ -3237,8 +3227,8 @@ wl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint3 static inline void wl_shell_surface_set_popup(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_POPUP, seat, serial, parent, x, y, flags); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_POPUP, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial, parent, x, y, flags); } /** @@ -3266,8 +3256,8 @@ wl_shell_surface_set_popup(struct wl_shell_surface *wl_shell_surface, struct wl_ static inline void wl_shell_surface_set_maximized(struct wl_shell_surface *wl_shell_surface, struct wl_output *output) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_MAXIMIZED, output); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, output); } /** @@ -3284,8 +3274,8 @@ wl_shell_surface_set_maximized(struct wl_shell_surface *wl_shell_surface, struct static inline void wl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char *title) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_TITLE, title); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, title); } /** @@ -3301,8 +3291,8 @@ wl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char static inline void wl_shell_surface_set_class(struct wl_shell_surface *wl_shell_surface, const char *class_) { - wl_proxy_marshal((struct wl_proxy *) wl_shell_surface, - WL_SHELL_SURFACE_SET_CLASS, class_); + wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface, + WL_SHELL_SURFACE_SET_CLASS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, class_); } #ifndef WL_SURFACE_ERROR_ENUM @@ -3457,10 +3447,8 @@ wl_surface_get_version(struct wl_surface *wl_surface) static inline void wl_surface_destroy(struct wl_surface *wl_surface) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_surface); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), WL_MARSHAL_FLAG_DESTROY); } /** @@ -3509,8 +3497,8 @@ wl_surface_destroy(struct wl_surface *wl_surface) static inline void wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_ATTACH, buffer, x, y); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_ATTACH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, buffer, x, y); } /** @@ -3541,8 +3529,8 @@ wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32 static inline void wl_surface_damage(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_DAMAGE, x, y, width, height); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_DAMAGE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y, width, height); } /** @@ -3586,8 +3574,8 @@ wl_surface_frame(struct wl_surface *wl_surface) { struct wl_proxy *callback; - callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_surface, - WL_SURFACE_FRAME, &wl_callback_interface, NULL); + callback = wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_FRAME, &wl_callback_interface, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, NULL); return (struct wl_callback *) callback; } @@ -3623,8 +3611,8 @@ wl_surface_frame(struct wl_surface *wl_surface) static inline void wl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *region) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_SET_OPAQUE_REGION, region); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_SET_OPAQUE_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, region); } /** @@ -3656,8 +3644,8 @@ wl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *re static inline void wl_surface_set_input_region(struct wl_surface *wl_surface, struct wl_region *region) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_SET_INPUT_REGION, region); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_SET_INPUT_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, region); } /** @@ -3684,8 +3672,8 @@ wl_surface_set_input_region(struct wl_surface *wl_surface, struct wl_region *reg static inline void wl_surface_commit(struct wl_surface *wl_surface) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_COMMIT); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_COMMIT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0); } /** @@ -3724,8 +3712,8 @@ wl_surface_commit(struct wl_surface *wl_surface) static inline void wl_surface_set_buffer_transform(struct wl_surface *wl_surface, int32_t transform) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_SET_BUFFER_TRANSFORM, transform); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_SET_BUFFER_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, transform); } /** @@ -3758,8 +3746,8 @@ wl_surface_set_buffer_transform(struct wl_surface *wl_surface, int32_t transform static inline void wl_surface_set_buffer_scale(struct wl_surface *wl_surface, int32_t scale) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_SET_BUFFER_SCALE, scale); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_SET_BUFFER_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, scale); } /** @@ -3801,8 +3789,8 @@ wl_surface_set_buffer_scale(struct wl_surface *wl_surface, int32_t scale) static inline void wl_surface_damage_buffer(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height) { - wl_proxy_marshal((struct wl_proxy *) wl_surface, - WL_SURFACE_DAMAGE_BUFFER, x, y, width, height); + wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_DAMAGE_BUFFER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y, width, height); } #ifndef WL_SEAT_CAPABILITY_ENUM @@ -3967,8 +3955,8 @@ wl_seat_get_pointer(struct wl_seat *wl_seat) { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, - WL_SEAT_GET_POINTER, &wl_pointer_interface, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat, + WL_SEAT_GET_POINTER, &wl_pointer_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL); return (struct wl_pointer *) id; } @@ -3989,8 +3977,8 @@ wl_seat_get_keyboard(struct wl_seat *wl_seat) { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, - WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat, + WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL); return (struct wl_keyboard *) id; } @@ -4011,8 +3999,8 @@ wl_seat_get_touch(struct wl_seat *wl_seat) { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, - WL_SEAT_GET_TOUCH, &wl_touch_interface, NULL); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat, + WL_SEAT_GET_TOUCH, &wl_touch_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL); return (struct wl_touch *) id; } @@ -4026,10 +4014,8 @@ wl_seat_get_touch(struct wl_seat *wl_seat) static inline void wl_seat_release(struct wl_seat *wl_seat) { - wl_proxy_marshal((struct wl_proxy *) wl_seat, - WL_SEAT_RELEASE); - - wl_proxy_destroy((struct wl_proxy *) wl_seat); + wl_proxy_marshal_flags((struct wl_proxy *) wl_seat, + WL_SEAT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_seat), WL_MARSHAL_FLAG_DESTROY); } #ifndef WL_POINTER_ERROR_ENUM @@ -4095,16 +4081,6 @@ enum wl_pointer_axis { * from a "finger" source may be in a smooth coordinate space with * kinetic scrolling whereas a "wheel" source may be in discrete steps * of a number of lines. - * - * The "continuous" axis source is a device generating events in a - * continuous coordinate space, but using something other than a - * finger. One example for this source is button-based scrolling where - * the vertical motion of a device is converted to scroll events while - * a button is held down. - * - * The "wheel tilt" axis source indicates that the actual device is a - * wheel but the scroll event is not caused by a rotation but a - * (usually sideways) tilt of the wheel. */ enum wl_pointer_axis_source { /** @@ -4117,10 +4093,20 @@ enum wl_pointer_axis_source { WL_POINTER_AXIS_SOURCE_FINGER = 1, /** * continuous coordinate space + * + * A device generating events in a continuous coordinate space, + * but using something other than a finger. One example for this + * source is button-based scrolling where the vertical motion of a + * device is converted to scroll events while a button is held + * down. */ WL_POINTER_AXIS_SOURCE_CONTINUOUS = 2, /** * a physical wheel tilt + * + * Indicates that the actual device is a wheel but the scroll + * event is not caused by a rotation but a (usually sideways) tilt + * of the wheel. * @since 6 */ WL_POINTER_AXIS_SOURCE_WHEEL_TILT = 3, @@ -4506,8 +4492,8 @@ wl_pointer_destroy(struct wl_pointer *wl_pointer) static inline void wl_pointer_set_cursor(struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, int32_t hotspot_x, int32_t hotspot_y) { - wl_proxy_marshal((struct wl_proxy *) wl_pointer, - WL_POINTER_SET_CURSOR, serial, surface, hotspot_x, hotspot_y); + wl_proxy_marshal_flags((struct wl_proxy *) wl_pointer, + WL_POINTER_SET_CURSOR, NULL, wl_proxy_get_version((struct wl_proxy *) wl_pointer), 0, serial, surface, hotspot_x, hotspot_y); } /** @@ -4522,10 +4508,8 @@ wl_pointer_set_cursor(struct wl_pointer *wl_pointer, uint32_t serial, struct wl_ static inline void wl_pointer_release(struct wl_pointer *wl_pointer) { - wl_proxy_marshal((struct wl_proxy *) wl_pointer, - WL_POINTER_RELEASE); - - wl_proxy_destroy((struct wl_proxy *) wl_pointer); + wl_proxy_marshal_flags((struct wl_proxy *) wl_pointer, + WL_POINTER_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_pointer), WL_MARSHAL_FLAG_DESTROY); } #ifndef WL_KEYBOARD_KEYMAP_FORMAT_ENUM @@ -4753,10 +4737,8 @@ wl_keyboard_destroy(struct wl_keyboard *wl_keyboard) static inline void wl_keyboard_release(struct wl_keyboard *wl_keyboard) { - wl_proxy_marshal((struct wl_proxy *) wl_keyboard, - WL_KEYBOARD_RELEASE); - - wl_proxy_destroy((struct wl_proxy *) wl_keyboard); + wl_proxy_marshal_flags((struct wl_proxy *) wl_keyboard, + WL_KEYBOARD_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_keyboard), WL_MARSHAL_FLAG_DESTROY); } /** @@ -4997,10 +4979,8 @@ wl_touch_destroy(struct wl_touch *wl_touch) static inline void wl_touch_release(struct wl_touch *wl_touch) { - wl_proxy_marshal((struct wl_proxy *) wl_touch, - WL_TOUCH_RELEASE); - - wl_proxy_destroy((struct wl_proxy *) wl_touch); + wl_proxy_marshal_flags((struct wl_proxy *) wl_touch, + WL_TOUCH_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_touch), WL_MARSHAL_FLAG_DESTROY); } #ifndef WL_OUTPUT_SUBPIXEL_ENUM @@ -5282,10 +5262,8 @@ wl_output_destroy(struct wl_output *wl_output) static inline void wl_output_release(struct wl_output *wl_output) { - wl_proxy_marshal((struct wl_proxy *) wl_output, - WL_OUTPUT_RELEASE); - - wl_proxy_destroy((struct wl_proxy *) wl_output); + wl_proxy_marshal_flags((struct wl_proxy *) wl_output, + WL_OUTPUT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_output), WL_MARSHAL_FLAG_DESTROY); } #define WL_REGION_DESTROY 0 @@ -5334,10 +5312,8 @@ wl_region_get_version(struct wl_region *wl_region) static inline void wl_region_destroy(struct wl_region *wl_region) { - wl_proxy_marshal((struct wl_proxy *) wl_region, - WL_REGION_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_region); + wl_proxy_marshal_flags((struct wl_proxy *) wl_region, + WL_REGION_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), WL_MARSHAL_FLAG_DESTROY); } /** @@ -5348,8 +5324,8 @@ wl_region_destroy(struct wl_region *wl_region) static inline void wl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height) { - wl_proxy_marshal((struct wl_proxy *) wl_region, - WL_REGION_ADD, x, y, width, height); + wl_proxy_marshal_flags((struct wl_proxy *) wl_region, + WL_REGION_ADD, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), 0, x, y, width, height); } /** @@ -5360,8 +5336,8 @@ wl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, static inline void wl_region_subtract(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height) { - wl_proxy_marshal((struct wl_proxy *) wl_region, - WL_REGION_SUBTRACT, x, y, width, height); + wl_proxy_marshal_flags((struct wl_proxy *) wl_region, + WL_REGION_SUBTRACT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), 0, x, y, width, height); } #ifndef WL_SUBCOMPOSITOR_ERROR_ENUM @@ -5417,10 +5393,8 @@ wl_subcompositor_get_version(struct wl_subcompositor *wl_subcompositor) static inline void wl_subcompositor_destroy(struct wl_subcompositor *wl_subcompositor) { - wl_proxy_marshal((struct wl_proxy *) wl_subcompositor, - WL_SUBCOMPOSITOR_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_subcompositor); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subcompositor, + WL_SUBCOMPOSITOR_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subcompositor), WL_MARSHAL_FLAG_DESTROY); } /** @@ -5439,8 +5413,8 @@ wl_subcompositor_get_subsurface(struct wl_subcompositor *wl_subcompositor, struc { struct wl_proxy *id; - id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_subcompositor, - WL_SUBCOMPOSITOR_GET_SUBSURFACE, &wl_subsurface_interface, NULL, surface, parent); + id = wl_proxy_marshal_flags((struct wl_proxy *) wl_subcompositor, + WL_SUBCOMPOSITOR_GET_SUBSURFACE, &wl_subsurface_interface, wl_proxy_get_version((struct wl_proxy *) wl_subcompositor), 0, NULL, surface, parent); return (struct wl_subsurface *) id; } @@ -5520,10 +5494,8 @@ wl_subsurface_get_version(struct wl_subsurface *wl_subsurface) static inline void wl_subsurface_destroy(struct wl_subsurface *wl_subsurface) { - wl_proxy_marshal((struct wl_proxy *) wl_subsurface, - WL_SUBSURFACE_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) wl_subsurface); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface, + WL_SUBSURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), WL_MARSHAL_FLAG_DESTROY); } /** @@ -5549,8 +5521,8 @@ wl_subsurface_destroy(struct wl_subsurface *wl_subsurface) static inline void wl_subsurface_set_position(struct wl_subsurface *wl_subsurface, int32_t x, int32_t y) { - wl_proxy_marshal((struct wl_proxy *) wl_subsurface, - WL_SUBSURFACE_SET_POSITION, x, y); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface, + WL_SUBSURFACE_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, x, y); } /** @@ -5575,8 +5547,8 @@ wl_subsurface_set_position(struct wl_subsurface *wl_subsurface, int32_t x, int32 static inline void wl_subsurface_place_above(struct wl_subsurface *wl_subsurface, struct wl_surface *sibling) { - wl_proxy_marshal((struct wl_proxy *) wl_subsurface, - WL_SUBSURFACE_PLACE_ABOVE, sibling); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface, + WL_SUBSURFACE_PLACE_ABOVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, sibling); } /** @@ -5588,8 +5560,8 @@ wl_subsurface_place_above(struct wl_subsurface *wl_subsurface, struct wl_surface static inline void wl_subsurface_place_below(struct wl_subsurface *wl_subsurface, struct wl_surface *sibling) { - wl_proxy_marshal((struct wl_proxy *) wl_subsurface, - WL_SUBSURFACE_PLACE_BELOW, sibling); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface, + WL_SUBSURFACE_PLACE_BELOW, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, sibling); } /** @@ -5612,8 +5584,8 @@ wl_subsurface_place_below(struct wl_subsurface *wl_subsurface, struct wl_surface static inline void wl_subsurface_set_sync(struct wl_subsurface *wl_subsurface) { - wl_proxy_marshal((struct wl_proxy *) wl_subsurface, - WL_SUBSURFACE_SET_SYNC); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface, + WL_SUBSURFACE_SET_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0); } /** @@ -5642,8 +5614,8 @@ wl_subsurface_set_sync(struct wl_subsurface *wl_subsurface) static inline void wl_subsurface_set_desync(struct wl_subsurface *wl_subsurface) { - wl_proxy_marshal((struct wl_proxy *) wl_subsurface, - WL_SUBSURFACE_SET_DESYNC); + wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface, + WL_SUBSURFACE_SET_DESYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0); } #ifdef __cplusplus diff --git a/tests/data/example-server.h b/tests/data/example-server.h index 3311c5d..7cfa4e7 100644 --- a/tests/data/example-server.h +++ b/tests/data/example-server.h @@ -3304,16 +3304,6 @@ enum wl_pointer_axis { * from a "finger" source may be in a smooth coordinate space with * kinetic scrolling whereas a "wheel" source may be in discrete steps * of a number of lines. - * - * The "continuous" axis source is a device generating events in a - * continuous coordinate space, but using something other than a - * finger. One example for this source is button-based scrolling where - * the vertical motion of a device is converted to scroll events while - * a button is held down. - * - * The "wheel tilt" axis source indicates that the actual device is a - * wheel but the scroll event is not caused by a rotation but a - * (usually sideways) tilt of the wheel. */ enum wl_pointer_axis_source { /** @@ -3326,10 +3316,20 @@ enum wl_pointer_axis_source { WL_POINTER_AXIS_SOURCE_FINGER = 1, /** * continuous coordinate space + * + * A device generating events in a continuous coordinate space, + * but using something other than a finger. One example for this + * source is button-based scrolling where the vertical motion of a + * device is converted to scroll events while a button is held + * down. */ WL_POINTER_AXIS_SOURCE_CONTINUOUS = 2, /** * a physical wheel tilt + * + * Indicates that the actual device is a wheel but the scroll + * event is not caused by a rotation but a (usually sideways) tilt + * of the wheel. * @since 6 */ WL_POINTER_AXIS_SOURCE_WHEEL_TILT = 3, diff --git a/tests/data/example.xml b/tests/data/example.xml index 29b63be..2d76940 100644 --- a/tests/data/example.xml +++ b/tests/data/example.xml @@ -812,7 +812,7 @@ which will subsequently be used in either the data_device.enter event (for drag-and-drop) or the data_device.selection event (for selections). Immediately - following the data_device_data_offer event, the new data_offer + following the data_device.data_offer event, the new data_offer object will send out data_offer.offer events to describe the mime types it offers. </description> @@ -1980,21 +1980,23 @@ from a "finger" source may be in a smooth coordinate space with kinetic scrolling whereas a "wheel" source may be in discrete steps of a number of lines. - - The "continuous" axis source is a device generating events in a - continuous coordinate space, but using something other than a - finger. One example for this source is button-based scrolling where - the vertical motion of a device is converted to scroll events while - a button is held down. - - The "wheel tilt" axis source indicates that the actual device is a - wheel but the scroll event is not caused by a rotation but a - (usually sideways) tilt of the wheel. </description> <entry name="wheel" value="0" summary="a physical wheel rotation" /> <entry name="finger" value="1" summary="finger on a touch surface" /> - <entry name="continuous" value="2" summary="continuous coordinate space"/> - <entry name="wheel_tilt" value="3" summary="a physical wheel tilt" since="6"/> + <entry name="continuous" value="2"> + <description summary="continuous coordinate space"> + A device generating events in a continuous coordinate space, but + using something other than a finger. One example for this source + is button-based scrolling where the vertical motion of a device + is converted to scroll events while a button is held down. + </description> + </entry> + <entry name="wheel_tilt" value="3" since="6"> + <description summary="a physical wheel tilt"> + Indicates that the actual device is a wheel but the scroll event is + not caused by a rotation but a (usually sideways) tilt of the wheel. + </description> + </entry> </enum> <event name="axis_source" since="5"> diff --git a/tests/data/small-client-core.h b/tests/data/small-client-core.h index d424757..348d2dc 100644 --- a/tests/data/small-client-core.h +++ b/tests/data/small-client-core.h @@ -159,8 +159,8 @@ intf_A_rq1(struct intf_A *intf_A, const struct wl_interface *interface, uint32_t { struct wl_proxy *untyped_new; - untyped_new = wl_proxy_marshal_constructor_versioned((struct wl_proxy *) intf_A, - INTF_A_RQ1, interface, version, interface->name, version, NULL); + untyped_new = wl_proxy_marshal_flags((struct wl_proxy *) intf_A, + INTF_A_RQ1, interface, version, 0, interface->name, version, NULL); return (void *) untyped_new; } @@ -173,8 +173,8 @@ intf_A_rq2(struct intf_A *intf_A, const char *str, int32_t i, uint32_t u, wl_fix { struct wl_proxy *typed_new; - typed_new = wl_proxy_marshal_constructor((struct wl_proxy *) intf_A, - INTF_A_RQ2, &intf_not_here_interface, NULL, str, i, u, f, fd, obj); + typed_new = wl_proxy_marshal_flags((struct wl_proxy *) intf_A, + INTF_A_RQ2, &intf_not_here_interface, wl_proxy_get_version((struct wl_proxy *) intf_A), 0, NULL, str, i, u, f, fd, obj); return (struct intf_not_here *) typed_new; } @@ -185,10 +185,8 @@ intf_A_rq2(struct intf_A *intf_A, const char *str, int32_t i, uint32_t u, wl_fix static inline void intf_A_destroy(struct intf_A *intf_A) { - wl_proxy_marshal((struct wl_proxy *) intf_A, - INTF_A_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) intf_A); + wl_proxy_marshal_flags((struct wl_proxy *) intf_A, + INTF_A_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) intf_A), WL_MARSHAL_FLAG_DESTROY); } #ifdef __cplusplus diff --git a/tests/data/small-client.h b/tests/data/small-client.h index 2a1f961..8c6abc5 100644 --- a/tests/data/small-client.h +++ b/tests/data/small-client.h @@ -159,8 +159,8 @@ intf_A_rq1(struct intf_A *intf_A, const struct wl_interface *interface, uint32_t { struct wl_proxy *untyped_new; - untyped_new = wl_proxy_marshal_constructor_versioned((struct wl_proxy *) intf_A, - INTF_A_RQ1, interface, version, interface->name, version, NULL); + untyped_new = wl_proxy_marshal_flags((struct wl_proxy *) intf_A, + INTF_A_RQ1, interface, version, 0, interface->name, version, NULL); return (void *) untyped_new; } @@ -173,8 +173,8 @@ intf_A_rq2(struct intf_A *intf_A, const char *str, int32_t i, uint32_t u, wl_fix { struct wl_proxy *typed_new; - typed_new = wl_proxy_marshal_constructor((struct wl_proxy *) intf_A, - INTF_A_RQ2, &intf_not_here_interface, NULL, str, i, u, f, fd, obj); + typed_new = wl_proxy_marshal_flags((struct wl_proxy *) intf_A, + INTF_A_RQ2, &intf_not_here_interface, wl_proxy_get_version((struct wl_proxy *) intf_A), 0, NULL, str, i, u, f, fd, obj); return (struct intf_not_here *) typed_new; } @@ -185,10 +185,8 @@ intf_A_rq2(struct intf_A *intf_A, const char *str, int32_t i, uint32_t u, wl_fix static inline void intf_A_destroy(struct intf_A *intf_A) { - wl_proxy_marshal((struct wl_proxy *) intf_A, - INTF_A_DESTROY); - - wl_proxy_destroy((struct wl_proxy *) intf_A); + wl_proxy_marshal_flags((struct wl_proxy *) intf_A, + INTF_A_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) intf_A), WL_MARSHAL_FLAG_DESTROY); } #ifdef __cplusplus diff --git a/tests/display-test.c b/tests/display-test.c index 3db7c95..bcb3267 100644 --- a/tests/display-test.c +++ b/tests/display-test.c @@ -24,6 +24,7 @@ * SOFTWARE. */ +#define _GNU_SOURCE #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -1005,9 +1006,16 @@ registry_handle_filtered(void *data, struct wl_registry *registry, } } +static void +registry_handle_remove_filtered(void *data, struct wl_registry *registry, + uint32_t id) +{ + assert(false); +} + static const struct wl_registry_listener registry_listener_filtered = { registry_handle_filtered, - NULL + registry_handle_remove_filtered, }; static void @@ -1044,6 +1052,58 @@ TEST(filtered_global_is_hidden) } static void +get_dynamic_globals(void *data) +{ + struct client *c = client_connect(); + struct wl_registry *registry; + + registry = wl_display_get_registry(c->wl_display); + wl_registry_add_listener(registry, ®istry_listener_filtered, data); + wl_display_roundtrip(c->wl_display); + + /* Wait for the server to create a new global */ + assert(stop_display(c, 1) >= 0); + + /* Check that we don't see it */ + wl_display_roundtrip(c->wl_display); + + /* Wait for the server to remove that global */ + assert(stop_display(c, 1) >= 0); + + /* Check that we don't get a global_remove event */ + wl_display_roundtrip(c->wl_display); + + wl_registry_destroy(registry); + client_disconnect_nocheck(c); +} + +TEST(filtered_dynamic_global_is_hidden) +{ + struct display *d; + struct wl_global *g; + + d = display_create(); + wl_display_set_global_filter(d->wl_display, global_filter, NULL); + + /* Create a client and let it enumerate the globals */ + client_create_noarg(d, get_dynamic_globals); + display_run(d); + + /* Dynamically create a new global */ + g = wl_global_create(d->wl_display, &wl_data_offer_interface, + 1, d, bind_data_offer); + + display_resume(d); + + /* Dynamically remove the global */ + wl_global_destroy(g); + + display_resume(d); + + display_destroy(d); +} + +static void check_bind_error(struct client *c) { uint32_t errorcode, id; @@ -1629,3 +1689,24 @@ TEST(global_remove) display_destroy(d); } + +static void +terminate_display(void *arg) +{ + struct wl_display *wl_display = arg; + wl_display_terminate(wl_display); +} + +TEST(no_source_terminate) +{ + struct display *d; + struct wl_event_loop *loop; + + d = display_create(); + loop = wl_display_get_event_loop(d->wl_display); + + wl_event_loop_add_idle(loop, terminate_display, d->wl_display); + + display_run(d); + display_destroy(d); +} diff --git a/tests/event-loop-test.c b/tests/event-loop-test.c index cbeaf8e..a51ba8f 100644 --- a/tests/event-loop-test.c +++ b/tests/event-loop-test.c @@ -24,6 +24,7 @@ * SOFTWARE. */ +#define _GNU_SOURCE #include <stdlib.h> #include <stdint.h> #include <assert.h> @@ -168,10 +169,22 @@ TEST(event_loop_signal) signal_callback, &got_it); assert(source); - wl_event_loop_dispatch(loop, 0); + assert(wl_event_loop_dispatch(loop, 0) == 0); assert(!got_it); - kill(getpid(), SIGUSR1); - wl_event_loop_dispatch(loop, 0); + assert(kill(getpid(), SIGUSR1) == 0); + /* + * On Linux the signal will be immediately visible in the epoll_wait() + * call. However, on FreeBSD we may need a small delay between kill() + * call and the signal being visible to the kevent() call. This + * sometimes happens when the signal processing and kevent processing + * runs on different CPUs, so becomes more likely when the system is + * under load (e.g. running all tests in parallel). + * See https://github.com/jiixyj/epoll-shim/pull/32 + * Passing 1ms as the timeout appears to avoid this race condition in + * all cases tested so far, but to be safe we use 1000ms which should + * be enough time even on a really slow (or emulated) system. + */ + assert(wl_event_loop_dispatch(loop, 1000) == 0); assert(got_it == 1); wl_event_source_remove(source); @@ -199,8 +212,12 @@ TEST(event_loop_multiple_same_signals) /* Try it more times */ for (i = 0; i < 5; ++i) { calls_no = 0; - kill(getpid(), SIGUSR1); - assert(wl_event_loop_dispatch(loop, 0) == 0); + assert(kill(getpid(), SIGUSR1) == 0); + /* + * We need a non-zero timeout here to allow the test to pass + * on non-Linux systems (see comment in event_loop_signal). + */ + assert(wl_event_loop_dispatch(loop, 1000) == 0); assert(calls_no == 2); } @@ -208,8 +225,12 @@ TEST(event_loop_multiple_same_signals) /* Try it again with one source */ calls_no = 0; - kill(getpid(), SIGUSR1); - assert(wl_event_loop_dispatch(loop, 0) == 0); + assert(kill(getpid(), SIGUSR1) == 0); + /* + * We need a non-zero timeout here to allow the test to pass + * on non-Linux systems (see comment in event_loop_signal). + */ + assert(wl_event_loop_dispatch(loop, 1000) == 0); assert(calls_no == 1); wl_event_source_remove(s2); diff --git a/tests/fixed-test.c b/tests/fixed-test.c index 47a4dae..0b58797 100644 --- a/tests/fixed-test.c +++ b/tests/fixed-test.c @@ -23,6 +23,7 @@ * SOFTWARE. */ +#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <assert.h> diff --git a/tests/map-test.c b/tests/map-test.c index 8ecc1aa..03568ea 100644 --- a/tests/map-test.c +++ b/tests/map-test.c @@ -119,3 +119,19 @@ TEST(map_flags) wl_map_release(&map); } + +static enum wl_iterator_result never_run(void *element, void *data, uint32_t flags) +{ + assert(0); +} + +TEST(map_iter_empty) +{ + struct wl_map map; + + wl_map_init(&map, WL_MAP_SERVER_SIDE); + + wl_map_for_each(&map, never_run, NULL); + + wl_map_release(&map); +} diff --git a/tests/meson.build b/tests/meson.build index a32ac50..5efd6f7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,3 +1,7 @@ +if not get_option('libraries') + error('-Dtests=true requires -Dlibraries=true') +endif + test_runner = static_library( 'test-runner', sources: [ @@ -9,6 +13,7 @@ test_runner = static_library( dependencies: [ cc.find_library('dl', required: false), dependency('threads'), + epoll_dep, ffi_dep, wayland_util_dep, wayland_private_dep, @@ -64,17 +69,19 @@ executable( dependencies: test_runner_dep ) -test( - 'cpp-compile-test', - executable( +if add_languages('cpp', native: false) + test( 'cpp-compile-test', - 'cpp-compile-test.cpp', - wayland_server_protocol_h, - include_directories: src_inc + executable( + 'cpp-compile-test', + 'cpp-compile-test.cpp', + wayland_server_protocol_h, + include_directories: src_inc + ) ) -) +endif -sed_path = find_program('sed').path() +sed_path = find_program('sed').full_path() if get_option('scanner') test( @@ -152,7 +159,7 @@ tests = { foreach test_name, test_extra_sources: tests test_sources = [ test_name + '.c' ] + test_extra_sources - test_deps = [test_runner_dep] + test_deps = [test_runner_dep, epoll_dep] bin = executable(test_name, test_sources, dependencies: test_deps) test( test_name, diff --git a/tests/message-test.c b/tests/message-test.c index 4e69392..86f387a 100644 --- a/tests/message-test.c +++ b/tests/message-test.c @@ -63,9 +63,8 @@ TEST(message_count_arrays) { "middle", "iufasonh", NULL }, { "multiple", "aaiufaasonhaa", NULL }, { "leading_version", "2aaiufaasonhaa", NULL }, - { "among_nullables", "iufsa?oa?nah", NULL }, - { "nullable", "?aiufs?a?onh?a", NULL }, - { "all_mixed", "2?aiufas?oa?na", NULL }, + { "among_nullables", "iufsa?oa?sah", NULL }, + { "all_mixed", "2aiufas?oa?sa", NULL }, }; const struct { const struct wl_message *message; @@ -81,8 +80,7 @@ TEST(message_count_arrays) { &fake_messages[5], 6 }, { &fake_messages[6], 6 }, { &fake_messages[7], 3 }, - { &fake_messages[8], 3 }, - { &fake_messages[9], 4 }, + { &fake_messages[8], 4 }, }; for (i = 0; i < ARRAY_LENGTH(messages); ++i) { diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 102622c..8d8c3ab 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -23,6 +23,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "../config.h" #define _GNU_SOURCE @@ -46,26 +47,44 @@ static int fall_back; -static int (*real_socket)(int, int, int); -static int wrapped_calls_socket; +/* Play nice with sanitizers + * + * Sanitizers need to intercept syscalls in the compiler run-time library. As + * this isn't a separate ELF object, the usual dlsym(RTLD_NEXT) approach won't + * work: there can only be one function named "socket" etc. To support this, the + * sanitizer library names its interceptors with the prefix __interceptor_ ("__" + * being reserved for the implementation) and then weakly aliases it to the real + * function. The functions we define below will override the weak alias, and we + * can call them by the __interceptor_ name directly. This allows the sanitizer + * to do its work before calling the next version of the function via dlsym. + * + * However! We also don't know which of these functions the sanitizer actually + * wants to override, so we have to declare our own weak symbols for + * __interceptor_ and check at run time if they linked to anything or not. +*/ -static int (*real_fcntl)(int, int, ...); -static int wrapped_calls_fcntl; +#define DECL(ret_type, func, ...) \ + ret_type __interceptor_ ## func(__VA_ARGS__) __attribute__((weak)); \ + static ret_type (*real_ ## func)(__VA_ARGS__); \ + static int wrapped_calls_ ## func; -static ssize_t (*real_recvmsg)(int, struct msghdr *, int); -static int wrapped_calls_recvmsg; +#define REAL(func) (__interceptor_ ## func) ? \ + __interceptor_ ## func : \ + (__typeof__(&__interceptor_ ## func))dlsym(RTLD_NEXT, #func) -static int (*real_epoll_create1)(int); -static int wrapped_calls_epoll_create1; +DECL(int, socket, int, int, int); +DECL(int, fcntl, int, int, ...); +DECL(ssize_t, recvmsg, int, struct msghdr *, int); +DECL(int, epoll_create1, int); static void init_fallbacks(int do_fallbacks) { fall_back = do_fallbacks; - real_socket = dlsym(RTLD_NEXT, "socket"); - real_fcntl = dlsym(RTLD_NEXT, "fcntl"); - real_recvmsg = dlsym(RTLD_NEXT, "recvmsg"); - real_epoll_create1 = dlsym(RTLD_NEXT, "epoll_create1"); + real_socket = REAL(socket); + real_fcntl = REAL(fcntl); + real_recvmsg = REAL(recvmsg); + real_epoll_create1 = REAL(epoll_create1); } __attribute__ ((visibility("default"))) int @@ -82,10 +101,11 @@ socket(int domain, int type, int protocol) } __attribute__ ((visibility("default"))) int -fcntl(int fd, int cmd, ...) +(fcntl)(int fd, int cmd, ...) { va_list ap; - void *arg; + int arg; + int has_arg; wrapped_calls_fcntl++; @@ -93,12 +113,27 @@ fcntl(int fd, int cmd, ...) errno = EINVAL; return -1; } + switch (cmd) { + case F_DUPFD_CLOEXEC: + case F_DUPFD: + case F_SETFD: + va_start(ap, cmd); + arg = va_arg(ap, int); + has_arg = 1; + va_end(ap); + break; + case F_GETFD: + has_arg = 0; + break; + default: + fprintf(stderr, "Unexpected fctnl cmd %d\n", cmd); + abort(); + } - va_start(ap, cmd); - arg = va_arg(ap, void*); - va_end(ap); - - return real_fcntl(fd, cmd, arg); + if (has_arg) { + return real_fcntl(fd, cmd, arg); + } + return real_fcntl(fd, cmd); } __attribute__ ((visibility("default"))) ssize_t @@ -307,7 +342,13 @@ do_os_wrappers_recvmsg_cloexec(int n) struct marshal_data data; data.nr_fds_begin = count_open_fds(); +#if HAVE_BROKEN_MSG_CMSG_CLOEXEC + /* We call the fallback directly on FreeBSD versions with a broken + * MSG_CMSG_CLOEXEC, so we don't call the local recvmsg() wrapper. */ + data.wrapped_calls = 0; +#else data.wrapped_calls = n; +#endif setup_marshal_data(&data); data.nr_fds_conn = count_open_fds(); diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c index b66e761..94e437d 100644 --- a/tests/protocol-logger-test.c +++ b/tests/protocol-logger-test.c @@ -34,125 +34,166 @@ #include "wayland-client.h" #include "wayland-server.h" -#include "wayland-util.h" #include "test-runner.h" +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) + /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */ static const char * require_xdg_runtime_dir(void) { char *val = getenv("XDG_RUNTIME_DIR"); - assert(val && "set $XDG_RUNTIME_DIR to run this test"); + assert(val && val[0] == '/' && "set $XDG_RUNTIME_DIR to run this test"); return val; } +struct expected_compositor_message { + enum wl_protocol_logger_type type; + const char *class; + int opcode; + const char *message_name; + int args_count; +}; + struct compositor { struct wl_display *display; struct wl_event_loop *loop; - int message; - struct wl_client *client; -}; + struct wl_protocol_logger *logger; -struct client { - struct wl_display *display; - struct wl_callback *cb; - int message; + struct expected_compositor_message *expected_msg; + int expected_msg_count; + int actual_msg_count; + struct wl_client *client; }; -struct message { - enum wl_protocol_logger_type type; +struct expected_client_message { + enum wl_client_message_type type; + enum wl_client_message_discarded_reason discarded_reason; const char *class; int opcode; const char *message_name; int args_count; -} messages[] = { - { - .type = WL_PROTOCOL_LOGGER_REQUEST, - .class = "wl_display", - .opcode = 0, - .message_name = "sync", - .args_count = 1, - }, - { - .type = WL_PROTOCOL_LOGGER_EVENT, - .class = "wl_callback", - .opcode = 0, - .message_name = "done", - .args_count = 1, - }, - { - .type = WL_PROTOCOL_LOGGER_EVENT, - .class = "wl_display", - .opcode = 1, - .message_name = "delete_id", - .args_count = 1, - }, }; -struct client_message { - enum wl_protocol_logger_client_type type; - const char *class; - int opcode; - const char *message_name; - int args_count; -} client_messages[] = { - { - .type = WL_PROTOCOL_LOGGER_CLIENT_REQUEST, - .class = "wl_display", - .opcode = 0, - .message_name = "sync", - .args_count = 1, - }, - { - .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT, - .class = "wl_display", - .opcode = 1, - .message_name = "delete_id", - .args_count = 1, - }, - { - .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT, - .class = "wl_callback", - .opcode = 0, - .message_name = "done", - .args_count = 1, - }, +struct client { + struct wl_display *display; + struct wl_callback *cb; + struct wl_client_observer *sequence_observer; + struct wl_client_observer *stderr_logger; + + struct expected_client_message *expected_msg; + int expected_msg_count; + int actual_msg_count; }; +#define ASSERT_LT(arg1, arg2, ...) \ + if (arg1 >= arg2) \ + fprintf(stderr, __VA_ARGS__); \ + assert(arg1 < arg2) + +#define ASSERT_EQ(arg1, arg2, ...) \ + if (arg1 != arg2) \ + fprintf(stderr, __VA_ARGS__); \ + assert(arg1 == arg2) + +#define ASSERT_STR_EQ(arg1, arg2, ...) \ + if (strcmp(arg1, arg2) != 0) \ + fprintf(stderr, __VA_ARGS__); \ + assert(strcmp(arg1, arg2) == 0) + static void -logger_func(void *user_data, enum wl_protocol_logger_type type, - const struct wl_protocol_logger_message *message) +compositor_sequence_observer_func( + void *user_data, enum wl_protocol_logger_type actual_type, + const struct wl_protocol_logger_message *actual_msg) { struct compositor *c = user_data; - struct message *msg = &messages[c->message++]; - - assert(msg->type == type); - assert(strcmp(msg->class, wl_resource_get_class(message->resource)) == 0); - assert(msg->opcode == message->message_opcode); - assert(strcmp(msg->message_name, message->message->name) == 0); - assert(msg->args_count == message->arguments_count); - - c->client = wl_resource_get_client(message->resource); + struct expected_compositor_message *expected_msg; + int actual_msg_count = c->actual_msg_count++; + char details_msg[256]; + + c->client = wl_resource_get_client(actual_msg->resource); + + if (!c->expected_msg) + return; + + ASSERT_LT(actual_msg_count, c->expected_msg_count, + "actual count %d exceeds expected count %d\n", + actual_msg_count, c->expected_msg_count); + + expected_msg = &c->expected_msg[actual_msg_count]; + + snprintf(details_msg, sizeof details_msg, + "compositor msg %d of %d actual [%d, '%s', %d, '%s', %d] vs " + "expected [%d, '%s', %d, '%s', %d]\n", + c->actual_msg_count, c->expected_msg_count, actual_type, + wl_resource_get_class(actual_msg->resource), + actual_msg->message_opcode, actual_msg->message->name, + actual_msg->arguments_count, expected_msg->type, + expected_msg->class, expected_msg->opcode, + expected_msg->message_name, expected_msg->args_count); + + ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s", + details_msg); + ASSERT_STR_EQ(expected_msg->class, + wl_resource_get_class(actual_msg->resource), + "class mismatch: %s", details_msg); + ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode, + "opcode mismatch: %s", details_msg); + ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name, + "message name mismatch: %s", details_msg); + ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count, + "arg count mismatch: %s", details_msg); } static void -client_logger_func(void *user_data, enum wl_protocol_logger_client_type type, - const struct wl_protocol_logger_client_message *message) +client_sequence_observer_func( + void *user_data, enum wl_client_message_type actual_type, + const struct wl_client_observed_message *actual_msg) { struct client *c = user_data; - struct client_message *msg = &client_messages[c->message++]; - - assert(msg->type == type); - assert(strcmp(msg->class, wl_proxy_get_class(message->proxy)) == 0); - assert(msg->opcode == message->message_opcode); - assert(strcmp(msg->message_name, message->message->name) == 0); - assert(msg->args_count == message->arguments_count); + struct expected_client_message *expected_msg; + int actual_msg_count = c->actual_msg_count++; + char details_msg[256]; + + if (!c->expected_msg) + return; + + ASSERT_LT(actual_msg_count, c->expected_msg_count, + "actual count %d exceeds expected count %d\n", + actual_msg_count, c->expected_msg_count); + expected_msg = &c->expected_msg[actual_msg_count]; + + snprintf(details_msg, sizeof details_msg, + "client msg %d of %d actual [%d, %d, '%s', %d, '%s', %d] vs " + "expected [%d, %d, '%s', %d, '%s', %d]\n", + c->actual_msg_count, c->expected_msg_count, actual_type, + actual_msg->discarded_reason, + wl_proxy_get_class(actual_msg->proxy), + actual_msg->message_opcode, actual_msg->message->name, + actual_msg->arguments_count, expected_msg->type, + expected_msg->discarded_reason, expected_msg->class, + expected_msg->opcode, expected_msg->message_name, + expected_msg->args_count); + + ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s", + details_msg); + ASSERT_EQ(expected_msg->discarded_reason, actual_msg->discarded_reason, + "discarded reason mismatch: %s", details_msg); + ASSERT_STR_EQ(expected_msg->class, + wl_proxy_get_class(actual_msg->proxy), + "class mismatch: %s", details_msg); + ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode, + "opcode mismatch: %s", details_msg); + ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name, + "message name mismatch: %s", details_msg); + ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count, + "arg count mismatch: %s", details_msg); } -// A slightly simplified version of get_next_argument() from src/connection.c -static const char* -get_next_argument_type(const char *signature, char* type) +// A slightly simplified version of get_next_argument() from src/connection.c +static const char * +get_next_argument_type(const char *signature, char *type) { for (; *signature; ++signature) { assert(strchr("iufsonah?", *signature) != NULL); @@ -169,7 +210,6 @@ get_next_argument_type(const char *signature, char* type) return signature + 1; case '?': break; - } } *type = 0; @@ -179,85 +219,101 @@ get_next_argument_type(const char *signature, char* type) // This duplicates what the internal wl_closure_print function does, and can be // used as a starting point for a client or server that wants to log messages. static void -client_log_to_stderr_demo(void *user_data, - enum wl_protocol_logger_client_type type, - const struct wl_protocol_logger_client_message *message) { +client_log_to_stderr_demo(void *user_data, enum wl_client_message_type type, + const struct wl_client_observed_message *message) +{ int i; char arg_type; const char *signature = message->message->signature; - const union wl_argument* args = message->arguments; - struct wl_proxy* arg_proxy; - const char* arg_class; + const union wl_argument *args = message->arguments; + struct wl_proxy *arg_proxy; + const char *arg_class; struct timespec tp; - unsigned int time; + unsigned long long time; + FILE *f; + char *buffer; + size_t buffer_length; + + f = open_memstream(&buffer, &buffer_length); + if (f == NULL) + return; clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + time = (tp.tv_sec * 1000000LL) + (tp.tv_nsec / 1000); // Note: server logger will be given message->resource, and should // use wl_resource_get_class and wl_resolurce_get_id. - fprintf(stderr, "[%10.3f] %s%s@%u.%s(", - time / 1000.0, - (type == WL_PROTOCOL_LOGGER_CLIENT_REQUEST) ? " -> " : "", - wl_proxy_get_class(message->proxy), wl_proxy_get_id(message->proxy), - message->message->name); + fprintf(f, "[%7llu.%03llu] %s%s%s%s%s@%u.%s(", time / 1000, time % 1000, + (message->discarded_reason_str ? "discarded[" : ""), + (message->discarded_reason_str ? message->discarded_reason_str + : ""), + (message->discarded_reason_str ? "] " : ""), + (type == WL_CLIENT_MESSAGE_REQUEST) ? " -> " : "", + wl_proxy_get_class(message->proxy), + wl_proxy_get_id(message->proxy), message->message->name); for (i = 0; i < message->arguments_count; i++) { signature = get_next_argument_type(signature, &arg_type); if (i > 0) - fprintf(stderr, ", "); + fprintf(f, ", "); switch (arg_type) { case 'u': - fprintf(stderr, "%u", args[i].u); + fprintf(f, "%u", args[i].u); break; case 'i': - fprintf(stderr, "%d", args[i].i); + fprintf(f, "%d", args[i].i); break; case 'f': - fprintf(stderr, "%f", wl_fixed_to_double(args[i].f)); + fprintf(f, "%f", wl_fixed_to_double(args[i].f)); break; case 's': if (args[i].s) - fprintf(stderr, "\"%s\"", args[i].s); + fprintf(f, "\"%s\"", args[i].s); else - fprintf(stderr, "nil"); + fprintf(f, "nil"); break; case 'o': if (args[i].o) { // Note: server logger should instead use // wl_resource_from_object, and then - // wl_resource_get_class and wl_resource_get_id. + // wl_resource_get_class and + // wl_resource_get_id. arg_proxy = wl_proxy_from_object(args[i].o); arg_class = wl_proxy_get_class(arg_proxy); - fprintf(stderr, "%s@%u", + fprintf(f, "%s@%u", arg_class ? arg_class : "[unknown]", wl_proxy_get_id(arg_proxy)); } else { - fprintf(stderr, "nil"); + fprintf(f, "nil"); } break; case 'n': - fprintf(stderr, "new id %s@", - (message->message->types[i]) ? - message->message->types[i]->name : - "[unknown]"); + fprintf(f, "new id %s@", + (message->message->types[i]) + ? message->message->types[i]->name + : "[unknown]"); if (args[i].n != 0) - fprintf(stderr, "%u", args[i].n); + fprintf(f, "%u", args[i].n); else - fprintf(stderr, "nil"); + fprintf(f, "nil"); break; case 'a': - fprintf(stderr, "array"); + fprintf(f, "array"); break; case 'h': - fprintf(stderr, "fd %d", args[i].h); + fprintf(f, "fd %d", args[i].h); break; } } - fprintf(stderr, ")\n"); + fprintf(f, ")\n"); + + if (fclose(f) == 0) { + fprintf(stderr, "%s", buffer); + free(buffer); + } } static void @@ -270,46 +326,486 @@ static const struct wl_callback_listener callback_listener = { callback_done, }; +static void +logger_setup(struct compositor *compositor, struct client *client) +{ + const char *socket; + + require_xdg_runtime_dir(); + + compositor->display = wl_display_create(); + compositor->loop = wl_display_get_event_loop(compositor->display); + socket = wl_display_add_socket_auto(compositor->display); + + compositor->logger = wl_display_add_protocol_logger( + compositor->display, compositor_sequence_observer_func, + compositor); + + client->display = wl_display_connect(socket); + client->sequence_observer = wl_display_create_client_observer( + client->display, client_sequence_observer_func, client); + client->stderr_logger = wl_display_create_client_observer( + client->display, client_log_to_stderr_demo, client); +} + +static void +logger_teardown(struct compositor *compositor, struct client *client) +{ + wl_client_observer_destroy(client->sequence_observer); + wl_client_observer_destroy(client->stderr_logger); + wl_display_disconnect(client->display); + + wl_client_destroy(compositor->client); + wl_protocol_logger_destroy(compositor->logger); + wl_display_destroy(compositor->display); +} + TEST(logger) { test_set_timeout(1); - const char *socket; + struct expected_compositor_message compositor_messages[] = { + { + .type = WL_PROTOCOL_LOGGER_REQUEST, + .class = "wl_display", + .opcode = 0, + .message_name = "sync", + .args_count = 1, + }, + { + .type = WL_PROTOCOL_LOGGER_EVENT, + .class = "wl_callback", + .opcode = 0, + .message_name = "done", + .args_count = 1, + }, + { + .type = WL_PROTOCOL_LOGGER_EVENT, + .class = "wl_display", + .opcode = 1, + .message_name = "delete_id", + .args_count = 1, + }, + }; + struct expected_client_message client_messages[] = { + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 0, + .message_name = "sync", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 1, + .message_name = "delete_id", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_callback", + .opcode = 0, + .message_name = "done", + .args_count = 1, + }, + }; struct compositor compositor = { 0 }; struct client client = { 0 }; - struct wl_protocol_logger *logger; - struct wl_protocol_logger_client *logger_client; - struct wl_protocol_logger_client *logger_client_demo; - require_xdg_runtime_dir(); + logger_setup(&compositor, &client); + + compositor.expected_msg = &compositor_messages[0]; + compositor.expected_msg_count = ARRAY_LENGTH(compositor_messages); + + client.expected_msg = &client_messages[0]; + client.expected_msg_count = ARRAY_LENGTH(client_messages); + + client.cb = wl_display_sync(client.display); + wl_callback_add_listener(client.cb, &callback_listener, NULL); + wl_display_flush(client.display); + + while (compositor.actual_msg_count < compositor.expected_msg_count) { + wl_event_loop_dispatch(compositor.loop, -1); + wl_display_flush_clients(compositor.display); + } + + while (client.actual_msg_count < client.expected_msg_count) { + wl_display_dispatch(client.display); + } + + logger_teardown(&compositor, &client); +} + +TEST(client_discards_if_dead_on_dispatch) +{ + test_set_timeout(1); + + struct expected_client_message client_messages[] = { + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 0, + .message_name = "sync", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 1, + .message_name = "delete_id", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = + WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, + .class = "wl_callback", + .opcode = 0, + .message_name = "done", + .args_count = 1, + }, + }; + struct compositor compositor = { 0 }; + struct client client = { 0 }; + + logger_setup(&compositor, &client); - compositor.display = wl_display_create(); - compositor.loop = wl_display_get_event_loop(compositor.display); - socket = wl_display_add_socket_auto(compositor.display); + compositor.expected_msg_count = 3; - logger = wl_display_add_protocol_logger(compositor.display, - logger_func, &compositor); + client.expected_msg = &client_messages[0]; + client.expected_msg_count = ARRAY_LENGTH(client_messages); - client.display = wl_display_connect(socket); - logger_client = wl_display_add_protocol_logger_client( - client.display, client_logger_func, &client); - logger_client_demo = wl_display_add_protocol_logger_client( - client.display, client_log_to_stderr_demo, &client); client.cb = wl_display_sync(client.display); wl_callback_add_listener(client.cb, &callback_listener, NULL); wl_display_flush(client.display); - while (compositor.message < 3) { + while (compositor.actual_msg_count < compositor.expected_msg_count) { + wl_event_loop_dispatch(compositor.loop, -1); + wl_display_flush_clients(compositor.display); + } + + wl_display_prepare_read(client.display); + wl_display_read_events(client.display); + + // To get a WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, we + // destroy the callback after reading client events, but before + // dispatching them. + wl_callback_destroy(client.cb); + + while (client.actual_msg_count < client.expected_msg_count) { + wl_display_dispatch(client.display); + } + + logger_teardown(&compositor, &client); +} + +TEST(client_discards_if_no_listener_on_dispatch) +{ + test_set_timeout(1); + + struct expected_client_message client_messages[] = { + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 0, + .message_name = "sync", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 1, + .message_name = "delete_id", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = + WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH, + .class = "wl_callback", + .opcode = 0, + .message_name = "done", + .args_count = 1, + }, + }; + struct compositor compositor = { 0 }; + struct client client = { 0 }; + + logger_setup(&compositor, &client); + + compositor.expected_msg_count = 3; + + client.expected_msg = &client_messages[0]; + client.expected_msg_count = ARRAY_LENGTH(client_messages); + + client.cb = wl_display_sync(client.display); + wl_display_flush(client.display); + + while (compositor.actual_msg_count < compositor.expected_msg_count) { + wl_event_loop_dispatch(compositor.loop, -1); + wl_display_flush_clients(compositor.display); + } + + while (client.actual_msg_count < client.expected_msg_count) { + wl_display_dispatch(client.display); + } + + wl_callback_destroy(client.cb); + + logger_teardown(&compositor, &client); +} + +TEST(client_discards_if_invalid_id_on_demarshal) +{ + test_set_timeout(1); + + struct expected_client_message client_messages[] = { + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 0, + .message_name = "sync", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = + WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL, + .class = "[unknown]", + .opcode = 0, + .message_name = "[event 0, 0 fds, 12 bytes]", + .args_count = 0, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 1, + .message_name = "delete_id", + .args_count = 1, + }, + }; + struct compositor compositor = { 0 }; + struct client client = { 0 }; + + logger_setup(&compositor, &client); + + compositor.expected_msg_count = 3; + + client.expected_msg = &client_messages[0]; + client.expected_msg_count = ARRAY_LENGTH(client_messages); + + client.cb = wl_display_sync(client.display); + wl_display_flush(client.display); + + while (compositor.actual_msg_count < compositor.expected_msg_count) { + wl_event_loop_dispatch(compositor.loop, -1); + wl_display_flush_clients(compositor.display); + } + + // To get a WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL, we + // destroy the callback before reading and dispatching client events. + wl_callback_destroy(client.cb); + + while (client.actual_msg_count < client.expected_msg_count) { + wl_display_dispatch(client.display); + } + + logger_teardown(&compositor, &client); +} + +static const struct wl_keyboard_interface keyboard_interface = { 0 }; + +static void +seat_get_pointer(struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + assert(false && "Not expected to be called by client."); +} + +static void +seat_get_keyboard(struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *keyboard_res; + + keyboard_res = + wl_resource_create(client, &wl_keyboard_interface, + wl_resource_get_version(resource), id); + wl_resource_set_implementation(keyboard_res, &keyboard_interface, NULL, + NULL); + + wl_keyboard_send_key(keyboard_res, 0, 0, 0, 0); +} + +static void +seat_get_touch(struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + assert(false && "Not expected to be called by client."); +} + +static void +seat_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_seat_interface seat_interface = { + &seat_get_pointer, + &seat_get_keyboard, + &seat_get_touch, + &seat_release, +}; + +static void +bind_seat(struct wl_client *client, void *data, uint32_t vers, uint32_t id) +{ + struct wl_resource *seat_res; + + seat_res = wl_resource_create(client, &wl_seat_interface, vers, id); + wl_resource_set_implementation(seat_res, &seat_interface, NULL, NULL); +} + +static void +registry_seat_listener_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *intf, + uint32_t ver) +{ + uint32_t *seat_id_ptr = data; + + if (strcmp(intf, wl_seat_interface.name) == 0) { + *seat_id_ptr = id; + } +} + +static const struct wl_registry_listener registry_seat_listener = { + registry_seat_listener_handle_global, NULL +}; + +TEST(client_discards_if_zombie_on_demarshal) +{ + test_set_timeout(1); + + struct expected_client_message client_messages[] = { + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_display", + .opcode = 1, + .message_name = "get_registry", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_registry", + .opcode = 0, + .message_name = "global", + .args_count = 3, + }, + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_registry", + .opcode = 0, + .message_name = "bind", + .args_count = 4, + }, + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_seat", + .opcode = 1, + .message_name = "get_keyboard", + .args_count = 1, + }, + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_keyboard", + .opcode = 0, + .message_name = "release", + .args_count = 0, + }, + { + .type = WL_CLIENT_MESSAGE_REQUEST, + .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, + .class = "wl_seat", + .opcode = 3, + .message_name = "release", + .args_count = 0, + }, + { + .type = WL_CLIENT_MESSAGE_EVENT, + .discarded_reason = + WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL, + .class = "[zombie]", + .opcode = 3, + .message_name = "[event 3, 0 fds, 24 bytes]", + .args_count = 0, + }, + }; + + struct compositor compositor = { 0 }; + struct client client = { 0 }; + struct wl_global *g_keyboard; + struct wl_registry *registry; + struct wl_seat *seat; + struct wl_keyboard *keyboard; + int32_t seat_id; + + logger_setup(&compositor, &client); + + client.expected_msg = &client_messages[0]; + client.expected_msg_count = ARRAY_LENGTH(client_messages); + + g_keyboard = wl_global_create(compositor.display, &wl_seat_interface, + 5, &compositor.display, bind_seat); + + registry = wl_display_get_registry(client.display); + wl_registry_add_listener(registry, ®istry_seat_listener, &seat_id); + wl_display_flush(client.display); + + compositor.actual_msg_count = 0; + compositor.expected_msg_count = 2; + + while (compositor.actual_msg_count < compositor.expected_msg_count) { wl_event_loop_dispatch(compositor.loop, -1); wl_display_flush_clients(compositor.display); } wl_display_dispatch(client.display); - wl_display_disconnect(client.display); - wl_protocol_logger_client_destroy(logger_client); - wl_protocol_logger_client_destroy(logger_client_demo); - wl_client_destroy(compositor.client); - wl_protocol_logger_destroy(logger); - wl_display_destroy(compositor.display); + seat = wl_registry_bind(registry, seat_id, &wl_seat_interface, 5); + keyboard = wl_seat_get_keyboard(seat); + wl_display_flush(client.display); + + compositor.actual_msg_count = 0; + compositor.expected_msg_count = 3; + + while (compositor.actual_msg_count < compositor.expected_msg_count) { + wl_event_loop_dispatch(compositor.loop, -1); + wl_display_flush_clients(compositor.display); + } + + wl_keyboard_release(keyboard); + wl_seat_release(seat); + + wl_display_dispatch(client.display); + + wl_registry_destroy(registry); + + wl_global_destroy(g_keyboard); + + logger_teardown(&compositor, &client); } diff --git a/tests/queue-test.c b/tests/queue-test.c index 86a3602..4129310 100644 --- a/tests/queue-test.c +++ b/tests/queue-test.c @@ -23,14 +23,18 @@ * SOFTWARE. */ +#define _GNU_SOURCE /* For memrchr */ #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <stdbool.h> +#include <string.h> #include <unistd.h> +#include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h> +#include <signal.h> #include "wayland-client.h" #include "wayland-server.h" @@ -303,6 +307,170 @@ client_test_queue_set_queue_race(void) wl_display_disconnect(display); } +static char * +maybe_map_file(int fd, size_t *len) +{ + char *data; + + *len = lseek(fd, 0, SEEK_END); + data = mmap(0, *len, PROT_READ, MAP_PRIVATE, fd, 0); + + return data; +} + +static char * +map_file(int fd, size_t *len) +{ + char *data; + + data = maybe_map_file(fd, len); + assert(data != MAP_FAILED && "Failed to mmap file"); + + return data; +} + +static char * +last_line_of(char *s) +{ + size_t len = strlen(s); + char *last; + + last = memrchr(s, '\n', len); + /* If we found a newline at end of string, find the previous one. */ + if (last && last[1] == 0) + last = memrchr(s, '\n', len - 1); + /* If we have a newline, the last line starts after the newline. + * Otherwise, the whole string is the last line. */ + if (last) + last += 1; + else + last = s; + + return last; +} + +static void +client_test_queue_destroy_with_attached_proxies(void) +{ + struct wl_event_queue *queue; + struct wl_display *display; + struct wl_display *display_wrapper; + struct wl_callback *callback; + char *log; + size_t log_len; + char callback_name[24]; + int ret; + + display = wl_display_connect(NULL); + assert(display); + + /* Pretend we are in a separate thread where a thread-local queue is + * used. */ + queue = wl_display_create_queue(display); + assert(queue); + + /* Create a sync dispatching events on the thread-local queue. */ + display_wrapper = wl_proxy_create_wrapper(display); + assert(display_wrapper); + wl_proxy_set_queue((struct wl_proxy *) display_wrapper, queue); + callback = wl_display_sync(display_wrapper); + wl_proxy_wrapper_destroy(display_wrapper); + assert(callback != NULL); + + /* Destroy the queue before the attached object. */ + wl_event_queue_destroy(queue); + + /* Check that the log contains some information about the attached + * wl_callback proxy. */ + log = map_file(client_log_fd, &log_len); + ret = snprintf(callback_name, sizeof(callback_name), "wl_callback@%u", + wl_proxy_get_id((struct wl_proxy *) callback)); + assert(ret > 0 && ret < (int)sizeof(callback_name) && + "callback name creation failed (possibly truncated)"); + assert(strstr(last_line_of(log), callback_name)); + munmap(log, log_len); + + wl_callback_destroy(callback); + + wl_display_disconnect(display); +} + +static void +client_test_queue_proxy_event_to_destroyed_queue(void) +{ + struct wl_event_queue *queue; + struct wl_display *display; + struct wl_display *display_wrapper; + struct wl_callback *callback; + + display = wl_display_connect(NULL); + assert(display); + + /* Pretend we are in a separate thread where a thread-local queue is + * used. */ + queue = wl_display_create_queue(display); + assert(queue); + + /* Create a sync dispatching events on the thread-local queue. */ + display_wrapper = wl_proxy_create_wrapper(display); + assert(display_wrapper); + wl_proxy_set_queue((struct wl_proxy *) display_wrapper, queue); + callback = wl_display_sync(display_wrapper); + wl_proxy_wrapper_destroy(display_wrapper); + assert(callback != NULL); + wl_display_flush(display); + + /* Destroy the queue before the attached object. */ + wl_event_queue_destroy(queue); + + /* During this roundtrip we should receive the done event on 'callback', + * try to queue it to the destroyed queue, and abort. */ + wl_display_roundtrip(display); + + wl_callback_destroy(callback); + + wl_display_disconnect(display); +} + +static void +client_test_queue_destroy_default_with_attached_proxies(void) +{ + struct wl_display *display; + struct wl_callback *callback; + char *log; + size_t log_len; + char callback_name[24]; + int ret; + + display = wl_display_connect(NULL); + assert(display); + + /* Create a sync dispatching events on the default queue. */ + callback = wl_display_sync(display); + assert(callback != NULL); + + /* Destroy the default queue (by disconnecting) before the attached + * object. */ + wl_display_disconnect(display); + + /* Check that the log does not contain any warning about the attached + * wl_callback proxy. */ + log = maybe_map_file(client_log_fd, &log_len); + ret = snprintf(callback_name, sizeof(callback_name), "wl_callback@%u", + wl_proxy_get_id((struct wl_proxy *) callback)); + assert(ret > 0 && ret < (int)sizeof(callback_name) && + "callback name creation failed (possibly truncated)"); + assert(log == MAP_FAILED || strstr(log, callback_name) == NULL); + if (log != MAP_FAILED) + munmap(log, log_len); + + /* HACK: Directly free the memory of the wl_callback proxy to appease + * ASan. We would normally use wl_callback_destroy(), but since we have + * destroyed the associated wl_display, using this function would lead + * to memory errors. */ + free(callback); +} + static void dummy_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -382,3 +550,50 @@ TEST(queue_set_queue_race) display_destroy(d); } + +TEST(queue_destroy_with_attached_proxies) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_destroy_with_attached_proxies); + display_run(d); + + display_destroy(d); +} + +TEST(queue_proxy_event_to_destroyed_queue) +{ + struct display *d = display_create(); + struct client_info *ci; + char *client_log; + size_t client_log_len; + + test_set_timeout(2); + + ci = client_create_noarg(d, client_test_queue_proxy_event_to_destroyed_queue); + display_run(d); + + /* Check that the final line in the log mentions the expected reason + * for the abort. */ + client_log = map_file(ci->log_fd, &client_log_len); + assert(!strcmp(last_line_of(client_log), + "Tried to add event to destroyed queue\n")); + munmap(client_log, client_log_len); + + /* Check that the client aborted. */ + display_destroy_expect_signal(d, SIGABRT); +} + +TEST(queue_destroy_default_with_attached_proxies) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_destroy_default_with_attached_proxies); + display_run(d); + + display_destroy(d); +} diff --git a/tests/sanity-test.c b/tests/sanity-test.c index 98beca8..e614cfb 100644 --- a/tests/sanity-test.c +++ b/tests/sanity-test.c @@ -25,7 +25,9 @@ #include <stdlib.h> #include <assert.h> +#include <string.h> #include <sys/types.h> +#include <sys/mman.h> #include <signal.h> #include <unistd.h> @@ -174,13 +176,72 @@ FAIL_TEST(tc_client_fd_leaks_exec) { struct display *d = display_create(); - client_create_noarg(d, sanity_fd_leak); + client_create_noarg(d, sanity_fd_leak_exec); display_run(d); test_disable_coredumps(); display_destroy(d); } +static char * +map_file(int fd, size_t *len) +{ + char *data; + + *len = lseek(fd, 0, SEEK_END); + data = mmap(0, *len, PROT_READ, MAP_PRIVATE, fd, 0); + assert(data != MAP_FAILED && "Failed to mmap file"); + + return data; +} + +static void +sanity_client_log(void) +{ + char *log; + size_t log_len; + char *wayland_socket = strdup(getenv("WAYLAND_SOCKET")); + char *xdg_runtime_dir = strdup(getenv("XDG_RUNTIME_DIR")); + + unsetenv("WAYLAND_SOCKET"); + unsetenv("XDG_RUNTIME_DIR"); + + /* Try to connect to the default wayland display, which should fail since + * we have neither WAYLAND_SOCKET nor XDG_RUNTIME_DIR. */ + assert(!wl_display_connect(NULL)); + + /* Check that the client log contains some mention of XDG_RUNTIME_DIR. */ + log = map_file(client_log_fd, &log_len); + assert(strstr(log, "XDG_RUNTIME_DIR")); + munmap(log, log_len); + + /* Reset the environment variables we unset for the test. The test harness + * leak checker cares about the value of WAYLAND_SOCKET during teardown for + * correct fd accounting. */ + setenv("XDG_RUNTIME_DIR", xdg_runtime_dir, 0); + setenv("WAYLAND_SOCKET", wayland_socket, 0); + free(xdg_runtime_dir); + free(wayland_socket); +} + +TEST(tc_client_log) +{ + struct display *d = display_create(); + struct client_info *ci; + char *log; + size_t log_len; + + ci = client_create_noarg(d, sanity_client_log); + display_run(d); + + /* Check that the client log contains some mention of XDG_RUNTIME_DIR. */ + log = map_file(ci->log_fd, &log_len); + assert(strstr(log, "XDG_RUNTIME_DIR")); + munmap(log, log_len); + + display_destroy(d); +} + FAIL_TEST(timeout_tst) { test_set_timeout(1); diff --git a/tests/signal-test.c b/tests/signal-test.c index 7bbaa9f..f7e1bd6 100644 --- a/tests/signal-test.c +++ b/tests/signal-test.c @@ -115,3 +115,44 @@ TEST(signal_emit_to_more_listeners) assert(3 * counter == count); } + +struct signal_emit_mutable_data { + int count; + struct wl_listener *remove_listener; +}; + +static void +signal_notify_mutable(struct wl_listener *listener, void *data) +{ + struct signal_emit_mutable_data *test_data = data; + test_data->count++; +} + +static void +signal_notify_and_remove_mutable(struct wl_listener *listener, void *data) +{ + struct signal_emit_mutable_data *test_data = data; + signal_notify_mutable(listener, test_data); + wl_list_remove(&test_data->remove_listener->link); +} + +TEST(signal_emit_mutable) +{ + struct signal_emit_mutable_data data = {0}; + + /* l2 will remove l3 before l3 is notified */ + struct wl_signal signal; + struct wl_listener l1 = {.notify = signal_notify_mutable}; + struct wl_listener l2 = {.notify = signal_notify_and_remove_mutable}; + struct wl_listener l3 = {.notify = signal_notify_mutable}; + + wl_signal_init(&signal); + wl_signal_add(&signal, &l1); + wl_signal_add(&signal, &l2); + wl_signal_add(&signal, &l3); + + data.remove_listener = &l3; + wl_signal_emit_mutable(&signal, &data); + + assert(data.count == 2); +} diff --git a/tests/socket-test.c b/tests/socket-test.c index 8d39edc..78743dc 100644 --- a/tests/socket-test.c +++ b/tests/socket-test.c @@ -51,7 +51,7 @@ static const char * require_xdg_runtime_dir(void) { char *val = getenv("XDG_RUNTIME_DIR"); - assert(val && "set $XDG_RUNTIME_DIR to run this test"); + assert(val && val[0] == '/' && "set $XDG_RUNTIME_DIR to run this test"); return val; } diff --git a/tests/test-compositor.c b/tests/test-compositor.c index 468ee56..49d76d6 100644 --- a/tests/test-compositor.c +++ b/tests/test-compositor.c @@ -40,6 +40,8 @@ #include "test-runner.h" #include "test-compositor.h" +int client_log_fd = -1; + /* --- Protocol --- */ struct test_compositor; @@ -112,7 +114,7 @@ handle_client_destroy(void *data) case CLD_DUMPED: fprintf(stderr, "Client '%s' was killed by signal %d\n", ci->name, status.si_status); - ci->exit_code = status.si_status; + ci->kill_code = status.si_status; break; case CLD_EXITED: if (status.si_status != EXIT_SUCCESS) @@ -156,8 +158,20 @@ client_destroyed(struct wl_listener *listener, void *data) } static void +client_log_handler(const char *fmt, va_list arg) +{ + va_list arg_copy; + + va_copy(arg_copy, arg); + vdprintf(client_log_fd, fmt, arg_copy); + va_end(arg_copy); + + vfprintf(stderr, fmt, arg); +} + +static void run_client(void (*client_main)(void *data), void *data, - int wayland_sock, int client_pipe) + int wayland_sock, int client_pipe, int log_fd) { char s[8]; int cur_fds; @@ -173,6 +187,10 @@ run_client(void (*client_main)(void *data), void *data, snprintf(s, sizeof s, "%d", wayland_sock); setenv("WAYLAND_SOCKET", s, 0); + /* Capture the log to the specified file descriptor. */ + client_log_fd = log_fd; + wl_log_set_handler_client(client_log_handler); + cur_fds = count_open_fds(); client_main(data); @@ -188,6 +206,18 @@ run_client(void (*client_main)(void *data), void *data, check_fd_leaks(cur_fds); } +static int +create_log_fd(void) +{ + char logname[] = "/tmp/wayland-tests-log-XXXXXX"; + int log_fd = mkstemp(logname); + + if (log_fd >= 0) + unlink(logname); + + return log_fd; +} + static struct client_info * display_create_client(struct display *d, void (*client_main)(void *data), @@ -199,11 +229,15 @@ display_create_client(struct display *d, pid_t pid; int can_continue = 0; struct client_info *cl; + int log_fd; assert(pipe(pipe_cli) == 0 && "Failed creating pipe"); assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_wayl) == 0 && "Failed creating socket pair"); + log_fd = create_log_fd(); + assert(log_fd >= 0 && "Failed to create log fd"); + pid = fork(); assert(pid != -1 && "Fork failed"); @@ -211,10 +245,11 @@ display_create_client(struct display *d, close(sock_wayl[1]); close(pipe_cli[1]); - run_client(client_main, data, sock_wayl[0], pipe_cli[0]); + run_client(client_main, data, sock_wayl[0], pipe_cli[0], log_fd); close(sock_wayl[0]); close(pipe_cli[0]); + close(log_fd); exit(0); } @@ -231,6 +266,7 @@ display_create_client(struct display *d, cl->name = name; cl->pid = pid; cl->pipe = pipe_cli[1]; + cl->log_fd = log_fd; cl->destroy_listener.notify = &client_destroyed; cl->wl_client = wl_client_create(d->wl_display, sock_wayl[1]); @@ -329,7 +365,6 @@ struct display * display_create(void) { struct display *d = NULL; - struct wl_global *g; const char *socket_name; int stat = 0; @@ -350,9 +385,10 @@ display_create(void) wl_list_init(&d->waiting_for_resume); d->wfr_num = 0; - g = wl_global_create(d->wl_display, &test_compositor_interface, - 1, d, tc_bind); - assert(g && "Creating test global failed"); + d->test_global = wl_global_create(d->wl_display, + &test_compositor_interface, + 1, d, tc_bind); + assert(d->test_global && "Creating test global failed"); return d; } @@ -389,8 +425,10 @@ display_resume(struct display *d) wl_display_run(d->wl_display); } +/* If signum is 0, expect a successful client exit, otherwise + * expect the client to have been killed by that signal. */ void -display_destroy(struct display *d) +display_destroy_expect_signal(struct display *d, int signum) { struct client_info *cl, *next; int failed = 0; @@ -401,15 +439,25 @@ display_destroy(struct display *d) wl_list_for_each_safe(cl, next, &d->clients, link) { assert(cl->wl_client == NULL); - if (cl->exit_code != 0) { + if (signum != 0 && cl->kill_code != signum) { + ++failed; + fprintf(stderr, + "Client '%s' failed, expecting signal %d, " + "got %d\n", + cl->name, signum, cl->kill_code); + } + else if (signum == 0 && + (cl->kill_code != 0 || cl->exit_code != 0)) { ++failed; fprintf(stderr, "Client '%s' failed\n", cl->name); } close(cl->pipe); + close(cl->log_fd); free(cl); } + wl_global_destroy(d->test_global); wl_display_destroy(d->wl_display); free(d); @@ -419,6 +467,12 @@ display_destroy(struct display *d) } } +void +display_destroy(struct display *d) +{ + display_destroy_expect_signal(d, 0); +} + /* * --- Client helper functions --- */ diff --git a/tests/test-compositor.h b/tests/test-compositor.h index 180dad2..3fb390c 100644 --- a/tests/test-compositor.h +++ b/tests/test-compositor.h @@ -25,6 +25,7 @@ #include <stdint.h> #include <unistd.h> +#include <stdatomic.h> #include "wayland-server.h" #include "wayland-client.h" @@ -39,13 +40,16 @@ struct client_info { int pipe; pid_t pid; int exit_code; + int kill_code; struct wl_list link; void *data; /* for arbitrary use */ + int log_fd; }; struct display { struct wl_display *wl_display; + struct wl_global *test_global; struct wl_list clients; uint32_t clients_no; @@ -64,7 +68,7 @@ struct client { struct wl_display *wl_display; struct test_compositor *tc; - int display_stopped; + atomic_bool display_stopped; }; struct client *client_connect(void); @@ -88,6 +92,7 @@ void noop_request(struct client *); */ struct display *display_create(void); void display_destroy(struct display *d); +void display_destroy_expect_signal(struct display *d, int signum); void display_run(struct display *d); /* This function posts the display_resumed event to all waiting clients, @@ -104,6 +109,9 @@ void display_post_resume_events(struct display *d); * it then reruns the display. */ void display_resume(struct display *d); +/* The file descriptor containing the client log. This is only valid in the + * test client processes. */ +extern int client_log_fd; struct client_info *client_create_with_name(struct display *d, void (*client_main)(void *data), diff --git a/tests/test-helpers.c b/tests/test-helpers.c index 20b6690..3535744 100644 --- a/tests/test-helpers.c +++ b/tests/test-helpers.c @@ -41,6 +41,27 @@ #include "test-runner.h" +#if defined(__FreeBSD__) +#include <sys/sysctl.h> + +/* + * On FreeBSD, get file descriptor information using sysctl() since that does + * not depend on a mounted fdescfs (which provides /dev/fd/N for N > 2). + */ +int +count_open_fds(void) +{ + int error; + int nfds; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_NFDS, 0 }; + size_t len; + + len = sizeof(nfds); + error = sysctl(mib, 4, &nfds, &len, NULL, 0); + assert(error == 0 && "sysctl KERN_PROC_NFDS failed."); + return nfds; +} +#else int count_open_fds(void) { @@ -48,8 +69,12 @@ count_open_fds(void) struct dirent *ent; int count = 0; - dir = opendir("/proc/self/fd"); - assert(dir && "opening /proc/self/fd failed."); + /* + * Using /dev/fd instead of /proc/self/fd should allow this code to + * work on non-Linux operating systems. + */ + dir = opendir("/dev/fd"); + assert(dir && "opening /dev/fd failed."); errno = 0; while ((ent = readdir(dir))) { @@ -58,12 +83,13 @@ count_open_fds(void) continue; count++; } - assert(errno == 0 && "reading /proc/self/fd failed."); + assert(errno == 0 && "reading /dev/fd failed."); closedir(dir); return count; } +#endif void exec_fd_leak_check(int nr_expected_fds) diff --git a/tests/test-runner.c b/tests/test-runner.c index 8f08445..d07dab1 100644 --- a/tests/test-runner.c +++ b/tests/test-runner.c @@ -22,6 +22,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "../config.h" #define _GNU_SOURCE @@ -36,11 +37,16 @@ #include <dlfcn.h> #include <errno.h> #include <limits.h> +#include <signal.h> #include <sys/ptrace.h> +#ifdef HAVE_SYS_PROCCTL_H +#include <sys/procctl.h> +#elif defined(HAVE_SYS_PRCTL_H) #include <sys/prctl.h> #ifndef PR_SET_PTRACER # define PR_SET_PTRACER 0x59616d61 #endif +#endif #include "test-runner.h" @@ -174,7 +180,7 @@ set_xdg_runtime_dir(void) xrd_env = getenv("XDG_RUNTIME_DIR"); /* if XDG_RUNTIME_DIR is not set in environ, fallback to /tmp */ assert((snprintf(xdg_runtime_dir, PATH_MAX, "%s/wayland-tests-XXXXXX", - xrd_env ? xrd_env : "/tmp") < PATH_MAX) + (xrd_env && xrd_env[0] == '/') ? xrd_env : "/tmp") < PATH_MAX) && "test error: XDG_RUNTIME_DIR too long"); assert(mkdtemp(xdg_runtime_dir) && "test error: mkdtemp failed"); @@ -194,7 +200,7 @@ static void rmdir_xdg_runtime_dir(void) { const char *xrd_env = getenv("XDG_RUNTIME_DIR"); - assert(xrd_env && "No XDG_RUNTIME_DIR set"); + assert(xrd_env && xrd_env[0] == '/' && "No XDG_RUNTIME_DIR set"); /* rmdir may fail if some test didn't do clean up */ if (rmdir(xrd_env) == -1) @@ -226,6 +232,21 @@ stderr_reset_color(void) * Returns: 1 if a debugger is confirmed present; 0 if no debugger is * present or if it can't be determined. */ +#if defined(HAVE_SYS_PROCCTL_H) && defined(PROC_TRACE_STATUS) +static int +is_debugger_attached(void) +{ + int rc; + int status; + rc = procctl(P_PID, getpid(), PROC_TRACE_STATUS, &status); + if (rc == -1) { + perror("procctl"); + return 0; + } + /* -1=tracing disabled, 0=no debugger attached, >0=pid of debugger. */ + return status > 0; +} +#elif defined(HAVE_SYS_PRCTL_H) static int is_debugger_attached(void) { @@ -287,6 +308,7 @@ is_debugger_attached(void) return rc; } +#endif int main(int argc, char *argv[]) { |